-- allium: 1 -- bDS Project Metadata, Categories, Publishing Preferences -- Scope: core (Wave 1) -- Distilled from: src/main/engine/MetaEngine.ts, schema.ts use "./project.allium" as project surface MetadataControlSurface { facing _: MetadataOperator provides: UpdateProjectMetadataRequested(project, changes) AddCategoryRequested(project, name) RemoveCategoryRequested(project, name) UpdateCategorySettingsRequested(project, category, settings) SetPublishingPreferencesRequested(project, prefs) AppStarted(project) } surface PublishingPreferencesSurface { context prefs: PublishingPreferences exposes: prefs.ssh_host when prefs.ssh_host != null prefs.ssh_user when prefs.ssh_user != null prefs.ssh_remote_path when prefs.ssh_remote_path != null prefs.ssh_mode } value CategoryRenderSettings { render_in_lists: Boolean show_title: Boolean post_template_slug: String? list_template_slug: String? } entity ProjectMetadata { project: project/Project name: String description: String? public_url: String? main_language: String? -- ISO 639-1 default_author: String? max_posts_per_page: Integer -- 1..500, default 50 blogmark_category: String? pico_theme: String? -- 12+ named Pico CSS themes semantic_similarity_enabled: Boolean blog_languages: Set -- subset of supported languages categories: Set -- category names category_settings: Set } entity PublishingPreferences { ssh_host: String? ssh_user: String? ssh_remote_path: String? ssh_mode: scp | rsync } invariant DefaultCategories { -- New projects start with: article, picture, aside, page -- These are defaults, not invariants — user can remove them } invariant MetadataPersistedAsFiles { -- Four separate JSON files in meta/: -- meta/project.json — name, description, publicUrl, mainLanguage, etc. -- meta/categories.json — sorted category list -- meta/category-meta.json — per-category render settings -- meta/publishing.json — SSH connection details (non-secret) -- All writes are atomic (temp file + rename) } config { default_max_posts_per_page: Integer = 50 min_posts_per_page: Integer = 1 max_posts_per_page: Integer = 500 default_categories: Set = {"article", "picture", "aside", "page"} supported_pico_themes: Set = { "default", "amber", "blue", "cyan", "fuchsia", "green", "grey", "indigo", "jade", "lime", "orange", "pink", "pumpkin", "purple", "red", "sand", "slate", "violet", "yellow", "zinc" } } rule UpdateProjectMetadata { when: UpdateProjectMetadataRequested(project, changes) ensures: MetadataFieldsUpdated(project, changes) ensures: ProjectJsonWritten(project) } rule AddCategory { when: AddCategoryRequested(project, name) requires: not (name in project.metadata.categories) ensures: project.metadata.categories = project.metadata.categories + {name} ensures: CategoriesJsonWritten(project) } rule RemoveCategory { when: RemoveCategoryRequested(project, name) ensures: project.metadata.categories = project.metadata.categories - {name} ensures: CategorySettingsRemoved(project, name) ensures: CategoriesJsonWritten(project) ensures: CategoryMetaJsonWritten(project) } rule UpdateCategorySettings { when: UpdateCategorySettingsRequested(project, category, settings) ensures: CategorySettingsUpdated(project, category, settings) ensures: CategoryMetaJsonWritten(project) } rule SetPublishingPreferences { when: SetPublishingPreferencesRequested(project, prefs) ensures: project.publishing_preferences = prefs ensures: PublishingJsonWritten(project) } rule StartupSync { when: AppStarted(project) -- Loads metadata from filesystem, merges with DB, -- creates defaults for new projects ensures: ProjectMetadata.synced_from_filesystem(project) }