chore: more validation of plan vs spec

This commit is contained in:
2026-04-02 18:59:18 +02:00
parent aec533b54f
commit 1a24027723
25 changed files with 112 additions and 49 deletions

View File

@@ -194,10 +194,15 @@ Rules:
### `bds-core/ai` ### `bds-core/ai`
- one-shot AI client: `reqwest` + `serde_json` against OpenAI-compatible Chat Completions endpoint - one-shot AI client: `reqwest` + `serde_json` against OpenAI-compatible Chat Completions endpoint
- AI-assisted translation operation (translate field content to target language) - two-endpoint configuration: online endpoint (URL, API key, model) + airplane mode endpoint (URL, model)
- AI translate post operation (title, excerpt, content to target language)
- AI translate media metadata operation (title, alt, caption to target language)
- AI image description / alt text generation operation - AI image description / alt text generation operation
- AI title suggestion operation - AI post analysis operation (title, excerpt, slug suggestion)
- AI endpoint configuration model (endpoint URL, API key, model name) - AI taxonomy analysis operation (tag, category suggestions)
- AI language detection operation
- API key storage via OS keychain (macOS Keychain, Windows DPAPI, Linux libsecret)
- airplane mode gating: block online endpoint, use airplane endpoint or show toast
- error handling: surface failures as user-visible feedback, never silent - error handling: surface failures as user-visible feedback, never silent
### `bds-core/engine` ### `bds-core/engine`
@@ -213,16 +218,17 @@ Rules:
- preview controls - preview controls
- generation progress display - generation progress display
- render errors and diagnostics - render errors and diagnostics
- AI operation triggers in post editor (title suggestion), translation editor (translate), and media editor (alt text generation) - AI operation triggers in post editor (analysis, taxonomy), translation editor (translate), and media editor (alt text, translate)
- AI endpoint configuration in settings view - AI endpoint configuration in settings view (online + airplane mode endpoints)
### Validation ### Validation
- golden generated-site comparisons - golden generated-site comparisons
- preview route coverage - preview route coverage
- template compatibility suite - template compatibility suite
- one-shot AI client tests (mocked endpoint: translation, alt text, title suggestion) - one-shot AI client tests (mocked endpoint: all 6 operations)
- AI endpoint configuration persistence tests - AI endpoint configuration persistence tests
- AI airplane mode gating tests
## Milestone M5: Operate And Ship ## Milestone M5: Operate And Ship

View File

