initial commit

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-23 10:42:27 +02:00
commit cd998f24a9
57 changed files with 9751 additions and 0 deletions

198
specs/media.allium Normal file
View File

@@ -0,0 +1,198 @@
-- allium: 1
-- bDS Media Lifecycle
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/MediaEngine.ts, schema.ts
use "./project.allium" as project
surface MediaControlSurface {
facing _: MediaOperator
provides:
ImportMediaRequested(project, source_file)
UpdateMediaRequested(media, changes)
DeleteMediaRequested(media)
UpsertMediaTranslationRequested(media, language, title, alt, caption)
RebuildMediaFromFilesRequested(project)
}
value ThumbnailSet {
small: String -- 150px width (binary path)
medium: String -- 400px width (binary path)
large: String -- 800px width (binary path)
ai: String -- 448x448 JPEG for vision models (binary path)
}
value SidecarFile {
-- {media_file}.meta (YAML-like key-value format)
-- Fields: title, alt, caption, author, tags, language, linkedPostIds
-- Translations: {media_file}.{lang}.meta
path: String
}
surface SidecarFileSurface {
context sidecar: SidecarFile
exposes:
sidecar.path
}
entity Media {
project: project/Project
filename: String
original_name: String
mime_type: String
size: Integer
width: Integer?
height: Integer?
title: String?
alt: String?
caption: String?
author: String?
language: String?
file_path: String
sidecar_path: String
checksum: String?
tags: List<String>
created_at: Timestamp
updated_at: Timestamp
-- Relationships
translations: MediaTranslation with media = this
linked_posts: PostMediaLink with media_id = this.id
-- Derived
available_languages: translations -> language
thumbnails: ThumbnailSet
}
surface MediaSurface {
context media: Media
exposes:
media.project
media.filename
media.original_name
media.mime_type
media.size
media.width when media.width != null
media.height when media.height != null
media.title when media.title != null
media.alt when media.alt != null
media.caption when media.caption != null
media.author when media.author != null
media.language when media.language != null
media.file_path
media.sidecar_path
media.checksum when media.checksum != null
media.tags
media.created_at
media.updated_at
media.translations.count
media.linked_posts.count
media.available_languages
media.thumbnails.small
media.thumbnails.medium
media.thumbnails.large
media.thumbnails.ai
}
entity MediaTranslation {
media: Media
language: String
title: String?
alt: String?
caption: String?
}
invariant UniqueMediaTranslation {
for a in MediaTranslations:
for b in MediaTranslations:
(a != b and a.media = b.media) implies a.language != b.language
}
invariant DateBasedMediaLayout {
for m in Media:
m.file_path = format("media/{yyyy}/{mm}/{uuid}.{ext}",
yyyy: m.created_at.year,
mm: m.created_at.month_padded,
uuid: stem(m.filename),
ext: extension(m.filename))
}
rule ImportMedia {
when: ImportMediaRequested(project, source_file)
let uuid_name = generate_uuid() + extension(source_file)
let dest = format("media/{yyyy}/{mm}/{uuid_name}",
yyyy: now.year, mm: now.month_padded)
ensures: Media.created(
project: project,
filename: uuid_name,
original_name: source_file.name,
mime_type: detect_mime(source_file),
size: source_file.size,
width: detect_width(source_file),
height: detect_height(source_file),
file_path: dest,
tags: {}
)
ensures: FileCopied(source_file, dest)
ensures: SidecarWritten(media)
ensures: ThumbnailsGenerated(media)
ensures: SearchIndexUpdated(media)
}
rule UpdateMedia {
when: UpdateMediaRequested(media, changes)
ensures: MediaFieldsUpdated(media, changes)
ensures: media.updated_at = now
ensures: SidecarWritten(media)
-- Metadata changes flush to .meta sidecar
ensures: SearchIndexUpdated(media)
}
rule DeleteMedia {
when: DeleteMediaRequested(media)
ensures: not exists media
ensures: MediaFileDeleted(media)
ensures: SidecarDeleted(media)
ensures: ThumbnailsDeleted(media)
ensures:
for t in media.translations:
not exists t
ensures: SearchIndexUpdated(media)
}
rule UpsertMediaTranslation {
when: UpsertMediaTranslationRequested(media, language, title, alt, caption)
ensures: MediaTranslation.created(
media: media,
language: language,
title: title,
alt: alt,
caption: caption
)
ensures: TranslationSidecarWritten(media, language)
-- Writes {file}.{lang}.meta
}
rule RebuildMediaFromFiles {
when: RebuildMediaFromFilesRequested(project)
-- Scans media directory for .meta sidecars, reimports to DB
for sidecar in scan_directory(project.effective_data_dir + "/media", "*.meta"):
let parsed = parse_sidecar(sidecar)
ensures: Media.created(parsed)
-- or updated if already exists
@guidance
-- This is the filesystem-to-DB reconciliation path
-- Used after git pull or manual file changes
}
invariant SidecarRoundtrip {
-- Sidecar files faithfully represent DB metadata
for m in Media:
parse_sidecar(m.sidecar_path).title = m.title
parse_sidecar(m.sidecar_path).alt = m.alt
parse_sidecar(m.sidecar_path).caption = m.caption
parse_sidecar(m.sidecar_path).tags = m.tags
}