B2-1..B2-9: distill minor code behaviors into specs (post/project/template/media_processing/generation/dashboard)
This commit is contained in:
24
SPECGAPS.md
24
SPECGAPS.md
@@ -83,17 +83,17 @@ Gap categories: **SC** = spec correct, fix code | **CS** = code correct, update
|
|||||||
|
|
||||||
### B2. Lower Priority (implementation detail or minor)
|
### B2. Lower Priority (implementation detail or minor)
|
||||||
|
|
||||||
| ID | Behavior | Code Location |
|
| ID | Behavior | Code Location | Resolution |
|
||||||
|---|---|---|
|
|---|---|---|---|
|
||||||
| B2-1 | `editor_body/1` content resolver | `lib/bds/posts.ex:229-252` |
|
| ~~B2-1~~ | ~~`editor_body/1` content resolver~~ | `lib/bds/posts.ex:234-256` | **Resolved:** added `editor_body` derived field to the Post entity in post.allium (prefer DB draft content, else read markdown body from file, else empty; same for translations) |
|
||||||
| B2-2 | `sync_post_from_file/1` single-post reimport | `lib/bds/posts.ex:254-279` |
|
| ~~B2-2~~ | ~~`sync_post_from_file/1` single-post reimport~~ | `lib/bds/posts.ex:259` | **Resolved:** added `SyncPostFromFileRequested` surface event + `SyncPostFromFile` rule (re-read own .md file, upsert DB, re-sync links) to post.allium |
|
||||||
| B2-3 | `import_orphan_post_file/1` | `lib/bds/posts.ex:289-291` |
|
| ~~B2-3~~ | ~~`import_orphan_post_file/1`~~ | `lib/bds/posts.ex:293` | **Resolved:** added `ImportOrphanPostFileRequested` surface event + `ImportOrphanPostFile` rule (import a disk .md with no DB row, reject non-markdown) to post.allium |
|
||||||
| B2-4 | `dashboard_stats/1`, `post_counts_by_year_month/1` | `lib/bds/posts.ex:378-413` |
|
| ~~B2-4~~ | ~~`dashboard_stats/1`, `post_counts_by_year_month/1`~~ | `lib/bds/posts.ex:416-450` | **Resolved:** added `ComputeDashboardData` rule to editor_misc.allium (status-grouped counts, (year,month) timeline newest-first limited to recent months, plus clouds/recent) |
|
||||||
| B2-5 | `regenerate_missing_thumbnails/2` | `lib/bds/media.ex:47-48` |
|
| ~~B2-5~~ | ~~`regenerate_missing_thumbnails/2`~~ | `lib/bds/media/thumbnails.ex:51` | **Resolved:** added `RegenerateMissingThumbnailsRequested` surface event + `RegenerateMissingThumbnails` rule (raster images excl. SVG, regenerate only missing files, background task w/ counts) to media_processing.allium |
|
||||||
| B2-6 | Cache dir computation | `lib/bds/projects.ex:101-106` |
|
| ~~B2-6~~ | ~~Cache dir computation~~ | `lib/bds/projects.ex:142-147` | **Resolved:** added `cache_dir` derived field (`private_dir/projects/{id}`, :project_cache_root override) to the Project entity in project.allium |
|
||||||
| B2-7 | `remove_stale_published_templates` | `lib/bds/templates.ex:524-552` |
|
| ~~B2-7~~ | ~~`remove_stale_published_templates`~~ | `lib/bds/templates.ex:561` | **Resolved:** extended RebuildTemplatesFromFiles in template.allium to prune published templates whose file is neither scanned nor on disk (clearing references first) |
|
||||||
| B2-8 | Rendering Labels module (30+ i18n strings) | `lib/bds/rendering/labels.ex` |
|
| ~~B2-8~~ | ~~Rendering Labels module (30+ i18n strings)~~ | `lib/bds/rendering/labels.ex` | **Resolved:** captured as `RenderLabels` value + `LabelsUseContentLanguage` invariant in rendering.allium (with B1-6) — content-language gettext strings + month names |
|
||||||
| B2-9 | Progress reporting during reindex | `lib/bds/generation/progress.ex` |
|
| ~~B2-9~~ | ~~Progress reporting during reindex~~ | `lib/bds/generation/progress.ex` | **Resolved:** added `GenerationProgressReported` runtime event + `ProgressReporting` guarantee to generation.allium (count-based + phased fractions, via task progress channel) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -197,5 +197,5 @@ All reconciled to follow code. Specs must be self-consistent and match code.
|
|||||||
5. **A2-1 through A2-17** — spec drift (code is normative, update spec)
|
5. **A2-1 through A2-17** — spec drift (code is normative, update spec)
|
||||||
6. **D2-1 through D2-17** — untested rules
|
6. **D2-1 through D2-17** — untested rules
|
||||||
7. **D3-1 through D3-11** — partial test coverage
|
7. **D3-1 through D3-11** — partial test coverage
|
||||||
8. **B1-7 through B1-20** — minor code behaviors missing from spec
|
8. ~~**B2-1 through B2-9**~~ — all resolved: editor_body resolver, single-post reimport, orphan import, dashboard data, missing-thumbnail regen, cache dir, stale-template prune, render labels, generation progress reporting
|
||||||
9. **D4-1 through D4-7** — UI test coverage
|
9. **D4-1 through D4-7** — UI test coverage
|
||||||
|
|||||||
@@ -148,6 +148,16 @@ surface DashboardSurface {
|
|||||||
-- Single-click: preview tab. Double-click: pin tab.
|
-- Single-click: preview tab. Double-click: pin tab.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rule ComputeDashboardData {
|
||||||
|
when: DashboardRequested(project)
|
||||||
|
-- stats: post counts grouped by status (total = sum of all statuses),
|
||||||
|
-- plus media/image counts, total media size, tag and category counts.
|
||||||
|
-- timeline: posts grouped by (year, month) of created_at, newest first,
|
||||||
|
-- limited to the most recent config.dashboard_timeline_months with data.
|
||||||
|
-- tag_cloud / category_cloud / recent_posts populated per their guarantees.
|
||||||
|
ensures: Dashboard
|
||||||
|
}
|
||||||
|
|
||||||
-- ─── Menu editor view ────────────────────────────────────────
|
-- ─── Menu editor view ────────────────────────────────────────
|
||||||
|
|
||||||
-- Visual editor for the OPML navigation menu (meta/menu.opml).
|
-- Visual editor for the OPML navigation menu (meta/menu.opml).
|
||||||
|
|||||||
@@ -24,7 +24,15 @@ surface GenerationRuntimeSurface {
|
|||||||
|
|
||||||
provides:
|
provides:
|
||||||
PageRenderRequested(template, context)
|
PageRenderRequested(template, context)
|
||||||
|
GenerationProgressReported(current, total, label)
|
||||||
GenerateSiteCompleted(generation)
|
GenerateSiteCompleted(generation)
|
||||||
|
|
||||||
|
@guarantee ProgressReporting
|
||||||
|
-- Generation, reindex, and site-validation run as background tasks and
|
||||||
|
-- emit count-based progress (current/total + label, e.g. "Processing
|
||||||
|
-- N of M posts") via the task progress channel (see task.allium
|
||||||
|
-- ReportProgress / ProgressThrottled). Multi-phase work (validation)
|
||||||
|
-- maps each phase onto a fixed fraction of the 0.0..1.0 bar.
|
||||||
}
|
}
|
||||||
|
|
||||||
value GenerationSection {
|
value GenerationSection {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ surface MediaProcessingControlSurface {
|
|||||||
TagMediaRequested(media, tags)
|
TagMediaRequested(media, tags)
|
||||||
DeleteMediaRequested(media)
|
DeleteMediaRequested(media)
|
||||||
ValidateMediaRequested(project)
|
ValidateMediaRequested(project)
|
||||||
|
RegenerateMissingThumbnailsRequested(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
surface MediaProcessingRuntimeSurface {
|
surface MediaProcessingRuntimeSurface {
|
||||||
@@ -131,6 +132,18 @@ rule GenerateThumbnails {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rule RegenerateMissingThumbnails {
|
||||||
|
when: RegenerateMissingThumbnailsRequested(project)
|
||||||
|
-- Maintenance sweep over the project's raster images (images, excluding SVG),
|
||||||
|
-- oldest first. For each, any thumbnail file absent from disk is regenerated
|
||||||
|
-- from the original binary; images with a full set are skipped.
|
||||||
|
-- Runs as a background task reporting progress (current/total).
|
||||||
|
-- Returns counts: processed, generated, failed.
|
||||||
|
for media in project.media where is_image(media.mime_type) and not is_svg(media.mime_type):
|
||||||
|
if any_thumbnail_missing(media):
|
||||||
|
ensures: ThumbnailsRegenerated(media)
|
||||||
|
}
|
||||||
|
|
||||||
-- Thumbnail generation algorithm
|
-- Thumbnail generation algorithm
|
||||||
value ThumbnailGeneration {
|
value ThumbnailGeneration {
|
||||||
-- 1. Load source image
|
-- 1. Load source image
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ surface PostControlSurface {
|
|||||||
DeletePostRequested(post)
|
DeletePostRequested(post)
|
||||||
ArchivePostRequested(post)
|
ArchivePostRequested(post)
|
||||||
DiscardPostChangesRequested(post)
|
DiscardPostChangesRequested(post)
|
||||||
|
SyncPostFromFileRequested(post)
|
||||||
|
ImportOrphanPostFileRequested(project, relative_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
surface PostFilePathSurface {
|
surface PostFilePathSurface {
|
||||||
@@ -122,6 +124,10 @@ entity Post {
|
|||||||
-- Slug changes only allowed before first publish
|
-- Slug changes only allowed before first publish
|
||||||
content_location: if status = published: file_path else: content
|
content_location: if status = published: file_path else: content
|
||||||
-- Published: body in filesystem. Draft: body in DB field.
|
-- Published: body in filesystem. Draft: body in DB field.
|
||||||
|
editor_body: if content != null: content else: read_markdown_body(file_path)
|
||||||
|
-- Resolver used by editors: prefer the in-DB draft content, else read
|
||||||
|
-- the markdown body from the post's file. Empty string when neither
|
||||||
|
-- exists. The same resolver applies to PostTranslation records.
|
||||||
|
|
||||||
transitions status {
|
transitions status {
|
||||||
draft -> published
|
draft -> published
|
||||||
@@ -228,6 +234,25 @@ rule ArchivePost {
|
|||||||
ensures: post.status = archived
|
ensures: post.status = archived
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rule SyncPostFromFile {
|
||||||
|
when: SyncPostFromFileRequested(post)
|
||||||
|
requires: post.file_path != ""
|
||||||
|
-- Single-post reimport: re-reads the post's own .md file and upserts the DB
|
||||||
|
-- record from it (the filesystem is treated as truth for that one post).
|
||||||
|
-- Re-syncs the post's link graph. Errors out if the file is missing.
|
||||||
|
ensures: PostFieldsUpdated(post, parse_post_file(post.file_path))
|
||||||
|
ensures: PostLinksUpdated(post)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule ImportOrphanPostFile {
|
||||||
|
when: ImportOrphanPostFileRequested(project, relative_path)
|
||||||
|
-- Imports a .md file that exists on disk but has no DB record (an orphan,
|
||||||
|
-- e.g. surfaced by RunMetadataDiff). Rejects non-markdown / missing files.
|
||||||
|
ensures:
|
||||||
|
let new_post = Post.created(parse_post_file(relative_path))
|
||||||
|
SearchIndexUpdated(new_post)
|
||||||
|
}
|
||||||
|
|
||||||
rule DiscardPostChanges {
|
rule DiscardPostChanges {
|
||||||
when: DiscardPostChangesRequested(post)
|
when: DiscardPostChangesRequested(post)
|
||||||
requires: post.file_path != ""
|
requires: post.file_path != ""
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ entity Project {
|
|||||||
-- Linux: $XDG_CONFIG_HOME/bds (default ~/.config/bds)
|
-- Linux: $XDG_CONFIG_HOME/bds (default ~/.config/bds)
|
||||||
-- Windows: %APPDATA%\\bds
|
-- Windows: %APPDATA%\\bds
|
||||||
-- See PrivateArtifactsLiveInOsAppDir.
|
-- See PrivateArtifactsLiveInOsAppDir.
|
||||||
|
cache_dir: private_dir + "/projects/" + id
|
||||||
|
-- Per-project subtree of private_dir for that project's regenerable
|
||||||
|
-- artifacts (embeddings index + sidecar). Overridable in tests via the
|
||||||
|
-- :project_cache_root setting; never the repo or the project folder.
|
||||||
}
|
}
|
||||||
|
|
||||||
surface ProjectSurface {
|
surface ProjectSurface {
|
||||||
|
|||||||
@@ -175,6 +175,12 @@ rule RebuildTemplatesFromFiles {
|
|||||||
let parsed = parse_template_file(file)
|
let parsed = parse_template_file(file)
|
||||||
ensures: Template.created(parsed)
|
ensures: Template.created(parsed)
|
||||||
-- or updated if slug already exists
|
-- or updated if slug already exists
|
||||||
|
-- Prune stale published templates: a published Template whose file_path is
|
||||||
|
-- neither among the scanned files nor present on disk is deleted, clearing
|
||||||
|
-- its references first (posts' template_slug, tags' post_template_slug).
|
||||||
|
for template in project.templates where status = published and file_path != "":
|
||||||
|
if template.file_path not in scanned_files and not file_exists(template.file_path):
|
||||||
|
ensures: not exists template
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Exact Liquid subset required (distilled from bundled starter templates)
|
-- Exact Liquid subset required (distilled from bundled starter templates)
|
||||||
|
|||||||
Reference in New Issue
Block a user