@@ -31,10 +31,11 @@ Core is not a toy MVP. Core must already be a production-capable replacement for
- media translation records - media translation records
- post-link tracking records - post-link tracking records
- template file formats and lookup rules - template file formats and lookup rules
- menu document read format for rendering - menu document format (OPML at `meta/menu.opml`) — both read and write
- generated routes, feeds, sitemaps, and local preview behavior - generated routes, feeds, sitemaps, and local preview behavior
- metadata JSON file layout: `meta/project.json`, `meta/categories.json`, `meta/category-meta.json`, `meta/publishing.json`
- metadata diff and rebuild-from-filesystem behavior - metadata diff and rebuild-from-filesystem behavior
- full-text search behavior (SQLite FTS5 virtual tables for posts and media) - full-text search behavior (SQLite FTS5 virtual tables for posts and media, with Snowball stemmers for 24 languages via custom tokenizer)
- slug generation behavior (verify `deunicode` output matches current `transliteration` output for the project's content corpus) - slug generation behavior (verify `deunicode` output matches current `transliteration` output for the project's content corpus)
### May change intentionally ### May change intentionally
@@ -63,7 +64,7 @@ User-authored Python scripts are not compatible and are treated as legacy artifa
- **tokio as the async runtime**: preview server (axum), SSH publishing, file watching, and rfd async dialogs all require an async executor. tokio is the standard choice and is used workspace-wide. Synchronous engine code in bds-core does not use tokio directly — it remains callable from both async (bds-ui) and sync (bds-cli) contexts. - **tokio as the async runtime**: preview server (axum), SSH publishing, file watching, and rfd async dialogs all require an async executor. tokio is the standard choice and is used workspace-wide. Synchronous engine code in bds-core does not use tokio directly — it remains callable from both async (bds-ui) and sync (bds-cli) contexts.
- **Plain-text markdown editor first**: no WYSIWYG in core. Live preview is required. This is an intentional regression from the current app's Milkdown WYSIWYG editor. A rich editor can be added as an extension bucket after core ships — and the bds-editor widget provides a natural foundation for it. - **Plain-text markdown editor first**: no WYSIWYG in core. Live preview is required. This is an intentional regression from the current app's Milkdown WYSIWYG editor. A rich editor can be added as an extension bucket after core ships — and the bds-editor widget provides a natural foundation for it.
- **Lua for user-authored scripting**: `mlua` with Lua 5.4. Only user-authored macros, transforms, and utility scripts run through Lua. Built-in macros are native Rust — see the macro architecture section below. - **Lua for user-authored scripting**: `mlua` with Lua 5.4. Only user-authored macros, transforms, and utility scripts run through Lua. Built-in macros are native Rust — see the macro architecture section below.
- **One-shot AI operations are core scope**: translation, image description / alt text generation, and title suggestion use a simple OpenAI-compatible HTTP client (`reqwest` + `serde_json`). The endpoint is user-configurable (OpenAI, Anthropic-via-proxy, local Ollama, etc.). These are fire-and-forget request/response calls — no streaming, no tool use, no chat history. The AI chat UI, streaming responses, and tool execution remain in Extension Bucket C. - **One-shot AI operations are core scope**: six operations use a simple OpenAI-compatible HTTP client (`reqwest` + `serde_json`): (1) translate post, (2) translate media metadata, (3) image description / alt text, (4) post analysis (title + excerpt + slug suggestion), (5) taxonomy analysis (tag + category suggestions), (6) language detection. Two configurable endpoints: one for online use, one for airplane/offline mode (local model). These are fire-and-forget request/response calls — no streaming, no tool use, no chat history. The AI chat UI, streaming responses, and tool execution remain in Extension Bucket C.
## Iced Architecture Patterns ## Iced Architecture Patterns
@@ -159,7 +160,7 @@ bds-rust/
- Lua runtime for user-authored macros, transforms, and utility scripts - Lua runtime for user-authored macros, transforms, and utility scripts
- generated Lua scripting API docs - generated Lua scripting API docs
- FTS5 virtual tables for in-app post and media search - FTS5 virtual tables for in-app post and media search
- one-shot AI operations: translation, image description / alt text generation, title suggestion (configurable OpenAI-compatible endpoint via `reqwest`) - one-shot AI operations: translate post, translate media, image alt text, post analysis, taxonomy analysis, language detection (two configurable OpenAI-compatible endpoints: online + airplane mode, via `reqwest`)
### Deferred to extensions ### Deferred to extensions
@@ -244,9 +245,10 @@ This matrix is a release artifact, not a temporary note.
The current default templates use a small subset of the Liquid specification. Before implementing the Liquid render pipeline, inventory the exact features used. The current templates (12 files: 3 main templates, 5 macro templates, 4 partials in `src/main/engine/templates/`) use only: The current default templates use a small subset of the Liquid specification. Before implementing the Liquid render pipeline, inventory the exact features used. The current templates (12 files: 3 main templates, 5 macro templates, 4 partials in `src/main/engine/templates/`) use only:
- **Tags:** `if`/`elsif`/`else`, `for`, `assign`, `render` (partials), whitespace stripping (`{%- -%}`) - **Tags:** `if`/`elsif`/`else`, `for`, `assign`, `render` (partials), whitespace stripping (`{%- -%}`)
- **Filters (built-in):** `default`, `escape`, `url_encode`, `append`, `size` - **Filters (built-in):** `default`, `escape`, `url_encode`, `append`
- **Filters (custom, must be re-implemented in Rust):** `i18n` (translation lookup by key and language), `markdown` (markdown-to-HTML with macro expansion, URL rewriting, and media path canonicalization) - **Filters (custom, must be re-implemented in Rust):** `i18n` (translation lookup by key and language), `markdown` (markdown-to-HTML with macro expansion, URL rewriting, and media path canonicalization)
- **Not used:** `unless`, `case/when`, `capture`, `layout`, `include`, `comment`, `raw`, `date`, `truncate`, `split`, `join`, `where`, `group_by`, `map`, `sort`, `reverse`, `cycle`, `tablerow`, `increment`/`decrement`, and most other standard filters - **Operators and access:** `==`, `>`, `or`, `and`, bare truthiness, `blank` (nil/empty). Property access via dot notation (`object.property`), `.size` on arrays (this is property access, **not** a pipe filter), bracket notation for map lookups (`map[key]`).
- **Not used:** `unless`, `case/when`, `capture`, `layout`, `include`, `comment`, `raw`, `date`, `truncate`, `split`, `join`, `where`, `group_by`, `map`, `sort`, `reverse`, `cycle`, `tablerow`, `increment`/`decrement`, `size` (as a pipe filter), and most other standard filters
This means the Rust Liquid implementation only needs roughly 35% of the full specification. Use `liquid-rust` or a minimal custom engine scoped to just the features above. User templates may use additional features, but parity with the current default templates is the release gate. This means the Rust Liquid implementation only needs roughly 35% of the full specification. Use `liquid-rust` or a minimal custom engine scoped to just the features above. User templates may use additional features, but parity with the current default templates is the release gate.
@@ -268,6 +270,14 @@ Macro invocation syntax in content: `[[macro_name param1="value1" param2="value2
The current app uses the `transliteration` npm package for Unicode-to-ASCII conversion in slugs. The plan specifies `deunicode` for Rust. These libraries may produce different output for edge cases (CJK characters, Cyrillic, accented Latin, etc.). Add a slug compatibility test suite to M0 fixtures that runs both libraries against the project's actual content corpus and documents any divergences. The current app uses the `transliteration` npm package for Unicode-to-ASCII conversion in slugs. The plan specifies `deunicode` for Rust. These libraries may produce different output for edge cases (CJK characters, Cyrillic, accented Latin, etc.). Add a slug compatibility test suite to M0 fixtures that runs both libraries against the project's actual content corpus and documents any divergences.
### Two search systems
The app has two independent search systems — do not conflate them:
1. **FTS5 (in-app search)**: SQLite FTS5 virtual tables (`posts_fts`, `media_fts`) power the desktop app's search UI. These require custom Snowball tokenizers registered via `rusqlite`'s `fts5_tokenizer` API (using the `rust-stemmers` crate) for 24-language stemmed search. Both indexing and query-time stemming must match the content language. This is Wave 1 scope.
2. **Pagefind (generated site search)**: a client-side search index bundled with the generated static site. Pagefind indexes the generated HTML files and produces JavaScript/WASM artifacts that power search on the published website. This is Wave 4 scope, added to the generation pipeline via the `pagefind` crate's Rust library API.
### Client-side search index (Pagefind) ### Client-side search index (Pagefind)
If the current TypeScript app generates a Pagefind search index as part of site generation, the Rust app must produce the same artifact. Pagefind publishes a Rust library (`pagefind` crate) with a programmatic API (`pagefind::api::PagefindIndex`). The generation pipeline feeds rendered HTML to `PagefindIndex::add_html_file()` and writes the resulting index files via `PagefindIndex::get_files()`. No external binary, no npm — this is a pure Rust library dependency, fully compatible with the no-JavaScript constraint. If the current TypeScript app generates a Pagefind search index as part of site generation, the Rust app must produce the same artifact. Pagefind publishes a Rust library (`pagefind` crate) with a programmatic API (`pagefind::api::PagefindIndex`). The generation pipeline feeds rendered HTML to `PagefindIndex::add_html_file()` and writes the resulting index files via `PagefindIndex::get_files()`. No external binary, no npm — this is a pure Rust library dependency, fully compatible with the no-JavaScript constraint.
@@ -297,6 +307,7 @@ All crate choices for core scope, organized by subsystem. This prevents ad-hoc t
| `thiserror` | Typed error definitions in library crates | bds-core, bds-editor | | `thiserror` | Typed error definitions in library crates | bds-core, bds-editor |
| `anyhow` | Ergonomic error handling in application crates | bds-ui, bds-cli | | `anyhow` | Ergonomic error handling in application crates | bds-ui, bds-cli |
| `tokio` | Async runtime | Preview server, publish, file watching, async dialogs | | `tokio` | Async runtime | Preview server, publish, file watching, async dialogs |
| `rust-stemmers` | Snowball stemming for FTS5 | Custom FTS5 tokenizer for 24-language stemmed search |
### UI Framework (Wave 0 onward) ### UI Framework (Wave 0 onward)
@@ -345,7 +356,7 @@ All crate choices for core scope, organized by subsystem. This prevents ad-hoc t
| Crate | Purpose | Notes | | Crate | Purpose | Notes |
|---|---|---| |---|---|---|
| `ssh2` | SSH/SCP file upload | For publish-via-SSH targets | | `ssh2` | SSH/SCP file upload | For publish-via-SSH targets. Authentication via SSH agent (`SSH_AUTH_SOCK`) only — no password auth, no interactive prompts. |
| (shell out) | rsync invocation | rsync ships with macOS/Linux; invoke as child process | | (shell out) | rsync invocation | rsync ships with macOS/Linux; invoke as child process |
### Client-Side Search (Wave 4) ### Client-Side Search (Wave 4)
@@ -358,7 +369,7 @@ All crate choices for core scope, organized by subsystem. This prevents ad-hoc t
| Crate | Purpose | Notes | | Crate | Purpose | Notes |
|---|---|---| |---|---|---|
| `reqwest` | HTTP client for AI endpoints | OpenAI-compatible Chat Completions API. Used for translation, alt text, title suggestion. | | `reqwest` | HTTP client for AI endpoints | OpenAI-compatible Chat Completions API. Two endpoints: online + airplane mode (local). Used for translation, alt text, taxonomy, post analysis, language detection. |
### Scripting (Wave 6) ### Scripting (Wave 6)
@@ -384,7 +395,7 @@ The hard sequence is:
4. editor widget MVP (ropey + syntect + cosmic-text proof of concept) 4. editor widget MVP (ropey + syntect + cosmic-text proof of concept)
5. editors for posts, media, templates, scripts, and settings 5. editors for posts, media, templates, scripts, and settings
6. preview and generation parity 6. preview and generation parity
7. one-shot AI operations (translation, alt text, title suggestion) 7. one-shot AI operations (translate post, translate media, image alt text, post analysis, taxonomy analysis, language detection)
8. publish and integrity tooling 8. publish and integrity tooling
9. Lua built-ins plus generated script API docs 9. Lua built-ins plus generated script API docs
@@ -425,7 +436,7 @@ Anything outside that path is noise until the previous step is stable.
- preview is trustworthy - preview is trustworthy
- generation matches golden output - generation matches golden output
- route, feed, and sitemap parity is acceptable - route, feed, and sitemap parity is acceptable
- one-shot AI operations work (translation, image alt text, title suggestion) with a configurable OpenAI-compatible endpoint - one-shot AI operations work (all 6 operations) with two configurable OpenAI-compatible endpoints (online + airplane mode)
### Milestone M5: Operate And Ship ### Milestone M5: Operate And Ship
@@ -529,7 +540,7 @@ Not all of these are needed in Wave 0 — this is the full foundation set. Wave-
- `PostMediaEngine` - `PostMediaEngine`
- `TagEngine` - `TagEngine`
- `MetaEngine` - `MetaEngine`
- `TaskManager` - `TaskManager` — max 3 concurrent tasks, FIFO queue, 250ms progress throttle, cancellation support
- `MetadataDiffEngine` - `MetadataDiffEngine`
- `RebuildEngine` or equivalent rebuild services - `RebuildEngine` or equivalent rebuild services
@@ -688,7 +699,7 @@ By Wave 3, bds-editor must support the full feature set documented in the editor
- `rayon` for parallel page rendering during generation - `rayon` for parallel page rendering during generation
- `axum` for the preview HTTP server (runs on tokio) - `axum` for the preview HTTP server (runs on tokio)
- `pagefind` for client-side search index generation (Rust library API, not CLI binary) - `pagefind` for client-side search index generation (Rust library API, not CLI binary)
- `reqwest` for one-shot AI operations (translation, alt text, title suggestion) against an OpenAI-compatible endpoint - `reqwest` for one-shot AI operations (all 6 operations: translate post/media, image alt text, post analysis, taxonomy analysis, language detection) against two configurable OpenAI-compatible endpoints (online + airplane mode)
### Rendering pipeline ### Rendering pipeline
@@ -706,7 +717,7 @@ By Wave 3, bds-editor must support the full feature set documented in the editor
### Preview rules ### Preview rules
- preview binds only to localhost - preview server binds to `127.0.0.1:4123` (localhost only, fixed port)
- preview and generated HTML use local assets only - preview and generated HTML use local assets only
- preview must support drafts and language-prefixed routes - preview must support drafts and language-prefixed routes
- rendered language is controlled by project settings, not by UI locale - rendered language is controlled by project settings, not by UI locale
@@ -715,24 +726,30 @@ By Wave 3, bds-editor must support the full feature set documented in the editor
Wave 4 introduces the one-shot AI client in `bds-core`. This is a minimal `reqwest`-based HTTP client that sends single request/response calls to an OpenAI-compatible Chat Completions endpoint. No streaming, no tool use, no chat history. Wave 4 introduces the one-shot AI client in `bds-core`. This is a minimal `reqwest`-based HTTP client that sends single request/response calls to an OpenAI-compatible Chat Completions endpoint. No streaming, no tool use, no chat history.
**Operations:** **Operations (all 6 are core scope):**
- **Translation**: translate a post or media metadata field to a target language. Used from the translation editor and media editor. - **Translate post**: translate title, excerpt, and content to a target language. Used from the translation editor.
- **Image description / alt text**: generate an alt text description for a media item. Used from the media editor. - **Translate media metadata**: translate title, alt, and caption to a target language. Used from the media editor.
- **Title suggestion**: suggest a title from post content. Used from the post editor. - **Image description / alt text**: generate alt text and caption for a media item. Used from the media editor.
- **Post analysis**: suggest title, excerpt, and slug from post content. Used from the post editor.
- **Taxonomy analysis**: suggest tags and categories for a post. Used from the post editor.
- **Language detection**: detect the language of a text fragment. Used internally by other operations.
**Configuration:** **Configuration:**
- endpoint URL (default: none — AI features are opt-in) - **Online endpoint**: URL, API key, model name — for cloud AI providers (OpenAI, Anthropic-via-proxy, etc.)
- API key (stored securely, not in project files) - **Airplane mode endpoint**: URL, model name — for local models (Ollama, LM Studio, etc.) — no API key needed
- model name (configurable per operation or globally) - When airplane mode is active, only the airplane mode endpoint is used. Cloud endpoint calls are blocked.
- Default: no endpoints configured — AI features are opt-in.
- API keys stored securely via OS keychain (macOS Keychain, Windows DPAPI, Linux libsecret), never in project files.
**Constraints:** **Constraints:**
- AI operations are entirely optional. The app is fully functional without any AI endpoint configured. - AI operations are entirely optional. The app is fully functional without any AI endpoint configured.
- When no endpoint is configured, AI-related UI elements are disabled or hidden. - When no endpoint is configured, AI-related UI elements are disabled or hidden.
- When airplane mode is active, the online endpoint is disabled. If no airplane mode endpoint is configured, AI is unavailable with a user-visible toast.
- Error responses produce user-visible feedback (toast or inline error), never silent failures. - Error responses produce user-visible feedback (toast or inline error), never silent failures.
- Request/response payloads use the OpenAI Chat Completions wire format. This works with OpenAI, Anthropic-via-proxy, local Ollama, or any compatible endpoint. - Request/response payloads use the OpenAI Chat Completions wire format.
### Tests ### Tests
@@ -765,7 +782,9 @@ Wave 4 introduces the one-shot AI client in `bds-core`. This is a minimal `reqwe
- upload generated site via SCP or rsync - upload generated site via SCP or rsync
- upload media and thumbnails correctly - upload media and thumbnails correctly
- exclude metadata-only files from deploy targets where appropriate - SSH authentication via SSH agent (`SSH_AUTH_SOCK`) only — no password auth, no interactive prompts
- three upload targets (html, thumbnails, media) run as parallel tasks
- exclude `.meta` sidecar files from media uploads
- surface publish progress and failures in the UI - surface publish progress and failures in the UI
- detect and surface external file changes that affect open editors or preview accuracy - detect and surface external file changes that affect open editors or preview accuracy

