Phase 1 Workstream A+E: thesis substrate + dual-approval gate
- migration 0002_phase1_architect: thesis_lines (core spine + per-segment lines), thesis_nodes (+ append-only revisions), thesis_versions (one-canonical-per-line DB invariant), thesis_reviews (dual approval + feedback), segments. Reversible. - backend/mcp/architect_tools.py: agent draft tools (node tree, versions, segments, get_canonical fails-closed) — NO self-approval path. MCP-exposed. - backend/thesis_review.py + server.py routes: human-gated approval. Dual sign-off via thesis_required_approvals; atomic supersede; every action logged. - docs/PHASE_1.md (kickoff brief); docs/OPERATIONS.md (partner guide); start9/0.4 "Resolve duplicate names" fuzzy action. Verified on synthetic data: dual approval promotes correctly, exactly one canonical survives supersede, get_canonical fails closed, full interaction_log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
-- Reversal of 0002_phase1_architect.sql. Run manually (never auto-applied).
|
||||
DROP TABLE IF EXISTS thesis_reviews;
|
||||
DROP TABLE IF EXISTS thesis_node_revisions;
|
||||
DROP TABLE IF EXISTS thesis_nodes;
|
||||
DROP TABLE IF EXISTS thesis_versions;
|
||||
DROP TABLE IF EXISTS thesis_lines;
|
||||
DROP TABLE IF EXISTS segments;
|
||||
DELETE FROM schema_migrations WHERE filename = '0002_phase1_architect.sql';
|
||||
@@ -0,0 +1,124 @@
|
||||
-- Phase 1 — Workstream A/E: the Architect's thesis substrate.
|
||||
--
|
||||
-- ADDITIVE AND REVERSIBLE ONLY (guardrail #3). Reversal: 0002_phase1_architect.down.sql.
|
||||
-- Applied once by backend/core_migrations.py (schema_migrations ledger).
|
||||
--
|
||||
-- Models the "living messaging source of truth" as: multiple THESIS LINES (a core
|
||||
-- spine + per-segment narratives), each a tree of typed NODES with full revision
|
||||
-- history, frozen into immutable VERSIONS that a human signs off to make canonical.
|
||||
-- Dual approval + collaborative text feedback live in thesis_reviews. Segments are
|
||||
-- versioned defs that tie to the Phase-0 canonical_entities.segment pointer.
|
||||
|
||||
-- ============================================================================
|
||||
-- thesis_lines — each distinct narrative line. is_core=1 is the shared spine;
|
||||
-- others are segment-specific (Grant: different segments may carry different,
|
||||
-- related thesis lines). Full-length ids (not the 8-char CRM ids).
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS thesis_lines (
|
||||
id TEXT PRIMARY KEY, -- 'thl_' + uuid4 hex
|
||||
line_key TEXT NOT NULL UNIQUE, -- slug: 'core' | 'btc_native_hnwi' | ...
|
||||
name TEXT NOT NULL,
|
||||
segment_key TEXT, -- NULL for the core spine; else the segment this line serves
|
||||
is_core INTEGER NOT NULL DEFAULT 0,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
deleted_at TEXT
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- thesis_nodes — typed node tree per line (the unit of iteration).
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS thesis_nodes (
|
||||
id TEXT PRIMARY KEY, -- 'thn_' + uuid4 hex
|
||||
line_id TEXT NOT NULL REFERENCES thesis_lines(id) ON DELETE CASCADE,
|
||||
parent_id TEXT, -- tree edge; NULL for thesis_root
|
||||
node_type TEXT NOT NULL, -- thesis_root|throughline|section|claim|proof_point|objection|rebuttal|segment_cut
|
||||
ord REAL NOT NULL DEFAULT 0,-- REAL so insert-between never renumbers siblings
|
||||
title TEXT,
|
||||
body TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft', -- draft|candidate|approved|retired
|
||||
variant_group TEXT, -- nodes sharing this are competing phrasings of one idea (A/B)
|
||||
meta TEXT, -- JSON: tone tags, confidence, evidence_refs -> canonical_entities/source ids
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
deleted_at TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_thesis_nodes_line ON thesis_nodes(line_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_thesis_nodes_parent ON thesis_nodes(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_thesis_nodes_variant ON thesis_nodes(variant_group);
|
||||
|
||||
-- ============================================================================
|
||||
-- thesis_node_revisions — append-only per-node history (provenance + undo).
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS thesis_node_revisions (
|
||||
id TEXT PRIMARY KEY,
|
||||
node_id TEXT NOT NULL,
|
||||
line_id TEXT,
|
||||
rev_no INTEGER NOT NULL,
|
||||
node_type TEXT, title TEXT, body TEXT, status TEXT, ord REAL, variant_group TEXT, meta TEXT,
|
||||
change_summary TEXT,
|
||||
change_reason TEXT, -- WHY the edit
|
||||
actor_type TEXT, -- human | agent
|
||||
actor_id TEXT, -- users.id or 'architect'
|
||||
claude_session_id TEXT, -- ties an agent edit back to its reasoning session
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_thesis_revs_node ON thesis_node_revisions(node_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- thesis_versions — immutable named snapshots per line; ONE canonical per line.
|
||||
-- body_json freezes the published artifact (the Architect->Scribe contract).
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS thesis_versions (
|
||||
id TEXT PRIMARY KEY, -- 'thv_' + uuid4 hex
|
||||
line_id TEXT NOT NULL REFERENCES thesis_lines(id) ON DELETE CASCADE,
|
||||
version_no INTEGER NOT NULL,
|
||||
body_json TEXT NOT NULL, -- {throughline,pillars,claims,proof_points,segment_angles,voice,guardrails}
|
||||
status TEXT NOT NULL DEFAULT 'draft', -- draft|in_review|canonical|superseded
|
||||
rationale TEXT,
|
||||
parent_version_id TEXT,
|
||||
created_by TEXT, -- users.id or 'architect'
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
approved_at TEXT,
|
||||
superseded_by TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_thesis_versions_line ON thesis_versions(line_id, version_no);
|
||||
-- Hard invariant: at most one canonical version per line.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_thesis_one_canonical ON thesis_versions(line_id) WHERE status = 'canonical';
|
||||
|
||||
-- ============================================================================
|
||||
-- thesis_reviews — dual approval + collaborative text feedback. Both partners
|
||||
-- can comment/approve; the Architect ingests feedback to iterate. Promotion to
|
||||
-- canonical happens when distinct 'approve' reviews meet the required count
|
||||
-- (app_settings 'thesis_required_approvals', default 1) via the human route.
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS thesis_reviews (
|
||||
id TEXT PRIMARY KEY,
|
||||
version_id TEXT NOT NULL REFERENCES thesis_versions(id) ON DELETE CASCADE,
|
||||
reviewer_user_id TEXT NOT NULL, -- users.id (the human partner)
|
||||
decision TEXT NOT NULL, -- approve | request_changes | comment
|
||||
feedback TEXT, -- free-text the Architect reads to iterate
|
||||
target_node_id TEXT, -- optional: feedback scoped to one node
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_thesis_reviews_version ON thesis_reviews(version_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- segments — versioned LP segment definitions; one active per segment_key.
|
||||
-- canonical_entities.segment (Phase 0) stores the segment_key pointer.
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS segments (
|
||||
id TEXT PRIMARY KEY,
|
||||
segment_key TEXT NOT NULL, -- slug
|
||||
name TEXT NOT NULL,
|
||||
definition TEXT,
|
||||
needs_to_hear TEXT,
|
||||
avoid TEXT,
|
||||
version_no INTEGER NOT NULL DEFAULT 1,
|
||||
status TEXT NOT NULL DEFAULT 'active', -- active | retired
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_segments_one_active ON segments(segment_key) WHERE status = 'active';
|
||||
Reference in New Issue
Block a user