Darkwing's design note and SQLite prototype as records. Sage accepts seven of eight open questions; the PM launching sessions goes to Jason. Co-Authored-By: Claude Opus 5.5 <[email protected]>
109 lines
6.9 KiB
SQL
109 lines
6.9 KiB
SQL
PRAGMA journal_mode = WAL;
|
|
PRAGMA foreign_keys = ON;
|
|
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL) STRICT;
|
|
CREATE TABLE events (
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
id TEXT NOT NULL UNIQUE,
|
|
at TEXT NOT NULL,
|
|
business TEXT NOT NULL,
|
|
kind TEXT NOT NULL,
|
|
actor_role TEXT, actor_run TEXT,
|
|
subject TEXT,
|
|
corrects TEXT REFERENCES events(id),
|
|
body TEXT NOT NULL CHECK (json_valid(body))
|
|
) STRICT;
|
|
CREATE TABLE role_claims (
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
at TEXT NOT NULL,
|
|
business TEXT NOT NULL, role TEXT NOT NULL,
|
|
op TEXT NOT NULL CHECK (op IN ('claim','release','revoke')),
|
|
holder_run TEXT NOT NULL,
|
|
harness TEXT NOT NULL, address TEXT,
|
|
by TEXT NOT NULL, reason TEXT,
|
|
decision TEXT
|
|
) STRICT;
|
|
CREATE TABLE decisions (
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
id TEXT NOT NULL UNIQUE,
|
|
at TEXT NOT NULL,
|
|
business TEXT NOT NULL, project TEXT,
|
|
raised_by_role TEXT NOT NULL, raised_by_run TEXT NOT NULL,
|
|
class TEXT NOT NULL CHECK (class IN ('routine','within-role','cross-role','gated')),
|
|
action TEXT NOT NULL,
|
|
route_to TEXT NOT NULL,
|
|
question TEXT NOT NULL,
|
|
options TEXT NOT NULL CHECK (json_valid(options) AND json_array_length(options) BETWEEN 2 AND 9),
|
|
recommendation TEXT NOT NULL,
|
|
task_ref TEXT, requirement_ref TEXT,
|
|
supersedes TEXT REFERENCES decisions(id)
|
|
) STRICT;
|
|
CREATE TABLE decision_events (
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
decision TEXT NOT NULL REFERENCES decisions(id),
|
|
at TEXT NOT NULL,
|
|
op TEXT NOT NULL CHECK (op IN ('seen','resolved','withdrawn','expired')),
|
|
by TEXT NOT NULL,
|
|
choice TEXT, note TEXT, via TEXT
|
|
) STRICT;
|
|
CREATE TABLE messages (
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
id TEXT NOT NULL UNIQUE,
|
|
at TEXT NOT NULL,
|
|
business TEXT NOT NULL,
|
|
from_role TEXT NOT NULL, from_run TEXT NOT NULL,
|
|
to_role TEXT NOT NULL,
|
|
class TEXT NOT NULL,
|
|
in_reply_to TEXT REFERENCES messages(id),
|
|
decision TEXT REFERENCES decisions(id),
|
|
corrects TEXT REFERENCES messages(id),
|
|
body TEXT NOT NULL
|
|
) STRICT;
|
|
CREATE TABLE deliveries (
|
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
message TEXT NOT NULL REFERENCES messages(id),
|
|
at TEXT NOT NULL,
|
|
op TEXT NOT NULL CHECK (op IN ('routed','delivered','failed','read')),
|
|
holder_run TEXT, transport TEXT, address TEXT, detail TEXT
|
|
) STRICT;
|
|
CREATE TRIGGER decisions_resolve_once BEFORE INSERT ON decision_events
|
|
WHEN NEW.op IN ('resolved','withdrawn','expired') AND EXISTS (
|
|
SELECT 1 FROM decision_events WHERE decision = NEW.decision AND op IN ('resolved','withdrawn','expired'))
|
|
BEGIN SELECT RAISE(ABORT, 'decision already closed'); END;
|
|
CREATE TRIGGER decisions_resolved_choice BEFORE INSERT ON decision_events
|
|
WHEN NEW.op = 'resolved' AND (NEW.choice IS NULL OR NOT EXISTS (
|
|
SELECT 1 FROM decisions d, json_each(d.options) o WHERE d.id = NEW.decision AND json_extract(o.value,'$.key') = NEW.choice))
|
|
BEGIN SELECT RAISE(ABORT, 'resolution must name one of the options'); END;
|
|
CREATE TRIGGER role_one_holder BEFORE INSERT ON role_claims
|
|
WHEN NEW.op = 'claim' AND (SELECT op FROM role_claims WHERE business = NEW.business AND role = NEW.role ORDER BY seq DESC LIMIT 1) = 'claim'
|
|
BEGIN SELECT RAISE(ABORT, 'role already held'); END;
|
|
CREATE TRIGGER role_release_by_holder BEFORE INSERT ON role_claims
|
|
WHEN NEW.op IN ('release','revoke') AND COALESCE((SELECT op FROM role_claims WHERE business = NEW.business AND role = NEW.role ORDER BY seq DESC LIMIT 1),'') <> 'claim'
|
|
BEGIN SELECT RAISE(ABORT, 'role is not held'); END;
|
|
CREATE TRIGGER role_release_same_run BEFORE INSERT ON role_claims
|
|
WHEN NEW.op = 'release' AND (SELECT holder_run FROM role_claims WHERE business = NEW.business AND role = NEW.role ORDER BY seq DESC LIMIT 1) <> NEW.holder_run
|
|
BEGIN SELECT RAISE(ABORT, 'only the holder releases; others revoke'); END;
|
|
CREATE TRIGGER role_revoke_needs_decision BEFORE INSERT ON role_claims
|
|
WHEN NEW.op = 'revoke' AND NEW.decision IS NULL
|
|
BEGIN SELECT RAISE(ABORT, 'revoke needs a resolved decision'); END;
|
|
CREATE TRIGGER meta_no_update BEFORE UPDATE ON meta BEGIN SELECT RAISE(ABORT, 'meta is append-only'); END;
|
|
CREATE TRIGGER meta_no_delete BEFORE DELETE ON meta BEGIN SELECT RAISE(ABORT, 'meta is append-only'); END;
|
|
CREATE TRIGGER events_no_update BEFORE UPDATE ON events BEGIN SELECT RAISE(ABORT, 'events is append-only'); END;
|
|
CREATE TRIGGER events_no_delete BEFORE DELETE ON events BEGIN SELECT RAISE(ABORT, 'events is append-only'); END;
|
|
CREATE TRIGGER role_claims_no_update BEFORE UPDATE ON role_claims BEGIN SELECT RAISE(ABORT, 'role_claims is append-only'); END;
|
|
CREATE TRIGGER role_claims_no_delete BEFORE DELETE ON role_claims BEGIN SELECT RAISE(ABORT, 'role_claims is append-only'); END;
|
|
CREATE TRIGGER decisions_no_update BEFORE UPDATE ON decisions BEGIN SELECT RAISE(ABORT, 'decisions is append-only'); END;
|
|
CREATE TRIGGER decisions_no_delete BEFORE DELETE ON decisions BEGIN SELECT RAISE(ABORT, 'decisions is append-only'); END;
|
|
CREATE TRIGGER decision_events_no_update BEFORE UPDATE ON decision_events BEGIN SELECT RAISE(ABORT, 'decision_events is append-only'); END;
|
|
CREATE TRIGGER decision_events_no_delete BEFORE DELETE ON decision_events BEGIN SELECT RAISE(ABORT, 'decision_events is append-only'); END;
|
|
CREATE TRIGGER messages_no_update BEFORE UPDATE ON messages BEGIN SELECT RAISE(ABORT, 'messages is append-only'); END;
|
|
CREATE TRIGGER messages_no_delete BEFORE DELETE ON messages BEGIN SELECT RAISE(ABORT, 'messages is append-only'); END;
|
|
CREATE TRIGGER deliveries_no_update BEFORE UPDATE ON deliveries BEGIN SELECT RAISE(ABORT, 'deliveries is append-only'); END;
|
|
CREATE TRIGGER deliveries_no_delete BEFORE DELETE ON deliveries BEGIN SELECT RAISE(ABORT, 'deliveries is append-only'); END;
|
|
CREATE TRIGGER meta_no_replace BEFORE INSERT ON meta WHEN EXISTS (SELECT 1 FROM meta WHERE key = NEW.key) BEGIN SELECT RAISE(ABORT, 'meta is append-only'); END;
|
|
CREATE TRIGGER events_no_replace BEFORE INSERT ON events WHEN EXISTS (SELECT 1 FROM events WHERE seq = NEW.seq OR id = NEW.id) BEGIN SELECT RAISE(ABORT, 'events is append-only'); END;
|
|
CREATE TRIGGER role_claims_no_replace BEFORE INSERT ON role_claims WHEN EXISTS (SELECT 1 FROM role_claims WHERE seq = NEW.seq) BEGIN SELECT RAISE(ABORT, 'role_claims is append-only'); END;
|
|
CREATE TRIGGER decisions_no_replace BEFORE INSERT ON decisions WHEN EXISTS (SELECT 1 FROM decisions WHERE seq = NEW.seq OR id = NEW.id) BEGIN SELECT RAISE(ABORT, 'decisions is append-only'); END;
|
|
CREATE TRIGGER decision_events_no_replace BEFORE INSERT ON decision_events WHEN EXISTS (SELECT 1 FROM decision_events WHERE seq = NEW.seq) BEGIN SELECT RAISE(ABORT, 'decision_events is append-only'); END;
|
|
CREATE TRIGGER messages_no_replace BEFORE INSERT ON messages WHEN EXISTS (SELECT 1 FROM messages WHERE seq = NEW.seq OR id = NEW.id) BEGIN SELECT RAISE(ABORT, 'messages is append-only'); END;
|
|
CREATE TRIGGER deliveries_no_replace BEFORE INSERT ON deliveries WHEN EXISTS (SELECT 1 FROM deliveries WHERE seq = NEW.seq) BEGIN SELECT RAISE(ABORT, 'deliveries is append-only'); END;
|