View File

@@ -11,7 +11,7 @@ Extensions begin only after the core plan is already usable end to end.
1. No extension may break the core compatibility contract. 1. No extension may break the core compatibility contract.
2. Extensions must reuse core models, engines, and persistence rules rather than invent parallel formats. 2. Extensions must reuse core models, engines, and persistence rules rather than invent parallel formats.
3. UI features must still be tied to underlying functionality; no placeholder shells. 3. UI features must still be tied to underlying functionality; no placeholder shells.
4. AI features remain gated by offline mode and must prefer local models or provide explicit user feedback when unavailable. One-shot AI operations (translation, alt text, title suggestion) are part of core but respect the same offline gating. 4. AI features remain gated by offline mode and must prefer local models or provide explicit user feedback when unavailable. One-shot AI operations (6 operations: translate post/media, image alt text, post analysis, taxonomy analysis, language detection) are part of core with two configurable OpenAI-compatible endpoints (online + airplane mode), and respect the same offline gating.
5. Extensions use the same Iced + muda + rfd platform stack as core. No additional UI frameworks. 5. Extensions use the same Iced + muda + rfd platform stack as core. No additional UI frameworks.
## Extension Buckets ## Extension Buckets

View File

@@ -1,24 +1,22 @@
-- allium: 1 -- allium: 1
-- bDS AI Integration -- bDS AI Integration
-- Scope: core (one-shot operations), extension Bucket C (chat + streaming)
-- Distilled from: src/main/engine/ChatEngine.ts, ai/providers.ts, -- Distilled from: src/main/engine/ChatEngine.ts, ai/providers.ts,
-- ai/chat.ts, ai/tasks.ts, SecureKeyStore.ts -- ai/chat.ts, ai/tasks.ts, SecureKeyStore.ts
-- Note: Rust rewrite simplifies providers to two configurable
-- OpenAI-compatible endpoints (online + airplane mode),
-- replacing the TypeScript app's 4 named providers.
use "./post.allium" as post use "./post.allium" as post
use "./media.allium" as media use "./media.allium" as media
value AiProvider { entity AiEndpoint {
kind: opencode_zen | mistral | ollama | lm_studio kind: online | airplane
-- OpenCode Zen: routes claude* to Anthropic Messages API, url: String
-- everything else to OpenAI Chat Completions api_key: String? -- encrypted via SecureKeyStore; null for local models
-- Mistral: native Mistral API model: String
-- Ollama: localhost:11434, local models -- online: cloud provider (OpenAI, Anthropic-via-proxy, etc.)
-- LM Studio: localhost:1234, local models -- airplane: local model (Ollama, LM Studio, etc.)
}
value AiModel {
provider: AiProvider
name: String
modalities: Set<String> -- text, vision, etc.
} }
entity SecureKeyStore { entity SecureKeyStore {
@@ -30,7 +28,7 @@ entity SecureKeyStore {
entity ChatConversation { entity ChatConversation {
title: String title: String
model: String -- default: claude-sonnet-4-5 model: String
created_at: Timestamp created_at: Timestamp
updated_at: Timestamp updated_at: Timestamp
@@ -47,6 +45,9 @@ entity ChatMessage {
} }
-- One-shot AI tasks (core scope, no streaming) -- One-shot AI tasks (core scope, no streaming)
-- All use OpenAI Chat Completions wire format.
-- When airplane_mode: use airplane endpoint. When online: use online endpoint.
-- When no endpoint configured for current mode: disable AI, show toast.
rule AnalyzeTaxonomy { rule AnalyzeTaxonomy {
when: AnalyzeTaxonomyRequested(post) when: AnalyzeTaxonomyRequested(post)
@@ -92,11 +93,11 @@ rule TranslateMedia {
ensures: MediaTranslationResult(title, alt, caption) ensures: MediaTranslationResult(title, alt, caption)
} }
-- Chat (extension scope, with streaming and tool use) -- Chat (extension Bucket C scope, with streaming and tool use)
rule StartChat { rule StartChat {
when: StartChatRequested(model) when: StartChatRequested(model)
ensures: ChatConversation.created(model: model ?? "claude-sonnet-4-5") ensures: ChatConversation.created(model: model)
} }
rule SendChatMessage { rule SendChatMessage {
@@ -112,15 +113,26 @@ rule SendChatMessage {
-- Model catalog -- Model catalog
rule RefreshModelCatalog { rule RefreshModelCatalog {
when: RefreshModelCatalogRequested(provider) when: RefreshModelCatalogRequested(endpoint)
-- Queries the endpoint's model list API
-- 5-minute cache TTL -- 5-minute cache TTL
ensures: ModelCatalogUpdated(provider) ensures: ModelCatalogUpdated(endpoint)
} }
invariant AirplaneModeGating { invariant AirplaneModeGating {
-- All AI activities must be gated by airplane (offline) mode -- All AI activities must be gated by airplane (offline) mode
-- When offline: either use local model (Ollama/LM Studio) or -- When airplane_mode = true: only the airplane endpoint is used
-- inform the user via toast notification -- When airplane_mode = false: the online endpoint is used
-- If the active endpoint is not configured: AI is unavailable,
-- show toast notification to user
}
invariant TwoEndpointModel {
-- Two configurable OpenAI-compatible endpoints:
-- online: for cloud providers (requires API key)
-- airplane: for local models (no API key required)
-- Both use the OpenAI Chat Completions wire format.
-- This replaces the TypeScript app's 4 named provider model.
} }
invariant SecureKeyStorage { invariant SecureKeyStorage {

View File

@@ -54,6 +54,12 @@ use "./metadata_diff.allium" as metadata_diff -- DB/filesystem diff and rebuil
-- --
-- 2. Liquid subset: see template.allium for the exact subset. -- 2. Liquid subset: see template.allium for the exact subset.
-- Only 5 tags, 4 standard filters, 2 custom filters, 5 operators. -- Only 5 tags, 4 standard filters, 2 custom filters, 5 operators.
-- .size is property access on arrays, NOT a pipe filter.
-- --
-- 3. Macro calling convention: [[macroslug param1=value1 ...]] -- 3. Macro calling convention: [[macroslug param1=value1 ...]]
-- Double-bracket syntax, not Liquid tags. Identical to current app. -- Double-bracket syntax, not Liquid tags. Identical to current app.
--
-- 4. AI provider model: Rust rewrite uses two configurable OpenAI-compatible
-- endpoints (online + airplane mode) instead of the TypeScript app's
-- 4 named providers (OpenCode Zen, Mistral, Ollama, LM Studio).
-- See ai.allium for details.

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS CLI / App Notification Sync -- bDS CLI / App Notification Sync
-- Scope: extension (Bucket G — MCP + Automation)
-- Distilled from: src/main/engine/CliNotifier.ts, NotificationWatcher.ts -- Distilled from: src/main/engine/CliNotifier.ts, NotificationWatcher.ts
entity DbNotification { entity DbNotification {

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Semantic Similarity / Embeddings -- bDS Semantic Similarity / Embeddings
-- Scope: extension (Bucket D — Embeddings + Duplicate Detection)
-- Distilled from: src/main/engine/EmbeddingEngine.ts -- Distilled from: src/main/engine/EmbeddingEngine.ts
use "./post.allium" as post use "./post.allium" as post

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Static Site Generation -- bDS Static Site Generation
-- Scope: core (Wave 4)
-- Distilled from: src/main/engine/BlogGenerationEngine.ts, -- Distilled from: src/main/engine/BlogGenerationEngine.ts,
-- PageRenderer.ts, GenerationWorkerPool, RoutePageGenerationService -- PageRenderer.ts, GenerationWorkerPool, RoutePageGenerationService

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Git Integration -- bDS Git Integration
-- Scope: extension (Bucket A — Git + Validation)
-- Distilled from: src/main/engine/GitEngine.ts -- Distilled from: src/main/engine/GitEngine.ts
use "./post.allium" as post use "./post.allium" as post

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Internationalization -- bDS Internationalization
-- Scope: core (Wave 0 onward — split localization is mandatory)
-- Distilled from: src/main/shared/i18n.ts, i18n/locales/*.json -- Distilled from: src/main/shared/i18n.ts, i18n/locales/*.json
value SupportedLanguage { value SupportedLanguage {

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS MCP Server (Model Context Protocol) -- bDS MCP Server (Model Context Protocol)
-- Scope: extension (Bucket G — MCP + Automation)
-- Distilled from: src/main/engine/MCPServer.ts, ProposalStore, MCPAgentConfigEngine.ts -- Distilled from: src/main/engine/MCPServer.ts, ProposalStore, MCPAgentConfigEngine.ts
use "./post.allium" as post use "./post.allium" as post

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Media Lifecycle -- bDS Media Lifecycle
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/MediaEngine.ts, schema.ts -- Distilled from: src/main/engine/MediaEngine.ts, schema.ts
use "./project.allium" as project use "./project.allium" as project

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Navigation Menu -- bDS Navigation Menu
-- Scope: core (read for rendering), extension Bucket F (menu editor UI)
-- Distilled from: src/main/engine/MenuEngine.ts -- Distilled from: src/main/engine/MenuEngine.ts
value MenuItem { value MenuItem {

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Project Metadata, Categories, Publishing Preferences -- bDS Project Metadata, Categories, Publishing Preferences
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/MetaEngine.ts, schema.ts -- Distilled from: src/main/engine/MetaEngine.ts, schema.ts
use "./project.allium" as project use "./project.allium" as project

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Metadata Diff and Rebuild -- bDS Metadata Diff and Rebuild
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/MetadataDiffEngine.ts -- Distilled from: src/main/engine/MetadataDiffEngine.ts
use "./post.allium" as post use "./post.allium" as post

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Post Lifecycle -- bDS Post Lifecycle
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/PostEngine.ts, postFileUtils.ts, schema.ts -- Distilled from: src/main/engine/PostEngine.ts, postFileUtils.ts, schema.ts
use "./project.allium" as project use "./project.allium" as project

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Local Preview Server -- bDS Local Preview Server
-- Scope: core (Wave 4)
-- Distilled from: src/main/engine/PreviewServer.ts, PageRenderer.ts -- Distilled from: src/main/engine/PreviewServer.ts, PageRenderer.ts
use "./template.allium" as template use "./template.allium" as template

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Project Management -- bDS Project Management
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/ProjectEngine.ts, schema.ts -- Distilled from: src/main/engine/ProjectEngine.ts, schema.ts
entity Project { entity Project {

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS SSH Publishing -- bDS SSH Publishing
-- Scope: core (Wave 5)
-- Distilled from: src/main/engine/PublishEngine.ts -- Distilled from: src/main/engine/PublishEngine.ts
use "./metadata.allium" as meta use "./metadata.allium" as meta

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Scripting System -- bDS Scripting System
-- Scope: core (Wave 6 — Lua runtime and scripting docs)
-- Distilled from: src/main/engine/ScriptEngine.ts, schema.ts -- Distilled from: src/main/engine/ScriptEngine.ts, schema.ts
-- Note: TypeScript app uses Python/Pyodide. Rust rewrite uses Lua. -- Note: TypeScript app uses Python/Pyodide. Rust rewrite uses Lua.
-- Behavioural contract is identical; only runtime changes. -- Behavioural contract is identical; only runtime changes.

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Full-Text Search -- bDS Full-Text Search
-- Scope: core (Wave 1 — FTS5 in-app search with Snowball stemmers)
-- Distilled from: src/main/engine/PostEngine.ts (FTS methods), -- Distilled from: src/main/engine/PostEngine.ts (FTS methods),
-- MediaEngine.ts (FTS methods), stemmer.ts -- MediaEngine.ts (FTS methods), stemmer.ts

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Tag System -- bDS Tag System
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/TagEngine.ts, schema.ts -- Distilled from: src/main/engine/TagEngine.ts, schema.ts
use "./project.allium" as project use "./project.allium" as project

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Background Task Manager -- bDS Background Task Manager
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/TaskManager.ts -- Distilled from: src/main/engine/TaskManager.ts
entity Task { entity Task {

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Liquid Template System -- bDS Liquid Template System
-- Scope: core (Wave 1 data, Wave 4 rendering)
-- Distilled from: src/main/engine/TemplateEngine.ts, PageRenderer.ts, schema.ts, -- Distilled from: src/main/engine/TemplateEngine.ts, PageRenderer.ts, schema.ts,
-- bundled starter templates in src/main/engine/templates/ -- bundled starter templates in src/main/engine/templates/

View File

@@ -1,5 +1,6 @@
-- allium: 1 -- allium: 1
-- bDS Translation System -- bDS Translation System
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/PostEngine.ts (translation methods), -- Distilled from: src/main/engine/PostEngine.ts (translation methods),
-- postTranslationFileUtils.ts, MediaEngine.ts -- postTranslationFileUtils.ts, MediaEngine.ts