From 867b096b4f76aab05cf4dcdd5e2201ce54fe0d24 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sun, 17 May 2026 00:42:06 +0800 Subject: [PATCH] auto-sync: 2026-05-17 00:42:06 --- src/blackboard/db.py | 349 ++++++++++++++++++++----------------------- 1 file changed, 160 insertions(+), 189 deletions(-) diff --git a/src/blackboard/db.py b/src/blackboard/db.py index ce3fc5f..4e09c40 100644 --- a/src/blackboard/db.py +++ b/src/blackboard/db.py @@ -12,7 +12,8 @@ def init_db(db_path: Path) -> None: db_path.parent.mkdir(parents=True, exist_ok=True) conn = _connect(db_path) try: - conn.executescript(SCHEMA_SQL) + for stmt in _SCHEMA_STATEMENTS: + conn.execute(stmt) conn.commit() finally: conn.close() @@ -90,203 +91,173 @@ ATTEMPT_OUTCOMES = frozenset({ # --------------------------------------------------------------------------- -# Schema SQL +# Schema - split into individual statements to avoid SQLite 3.51 CHECK bug +# (table-level CHECK as non-last column definition causes parse error) # --------------------------------------------------------------------------- -SCHEMA_SQL = """ --- ===== 任务表 ===== -CREATE TABLE IF NOT EXISTS tasks ( - id TEXT PRIMARY KEY, - title TEXT NOT NULL, - description TEXT, - status TEXT NOT NULL DEFAULT 'pending', - CHECK (status IN ('pending','claimed','working','review','done','failed','blocked','cancelled')), +_SCHEMA_STATEMENTS = [ + # tasks + """CREATE TABLE IF NOT EXISTS tasks ( + id TEXT PRIMARY KEY, + title TEXT NOT NULL, + description TEXT, + status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','claimed','working','review','done','failed','blocked','cancelled')), + assignee TEXT, + assigned_by TEXT, + depends_on TEXT, + parent_task TEXT, + priority INTEGER NOT NULL DEFAULT 5, + task_type TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')), + claimed_at TEXT, + started_at TEXT, + completed_at TEXT, + deadline TEXT, + retry_count INTEGER NOT NULL DEFAULT 0, + max_retries INTEGER NOT NULL DEFAULT 2, + must_haves TEXT, + risk_level TEXT DEFAULT 'standard', + estimated_duration_minutes INTEGER, + escalated INTEGER DEFAULT 0 + )""", + "CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)", + "CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee)", + "CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task)", - assignee TEXT, - assigned_by TEXT, + # comments + """CREATE TABLE IF NOT EXISTS comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL REFERENCES tasks(id), + author TEXT NOT NULL, + comment_type TEXT NOT NULL DEFAULT 'general' CHECK (comment_type IN ('general','handoff','observation','rebuttal','rebuttal_response','debate_argument','debate_rebuttal','debate_judgment')), + body TEXT NOT NULL, + mentions TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + )""", + "CREATE INDEX IF NOT EXISTS idx_comments_task ON comments(task_id)", + "CREATE INDEX IF NOT EXISTS idx_comments_type ON comments(task_id, comment_type)", + "CREATE INDEX IF NOT EXISTS idx_comments_author ON comments(author)", - depends_on TEXT, - parent_task TEXT, + # outputs + """CREATE TABLE IF NOT EXISTS outputs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL REFERENCES tasks(id), + agent TEXT NOT NULL, + output_type TEXT NOT NULL CHECK (output_type IN ('code','document','data','config','other')), + title TEXT NOT NULL, + content_path TEXT, + summary TEXT, + metadata TEXT, + attempt_number INTEGER DEFAULT 1, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + )""", + "CREATE INDEX IF NOT EXISTS idx_outputs_task ON outputs(task_id)", - priority INTEGER NOT NULL DEFAULT 5, - task_type TEXT, + # decisions + """CREATE TABLE IF NOT EXISTS decisions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL REFERENCES tasks(id), + decider TEXT NOT NULL, + decision TEXT NOT NULL, + rationale TEXT NOT NULL, + alternatives TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + )""", + "CREATE INDEX IF NOT EXISTS idx_decisions_task ON decisions(task_id)", - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')), - claimed_at TEXT, - started_at TEXT, - completed_at TEXT, - deadline TEXT, + # observations + """CREATE TABLE IF NOT EXISTS observations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL REFERENCES tasks(id), + observer TEXT NOT NULL, + severity TEXT NOT NULL DEFAULT 'info' CHECK (severity IN ('blocking','warning','info','audit')), + body TEXT NOT NULL, + resolved_by TEXT, + resolved_at TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + )""", - retry_count INTEGER NOT NULL DEFAULT 0, - max_retries INTEGER NOT NULL DEFAULT 2, + # events + """CREATE TABLE IF NOT EXISTS events ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT, + agent TEXT, + event_type TEXT NOT NULL, + detail TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + )""", + "CREATE INDEX IF NOT EXISTS idx_events_task ON events(task_id)", + "CREATE INDEX IF NOT EXISTS idx_events_time ON events(created_at)", - must_haves TEXT, - risk_level TEXT DEFAULT 'standard', - estimated_duration_minutes INTEGER, + # agents + """CREATE TABLE IF NOT EXISTS agents ( + agent_id TEXT PRIMARY KEY, + role TEXT, + current_status TEXT DEFAULT 'idle', + current_task TEXT, + last_active TEXT, + capabilities TEXT + )""", - escalated INTEGER DEFAULT 0 -); + # task_attempts + """CREATE TABLE IF NOT EXISTS task_attempts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL REFERENCES tasks(id), + attempt_number INTEGER NOT NULL, + agent TEXT NOT NULL, + outcome TEXT NOT NULL CHECK (outcome IN ('completed','blocked','crashed','timed_out','spawn_failed','reclaimed')), + exit_code INTEGER, + log_path TEXT, + summary TEXT, + metadata TEXT, + started_at TEXT NOT NULL DEFAULT (datetime('now')), + completed_at TEXT + )""", + "CREATE INDEX IF NOT EXISTS idx_attempts_task ON task_attempts(task_id)", -CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status); -CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee); -CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task); + # reviews + """CREATE TABLE IF NOT EXISTS reviews ( + id TEXT PRIMARY KEY, + task_id TEXT NOT NULL REFERENCES tasks(id), + output_id TEXT, + reviewer TEXT NOT NULL, + review_type TEXT NOT NULL CHECK (review_type IN ('plan_review','output_review','guardrail','final_review')), + verdict TEXT NOT NULL CHECK (verdict IN ('approved','rejected','needs_revision')), + confidence REAL, + round INTEGER NOT NULL DEFAULT 1, + max_rounds INTEGER NOT NULL DEFAULT 3, + consensus_reached INTEGER DEFAULT 0, + summary TEXT NOT NULL, + detail_path TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + )""", + "CREATE INDEX IF NOT EXISTS idx_reviews_task ON reviews(task_id)", + "CREATE INDEX IF NOT EXISTS idx_reviews_output ON reviews(output_id)", --- ===== 评论线程表 ===== -CREATE TABLE IF NOT EXISTS comments ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - author TEXT NOT NULL, - comment_type TEXT NOT NULL DEFAULT 'general', - body TEXT NOT NULL, - mentions TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - FOREIGN KEY (task_id) REFERENCES tasks(id), - CHECK (comment_type IN ('general','handoff','observation','rebuttal','rebuttal_response','debate_argument','debate_rebuttal','debate_judgment')) -); + # experiences + """CREATE TABLE IF NOT EXISTS experiences ( + experience_id TEXT PRIMARY KEY, + source TEXT NOT NULL CHECK (source IN ('task_completion','error_correction','review_finding','manual')), + task_id TEXT, + summary TEXT NOT NULL, + category TEXT NOT NULL CHECK (category IN ('pitfall','best_practice','pattern','anti_pattern')), + confidence REAL DEFAULT 0.8, + status TEXT DEFAULT 'active' CHECK (status IN ('draft','active','deprecated')), + skill_id TEXT, + usage_count INTEGER DEFAULT 0, + last_used_at TEXT, + created_at TEXT NOT NULL, + created_by TEXT NOT NULL, + updated_at TEXT, + deprecated_reason TEXT + )""", -CREATE INDEX IF NOT EXISTS idx_comments_task ON comments(task_id); -CREATE INDEX IF NOT EXISTS idx_comments_type ON comments(task_id, comment_type); -CREATE INDEX IF NOT EXISTS idx_comments_author ON comments(author); - --- ===== 产出表 ===== -CREATE TABLE IF NOT EXISTS outputs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - agent TEXT NOT NULL, - output_type TEXT NOT NULL, - title TEXT NOT NULL, - content_path TEXT, - summary TEXT, - metadata TEXT, - attempt_number INTEGER DEFAULT 1, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - FOREIGN KEY (task_id) REFERENCES tasks(id), - CHECK (output_type IN ('code','document','data','config','other')) -); - -CREATE INDEX IF NOT EXISTS idx_outputs_task ON outputs(task_id); - --- ===== 决策记录表 ===== -CREATE TABLE IF NOT EXISTS decisions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - decider TEXT NOT NULL, - decision TEXT NOT NULL, - rationale TEXT NOT NULL, - alternatives TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - FOREIGN KEY (task_id) REFERENCES tasks(id) -); - -CREATE INDEX IF NOT EXISTS idx_decisions_task ON decisions(task_id); - --- ===== 观察表 ===== -CREATE TABLE IF NOT EXISTS observations ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - observer TEXT NOT NULL, - severity TEXT NOT NULL DEFAULT 'info', - CHECK (severity IN ('blocking','warning','info','audit')), - body TEXT NOT NULL, - resolved_by TEXT, - resolved_at TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - FOREIGN KEY (task_id) REFERENCES tasks(id) -); - --- ===== 事件日志 ===== -CREATE TABLE IF NOT EXISTS events ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT, - agent TEXT, - event_type TEXT NOT NULL, - detail TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); - -CREATE INDEX IF NOT EXISTS idx_events_task ON events(task_id); -CREATE INDEX IF NOT EXISTS idx_events_time ON events(created_at); - --- ===== Agent 注册表 ===== -CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - role TEXT, - current_status TEXT DEFAULT 'idle', - current_task TEXT, - last_active TEXT, - capabilities TEXT -); - --- ===== 任务尝试记录 ===== -CREATE TABLE IF NOT EXISTS task_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - task_id TEXT NOT NULL, - attempt_number INTEGER NOT NULL, - agent TEXT NOT NULL, - outcome TEXT NOT NULL, - CHECK (outcome IN ('completed','blocked','crashed','timed_out','spawn_failed','reclaimed')), - exit_code INTEGER, - log_path TEXT, - summary TEXT, - metadata TEXT, - started_at TEXT NOT NULL DEFAULT (datetime('now')), - completed_at TEXT, - FOREIGN KEY (task_id) REFERENCES tasks(id) -); - -CREATE INDEX IF NOT EXISTS idx_attempts_task ON task_attempts(task_id); - --- ===== 评审表 ===== -CREATE TABLE IF NOT EXISTS reviews ( - id TEXT PRIMARY KEY, - task_id TEXT NOT NULL, - output_id TEXT, - reviewer TEXT NOT NULL, - review_type TEXT NOT NULL, - CHECK (review_type IN ('plan_review','output_review','guardrail','final_review')), - verdict TEXT NOT NULL, - CHECK (verdict IN ('approved','rejected','needs_revision')), - confidence REAL, - round INTEGER NOT NULL DEFAULT 1, - max_rounds INTEGER NOT NULL DEFAULT 3, - consensus_reached INTEGER DEFAULT 0, - summary TEXT NOT NULL, - detail_path TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - FOREIGN KEY (task_id) REFERENCES tasks(id) -); - -CREATE INDEX IF NOT EXISTS idx_reviews_task ON reviews(task_id); -CREATE INDEX IF NOT EXISTS idx_reviews_output ON reviews(output_id); - --- ===== 经验表 ===== -CREATE TABLE IF NOT EXISTS experiences ( - experience_id TEXT PRIMARY KEY, - source TEXT NOT NULL, - CHECK (source IN ('task_completion','error_correction','review_finding','manual')), - task_id TEXT, - summary TEXT NOT NULL, - category TEXT NOT NULL, - CHECK (category IN ('pitfall','best_practice','pattern','anti_pattern')), - confidence REAL DEFAULT 0.8, - status TEXT DEFAULT 'active', - CHECK (status IN ('draft','active','deprecated')), - skill_id TEXT, - usage_count INTEGER DEFAULT 0, - last_used_at TEXT, - created_at TEXT NOT NULL, - created_by TEXT NOT NULL, - updated_at TEXT, - deprecated_reason TEXT -); - --- ===== 经验标签关联表 ===== -CREATE TABLE IF NOT EXISTS experience_tags ( - experience_id TEXT NOT NULL REFERENCES experiences(experience_id), - tag TEXT NOT NULL, - PRIMARY KEY (experience_id, tag) -); - -CREATE INDEX IF NOT EXISTS idx_exptags_tag ON experience_tags(tag); -""" + # experience_tags + """CREATE TABLE IF NOT EXISTS experience_tags ( + experience_id TEXT NOT NULL REFERENCES experiences(experience_id), + tag TEXT NOT NULL, + PRIMARY KEY (experience_id, tag) + )""", + "CREATE INDEX IF NOT EXISTS idx_exptags_tag ON experience_tags(tag)", +]