chore(repo): reinitialize repository

This commit is contained in:
2026-03-18 11:29:54 +08:00
commit 24871e213a
288 changed files with 44369 additions and 0 deletions
@@ -0,0 +1,335 @@
CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY,
slug TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
root_path TEXT NOT NULL UNIQUE,
default_branch TEXT NOT NULL,
status TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS workspaces (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
slug TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
root_path TEXT NOT NULL UNIQUE,
base_branch TEXT NOT NULL,
worktree_branch TEXT NOT NULL UNIQUE,
runtime_backend TEXT NOT NULL,
runtime_ref TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_workspaces_project ON workspaces(project_id, status, slug);
CREATE TABLE IF NOT EXISTS roles (
name TEXT PRIMARY KEY,
title TEXT NOT NULL,
category TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
is_enabled INTEGER NOT NULL DEFAULT 1,
is_builtin INTEGER NOT NULL DEFAULT 1,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS role_prompts (
id TEXT PRIMARY KEY,
role_name TEXT NOT NULL REFERENCES roles(name) ON DELETE CASCADE,
workspace_id TEXT REFERENCES workspaces(id) ON DELETE CASCADE,
prompt_kind TEXT NOT NULL,
content_markdown TEXT NOT NULL,
version INTEGER NOT NULL,
updated_by TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_prompts_global
ON role_prompts(role_name, prompt_kind)
WHERE workspace_id IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_prompts_workspace
ON role_prompts(role_name, workspace_id, prompt_kind)
WHERE workspace_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_role_prompts_role ON role_prompts(role_name, prompt_kind, workspace_id);
CREATE TABLE IF NOT EXISTS role_configs (
id TEXT PRIMARY KEY,
role_name TEXT NOT NULL REFERENCES roles(name) ON DELETE CASCADE,
workspace_id TEXT REFERENCES workspaces(id) ON DELETE CASCADE,
model TEXT NOT NULL DEFAULT '',
model_provider TEXT NOT NULL DEFAULT '',
provider_name TEXT NOT NULL DEFAULT '',
provider_base_url TEXT NOT NULL DEFAULT '',
provider_wire_api TEXT NOT NULL DEFAULT '',
reasoning_effort TEXT NOT NULL DEFAULT '',
plan_model TEXT NOT NULL DEFAULT '',
plan_reasoning_effort TEXT NOT NULL DEFAULT '',
disable_response_storage INTEGER NOT NULL DEFAULT 0,
shell_env_inherit TEXT NOT NULL DEFAULT 'core',
shell_env_overrides_json TEXT NOT NULL DEFAULT '{}',
extra_config_json TEXT NOT NULL DEFAULT '{}',
version INTEGER NOT NULL,
updated_by TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_configs_global
ON role_configs(role_name)
WHERE workspace_id IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_configs_workspace
ON role_configs(role_name, workspace_id)
WHERE workspace_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_role_configs_role ON role_configs(role_name, workspace_id);
CREATE TABLE IF NOT EXISTS skills (
id TEXT PRIMARY KEY,
skill_key TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
source_type TEXT NOT NULL,
content_markdown TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
version INTEGER NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_skills_status ON skills(status, name);
CREATE TABLE IF NOT EXISTS role_skill_bindings (
id TEXT PRIMARY KEY,
role_name TEXT NOT NULL REFERENCES roles(name) ON DELETE CASCADE,
workspace_id TEXT REFERENCES workspaces(id) ON DELETE CASCADE,
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
is_enabled INTEGER NOT NULL DEFAULT 1,
sort_order INTEGER NOT NULL DEFAULT 0,
config_json TEXT NOT NULL DEFAULT '{}',
version INTEGER NOT NULL,
updated_by TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_skill_bindings_global
ON role_skill_bindings(role_name, skill_id)
WHERE workspace_id IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_skill_bindings_workspace
ON role_skill_bindings(role_name, workspace_id, skill_id)
WHERE workspace_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_role_skill_bindings_role ON role_skill_bindings(role_name, workspace_id, sort_order);
CREATE TABLE IF NOT EXISTS config_change_logs (
id TEXT PRIMARY KEY,
config_domain TEXT NOT NULL,
entity_id TEXT NOT NULL,
workspace_id TEXT REFERENCES workspaces(id) ON DELETE SET NULL,
change_action TEXT NOT NULL,
before_json TEXT NOT NULL DEFAULT '{}',
after_json TEXT NOT NULL DEFAULT '{}',
changed_by TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_config_change_logs_domain ON config_change_logs(config_domain, created_at);
CREATE INDEX IF NOT EXISTS idx_config_change_logs_workspace ON config_change_logs(workspace_id, created_at);
CREATE TABLE IF NOT EXISTS topics (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
slug TEXT NOT NULL,
title TEXT NOT NULL,
space TEXT NOT NULL,
status TEXT NOT NULL,
source_topic_id TEXT REFERENCES topics(id) ON DELETE SET NULL,
owner_role_name TEXT REFERENCES roles(name) ON DELETE SET NULL,
summary TEXT NOT NULL DEFAULT '',
meta_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
closed_at TEXT,
UNIQUE(workspace_id, slug)
);
CREATE INDEX IF NOT EXISTS idx_topics_workspace_space ON topics(workspace_id, space, updated_at);
CREATE TABLE IF NOT EXISTS topic_documents (
id TEXT PRIMARY KEY,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
kind TEXT NOT NULL,
content_markdown TEXT NOT NULL,
version INTEGER NOT NULL,
updated_by_role_name TEXT REFERENCES roles(name) ON DELETE SET NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(topic_id, kind)
);
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
from_role_name TEXT NOT NULL REFERENCES roles(name),
to_expr TEXT NOT NULL,
type TEXT NOT NULL,
stage TEXT NOT NULL,
round INTEGER,
reply_to_message_id TEXT REFERENCES messages(id) ON DELETE SET NULL,
body_markdown TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_messages_workspace_topic ON messages(workspace_id, topic_id, created_at);
CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to_message_id, created_at);
CREATE TABLE IF NOT EXISTS message_deliveries (
message_id TEXT NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
recipient_role_name TEXT NOT NULL REFERENCES roles(name),
state TEXT NOT NULL,
delivered_at TEXT NOT NULL,
read_at TEXT,
archived_at TEXT,
updated_at TEXT NOT NULL,
PRIMARY KEY (message_id, recipient_role_name)
);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_role ON message_deliveries(recipient_role_name, state, updated_at);
CREATE TABLE IF NOT EXISTS role_threads (
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
role_name TEXT NOT NULL REFERENCES roles(name),
thread_id TEXT NOT NULL,
last_message_id TEXT REFERENCES messages(id) ON DELETE SET NULL,
created_at TEXT NOT NULL,
last_used_at TEXT NOT NULL,
PRIMARY KEY (workspace_id, topic_id, role_name)
);
CREATE INDEX IF NOT EXISTS idx_role_threads_role ON role_threads(role_name, last_used_at);
CREATE TABLE IF NOT EXISTS requirements (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
title TEXT NOT NULL,
body_markdown TEXT NOT NULL,
status TEXT NOT NULL,
priority INTEGER NOT NULL,
created_by_role_name TEXT NOT NULL REFERENCES roles(name),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
completed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_requirements_workspace_status ON requirements(workspace_id, status, priority, created_at);
CREATE INDEX IF NOT EXISTS idx_requirements_topic ON requirements(topic_id, created_at);
CREATE TABLE IF NOT EXISTS discovery_rounds (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
phase TEXT NOT NULL,
request_document_id TEXT REFERENCES topic_documents(id) ON DELETE SET NULL,
result_document_id TEXT REFERENCES topic_documents(id) ON DELETE SET NULL,
started_at TEXT NOT NULL,
completed_at TEXT,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_discovery_rounds_topic ON discovery_rounds(topic_id, started_at);
CREATE TABLE IF NOT EXISTS discovery_candidates (
id TEXT PRIMARY KEY,
round_id TEXT NOT NULL REFERENCES discovery_rounds(id) ON DELETE CASCADE,
proposer_role_name TEXT NOT NULL REFERENCES roles(name),
status TEXT NOT NULL,
title TEXT NOT NULL,
problem TEXT NOT NULL DEFAULT '',
evidence TEXT NOT NULL DEFAULT '',
proposal TEXT NOT NULL DEFAULT '',
expected_impact TEXT NOT NULL DEFAULT '',
risk TEXT NOT NULL DEFAULT '',
how_to_verify TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_discovery_candidates_round ON discovery_candidates(round_id, status, updated_at);
CREATE TABLE IF NOT EXISTS discovery_votes (
id TEXT PRIMARY KEY,
round_id TEXT NOT NULL REFERENCES discovery_rounds(id) ON DELETE CASCADE,
candidate_id TEXT NOT NULL REFERENCES discovery_candidates(id) ON DELETE CASCADE,
voter_role_name TEXT NOT NULL REFERENCES roles(name),
vote TEXT NOT NULL,
reason TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_discovery_votes_unique ON discovery_votes(candidate_id, voter_role_name);
CREATE INDEX IF NOT EXISTS idx_discovery_votes_round ON discovery_votes(round_id, created_at);
CREATE TABLE IF NOT EXISTS workflow_runs (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
role_name TEXT NOT NULL REFERENCES roles(name),
stage TEXT NOT NULL,
mode TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
request_message_id TEXT REFERENCES messages(id) ON DELETE SET NULL,
thread_id TEXT NOT NULL DEFAULT '',
prior_thread_id TEXT NOT NULL DEFAULT '',
config_snapshot_json TEXT NOT NULL DEFAULT '{}',
command_json TEXT NOT NULL DEFAULT '[]',
reply_message_id TEXT REFERENCES messages(id) ON DELETE SET NULL,
exit_code INTEGER NOT NULL DEFAULT 0,
started_at TEXT NOT NULL,
completed_at TEXT,
error_message TEXT NOT NULL DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_workflow_runs_topic ON workflow_runs(topic_id, started_at);
CREATE INDEX IF NOT EXISTS idx_workflow_runs_role ON workflow_runs(role_name, status, started_at);
CREATE TABLE IF NOT EXISTS workflow_run_logs (
run_id TEXT NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,
seq INTEGER NOT NULL,
stream TEXT NOT NULL,
content TEXT NOT NULL,
created_at TEXT NOT NULL,
PRIMARY KEY (run_id, seq)
);
CREATE TABLE IF NOT EXISTS merge_requests (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
workflow_run_id TEXT REFERENCES workflow_runs(id) ON DELETE SET NULL,
requested_by_role_name TEXT REFERENCES roles(name) ON DELETE SET NULL,
target_branch TEXT NOT NULL,
status TEXT NOT NULL,
summary TEXT NOT NULL DEFAULT '',
files_changed INTEGER NOT NULL DEFAULT 0,
insertions INTEGER NOT NULL DEFAULT 0,
deletions INTEGER NOT NULL DEFAULT 0,
changed_files_json TEXT NOT NULL DEFAULT '[]',
created_at TEXT NOT NULL,
merged_at TEXT,
error_message TEXT NOT NULL DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_merge_requests_topic ON merge_requests(topic_id, created_at);
@@ -0,0 +1,4 @@
ALTER TABLE workspaces ADD COLUMN provision_state TEXT NOT NULL DEFAULT 'pending';
ALTER TABLE workspaces ADD COLUMN provision_error TEXT NOT NULL DEFAULT '';
ALTER TABLE workspaces ADD COLUMN runtime_endpoint TEXT NOT NULL DEFAULT '';
ALTER TABLE workspaces ADD COLUMN last_provisioned_at TEXT;
@@ -0,0 +1 @@
ALTER TABLE workspaces ADD COLUMN agent_max_jobs INTEGER NOT NULL DEFAULT 5;
@@ -0,0 +1,117 @@
UPDATE role_prompts
SET
content_markdown = '你是 product。
你负责当前主题的范围控制。把用户意图和 discovery 证据沉淀为 PRD 与决策日志,冻结要构建的内容,并持续消除交付阻塞。
规则:
- 不要编写实现代码。
- 所有决策都必须具体、可验证。
- 保持范围收敛;优先交付更小但边界清晰的切片,不要放大成含糊的大范围。
- 当 backend 或 frontend 进入执行阶段时,他们拿到的应该是冻结后的目标,而不是未决问题。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-product' AND updated_by = 'builtin-seed';
UPDATE role_prompts
SET
content_markdown = '你是 backend。
你只负责实现后端代码。遵循已经冻结的范围,保持行为一致,优先做小而可验证的改动。
规则:
- 编写可投入生产的代码,不写计划稿。
- 维护数据完整性和 API 清晰度。
- 行为变化时补充或更新后端测试。
- 只有在接口契约或冻结范围互相矛盾时才升级反馈。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-backend' AND updated_by = 'builtin-seed';
UPDATE role_prompts
SET
content_markdown = '你是 frontend。
你负责实现冻结范围内的用户界面。保持体验清晰、响应迅速,并与当前产品契约一致。
规则:
- 编写真实可运行的 UI 代码,不要交付线框或伪代码。
- 处理空状态、加载状态和错误状态。
- 保证表单、文案和反馈状态可用。
- 行为变化时补充或更新前端测试。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-frontend' AND updated_by = 'builtin-seed';
UPDATE role_prompts
SET
content_markdown = '你是 reviewer。
你要基于冻结范围和现有证据审查已交付的改动。你的职责是判断这项工作是否真的可以合并。
规则:
- 除非为了完成验证只需要一个很小的修复,否则不要顺手开发新功能。
- 对照 PRD、决策日志和验收标准检查实现。
- 在批准前核查测试、变更文件以及面向用户的证明材料。
- 当证据缺失或出现范围漂移时,要明确指出问题并退回修正。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-reviewer' AND updated_by = 'builtin-seed';
UPDATE role_prompts
SET
content_markdown = '你是 discovery_ux。
你需要从 UX 视角审查产品,并提出基于证据的需求候选。
重点关注:
- 用户流程阻力
- 容易引起误解的文案
- 薄弱的空状态、加载状态和错误状态
- 阻碍任务完成的交互缺口
每条提案都必须足够具体,便于 product 评估并冻结。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-discovery_ux' AND updated_by = 'builtin-seed';
UPDATE role_prompts
SET
content_markdown = '你是 discovery_quality。
你需要从质量与韧性视角审查产品,并提出基于证据的需求候选。
重点关注:
- 错误处理缺口
- 边界情况
- 缺失的保护措施与兜底行为
- 可测试性与回归风险
每条提案都必须明确说明具体问题,以及怎样才算可验证。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-discovery_quality' AND updated_by = 'builtin-seed';
UPDATE role_prompts
SET
content_markdown = '你是 discovery_growth。
你需要从增长视角审查产品,并提出基于证据的需求候选。
重点关注:
- 价值表达
- 可发现性
- 激活阻力
- 转化与留存信号
每条提案都必须描述面向用户的收益,而不只是实现思路。',
version = version + 1,
updated_by = 'builtin-migration-0004',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-discovery_growth' AND updated_by = 'builtin-seed';
@@ -0,0 +1,57 @@
ALTER TABLE roles ADD COLUMN executor_kind TEXT NOT NULL DEFAULT 'codex';
UPDATE roles
SET executor_kind = 'human'
WHERE name = 'user';
CREATE TABLE IF NOT EXISTS human_tasks (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
role_name TEXT NOT NULL REFERENCES roles(name) ON DELETE CASCADE,
prompt_message_id TEXT NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
status TEXT NOT NULL,
answered_message_id TEXT REFERENCES messages(id) ON DELETE SET NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(prompt_message_id, role_name)
);
CREATE INDEX IF NOT EXISTS idx_human_tasks_workspace_status ON human_tasks(workspace_id, status, updated_at);
CREATE INDEX IF NOT EXISTS idx_human_tasks_topic_status ON human_tasks(topic_id, status, updated_at);
CREATE INDEX IF NOT EXISTS idx_human_tasks_role_status ON human_tasks(role_name, status, updated_at);
INSERT INTO human_tasks(
id,
workspace_id,
topic_id,
role_name,
prompt_message_id,
status,
answered_message_id,
created_at,
updated_at
)
SELECT
'human-task-' || d.message_id || '-' || d.recipient_role_name,
m.workspace_id,
m.topic_id,
d.recipient_role_name,
d.message_id,
'pending',
NULL,
d.delivered_at,
d.updated_at
FROM message_deliveries d
JOIN messages m ON m.id = d.message_id
WHERE d.recipient_role_name = 'user'
AND d.state IN ('pending', 'received', 'read')
AND NOT EXISTS (
SELECT 1
FROM human_tasks ht
WHERE ht.prompt_message_id = d.message_id
AND ht.role_name = d.recipient_role_name
);
DELETE FROM message_deliveries
WHERE recipient_role_name = 'user';
@@ -0,0 +1,70 @@
CREATE TABLE IF NOT EXISTS lanes (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
name TEXT NOT NULL,
slug TEXT NOT NULL,
status TEXT NOT NULL,
base_branch TEXT NOT NULL DEFAULT '',
branch_name TEXT NOT NULL DEFAULT '',
worktree_path TEXT NOT NULL DEFAULT '',
container_name TEXT NOT NULL DEFAULT '',
runtime_endpoint TEXT NOT NULL DEFAULT '',
created_by_role_name TEXT NOT NULL REFERENCES roles(name),
result_summary_markdown TEXT NOT NULL DEFAULT '',
error_message TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
started_at TEXT,
completed_at TEXT,
UNIQUE(topic_id, slug),
UNIQUE(workspace_id, branch_name),
UNIQUE(workspace_id, worktree_path),
UNIQUE(workspace_id, container_name)
);
CREATE INDEX IF NOT EXISTS idx_lanes_topic ON lanes(topic_id, created_at);
CREATE INDEX IF NOT EXISTS idx_lanes_workspace_status ON lanes(workspace_id, status, updated_at);
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
lane_id TEXT NOT NULL REFERENCES lanes(id) ON DELETE CASCADE,
title TEXT NOT NULL,
body_markdown TEXT NOT NULL,
acceptance_markdown TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
priority INTEGER NOT NULL DEFAULT 0,
task_order INTEGER NOT NULL DEFAULT 0,
created_by_role_name TEXT NOT NULL REFERENCES roles(name),
blocking_reason_markdown TEXT NOT NULL DEFAULT '',
result_summary_markdown TEXT NOT NULL DEFAULT '',
assigned_run_id TEXT REFERENCES workflow_runs(id) ON DELETE SET NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
started_at TEXT,
completed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_tasks_topic ON tasks(topic_id, status, priority, task_order, created_at);
CREATE INDEX IF NOT EXISTS idx_tasks_lane ON tasks(lane_id, status, task_order, created_at);
CREATE TABLE IF NOT EXISTS task_dependencies (
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
depends_on_task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
PRIMARY KEY (task_id, depends_on_task_id)
);
CREATE INDEX IF NOT EXISTS idx_task_dependencies_upstream ON task_dependencies(depends_on_task_id, task_id);
CREATE TABLE IF NOT EXISTS task_events (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
event_type TEXT NOT NULL,
body_markdown TEXT NOT NULL DEFAULT '',
created_by_role_name TEXT NOT NULL REFERENCES roles(name),
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_task_events_task ON task_events(task_id, created_at);
@@ -0,0 +1,115 @@
DELETE FROM workflow_run_logs
WHERE run_id IN (
SELECT id
FROM workflow_runs
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
)
);
DELETE FROM merge_requests
WHERE requested_by_role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
)
OR workflow_run_id IN (
SELECT id
FROM workflow_runs
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
)
);
DELETE FROM workflow_runs
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM message_deliveries
WHERE recipient_role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM role_threads
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM human_tasks
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM messages
WHERE from_role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
)
OR to_expr LIKE '%product%'
OR to_expr LIKE '%backend%'
OR to_expr LIKE '%frontend%'
OR to_expr LIKE '%reviewer%'
OR to_expr LIKE '%discovery_ux%'
OR to_expr LIKE '%discovery_quality%'
OR to_expr LIKE '%discovery_growth%';
DELETE FROM topic_documents
WHERE topic_id IN (
SELECT id
FROM topics
WHERE space IN ('pool', 'discovery')
);
DELETE FROM topics
WHERE space IN ('pool', 'discovery');
DROP TABLE IF EXISTS discovery_votes;
DROP TABLE IF EXISTS discovery_candidates;
DROP TABLE IF EXISTS discovery_rounds;
DROP TABLE IF EXISTS requirements;
@@ -0,0 +1,76 @@
DELETE FROM task_events
WHERE created_by_role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM tasks
WHERE created_by_role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM lanes
WHERE created_by_role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM role_skill_bindings
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM role_configs
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM role_prompts
WHERE role_name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
DELETE FROM roles
WHERE name IN (
'product',
'backend',
'frontend',
'reviewer',
'discovery_ux',
'discovery_quality',
'discovery_growth'
);
@@ -0,0 +1,2 @@
ALTER TABLE tasks ADD COLUMN task_kind TEXT NOT NULL DEFAULT 'execution';
ALTER TABLE tasks ADD COLUMN gate_policy TEXT NOT NULL DEFAULT 'none';
@@ -0,0 +1,6 @@
ALTER TABLE lanes ADD COLUMN purpose TEXT NOT NULL DEFAULT '';
ALTER TABLE tasks ADD COLUMN deliverables_json TEXT NOT NULL DEFAULT '[]';
ALTER TABLE tasks ADD COLUMN verification_mode TEXT NOT NULL DEFAULT 'none';
ALTER TABLE tasks ADD COLUMN batch_key TEXT NOT NULL DEFAULT '';
ALTER TABLE tasks ADD COLUMN replan_policy TEXT NOT NULL DEFAULT 'patch';
@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS task_graph_versions (
id TEXT PRIMARY KEY,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
version INTEGER NOT NULL,
status TEXT NOT NULL,
plan_json TEXT NOT NULL DEFAULT '{}',
plan_summary_markdown TEXT NOT NULL DEFAULT '',
created_by_role_name TEXT NOT NULL REFERENCES roles(name),
created_at TEXT NOT NULL,
confirmed_at TEXT,
supersedes_graph_version_id TEXT REFERENCES task_graph_versions(id) ON DELETE SET NULL,
UNIQUE(topic_id, version)
);
CREATE INDEX IF NOT EXISTS idx_task_graph_versions_topic ON task_graph_versions(topic_id, version DESC, created_at DESC);
@@ -0,0 +1,8 @@
ALTER TABLE chains RENAME TO lanes;
ALTER TABLE tasks RENAME COLUMN chain_id TO lane_id;
DROP INDEX IF EXISTS idx_chains_topic;
DROP INDEX IF EXISTS idx_chains_workspace_status;
DROP INDEX IF EXISTS idx_tasks_chain;
CREATE INDEX IF NOT EXISTS idx_lanes_topic ON lanes(topic_id, created_at);
CREATE INDEX IF NOT EXISTS idx_lanes_workspace_status ON lanes(workspace_id, status, updated_at);
CREATE INDEX IF NOT EXISTS idx_tasks_lane ON tasks(lane_id, status, task_order, created_at);
@@ -0,0 +1,19 @@
ALTER TABLE lanes ADD COLUMN head_commit TEXT NOT NULL DEFAULT '';
CREATE TABLE IF NOT EXISTS lane_syncs (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE,
downstream_lane_id TEXT NOT NULL REFERENCES lanes(id) ON DELETE CASCADE,
upstream_lane_id TEXT NOT NULL REFERENCES lanes(id) ON DELETE CASCADE,
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
upstream_commit TEXT NOT NULL,
merge_commit TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
error_message TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_lane_syncs_task ON lane_syncs(task_id, created_at);
CREATE INDEX IF NOT EXISTS idx_lane_syncs_downstream ON lane_syncs(downstream_lane_id, created_at);
@@ -0,0 +1 @@
ALTER TABLE workspaces DROP COLUMN agent_max_jobs;
@@ -0,0 +1 @@
ALTER TABLE roles DROP COLUMN category;
@@ -0,0 +1,19 @@
UPDATE roles
SET description = 'Runs on the host, talks to the user, maintains the task graph, and orchestrates execution.'
WHERE name = 'leader' AND is_builtin = 1;
UPDATE role_prompts
SET
content_markdown = '你是 leader。
你运行在宿主机上,是当前主题唯一的编排者。你负责与用户沟通、澄清目标、维护 task graph、启动执行,并汇总最终结果。lane 由系统从任务图自动派生。
规则:
- 先理解用户目标,再拆分任务;不要直接跳过澄清。
- 所有 task 都必须明确目标、依赖、验收标准。
- worker 只能向你升级阻塞;是否打扰用户由你决定。
- 你可以创建、更新、启动、停止 lane 与 task,但不要代替 worker 在容器里完成实现。',
version = version + 1,
updated_by = 'builtin-migration-0013',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-leader' AND updated_by = 'builtin-seed';
@@ -0,0 +1,3 @@
DROP TABLE IF EXISTS role_threads;
ALTER TABLE workspaces DROP COLUMN runtime_ref;
@@ -0,0 +1,2 @@
ALTER TABLE workflow_runs DROP COLUMN thread_id;
ALTER TABLE workflow_runs DROP COLUMN prior_thread_id;
@@ -0,0 +1,3 @@
ALTER TABLE workspaces DROP COLUMN runtime_endpoint;
DROP TABLE IF EXISTS config_change_logs;
@@ -0,0 +1,3 @@
ALTER TABLE topics DROP COLUMN source_topic_id;
ALTER TABLE topics DROP COLUMN owner_role_name;
ALTER TABLE topics DROP COLUMN meta_json;
@@ -0,0 +1,4 @@
ALTER TABLE messages DROP COLUMN round;
ALTER TABLE message_deliveries DROP COLUMN read_at;
ALTER TABLE message_deliveries DROP COLUMN archived_at;
@@ -0,0 +1,3 @@
ALTER TABLE role_configs DROP COLUMN plan_model;
ALTER TABLE role_configs DROP COLUMN plan_reasoning_effort;
ALTER TABLE role_configs DROP COLUMN disable_response_storage;
@@ -0,0 +1 @@
-- Reserved no-op migration to preserve contiguous historical numbering.
@@ -0,0 +1,6 @@
DELETE FROM role_prompts
WHERE prompt_kind <> 'system';
ALTER TABLE tasks DROP COLUMN gate_policy;
ALTER TABLE tasks DROP COLUMN verification_mode;
ALTER TABLE tasks DROP COLUMN replan_policy;
@@ -0,0 +1 @@
-- Reserved no-op migration to preserve contiguous historical numbering.
@@ -0,0 +1 @@
-- Reserved no-op migration to preserve contiguous historical numbering.
@@ -0,0 +1 @@
-- Reserved no-op migration to preserve contiguous historical numbering.
@@ -0,0 +1,4 @@
ALTER TABLE role_configs DROP COLUMN provider_name;
DROP TABLE IF EXISTS merge_requests;
DROP TABLE IF EXISTS topic_documents;
@@ -0,0 +1,102 @@
UPDATE role_prompts
SET
content_markdown = '你是 leader。
你运行在宿主机上,是当前主题唯一的编排者。你负责与用户沟通、澄清目标、维护 task graph、启动执行,并汇总最终结果。lane 由系统从任务图自动派生。
规则:
- 先理解用户目标,再拆分任务;不要直接跳过澄清。
- 所有 task 都必须明确目标、依赖、验收标准。
- worker 的问题和总结都只作为编排输入,不要把它们直接当作完成证明。
- worker 只能向你升级阻塞;是否打扰用户由你决定。
- 你可以创建、更新、启动、停止 lane 与 task,但不要代替 worker 在容器里完成实现。',
version = version + 1,
updated_by = 'builtin-migration-0029',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-leader';
UPDATE role_prompts
SET
content_markdown = '你是 worker。
你运行在一个 lane 对应的独立 worktree 与容器中。你只负责执行当前被分派的 task,并通过 inbox 向 leader 汇报进度、结果或阻塞。
规则:
- 只处理当前 task,不要自行扩展范围。
- 在开始前阅读 task、验收标准和 lane 上下文。
- 遇到阻塞时,先通过 inbox 向 leader 发一条具体问题,再结束当前 task,并在总结里写清阻塞点与所需决策。
- 行为变化时补充必要的验证与测试。',
version = version + 1,
updated_by = 'builtin-migration-0029',
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-role-prompt-worker';
UPDATE skills
SET
content_markdown = '---
name: inbox
description: Use Inbox V2 from a role runtime. Supports two operations only: send and ask. Use this when you need to post a workflow message or a blocking question into an existing topic with inbox api.
---
# Inbox
Use this skill when a role needs to communicate through Inbox V2.
This skill has two operations only:
- send: post a normal workflow message into an existing topic
- ask: post a blocking question that needs an answer from another role
## Before using either operation
Resolve the topic id first if you only know the workspace and topic slug:
```bash
inbox api GET "/api/v2/topics?workspace_id=<workspace_id>"
```
Pick the target topic record, then use its id in the message API below.
## send
Use send for handoffs, updates, decisions, or summaries.
```bash
inbox api POST /api/v2/topics/<topic_id>/messages --data {
"workspace_id": "<workspace_id>",
"from_role_name": "<current_role>",
"to_expr": "<target_role>",
"type": "chat",
"stage": "execution",
"body_markdown": "<message markdown>"
}
```
Rules:
- Set type to the actual intent: chat, proposal, decision, or summary.
- Keep stage aligned with the current workflow state.
- Add reply_to_message_id when replying to a specific message.
## ask
Use ask when you are blocked and need a concrete answer.
```bash
inbox api POST /api/v2/topics/<topic_id>/messages --data {
"workspace_id": "<workspace_id>",
"from_role_name": "<current_role>",
"to_expr": "<target_role>",
"type": "question",
"stage": "<current_stage>",
"body_markdown": "<single concrete question and the decision you need>"
}
```
Rules:
- Ask one concrete blocker per message.
- State the decision, artifact, or approval you need back.
- Keep stage aligned with the task or planning stage you are in.
- Prefer ask over vague status pings.',
description = 'Use Inbox V2 from a role runtime with two operations: send and ask.',
version = version + 1,
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-skill-inbox';
@@ -0,0 +1 @@
-- handled by custom Go migration logic in applyMigration
@@ -0,0 +1,68 @@
UPDATE skills
SET
content_markdown = '---
name: inbox
description: "Use Inbox V2 from a role runtime. Supports two operations only: send and ask. Use this when you need to post a workflow message or a blocking question into an existing topic with inbox api."
---
# Inbox
Use this skill when a role needs to communicate through Inbox V2.
This skill has two operations only:
- send: post a normal workflow message into an existing topic
- ask: post a blocking question that needs an answer from another role
## Before using either operation
Resolve the topic id first if you only know the workspace and topic slug:
```bash
inbox api GET "/api/v2/topics?workspace_id=<workspace_id>"
```
Pick the target topic record, then use its id in the message API below.
## send
Use send for handoffs, updates, decisions, or summaries.
```bash
inbox api POST /api/v2/topics/<topic_id>/messages --data {
"workspace_id": "<workspace_id>",
"from_role_name": "<current_role>",
"to_expr": "<target_role>",
"type": "chat",
"stage": "execution",
"body_markdown": "<message markdown>"
}
```
Rules:
- Set type to the actual intent: chat, proposal, decision, or summary.
- Keep stage aligned with the current workflow state.
- Add reply_to_message_id when replying to a specific message.
## ask
Use ask when you are blocked and need a concrete answer.
```bash
inbox api POST /api/v2/topics/<topic_id>/messages --data {
"workspace_id": "<workspace_id>",
"from_role_name": "<current_role>",
"to_expr": "<target_role>",
"type": "question",
"stage": "<current_stage>",
"body_markdown": "<single concrete question and the decision you need>"
}
```
Rules:
- Ask one concrete blocker per message.
- State the decision, artifact, or approval you need back.
- Keep stage aligned with the task or planning stage you are in.
- Prefer ask over vague status pings.',
version = version + 1,
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = 'builtin-skill-inbox';
@@ -0,0 +1,2 @@
ALTER TABLE skills DROP COLUMN source_ref;
ALTER TABLE skills DROP COLUMN asset_root;