refactor(monorepo): extract coord-core

This commit is contained in:
2026-03-20 13:01:16 +08:00
parent 3cf7a15626
commit 1938eb8f07
65 changed files with 6586 additions and 85 deletions
+50
View File
@@ -0,0 +1,50 @@
package db
import (
"context"
"database/sql"
"embed"
"fmt"
"sort"
)
//go:embed schema/*.sql
var schemaFS embed.FS
func ApplyMigrations(ctx context.Context, db *sql.DB) error {
files, err := schemaFS.ReadDir("schema")
if err != nil {
return fmt.Errorf("read embedded schema directory: %w", err)
}
names := make([]string, 0, len(files))
for _, file := range files {
if file.IsDir() {
continue
}
names = append(names, file.Name())
}
sort.Strings(names)
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("begin schema transaction: %w", err)
}
defer tx.Rollback()
for _, name := range names {
content, err := schemaFS.ReadFile("schema/" + name)
if err != nil {
return fmt.Errorf("read embedded schema file %q: %w", name, err)
}
if _, err := tx.ExecContext(ctx, string(content)); err != nil {
return fmt.Errorf("apply schema file %q: %w", name, err)
}
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("commit schema transaction: %w", err)
}
return nil
}
+38
View File
@@ -0,0 +1,38 @@
package db
import (
"context"
"database/sql"
"fmt"
"os"
"path/filepath"
_ "modernc.org/sqlite"
)
func Open(ctx context.Context, dbPath string) (*sql.DB, error) {
if err := ensureParentDir(dbPath); err != nil {
return nil, err
}
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, fmt.Errorf("open sqlite database: %w", err)
}
if err := applyPragmas(ctx, db); err != nil {
_ = db.Close()
return nil, err
}
return db, nil
}
func ensureParentDir(dbPath string) error {
parent := filepath.Dir(dbPath)
if parent == "." || parent == "" {
return nil
}
return os.MkdirAll(parent, 0o755)
}
+23
View File
@@ -0,0 +1,23 @@
package db
import (
"context"
"database/sql"
"fmt"
)
func applyPragmas(ctx context.Context, db *sql.DB) error {
pragmas := []string{
"PRAGMA foreign_keys = ON;",
"PRAGMA journal_mode = WAL;",
"PRAGMA busy_timeout = 5000;",
}
for _, pragma := range pragmas {
if _, err := db.ExecContext(ctx, pragma); err != nil {
return fmt.Errorf("apply pragma %q: %w", pragma, err)
}
}
return nil
}
@@ -0,0 +1,51 @@
CREATE TABLE IF NOT EXISTS threads (
thread_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
subject TEXT NOT NULL,
created_by TEXT NOT NULL,
assigned_to TEXT NOT NULL,
status TEXT NOT NULL,
priority TEXT NOT NULL DEFAULT 'normal',
latest_message_id TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS messages (
message_id TEXT PRIMARY KEY,
thread_id TEXT NOT NULL,
from_agent TEXT NOT NULL,
to_agent TEXT NOT NULL,
kind TEXT NOT NULL,
summary TEXT NOT NULL,
body TEXT NOT NULL DEFAULT '',
payload_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
FOREIGN KEY(thread_id) REFERENCES threads(thread_id)
);
CREATE TABLE IF NOT EXISTS leases (
thread_id TEXT PRIMARY KEY,
agent_id TEXT NOT NULL,
lease_token TEXT NOT NULL,
claimed_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
released_at TEXT
);
CREATE TABLE IF NOT EXISTS artifacts (
artifact_id TEXT PRIMARY KEY,
message_id TEXT NOT NULL,
path TEXT NOT NULL,
kind TEXT NOT NULL,
metadata_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
FOREIGN KEY(message_id) REFERENCES messages(message_id)
);
CREATE INDEX IF NOT EXISTS idx_threads_status_assigned
ON threads(status, assigned_to, updated_at);
CREATE INDEX IF NOT EXISTS idx_messages_thread_created
ON messages(thread_id, created_at);
@@ -0,0 +1,52 @@
CREATE TABLE IF NOT EXISTS runs (
run_id TEXT PRIMARY KEY,
goal TEXT NOT NULL,
summary TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks (
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
title TEXT NOT NULL,
summary TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
default_to TEXT,
priority TEXT NOT NULL DEFAULT 'normal',
acceptance_json TEXT NOT NULL DEFAULT '[]',
latest_attempt_no INTEGER,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (run_id, task_id),
FOREIGN KEY(run_id) REFERENCES runs(run_id)
);
CREATE TABLE IF NOT EXISTS task_dependencies (
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
depends_on_task_id TEXT NOT NULL,
PRIMARY KEY (run_id, task_id, depends_on_task_id)
);
CREATE TABLE IF NOT EXISTS task_attempts (
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
attempt_no INTEGER NOT NULL,
assigned_to TEXT NOT NULL,
thread_id TEXT NOT NULL,
base_ref TEXT,
base_commit TEXT,
branch_name TEXT,
worktree_path TEXT,
workspace_status TEXT,
result_commit TEXT,
status TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (run_id, task_id, attempt_no)
);
CREATE INDEX IF NOT EXISTS idx_tasks_run_status
ON tasks(run_id, status, priority, updated_at);
@@ -0,0 +1,18 @@
CREATE TABLE IF NOT EXISTS events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
thread_id TEXT,
source TEXT NOT NULL,
event_type TEXT NOT NULL,
message_id TEXT,
summary TEXT NOT NULL DEFAULT '',
payload_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_events_run_event
ON events(run_id, event_id);
CREATE INDEX IF NOT EXISTS idx_events_thread_event
ON events(thread_id, event_id);
@@ -0,0 +1,45 @@
CREATE TABLE IF NOT EXISTS council_runs (
run_id TEXT PRIMARY KEY,
mode TEXT NOT NULL,
target_type TEXT NOT NULL,
output_mode TEXT NOT NULL,
only_unanimous INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS council_reviewers (
run_id TEXT NOT NULL,
reviewer_role TEXT NOT NULL,
task_id TEXT NOT NULL,
status TEXT NOT NULL,
PRIMARY KEY (run_id, reviewer_role)
);
CREATE TABLE IF NOT EXISTS council_findings (
run_id TEXT NOT NULL,
reviewer_role TEXT NOT NULL,
finding_id TEXT NOT NULL,
title TEXT NOT NULL,
summary TEXT NOT NULL,
proposal TEXT NOT NULL,
rationale TEXT NOT NULL,
confidence TEXT NOT NULL,
tags_json TEXT NOT NULL DEFAULT '[]',
target_refs_json TEXT NOT NULL DEFAULT '{}',
PRIMARY KEY (run_id, reviewer_role, finding_id)
);
CREATE TABLE IF NOT EXISTS council_groups (
run_id TEXT NOT NULL,
group_id TEXT NOT NULL,
proposal TEXT NOT NULL,
bucket TEXT NOT NULL,
support_count INTEGER NOT NULL,
supporters_json TEXT NOT NULL DEFAULT '[]',
dissenters_json TEXT NOT NULL DEFAULT '[]',
rationale_summary TEXT NOT NULL DEFAULT '',
tags_json TEXT NOT NULL DEFAULT '[]',
source_finding_ids_json TEXT NOT NULL DEFAULT '[]',
PRIMARY KEY (run_id, group_id)
);
@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS thread_reads (
thread_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
last_read_message_id TEXT NOT NULL,
last_read_at TEXT NOT NULL,
PRIMARY KEY(thread_id, agent_id),
FOREIGN KEY(thread_id) REFERENCES threads(thread_id),
FOREIGN KEY(last_read_message_id) REFERENCES messages(message_id)
);
CREATE INDEX IF NOT EXISTS idx_thread_reads_agent
ON thread_reads(agent_id, last_read_at);
@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS council_inputs (
run_id TEXT PRIMARY KEY,
prompt TEXT NOT NULL DEFAULT '',
target_file TEXT NOT NULL DEFAULT '',
repo_path TEXT NOT NULL DEFAULT '',
target_task_id TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS council_reports (
run_id TEXT PRIMARY KEY,
show_json TEXT NOT NULL DEFAULT '[]',
summary_json TEXT NOT NULL DEFAULT '{}',
markdown_path TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);