151 lines
6.5 KiB
Plaintext
151 lines
6.5 KiB
Plaintext
-- allium: 1
|
|
-- bDS Metadata Diff and Rebuild
|
|
-- Scope: core (Wave 1)
|
|
-- Distilled from: src/main/engine/MetadataDiffEngine.ts
|
|
|
|
use "./post.allium" as post
|
|
use "./media.allium" as media
|
|
use "./script.allium" as script
|
|
use "./template.allium" as template
|
|
|
|
surface MetadataMaintenanceSurface {
|
|
facing _: MaintenanceOperator
|
|
|
|
provides:
|
|
MetadataDiffRequested(project)
|
|
RebuildDatabaseRequested(project)
|
|
RebuildFromFilesystemRequested(project, entity_type)
|
|
RepairMetadataDiffItemRequested(project, direction, item)
|
|
}
|
|
|
|
value DiffField {
|
|
name: String
|
|
db_value: String
|
|
file_value: String
|
|
}
|
|
|
|
value DiffReport {
|
|
entity_type: String -- project, categories, category_meta, publishing,
|
|
-- post, post_translation, media, media_translation,
|
|
-- script, template, embedding
|
|
entity_id: String
|
|
differences: List<DiffField>
|
|
}
|
|
|
|
value OrphanReport {
|
|
file_path: String
|
|
-- File exists on disk but has no DB record
|
|
}
|
|
|
|
rule RunMetadataDiff {
|
|
when: MetadataDiffRequested(project)
|
|
-- Runs as background task via TaskManager
|
|
-- Compares DB records against filesystem files for:
|
|
-- project metadata (including a missing description), posts,
|
|
-- translations, media, Lua scripts, templates
|
|
-- Detected fields: tags, categories, title, excerpt, author,
|
|
-- language, status, templateSlug, dates
|
|
-- Portable project metadata has a project-scoped database snapshot in the
|
|
-- settings table. Compare its project, category list, per-category settings,
|
|
-- and publishing preferences against the matching meta/*.json files.
|
|
for metadata_type in {"project", "categories", "category_meta", "publishing"}:
|
|
let db_metadata = ProjectMetadataSetting(project.id, metadata_type)
|
|
let file_metadata = ReadProjectMetadataFile(project, metadata_type)
|
|
let metadata_diffs = compare_fields(db_metadata, file_metadata)
|
|
if metadata_diffs.count > 0:
|
|
ensures:
|
|
DiffReport.created(
|
|
entity_type: metadata_type,
|
|
entity_id: project.id,
|
|
differences: metadata_diffs
|
|
)
|
|
for post in project.posts:
|
|
let file_data = parse_post_file(post.file_path)
|
|
let diffs = compare_fields(post, file_data)
|
|
if diffs.count > 0:
|
|
ensures: DiffReport.created(entity_type: "post", entity_id: post.id, differences: diffs)
|
|
|
|
-- Translation files only carry language-specific metadata. Shared status and
|
|
-- timestamp fields come from the canonical post and must not be reported as
|
|
-- missing when they are absent from the translation file.
|
|
for translation in project.post_translations:
|
|
let translation_file_data = parse_post_file(translation.file_path)
|
|
let translation_diffs = compare_translation_specific_fields(translation, translation_file_data)
|
|
if translation_diffs.count > 0:
|
|
ensures:
|
|
DiffReport.created(
|
|
entity_type: "post_translation",
|
|
entity_id: translation.id,
|
|
differences: translation_diffs
|
|
)
|
|
|
|
-- Detect orphan files (on disk but not in DB)
|
|
for file in scan_directory(project.public_dir + "/posts", "*.md"):
|
|
let matching = Posts where file_path = file
|
|
if matching.count = 0:
|
|
ensures: OrphanReport.created(file_path: file)
|
|
|
|
-- Same pattern for media sidecars (media), media translation sidecars
|
|
-- (media_translation), scripts, templates, and embeddings.
|
|
-- Embedding diffs compare the stored content_hash against the live post
|
|
-- content to detect vectors that need recomputation.
|
|
}
|
|
|
|
rule RebuildDatabase {
|
|
when: RebuildDatabaseRequested(project)
|
|
-- The filesystem is the complete source of truth for portable project data.
|
|
-- Existing project-scoped content and relationship rows are removed before
|
|
-- project.json, posts, translations, media sidecars and links, tags,
|
|
-- templates, and Lua scripts are imported. Python files in scripts/ are ignored.
|
|
ensures: ProjectMetadataImported(project, "meta/project.json")
|
|
ensures: ProjectMetadataSettingsImported(project, "meta/*.json")
|
|
ensures: post/RebuildPostsFromFiles(project)
|
|
ensures: media/RebuildMediaFromFiles(project)
|
|
ensures: script/RebuildScriptsFromFiles(project)
|
|
ensures: template/RebuildTemplatesFromFiles(project)
|
|
@guidance
|
|
-- Rebuilding media trusts persisted sidecar size, dimensions, and metadata.
|
|
-- Thumbnail regeneration remains a separate maintenance operation.
|
|
}
|
|
|
|
rule RepairMetadataDiffItem {
|
|
when: RepairMetadataDiffItemRequested(project, direction, item)
|
|
-- Resolves a single diff in one direction.
|
|
-- direction = file_to_db (filesystem wins) | db_to_file (database wins)
|
|
-- Dispatched per item.entity_type:
|
|
-- project | categories | category_meta | publishing -> project metadata sync/flush
|
|
-- post -> sync_post_from_file / rewrite_published_post
|
|
-- post_translation -> sync_post_translation_from_file / rewrite_published_post_translation
|
|
-- media -> sync_media_from_sidecar / sync_media_sidecar
|
|
-- media_translation -> sync_media_translation_from_sidecar / sync_media_translation_sidecar
|
|
-- script -> sync_script_from_file / sync_published_script_file
|
|
-- template -> sync_template_from_file / sync_published_template_file
|
|
-- embedding -> sync_post (file_to_db) / refresh_snapshot (db_to_file)
|
|
-- Unknown entity_type or direction -> unsupported error.
|
|
}
|
|
|
|
rule RebuildFromFilesystem {
|
|
when: RebuildFromFilesystemRequested(project, entity_type)
|
|
-- The inverse of metadata diff: filesystem is treated as truth
|
|
-- Reads all files and upserts into DB
|
|
ensures:
|
|
if entity_type = "post":
|
|
post/RebuildPostsFromFiles(project)
|
|
if entity_type = "media":
|
|
media/RebuildMediaFromFiles(project)
|
|
if entity_type = "script":
|
|
script/RebuildScriptsFromFiles(project)
|
|
if entity_type = "template":
|
|
template/RebuildTemplatesFromFiles(project)
|
|
}
|
|
|
|
invariant ThreeWaySync {
|
|
-- Metadata must stay in sync across three representations:
|
|
-- 1. Database records
|
|
-- 2. Filesystem files (frontmatter/sidecars)
|
|
-- 3. Generated site output
|
|
-- MetadataDiff detects divergence between (1) and (2)
|
|
-- Rebuild resolves divergence by treating (2) as truth
|
|
-- Site generation consumes (1) to produce (3)
|
|
}
|