Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
105 lines
3.7 KiB
Rust
105 lines
3.7 KiB
Rust
//! kei-content-store EntitySchemas — declarative specs consumed by
|
|
//! `kei_entity_store::Store` and its verb templates.
|
|
//!
|
|
//! Shape (multi-schema convergence, 2026-04-23):
|
|
//!
|
|
//! - `CONTENT_SCHEMA`: primary entity `content_units` (assets; INTEGER
|
|
//! PK; engine-owned create/get/list/search/update/delete + FTS).
|
|
//! - `CAMPAIGNS_SCHEMA`: plain-CRUD INTEGER-PK table promoted to engine
|
|
//! on this pass (create/get only — no idempotency or dedup).
|
|
//! - `ALL_SCHEMAS`: the `&[&EntitySchema]` slice `Store::open` hands
|
|
//! to the engine.
|
|
//!
|
|
//! Secondary tables that stay in `custom_migrations` (on CONTENT_SCHEMA)
|
|
//! and keep bespoke SQL in their sibling modules:
|
|
//!
|
|
//! - `prompts` — hash-dedup via `INSERT OR IGNORE` + re-query by
|
|
//! `UNIQUE(prompt_hash, model)`; engine `create` is plain INSERT,
|
|
//! would break `prompt_dedup_by_hash` test. Sibling: `prompts.rs`.
|
|
//! - `campaign_assets` — composite `(campaign_id, asset_id)` PK, no
|
|
//! single-column PK; engine schemas require one PK field. Also uses
|
|
//! `INSERT OR IGNORE` for idempotent attach. Sibling: `campaigns.rs`.
|
|
|
|
use kei_entity_store::schema::{EdgeKeyKind, EntitySchema, FieldDef};
|
|
|
|
// ---- content_units (primary, assets) ---------------------------------
|
|
|
|
static FIELDS: &[FieldDef] = &[
|
|
FieldDef::pk("id"),
|
|
FieldDef::text_default("unit_type", "asset"),
|
|
FieldDef::text_nn("title"),
|
|
FieldDef::text("content"),
|
|
FieldDef::text("media_type"),
|
|
FieldDef::text("file_path"),
|
|
FieldDef::text("file_hash"),
|
|
FieldDef::text("provider"),
|
|
FieldDef::integer("cost_cents"),
|
|
FieldDef::integer("parent_id"),
|
|
FieldDef::created_at(),
|
|
FieldDef::updated_at(),
|
|
];
|
|
|
|
/// Secondary DDL co-located with `content_units` — indexes on the
|
|
/// primary table plus the two bespoke-CRUD tables (prompts,
|
|
/// campaign_assets). Kept byte-for-byte compatible with the legacy
|
|
/// pre-multi-schema DB layout.
|
|
const DDL_SECONDARY: &str = r#"
|
|
CREATE INDEX IF NOT EXISTS idx_cu_type ON content_units(unit_type);
|
|
CREATE INDEX IF NOT EXISTS idx_cu_hash ON content_units(file_hash) WHERE file_hash != '';
|
|
|
|
CREATE TABLE IF NOT EXISTS prompts (
|
|
id INTEGER PRIMARY KEY,
|
|
prompt_text TEXT NOT NULL,
|
|
prompt_hash TEXT NOT NULL,
|
|
prompt_type TEXT DEFAULT '',
|
|
model TEXT DEFAULT '',
|
|
version INTEGER DEFAULT 1,
|
|
parent_id INTEGER DEFAULT 0,
|
|
created_at INTEGER NOT NULL,
|
|
UNIQUE(prompt_hash, model)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS campaign_assets (
|
|
campaign_id INTEGER NOT NULL,
|
|
asset_id INTEGER NOT NULL,
|
|
PRIMARY KEY(campaign_id, asset_id)
|
|
);
|
|
"#;
|
|
|
|
pub static CONTENT_SCHEMA: EntitySchema = EntitySchema {
|
|
name: "asset",
|
|
table: "content_units",
|
|
fields: FIELDS,
|
|
enabled_verbs: &["create", "get", "list", "search", "update", "delete"],
|
|
fts_columns: Some(&["title", "content"]),
|
|
edge_table: None,
|
|
edge_key_kind: EdgeKeyKind::IntegerPair,
|
|
archived_field: None,
|
|
custom_migrations: &[DDL_SECONDARY],
|
|
};
|
|
|
|
// ---- campaigns (promoted 2026-04-23) --------------------------------
|
|
|
|
static CAMPAIGN_FIELDS: &[FieldDef] = &[
|
|
FieldDef::pk("id"),
|
|
FieldDef::text_nn("name"),
|
|
FieldDef::text_default("description", ""),
|
|
FieldDef::text_default("status", "draft"),
|
|
FieldDef::created_at(),
|
|
];
|
|
|
|
pub static CAMPAIGNS_SCHEMA: EntitySchema = EntitySchema {
|
|
name: "campaign",
|
|
table: "campaigns",
|
|
fields: CAMPAIGN_FIELDS,
|
|
enabled_verbs: &["create", "get"],
|
|
fts_columns: None,
|
|
edge_table: None,
|
|
edge_key_kind: EdgeKeyKind::IntegerPair,
|
|
archived_field: None,
|
|
custom_migrations: &[],
|
|
};
|
|
|
|
// ---- aggregate slice for Store::open --------------------------------
|
|
|
|
pub static ALL_SCHEMAS: &[&EntitySchema] = &[&CONTENT_SCHEMA, &CAMPAIGNS_SCHEMA];
|