117 lines
4.6 KiB
Plaintext
117 lines
4.6 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)
|
|
RebuildFromFilesystemRequested(project, entity_type)
|
|
RepairMetadataDiffItemRequested(project, direction, item)
|
|
}
|
|
|
|
value DiffField {
|
|
name: String
|
|
db_value: String
|
|
file_value: String
|
|
}
|
|
|
|
value DiffReport {
|
|
entity_type: String -- 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:
|
|
-- posts, translations, media, scripts, templates
|
|
-- Detected fields: tags, categories, title, excerpt, author,
|
|
-- language, status, templateSlug, dates
|
|
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 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)
|
|
}
|