KeiSeiKit-1.0/_primitives/_rust/kei-ledger/src/schema.rs
Parfii-bot 84319efcb6 feat(convergence/p3): Role expression (extends/relaxes) + DNA identity
Layer E + G. Role TOML gains extends/relaxes for parent-role
composition; agent spawn gets self-describing DNA identity alongside
UUID.

Role expression:
- _roles/*.toml gain optional `extends = "<parent>"` + `relaxes = [...]`
- compose.rs + verify.rs delegate to new role::resolve_role() with
  recursive extends-chain resolution + cycle detection
- explorer.toml: 28→18 LOC (extends read-only)
- edit-shared.toml: 31→23 LOC (extends edit-local, relaxes
  scope::files-whitelist for task-param override)

DNA identity:
- new dna.rs (159 LOC) — compose/render/parse round-trip
- AgentInvocation carries dna field (prepare.rs)
- Format: <role>::<caps-bitmap>::<sha4-scope>::<sha4-body>-<hex4-nonce>
- ≤ 80 chars total, greppable, parseable
- 11 capability codes in CAP_CODES table: NG, FW, FD, CP, CG, TG, ND,
  RF, SG, DT, BA

kei-ledger schema v2:
- ADD COLUMN dna TEXT + prefix index
- `kei-ledger fork --dna <string>` optional flag
- AgentRow.dna: Option<String>
- Backward compat: schema migration detects + applies on open

Docs: AGENT-SUBSTRATE-SCHEMA.md Layer E + Layer G sections + CAP_CODES table.

New deps: sha2 (workspace), rand 0.8.

Tests: kei-agent-runtime 50 (was 41, +9: 4 role + 5 DNA), kei-ledger
10 (was 9, +1 DNA roundtrip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 04:46:48 +08:00

53 lines
1.8 KiB
Rust

//! SQL schema for the agent ledger.
//!
//! Constructor Pattern: one cube = schema DDL + migration runner.
//! Single source of truth for table shape. Any structural change MUST
//! bump the migration list below; existing rows are preserved.
use rusqlite::{Connection, Result};
/// Ordered migrations. Index = schema version. Never reorder; append only.
pub const MIGRATIONS: &[&str] = &[
// v1 — initial schema (RULE 0.12, 2026-04-21)
"CREATE TABLE IF NOT EXISTS agents (
id TEXT PRIMARY KEY,
branch TEXT NOT NULL,
parent_branch TEXT,
spec_sha TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('running','done','failed','merged','rejected')),
started_ts INTEGER NOT NULL,
finished_ts INTEGER,
summary TEXT,
worktree_path TEXT
);
CREATE INDEX IF NOT EXISTS idx_parent ON agents(parent_branch);
CREATE INDEX IF NOT EXISTS idx_status ON agents(status);",
// v2 — Layer G DNA identity column + prefix index (2026-04-23)
"ALTER TABLE agents ADD COLUMN dna TEXT;
CREATE INDEX IF NOT EXISTS idx_agents_dna_prefix ON agents(substr(dna, 1, 30));",
];
/// Apply all pending migrations. Stores current version in pragma user_version.
pub fn migrate(conn: &Connection) -> Result<()> {
let current: i64 = conn
.query_row("PRAGMA user_version", [], |r| r.get(0))
.unwrap_or(0);
for (i, sql) in MIGRATIONS.iter().enumerate() {
let target = (i + 1) as i64;
if current < target {
conn.execute_batch(sql)?;
conn.pragma_update(None, "user_version", target)?;
}
}
Ok(())
}
/// Six required artefacts per agent (RULE 0.12 §completion bundle).
pub const REQUIRED_ARTEFACTS: &[&str] = &[
"spec.md",
"plan.md",
"progress.json",
"chatlog.md",
"handoffs.md",
"review.md",
];