fix: A1-15 add PreviewDraftOverlay and GenerationPublishedOnly invariants to specs
This commit is contained in:
@@ -24,6 +24,7 @@ Gap categories: **SC** = spec correct, fix code | **CS** = code correct, update
|
|||||||
| A1-12 | Real Pagefind integration for search | generation.allium:208 | Stub only: `pagefind-ui.js` is one-liner, `PagefindUI` never defined, search-runtime.js silently bails, client-side search non-functional | Fix code: bundle real Pagefind, build proper fragment index, wire PagefindUI |
|
| A1-12 | Real Pagefind integration for search | generation.allium:208 | Stub only: `pagefind-ui.js` is one-liner, `PagefindUI` never defined, search-runtime.js silently bails, client-side search non-functional | Fix code: bundle real Pagefind, build proper fragment index, wire PagefindUI |
|
||||||
| A1-13 | Git sidebar shows only "Working tree" placeholder | sidebar_views.allium:651-770 | `sidebar.ex:782-798` returns single entity_list item; `BDS.Git` has full status/diff/commit/history/fetch/pull/push/prune_lfs but sidebar doesn't use it | Fix code: wire sidebar `git_view/0` to `BDS.Git` — render branch, ahead/behind, status file list, commit input, history entries, action buttons per spec |
|
| A1-13 | Git sidebar shows only "Working tree" placeholder | sidebar_views.allium:651-770 | `sidebar.ex:782-798` returns single entity_list item; `BDS.Git` has full status/diff/commit/history/fetch/pull/push/prune_lfs but sidebar doesn't use it | Fix code: wire sidebar `git_view/0` to `BDS.Git` — render branch, ahead/behind, status file list, commit input, history entries, action buttons per spec |
|
||||||
| A1-14 | Embedding uses TF-IDF hash projection instead of real neural model | embedding.allium:44-53, invariants ModelCaching/VectorCacheInDb | `backends/in_app.ex` hashes terms into sparse vectors via `:erlang.phash2`; no ONNX model, no `"query: "` prefix, no mean pooling, vectors stored as JSON text not Float32Array BLOB, snapshot-based neighbor lookup instead of USearch HNSW index | Fix code: (1) add Bumblebee + ONNX runtime deps to run `Xenova/multilingual-e5-small`, (2) implement lazy model download + cache in app data dir, (3) `"query: "` prefix + mean pooling + L2 norm in backend, (4) store vectors as binary BLOB (1536 bytes), (5) replace JSON snapshot with USearch HNSW index (cosine, M=16, ef=128/64, 5s debounce), (6) cross-language semantic similarity must work |
|
| A1-14 | Embedding uses TF-IDF hash projection instead of real neural model | embedding.allium:44-53, invariants ModelCaching/VectorCacheInDb | `backends/in_app.ex` hashes terms into sparse vectors via `:erlang.phash2`; no ONNX model, no `"query: "` prefix, no mean pooling, vectors stored as JSON text not Float32Array BLOB, snapshot-based neighbor lookup instead of USearch HNSW index | Fix code: (1) add Bumblebee + ONNX runtime deps to run `Xenova/multilingual-e5-small`, (2) implement lazy model download + cache in app data dir, (3) `"query: "` prefix + mean pooling + L2 norm in backend, (4) store vectors as binary BLOB (1536 bytes), (5) replace JSON snapshot with USearch HNSW index (cosine, M=16, ef=128/64, 5s debounce), (6) cross-language semantic similarity must work |
|
||||||
|
| A1-15 | ~~Preview vs generation content source strategy undocumented~~ | preview.allium (no invariant), generation.allium (no invariant) | Generation uses only published .md file content (`Generation.Data` snapshots set `content: nil`); preview includes published+draft posts and prefers DB content over file (`Preview.Router` queries `:published`/`:draft`, uses `editor_body`) | **Resolved:** added `PreviewDraftOverlay` invariant to preview.allium and `GenerationPublishedOnly` invariant to generation.allium; both cross-reference each other; code already correct, 3 tests added for draft-in-preview behavior |
|
||||||
|
|
||||||
### A2. Spec Should Update (code is normative)
|
### A2. Spec Should Update (code is normative)
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,23 @@ surface GenerationStatusSurface {
|
|||||||
generation.generated_files.count
|
generation.generated_files.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invariant GenerationPublishedOnly {
|
||||||
|
-- Generation renders the *published* state of the blog, never draft content.
|
||||||
|
--
|
||||||
|
-- Post universe: posts that have a published .md file on disk.
|
||||||
|
-- This includes status=published posts and status=draft posts that were
|
||||||
|
-- previously published (they still have a file_path with last-published content).
|
||||||
|
-- Posts that have never been published (no file_path) are excluded entirely.
|
||||||
|
--
|
||||||
|
-- Content source: always the .md file on disk (the last-published snapshot).
|
||||||
|
-- The DB content field (which holds draft edits) is never read during generation.
|
||||||
|
-- Snapshots set content=nil to ensure file-based resolution.
|
||||||
|
--
|
||||||
|
-- Contrast with preview (see preview.allium PreviewDraftOverlay):
|
||||||
|
-- Preview includes all drafts and prefers DB content over file content,
|
||||||
|
-- giving the author a live view of unpublished edits.
|
||||||
|
}
|
||||||
|
|
||||||
invariant IncrementalByContentHash {
|
invariant IncrementalByContentHash {
|
||||||
-- Files are only written when content_hash changes
|
-- Files are only written when content_hash changes
|
||||||
-- generatedFileHashes table tracks (projectId, relativePath, contentHash)
|
-- generatedFileHashes table tracks (projectId, relativePath, contentHash)
|
||||||
|
|||||||
@@ -49,18 +49,24 @@ rule StopPreview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- Route resolution
|
-- Route resolution
|
||||||
|
-- Preview renders all posts (published + draft) on-demand via Liquid templates.
|
||||||
|
-- Content priority: DB content (draft edits) over published .md file content.
|
||||||
|
-- See invariant PreviewDraftOverlay below.
|
||||||
|
|
||||||
rule ServePostPreview {
|
rule ServePostPreview {
|
||||||
when: PreviewRequest(path)
|
when: PreviewRequest(path)
|
||||||
requires: is_post_path(path)
|
requires: is_post_path(path)
|
||||||
-- path matches "/{yyyy}/{mm}/{dd}/{slug}"
|
-- path matches "/{yyyy}/{mm}/{dd}/{slug}"
|
||||||
-- Renders post via Liquid template with full PageRenderer context
|
-- Finds post by slug+date regardless of status (published or draft).
|
||||||
|
-- Content resolved via editor_body: DB content if present, else .md file.
|
||||||
|
-- Renders via Liquid template with full PageRenderer context.
|
||||||
ensures: PreviewResponse(rendered_html)
|
ensures: PreviewResponse(rendered_html)
|
||||||
}
|
}
|
||||||
|
|
||||||
rule ServeDraftPreview {
|
rule ServeDraftPreview {
|
||||||
when: PreviewDraftRequest(path, post_id)
|
when: PreviewDraftRequest(path, post_id)
|
||||||
-- Renders draft content (from DB, not filesystem)
|
-- Explicit draft preview by post_id (used by editor preview pane).
|
||||||
|
-- Renders draft content (from DB, not filesystem).
|
||||||
ensures: PreviewResponse(rendered_html)
|
ensures: PreviewResponse(rendered_html)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +74,7 @@ rule ServeArchivePreview {
|
|||||||
when: PreviewRequest(path)
|
when: PreviewRequest(path)
|
||||||
requires: is_archive_path(path)
|
requires: is_archive_path(path)
|
||||||
-- Category, tag, date archives with pagination
|
-- Category, tag, date archives with pagination
|
||||||
|
-- Includes both published and draft posts in listings.
|
||||||
ensures: PreviewResponse(rendered_html)
|
ensures: PreviewResponse(rendered_html)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +99,28 @@ rule ServeLanguagePrefixedRoute {
|
|||||||
ensures: PreviewResponse(translated_html)
|
ensures: PreviewResponse(translated_html)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invariant PreviewDraftOverlay {
|
||||||
|
-- Preview is the draft workspace: it shows what the blog *will* look like,
|
||||||
|
-- not what it currently looks like on the published site.
|
||||||
|
--
|
||||||
|
-- Post universe: all posts with status in {published, draft}.
|
||||||
|
-- Archived posts are excluded.
|
||||||
|
--
|
||||||
|
-- Content priority (per post):
|
||||||
|
-- 1. DB content field (draft edits not yet published) → used when non-nil
|
||||||
|
-- 2. Published .md file (last-published snapshot) → used when DB content is nil
|
||||||
|
-- 3. Empty string → fallback if neither exists
|
||||||
|
--
|
||||||
|
-- This means:
|
||||||
|
-- - A purely draft post (never published) renders from DB content.
|
||||||
|
-- - A published-then-edited post renders from DB content (the draft edits).
|
||||||
|
-- - A published post with no pending edits renders from its .md file.
|
||||||
|
--
|
||||||
|
-- Contrast with generation (see generation.allium GenerationPublishedOnly):
|
||||||
|
-- Generation uses *only* published .md file content, never DB draft content,
|
||||||
|
-- and excludes posts that have never been published.
|
||||||
|
}
|
||||||
|
|
||||||
invariant ThemeSwitching {
|
invariant ThemeSwitching {
|
||||||
-- Preview supports live theme/mode switching via query params
|
-- Preview supports live theme/mode switching via query params
|
||||||
-- ?theme=amber&mode=dark etc.
|
-- ?theme=amber&mode=dark etc.
|
||||||
|
|||||||
Reference in New Issue
Block a user