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
+790
View File
@@ -0,0 +1,790 @@
# Inbox V2 数据库设计
本文档是 `inbox v2` 的数据库开发基线。
它服务于三件事:
1. 明确 `v2` 到底使用哪些数据库。
2. 明确每张表的字段、主键、外键和作用。
3. 明确各张表之间的关系,作为后续 `store / app / http` 实现依据。
## 1. 物理数据库
`v2` 先只使用 **1 个物理数据库**
| 数据库 | 引擎 | 是否采用 | 用途 |
| --- | --- | --- | --- |
| `inbox.db` | SQLite | 是 | 所有运行时业务数据、在线可编辑配置、配置审计 |
| Redis | - | 否 | 当前不需要单独缓存或队列 |
| PostgreSQL | - | 否 | 当前先不引入额外运维复杂度 |
| 独立日志库 | - | 否 | 执行日志先跟主库放一起 |
| 向量库 | - | 否 | 当前主流程不依赖向量检索 |
一句话:
- `V2` 先只有一个库:`inbox.db`
## 2. 关键决策
### 2.1 配置入库
因为 `roles / prompts / skills / config` 需要满足:
- 前端可编辑
- 保存后立即生效
- 不需要改文件后再发版
所以第二版里,这些内容的**运行时真源就是数据库**。
文件只保留这些作用:
- 默认模板
- 初始化导入源
- 内置技能的原始资产
### 2.2 立即生效的定义
这里的“立即生效”定义为:
- 配置保存成功后
- **下一次新的 agent run / 新的消息处理 / 新的 HTTP 请求** 立即读取最新配置
不包含:
- 已经运行中的 agent 进程半路热切换配置
### 2.3 配置覆盖规则
在线配置支持两层作用域:
1. `global`
2. `workspace override`
解析优先级固定为:
`workspace row > global row`
也就是说:
- 如果某个 workspace 有自己的 prompt/config/binding,就优先使用它
- 否则回退到全局默认
### 2.4 统一建模原则
1. 所有业务主键统一使用 `TEXT`
2. 所有时间统一使用 UTC RFC3339
3. 核心业务关系用结构化表,不用 JSON blob 代替
4. JSON 只用于快照和弱结构扩展字段
5. `topic` 是业务主实体
6. `product` 接管需求整理文档
7. `clarifier_state / clarifier_artifacts / topic_records``v2` 中不再保留
## 3. 数据域划分
| 数据域 | 表 |
| --- | --- |
| 系统 | `schema_migrations` |
| 项目与工作区 | `projects`, `workspaces` |
| 在线角色配置 | `roles`, `role_prompts`, `role_configs`, `skills`, `role_skill_bindings` |
| Topic 与文档 | `topics`, `topic_documents` |
| 消息协作 | `messages`, `message_deliveries` |
| Requirement / Discovery / Workflow / Merge | `requirements`, `discovery_rounds`, `discovery_candidates`, `discovery_votes`, `workflow_runs`, `workflow_run_logs`, `merge_requests` |
## 4. 表清单总览
| 表名 | 数据域 | 说明 |
| --- | --- | --- |
| `schema_migrations` | 系统 | 迁移历史 |
| `projects` | 项目 | 项目主表 |
| `workspaces` | 工作区 | 工作区主表 |
| `roles` | 在线角色配置 | 角色定义 |
| `role_prompts` | 在线角色配置 | 角色提示词 |
| `role_configs` | 在线角色配置 | 角色模型和运行配置 |
| `skills` | 在线角色配置 | 技能定义 |
| `role_skill_bindings` | 在线角色配置 | 角色与技能绑定 |
| `topics` | Topic | 统一 topic 主实体 |
| `topic_documents` | Topic | `PRD``Decision Log`、Discovery 文档 |
| `messages` | 消息协作 | 消息信封与正文 |
| `message_deliveries` | 消息协作 | 消息收件与归档状态 |
| `requirements` | Requirement | pool 队列项 |
| `discovery_rounds` | Discovery | discovery 轮次 |
| `discovery_candidates` | Discovery | discovery 候选项 |
| `discovery_votes` | Discovery | discovery 投票 |
| `workflow_runs` | Workflow | 角色执行记录 |
| `workflow_run_logs` | Workflow | 执行日志流 |
| `merge_requests` | Merge | merge 请求记录 |
## 5. 表字段定义
### 5.1 `schema_migrations`
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `version` | `INTEGER` | PK | 迁移版本号 |
| `name` | `TEXT` | NOT NULL | 迁移名称 |
| `applied_at` | `TEXT` | NOT NULL | 执行时间 |
### 5.2 `projects`
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 项目 ID |
| `slug` | `TEXT` | NOT NULL, UNIQUE | 项目标识 |
| `name` | `TEXT` | NOT NULL | 展示名称 |
| `root_path` | `TEXT` | NOT NULL, UNIQUE | 项目根目录 |
| `default_branch` | `TEXT` | NOT NULL | 默认分支 |
| `status` | `TEXT` | NOT NULL | `active / archived` |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
关系:
- `projects 1 -> N workspaces`
### 5.3 `workspaces`
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 工作区 ID |
| `project_id` | `TEXT` | FK -> `projects.id` | 所属项目 |
| `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 | `local / container / remote` |
| `status` | `TEXT` | NOT NULL | `active / paused / archived` |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
关系:
- `workspaces N -> 1 projects`
- `workspaces 1 -> N topics`
- `workspaces 1 -> N messages`
- `workspaces 1 -> N requirements`
- `workspaces 1 -> N discovery_rounds`
- `workspaces 1 -> N workflow_runs`
- `workspaces 1 -> N merge_requests`
### 5.4 `roles`
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `name` | `TEXT` | PK | 角色名,例如 `product` |
| `title` | `TEXT` | NOT NULL | 展示名称 |
| `category` | `TEXT` | NOT NULL | `product / delivery / review / discovery` |
| `description` | `TEXT` | NOT NULL DEFAULT `''` | 角色说明 |
| `is_enabled` | `INTEGER` | NOT NULL | 1 启用,0 禁用 |
| `is_builtin` | `INTEGER` | NOT NULL | 1 内置,0 自定义 |
| `sort_order` | `INTEGER` | NOT NULL DEFAULT `0` | 排序 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
关系:
- `roles 1 -> N role_prompts`
- `roles 1 -> N role_configs`
- `roles 1 -> N role_skill_bindings`
- `roles 1 -> N messages`
- `roles 1 -> N message_deliveries`
- `roles 1 -> N requirements`
- `roles 1 -> N discovery_candidates`
- `roles 1 -> N discovery_votes`
- `roles 1 -> N workflow_runs`
### 5.5 `role_prompts`
当前生效的提示词表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 提示词记录 ID |
| `role_name` | `TEXT` | FK -> `roles.name` | 所属角色 |
| `workspace_id` | `TEXT` | FK -> `workspaces.id`, NULLABLE | 为空表示全局默认;非空表示 workspace override |
| `prompt_kind` | `TEXT` | NOT NULL | `system / clarification / plan / review / freeze / execution / verification / discovery` |
| `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 | 更新时间 |
唯一约束:
- 全局:每个 `(role_name, prompt_kind)` 只能有 1 条全局默认记录
- 工作区:每个 `(role_name, workspace_id, prompt_kind)` 只能有 1 条 override 记录
关系:
- `role_prompts N -> 1 roles`
- `role_prompts N -> 1 workspaces`(可空)
### 5.6 `role_configs`
当前生效的角色运行配置表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 配置记录 ID |
| `role_name` | `TEXT` | FK -> `roles.name` | 所属角色 |
| `workspace_id` | `TEXT` | FK -> `workspaces.id`, NULLABLE | 为空表示全局默认;非空表示 workspace override |
| `model` | `TEXT` | NOT NULL DEFAULT `''` | 默认模型 |
| `model_provider` | `TEXT` | NOT NULL DEFAULT `''` | 模型提供方 |
| `provider_name` | `TEXT` | NOT NULL DEFAULT `''` | Provider 名称 |
| `provider_base_url` | `TEXT` | NOT NULL DEFAULT `''` | Provider Base URL |
| `provider_wire_api` | `TEXT` | NOT NULL DEFAULT `''` | Provider Wire API |
| `reasoning_effort` | `TEXT` | NOT NULL DEFAULT `''` | 默认 reasoning 等级 |
| `plan_model` | `TEXT` | NOT NULL DEFAULT `''` | 规划阶段专用模型 |
| `plan_reasoning_effort` | `TEXT` | NOT NULL DEFAULT `''` | 规划阶段 reasoning |
| `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 | 更新时间 |
唯一约束:
- 全局:每个 `role_name` 只能有 1 条全局默认配置
- 工作区:每个 `(role_name, workspace_id)` 只能有 1 条 override 配置
关系:
- `role_configs N -> 1 roles`
- `role_configs N -> 1 workspaces`(可空)
### 5.7 `skills`
技能定义表。运行时真正使用数据库中的技能内容。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 技能 ID |
| `skill_key` | `TEXT` | NOT NULL, UNIQUE | 稳定标识 |
| `name` | `TEXT` | NOT NULL | 展示名称 |
| `description` | `TEXT` | NOT NULL DEFAULT `''` | 技能说明 |
| `source_type` | `TEXT` | NOT NULL | `builtin / imported / custom` |
| `content_markdown` | `TEXT` | NOT NULL DEFAULT `''` | 技能正文内容 |
| `status` | `TEXT` | NOT NULL | `active / disabled / archived` |
| `version` | `INTEGER` | NOT NULL | 当前版本号 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
关系:
- `skills 1 -> N role_skill_bindings`
### 5.8 `role_skill_bindings`
角色与技能的绑定表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 绑定记录 ID |
| `role_name` | `TEXT` | FK -> `roles.name` | 角色 |
| `workspace_id` | `TEXT` | FK -> `workspaces.id`, NULLABLE | 为空表示全局默认;非空表示 workspace override |
| `skill_id` | `TEXT` | FK -> `skills.id` | 技能 |
| `is_enabled` | `INTEGER` | NOT NULL | 1 启用,0 禁用 |
| `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 | 更新时间 |
唯一约束:
- 全局:每个 `(role_name, skill_id)` 只能有 1 条全局默认绑定
- 工作区:每个 `(role_name, workspace_id, skill_id)` 只能有 1 条 override 绑定
关系:
- `role_skill_bindings N -> 1 roles`
- `role_skill_bindings N -> 1 skills`
- `role_skill_bindings N -> 1 workspaces`(可空)
### 5.10 `topics`
这是第二版的主实体表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | topic ID |
| `workspace_id` | `TEXT` | FK -> `workspaces.id` | 所属工作区 |
| `slug` | `TEXT` | NOT NULL | 稳定标识 |
| `title` | `TEXT` | NOT NULL | 标题 |
| `space` | `TEXT` | NOT NULL | `discovery / clarify / pool / workflow` |
| `status` | `TEXT` | NOT NULL | 当前状态 |
| `source_topic_id` | `TEXT` | FK -> `topics.id`, NULLABLE | 来源 topic |
| `owner_role_name` | `TEXT` | FK -> `roles.name`, NULLABLE | 当前主责角色 |
| `summary` | `TEXT` | NOT NULL DEFAULT `''` | 摘要 |
| `meta_json` | `TEXT` | NOT NULL DEFAULT `{}` | 扩展元数据 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
| `closed_at` | `TEXT` | NULLABLE | 关闭时间 |
唯一约束:
- 每个 workspace 下 `slug` 唯一
关系:
- `topics N -> 1 workspaces`
- `topics N -> 1 topics``source_topic_id`
- `topics N -> 1 roles``owner_role_name`
- `topics 1 -> N topic_documents`
- `topics 1 -> N messages`
- `topics 1 -> N requirements`
- `topics 1 -> N discovery_rounds`
- `topics 1 -> N workflow_runs`
- `topics 1 -> N merge_requests`
### 5.11 `topic_documents`
topic 持有的正式文档。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 文档 ID |
| `topic_id` | `TEXT` | FK -> `topics.id` | 所属 topic |
| `kind` | `TEXT` | NOT NULL | `prd / decision_log / discovery_request / discovery_result` |
| `content_markdown` | `TEXT` | NOT NULL | Markdown 正文 |
| `version` | `INTEGER` | NOT NULL | 版本号 |
| `updated_by_role_name` | `TEXT` | FK -> `roles.name`, NULLABLE | 最后更新角色 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
唯一约束:
- 每个 topic 下每种 `kind` 只能有 1 条当前文档
说明:
- `PRD``Decision Log``v2` 中归 `product` 所有
### 5.12 `messages`
消息主表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | 消息 ID |
| `workspace_id` | `TEXT` | FK -> `workspaces.id` | 所属工作区 |
| `topic_id` | `TEXT` | FK -> `topics.id` | 所属 topic |
| `from_role_name` | `TEXT` | FK -> `roles.name` | 发送角色 |
| `to_expr` | `TEXT` | NOT NULL | 原始收件表达式,例如 `backend``all` |
| `type` | `TEXT` | NOT NULL | `chat / proposal / question / decision / summary` |
| `stage` | `TEXT` | NOT NULL | `clarification / plan / review / freeze / execution / verification / discovery` |
| `round` | `INTEGER` | NULLABLE | 回合号 |
| `reply_to_message_id` | `TEXT` | FK -> `messages.id`, NULLABLE | 回复目标消息 |
| `body_markdown` | `TEXT` | NOT NULL | 消息正文 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
关系:
- `messages N -> 1 workspaces`
- `messages N -> 1 topics`
- `messages N -> 1 roles`
- `messages N -> 1 messages``reply_to_message_id`
- `messages 1 -> N message_deliveries`
### 5.13 `message_deliveries`
每个实际收件人的投递状态。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `message_id` | `TEXT` | FK -> `messages.id` | 对应消息 |
| `recipient_role_name` | `TEXT` | FK -> `roles.name` | 实际收件角色 |
| `state` | `TEXT` | NOT NULL | `pending / received / read / archived` |
| `delivered_at` | `TEXT` | NOT NULL | 投递时间 |
| `read_at` | `TEXT` | NULLABLE | 已读时间 |
| `archived_at` | `TEXT` | NULLABLE | 归档时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
主键:
- `PRIMARY KEY (message_id, recipient_role_name)`
说明:
- `to: all` 会在这里展开成多条记录
- 它只表达“线程绑定”
- 不表达“这次执行的历史”
### 5.15 `requirements`
pool 阶段的结构化队列项。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | requirement ID |
| `workspace_id` | `TEXT` | FK -> `workspaces.id` | 所属工作区 |
| `topic_id` | `TEXT` | FK -> `topics.id` | 来源 topic |
| `title` | `TEXT` | NOT NULL | 标题 |
| `body_markdown` | `TEXT` | NOT NULL | 详细说明 |
| `status` | `TEXT` | NOT NULL | `pending / dispatched / completed / archived` |
| `priority` | `INTEGER` | NOT NULL | 优先级 |
| `created_by_role_name` | `TEXT` | FK -> `roles.name` | 创建角色,通常是 `product` |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
| `completed_at` | `TEXT` | NULLABLE | 完成时间 |
说明:
- `requirements``workspace + topic` 为来源
- 不再把 `project_id` 当成 requirement 的主归属
### 5.16 `discovery_rounds`
discovery 轮次主表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | round ID |
| `workspace_id` | `TEXT` | FK -> `workspaces.id` | 所属工作区 |
| `topic_id` | `TEXT` | FK -> `topics.id` | 关联 topic |
| `phase` | `TEXT` | NOT NULL | `collecting / voting / completed / cancelled` |
| `request_document_id` | `TEXT` | FK -> `topic_documents.id`, NULLABLE | discovery request 文档 |
| `result_document_id` | `TEXT` | FK -> `topic_documents.id`, NULLABLE | discovery result 文档 |
| `started_at` | `TEXT` | NOT NULL | 开始时间 |
| `completed_at` | `TEXT` | NULLABLE | 完成时间 |
| `updated_at` | `TEXT` | NOT NULL | 更新时间 |
关系:
- `discovery_rounds N -> 1 workspaces`
- `discovery_rounds N -> 1 topics`
- `discovery_rounds 1 -> N discovery_candidates`
### 5.17 `discovery_candidates`
discovery 候选项结构化表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | candidate ID |
| `round_id` | `TEXT` | FK -> `discovery_rounds.id` | 所属 round |
| `proposer_role_name` | `TEXT` | FK -> `roles.name` | 提出角色 |
| `status` | `TEXT` | NOT NULL | `draft / ready / accepted / rejected / archived` |
| `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 | 更新时间 |
关系:
- `discovery_candidates N -> 1 discovery_rounds`
- `discovery_candidates N -> 1 roles`
- `discovery_candidates 1 -> N discovery_votes`
### 5.18 `discovery_votes`
discovery 候选项投票表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | vote ID |
| `round_id` | `TEXT` | FK -> `discovery_rounds.id` | 所属 round |
| `candidate_id` | `TEXT` | FK -> `discovery_candidates.id` | 所投候选项 |
| `voter_role_name` | `TEXT` | FK -> `roles.name` | 投票角色 |
| `vote` | `TEXT` | NOT NULL | `agree / reject / unclear` |
| `reason` | `TEXT` | NOT NULL DEFAULT `''` | 原因 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
唯一约束:
- 每个角色对每个候选项最多投 1 票
### 5.19 `workflow_runs`
执行阶段主表。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | run ID |
| `workspace_id` | `TEXT` | FK -> `workspaces.id` | 所属工作区 |
| `topic_id` | `TEXT` | FK -> `topics.id` | 所属 topic |
| `role_name` | `TEXT` | FK -> `roles.name` | 执行角色 |
| `stage` | `TEXT` | NOT NULL | `plan / review / execution / verification` |
| `mode` | `TEXT` | NOT NULL DEFAULT `''` | 运行模式 |
| `status` | `TEXT` | NOT NULL | `running / succeeded / failed / cancelled` |
| `request_message_id` | `TEXT` | FK -> `messages.id`, NULLABLE | 触发消息 |
| `config_snapshot_json` | `TEXT` | NOT NULL DEFAULT `{}` | 启动时解析出的 prompt/config/skills 快照 |
| `command_json` | `TEXT` | NOT NULL DEFAULT `[]` | 执行命令快照 |
| `reply_message_id` | `TEXT` | FK -> `messages.id`, NULLABLE | 执行完成后的回复消息 |
| `exit_code` | `INTEGER` | NOT NULL DEFAULT `0` | 退出码 |
| `started_at` | `TEXT` | NOT NULL | 开始时间 |
| `completed_at` | `TEXT` | NULLABLE | 完成时间 |
| `error_message` | `TEXT` | NOT NULL DEFAULT `''` | 错误信息 |
说明:
- 新的 run 启动时必须把解析后的角色配置快照写入 `config_snapshot_json`
- 这样后续即使 prompt/config 被改动,历史 run 仍然可追溯
### 5.20 `workflow_run_logs`
执行日志流。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `run_id` | `TEXT` | FK -> `workflow_runs.id` | 所属 run |
| `seq` | `INTEGER` | NOT NULL | 行序号 |
| `stream` | `TEXT` | NOT NULL | `stdout / stderr / system` |
| `content` | `TEXT` | NOT NULL | 日志内容 |
| `created_at` | `TEXT` | NOT NULL | 创建时间 |
主键:
- `PRIMARY KEY (run_id, seq)`
关系:
- `workflow_run_logs N -> 1 workflow_runs`
### 5.21 `merge_requests`
merge 请求记录。
| 字段 | 类型 | 约束 | 说明 |
| --- | --- | --- | --- |
| `id` | `TEXT` | PK | merge request ID |
| `workspace_id` | `TEXT` | FK -> `workspaces.id` | 所属工作区 |
| `topic_id` | `TEXT` | FK -> `topics.id` | 所属 topic |
| `workflow_run_id` | `TEXT` | FK -> `workflow_runs.id`, NULLABLE | 来源执行记录 |
| `requested_by_role_name` | `TEXT` | FK -> `roles.name`, NULLABLE | 发起角色 |
| `target_branch` | `TEXT` | NOT NULL | 目标分支 |
| `status` | `TEXT` | NOT NULL | `pending / merged / failed / cancelled` |
| `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` | NULLABLE | 合并时间 |
| `error_message` | `TEXT` | NOT NULL DEFAULT `''` | 失败原因 |
关系:
- `merge_requests N -> 1 workspaces`
- `merge_requests N -> 1 topics`
- `merge_requests N -> 1 workflow_runs`
- `merge_requests N -> 1 roles`
## 6. 表关系图
```mermaid
erDiagram
PROJECTS ||--o{ WORKSPACES : has
WORKSPACES ||--o{ TOPICS : owns
WORKSPACES ||--o{ MESSAGES : owns
WORKSPACES ||--o{ REQUIREMENTS : owns
WORKSPACES ||--o{ DISCOVERY_ROUNDS : owns
WORKSPACES ||--o{ WORKFLOW_RUNS : owns
WORKSPACES ||--o{ MERGE_REQUESTS : owns
ROLES ||--o{ ROLE_PROMPTS : has
ROLES ||--o{ ROLE_CONFIGS : has
ROLES ||--o{ ROLE_SKILL_BINDINGS : binds
SKILLS ||--o{ ROLE_SKILL_BINDINGS : used_by
TOPICS ||--o{ TOPIC_DOCUMENTS : has
TOPICS ||--o{ MESSAGES : contains
TOPICS ||--o{ REQUIREMENTS : produces
TOPICS ||--o{ DISCOVERY_ROUNDS : drives
TOPICS ||--o{ WORKFLOW_RUNS : executes
TOPICS ||--o{ MERGE_REQUESTS : results_in
MESSAGES ||--o{ MESSAGE_DELIVERIES : fans_out
MESSAGES ||--o{ MESSAGES : replies_to
DISCOVERY_ROUNDS ||--o{ DISCOVERY_CANDIDATES : includes
DISCOVERY_CANDIDATES ||--o{ DISCOVERY_VOTES : receives
WORKFLOW_RUNS ||--o{ WORKFLOW_RUN_LOGS : emits
WORKFLOW_RUNS ||--o| MERGE_REQUESTS : may_create
```
## 7. 关系说明
### 7.1 配置关系
| 主表 | 从表 | 关系 | 说明 |
| --- | --- | --- | --- |
| `roles` | `role_prompts` | 1:N | 一个角色可有多种 prompt kind,也可有 workspace override |
| `roles` | `role_configs` | 1:N | 一个角色可有全局配置和多个 workspace override |
| `roles` | `role_skill_bindings` | 1:N | 一个角色可绑定多个技能 |
| `skills` | `role_skill_bindings` | 1:N | 一个技能可被多个角色使用 |
| `workspaces` | `role_prompts / role_configs / role_skill_bindings` | 1:N | workspace 级覆盖配置 |
### 7.2 业务关系
| 主表 | 从表 | 关系 | 说明 |
| --- | --- | --- | --- |
| `projects` | `workspaces` | 1:N | 一个项目下有多个工作区 |
| `workspaces` | `topics` | 1:N | 一个工作区下有多个 topic |
| `topics` | `topic_documents` | 1:N | 一个 topic 下有多种文档 |
| `topics` | `messages` | 1:N | 一个 topic 下有多条消息 |
| `messages` | `message_deliveries` | 1:N | 一条消息可投递给多个角色 |
| `topics` | `requirements` | 1:N | 一个 topic 可产生多个 requirement |
| `topics` | `discovery_rounds` | 1:N | 一个 topic 可有多轮 discovery |
| `discovery_rounds` | `discovery_candidates` | 1:N | 一轮 discovery 可有多个候选项 |
| `discovery_candidates` | `discovery_votes` | 1:N | 一个候选项可收到多个角色投票 |
| `topics` | `workflow_runs` | 1:N | 一个 topic 可有多次执行 |
| `workflow_runs` | `workflow_run_logs` | 1:N | 一次执行有多条日志 |
| `topics` | `merge_requests` | 1:N | 一个 topic 可产生多个 merge 请求 |
### 7.3 特殊关系
| 字段 | 关系 | 说明 |
| --- | --- | --- |
| `topics.source_topic_id` | 自关联 | topic 可从另一个 topic 派生 |
| `messages.reply_to_message_id` | 自关联 | 消息可回复另一条消息 |
| `discovery_rounds.request_document_id` | 指向 `topic_documents` | discovery 输入文档 |
| `discovery_rounds.result_document_id` | 指向 `topic_documents` | discovery 输出文档 |
| `workflow_runs.config_snapshot_json` | 快照,不是 FK | 保存运行时解析后的配置结果 |
## 8. 读取优先级
以下三张配置表都采用相同规则:
- `role_prompts`
- `role_configs`
- `role_skill_bindings`
读取顺序固定为:
1. 先查当前 `workspace_id` 的覆盖记录
2. 如果没有,再查全局默认记录
这个规则是 `v2` 的硬约束,服务端、测试和前端都按这套理解实现。
## 9. 写入规则
### 9.1 配置写入
当用户在前端修改:
- 角色
- 提示词
- 技能
- 角色配置
- 技能绑定
服务端必须同时做两件事:
1. 更新当前生效表
### 9.2 新执行启动
当新的 `workflow_runs` 启动时:
1. 解析角色最终生效配置
- prompt
- role config
- skill binding
- skill 内容
2. 将解析结果写入 `workflow_runs.config_snapshot_json`
3. 再启动实际执行
这样可以同时满足:
- 配置立即生效
- 历史执行可追溯
## 10. V2 明确不保留的旧表
| 旧表 | V2 处理 |
| --- | --- |
| `topic_records` | 删除,合并进 `topics` |
| `clarifier_state` | 删除,状态回归 `topics` |
| `clarifier_artifacts` | 删除,合并进 `topic_documents` |
| `message_mailboxes` | 改为 `message_deliveries` |
| `dispatches` | 改为 `workflow_runs` |
| `dispatch_live_lines` | 改为 `workflow_run_logs` |
| `discovery_round_proposals` | 删除,改为 `discovery_candidates` |
| `discovery_round_votes` JSON blob | 删除,改为结构化 `discovery_votes` |
## 11. 对开发的直接约束
这份文档对 `v2` 开发有三条直接约束:
1. 不允许再出现“同一业务对象散落多个旧表”的设计。
2. 不允许再把在线可编辑配置只放文件、不进数据库。
3. 不允许再把 discovery 和 workflow 的核心结构塞回 JSON blob。
这份文档就是 `inbox v2` 的数据库基线,后续如果要改,应该直接更新这份文档,而不是各模块各自理解一版。
## 当前工作流图扩展
当前运行时已经在 `chains` / `tasks` / `workflow_runs` 上扩展了 leader-managed graph 字段,作为后续 DAG planning 的基础。
### chains
除基础标识与运行态字段外,`chains` 当前还包含:
- `purpose`:这条 chain 的目标与职责摘要
### tasks
除基础标题、正文、状态、依赖字段外,`tasks` 当前还包含:
- `task_kind`
- `execution`
- `gate`
- `verification`
- `milestone`
- `gate_policy`
- `none`
- `hard_stop`
- `ask_user`
- `leader_replan`
- `deliverables_json`
- `verification_mode`
- `none`
- `auto`
- `human`
- `batch_key`
- `replan_policy`
- `patch`
- `clarify`
- `stop`
说明:
- `task_kind = milestone` 是当前 milestone 的落地方式,不单独建表。
- `gate` task 用于前置检查;存在 gate 时,其它 task 默认先不放行。
### workflow_runs
`leader` 的结构化 planning 结果当前会持久化到:
- `workflow_runs.command_json`
其中包含:
- `plan_version`
- `plan_mode`
- `replan_reason`
- `supersedes`
- `execution_mode`
- `leader_reply`
- `chains`
- `tasks`
- `start_nodes`
说明:
- 空图首次规划使用 `plan_mode = initial`
- 主题已有 graph 时,leader 必须使用 `plan_mode = patch`
- 当前系统不允许 patch 模式扩图;patch 只能复用现有 graph
+380
View File
@@ -0,0 +1,380 @@
# Inbox V2 P1
本文档定义 `inbox v2``P1` 范围。
`P1` 只包含“独立模块”。
这里的独立模块,不是指“所有包彼此完全隔离”,而是指:
- 不依赖 `store``runtime``agent runner``HTTP route``app service`
- 可以单独实现、单独测试
- 可以作为后续 `HTTP``app service``store``client` 的公共基础
- 允许按分层方向依赖更底层的 `P1` 模块
换句话说:
- `P1` 要先固定“底层规则和模型”
- `P1` 不要先混入“流程装配和基础设施”
- `P1` 内部允许存在清晰的自底向上依赖
`P1` 的目标不是先把服务跑起来,而是先把最底层、最稳定、最可复用的规则和模型固定下来。
数据库设计草案见 [docs/inbox-v2-database.md](./inbox-v2-database.md)。
## 背景
`inbox v2` 的主线已经明确:
- `HTTP` 是唯一业务入口
- `server` 是唯一正式启动入口
- `CLI` 只是给 AI 使用的 `HTTP` 封装
- `clarifier` 角色移除
- `product` 接管需求整理,并继续输出:
- `PRD`
- `Decision Log`
- `discovery` 保留
- 交互路径暂时保持:
- discovery
- 需求整理
- pool
- workflow
在这条主线下,最先该实现的不是 server,也不是 client,而是底层独立模块。
## P1 设计原则
1. `P1` 模块必须独立于基础设施层,但允许依赖更底层的 `P1` 模块。
2. `P1` 模块优先承载“稳定规则”,而不是“流程编排”。
3. `P1` 模块一旦定型,后续 `P2/P3` 都应复用它们,而不是复制一份。
4. `P1` 模块必须有单元测试。
5. `P1` 不实现 HTTP route、不实现 runtime 装配、不实现数据库。
## P1 分层
为了让后续实现可落地,`P1` 采用下面这套分层:
| 层级 | 类型 | 可以依赖 |
| --- | --- | --- |
| `L0` | 基础工具 | Go 标准库 |
| `L1` | 文档规范 | `L0` + Go 标准库 |
| `L2` | 核心领域 | `L0/L1` + Go 标准库 |
| `L3` | 领域组合规则 | `L0/L1/L2` + Go 标准库 |
约束只有一条:
- 依赖只能向下,不能横向乱连,更不能回依赖 `P2/P3`
## P1 模块清单
下面这些是 `inbox v2` 第一阶段应先落下的独立模块。
| 模块 | 建议路径 | 作用 | 说明 |
| --- | --- | --- | --- |
| 时间模块 | `internal/base/timeutil` | UTC 时间、标准时间格式、可注入 clock | 给记录、文档更新时间、事件时间统一格式 |
| 标识符模块 | `internal/base/idgen` | topic / message / requirement / merge request 等 ID 生成规则 | 先固定命名和生成策略 |
| slug 模块 | `internal/base/slug` | topic、workspace、project 的 slug 规范化 | 这是所有上层都会复用的基础规则 |
| HTTP 通用模块 | `internal/base/httpx` | 状态错误、JSON 编解码、通用响应封装 | 只做 transport 基础工具,不做具体业务 |
| 文档模板模块 | `internal/domain/docspec` | `PRD``Decision Log`、Discovery request/result 的模板与渲染规则 | `product` 将直接依赖它 |
| 消息模块 | `internal/domain/message` | message envelope、front matter 解析、渲染、收件人规则 | 统一所有消息文档格式 |
| Topic 模块 | `internal/domain/topic` | topic 的 space/status 生命周期规则 | 明确 clarify/pool/workflow/discovery 的推进规则 |
| Role 模块 | `internal/domain/role` | 角色分类、角色定义、角色能力边界 | 包括 `product``backend``frontend``reviewer`、discovery 系列 |
| Requirement 模块 | `internal/domain/requirement` | requirement 的结构、状态、优先级与验证规则 | pool 的结构化实体先独立出来 |
| Discovery 模块 | `internal/domain/discovery` | discovery 提案、投票、共识计算、结果文档规则 | 保留 discovery 的核心领域逻辑 |
| Workflow 模块 | `internal/domain/workflow` | workflow 阶段枚举、阶段合法流转、角色协作规则 | 只管规则,不管看板聚合 |
| Merge 模块 | `internal/domain/merge` | merge request 状态、元数据结构、状态流转规则 | 只管领域规则,不管 git 命令 |
## P1 依赖关系
下面是建议的依赖方向。
| 模块 | 层级 | 可依赖模块 |
| --- | --- | --- |
| `timeutil` | `L0` | 无 |
| `slug` | `L0` | 无 |
| `idgen` | `L0` | `timeutil`, `slug` |
| `httpx` | `L0` | 无 |
| `docspec` | `L1` | `timeutil` |
| `role` | `L2` | 无 |
| `topic` | `L2` | `slug` |
| `workflow` | `L2` | `role`, `topic` |
| `requirement` | `L2` | `slug` |
| `merge` | `L2` | `timeutil`, `idgen`, `slug` |
| `message` | `L3` | `role`, `topic`, `workflow`, `slug`, `timeutil` |
| `discovery` | `L3` | `role`, `docspec`, `idgen`, `timeutil`, `slug` |
这张表的意义是:
- `P1` 仍然是独立模块集合
- 但不是人为禁止复用
- 后续实现时可以从 `L0` 往上逐层推进
## P1 模块详细职责
### 1. `timeutil`
负责:
- `NowUTC()`
- RFC3339 / RFC3339Nano 格式化
- 可注入 clock
不负责:
- 调度
- 轮询
- timeout 策略
### 2. `idgen`
负责:
- message ID 规则
- requirement ID 规则
- merge request ID 规则
- discovery round ID / candidate ID 规则
不负责:
- 数据库存储
- 去重查询
### 3. `slug`
负责:
- topic slugify
- workspace slugify
- project slugify
- 安全截断规则
不负责:
- 校验是否重名
- 路径解析
### 4. `httpx`
负责:
- `WriteJSON`
- `WriteError`
- `ErrorWithStatus`
- 通用 JSON decode 辅助
- CORS 等通用 transport helper
不负责:
- route 注册
- 业务 request/response
### 5. `docspec`
这是 `P1` 里非常关键的模块。
负责:
- `PRD` 模板
- `Decision Log` 模板
- Discovery request 文档格式
- Discovery result 文档格式
- 文档 front matter 规范
- 文档固定章节顺序
不负责:
- 文档存储
- 文档版本管理
- 文档归属权限
这里要明确:
- 第二版中 `product` 会直接使用 `PRD``Decision Log`
- 模板沿用当前澄清模板,但所有权从 `clarifier` 转到 `product`
### 6. `message`
负责:
- message envelope 结构
- YAML front matter 解析
- markdown 渲染
- `from / to / type / topic / stage / reply_to / round` 规则
- 收件人解析
- 基础合法性校验
- 面向消息文档的稳定结构定义
不负责:
- 消息存储
- mailbox 查询
- HTTP handler
### 7. `topic`
负责:
- topic `space`
- `clarify`
- `pool`
- `workflow`
- `discovery`
- topic `status`
- space/status 的默认值
- 合法推进规则
- “能升不能降”之类的基础约束
不负责:
- topic record 存储
- topic 列表聚合
注意:
虽然 v2 去掉 `clarifier` 角色,但 `clarify` 这个对话整理阶段可以在兼容期继续存在;只是它背后的角色从 `clarifier` 变成 `product`
### 8. `role`
负责:
- 角色枚举和分类
- 角色合法性校验
- 角色阶段边界
- 哪些角色属于 discovery
- 哪些角色属于 workflow
- `product` 的正式职责定义
不负责:
- role 存储
- role 技能绑定
### 9. `requirement`
负责:
- requirement 结构
- requirement 状态
- `pending`
- `dispatched`
- `completed`
- `archived`
- 优先级范围和校验
- requirement 文本字段校验
不负责:
- requirement 查询
- requirement dispatch 执行
### 10. `discovery`
负责:
- discovery candidate 结构
- vote 结构
- 共识判断
- result markdown 生成
- discovery request 文本格式规则
- discovery 阶段的固定角色集合规则
不负责:
- 并发执行
- codex 调用
- round 持久化
### 11. `workflow`
负责:
- workflow 阶段枚举
- `plan`
- `review`
- `execution`
- `verification`
- workflow 阶段流转规则
- workflow 角色协作边界
- workflow 消息的基础规则
不负责:
- workflow board 聚合
- dispatch 查询
### 12. `merge`
负责:
- merge request 结构
- merge request 状态流转
- merge metadata 规则
不负责:
- git diff
- merge 命令执行
## P1 明确不做的东西
下面这些不属于 `P1`
- `server`
- `client`
- `http route`
- `app service`
- `store`
- `runtime`
- `agent runner`
- `workflow board`
- `pool dispatch`
- `merge` 的 git 执行
这些模块都依赖更高层或外部设施,不属于独立底层模块。
## P1 实现顺序
建议顺序如下:
1. `timeutil`
2. `slug`
3. `idgen`
4. `httpx`
5. `docspec`
6. `role`
7. `topic`
8. `workflow`
9. `requirement`
10. `merge`
11. `message`
12. `discovery`
原因很简单:
- 先做通用基础能力
- 再做文档规范
- 再做核心领域规则
- 最后做组合型领域模块
- 最后才进入 `P2``server/app/store`
## P1 输出结果
`P1` 完成后,应该得到这些结果:
1. 一组独立于基础设施的底层模块。
2. 一组稳定的领域结构、模板和状态流转规则。
3. 第二版可以明确复用的文档标准:
- `PRD`
- `Decision Log`
- Discovery request
- Discovery result
4. 一套从 `L0 -> L3` 的清晰依赖方向。
5. 后续 `P2` 实现 `HTTP server``app services` 时,不再需要重新发明这些规则。
## P1 完成标准
满足下面条件,`P1` 才算完成:
1. 上述每个模块都有明确目录。
2. 每个模块都有单元测试。
3. 每个模块都只依赖下层 `P1` 模块,不依赖任何 `P2/P3` 模块。
4. 文档模板和领域规则已经固定。
5. `product` 接管需求文档这一决策已经在底层模型中可以表达。
+648
View File
@@ -0,0 +1,648 @@
# Lane Worktree 物化与代码流转改造计划
本文档定义 `inbox v2``lane -> worktree -> runtime` 的代码流转改造方案。
目标很明确:
- 保留 `lane` 级隔离执行能力
- 让下游 `lane` 能消费上游 `lane` 的真实代码产物
- 避免 worker summary 充当“伪代码同步”
- 逐步把当前执行模型修正为“依赖图 = 代码图”
本文档按阶段拆分,便于逐步实现和回归。
## 背景
当前系统已经把术语统一到 `lane`,但运行时还存在一个关键缺口:
- `leader` 规划出 task DAG
- 系统按 task 依赖自动派生 execution lanes
- 每个 `lane` 都有独立 `worktree``podman` 容器
- 但跨 `lane` 的依赖目前只传递 worker summary,不传递代码
这会导致一个结果:
- 调度层认为 `Task B` 依赖 `Task A`
- 运行层却没有把 `Task A` 的代码产物同步给 `Task B`
- `Task B` 只能在自己的空 worktree 里“重建”上游结果
这不是真正的依赖执行,而是“读摘要后重写”。
## 真实问题示例:`todo-lane-smoke-2`
`todo-lane-smoke-2` 的 task 图如下:
1. `Scaffold full-stack project structure`
2. `Implement Todo API with SQLite`
3. `Implement React Todo UI`
4. `Integrate stack and verify runnable demo`
5. `Todo demo ready for review`
其依赖关系如下:
- `Scaffold full-stack project structure`:根任务
- `Implement Todo API with SQLite`:依赖 `Scaffold`
- `Implement React Todo UI`:依赖 `Scaffold`
- `Integrate stack and verify runnable demo`:依赖 `Backend API` + `Frontend App`
- `Todo demo ready for review`:依赖 `Integrate`
当前系统会把它派生成 4 条 execution lanes
- `Foundation Setup`
- `Backend API`
- `Frontend App`
- `Integration And Verification`
问题在于:
- `Backend API``Frontend App` 在各自 worktree 中写代码
- `Integration And Verification` 也在自己的 worktree 中执行
- 但它拿不到前两个 lane 的真实代码,只能拿到消息摘要
这意味着 `Integration` 不是在“上游代码之上做集成”,而是在“读了两段总结之后重新拼代码”。
## 总目标
实施完成后,系统应满足以下条件:
- 每个代码型 `lane` 都有可复用的 git 产物,而不是只有未提交改动
- 下游 `lane` 启动前可以显式物化上游 `lane` 的代码输入
- `lane` 之间的依赖关系能在 git 历史中体现出来
- merge 冲突会显式阻塞执行,而不是被摘要绕过
- `milestone` / `gate` 不再滥用独立 worktree
- `lane` 是“代码演进线”,不是“任意 task 容器”
## 术语
本文统一使用以下术语:
- `lane`:执行泳道,代表一条相对连续的代码演进线
- `worktree`:该 `lane` 对应的 git 工作目录
- `runtime`:该 `lane` 对应的容器与 worker 进程
- `snapshot`:某个 `lane` 在成功完成代码任务后的已提交 git 状态
- `materialization`:将上游 `lane` 的代码输入同步到当前 `lane`
## 设计原则
### 1. 代码依赖必须用代码流转表达
只要下游 task 需要基于上游代码继续开发、联调、验证,就必须物化上游代码,而不是只传摘要。
### 2. lane 隔离要保留,但不能成为代码孤岛
`lane` 的价值是隔离并行开发,不是让每个 worker 在自己的平行宇宙里重写同一份实现。
### 3. 失败要显式
如果下游 lane 在物化上游代码时发生 merge 冲突,应显式标记 `blocked`,并触发 replan 或人工介入。
### 4. 优先 clean replacement,不做兼容分叉
不增加“新老同步方式同时存在”的运行时兼容逻辑。
应使用显式 schema / 数据迁移把新模型落下。
### 5. runtime 职责保持窄边界
- `workspaceruntime` 负责 ensure worktree / container
- `taskexec` 负责调度与状态推进
- git 产物提交与代码物化应放在单独模块中
不要把代码同步逻辑散落到 handler、runtime、leader prompt 中。
## 非目标
本计划不包含以下内容:
- 多机调度
- 远程仓库 push / pull
- GitHub / GitLab PR 集成
- 二进制产物仓库
- 通用 artifact storage
- 对历史旧 topic 的运行时兼容兜底
## 理想工作流
`todo-lane-smoke-2` 举例,理想形态如下。
### Lane 拆分
保留 4 条 execution lanes,但语义要变:
1. `Foundation Setup lane`
`main` 起步,产出基础脚手架代码快照。
2. `Backend API lane`
`Foundation Setup` 的代码快照起步,负责后端与 SQLite。
3. `Frontend App lane`
`Foundation Setup` 的代码快照起步,负责 React 前端。
4. `Integration And Verification lane`
在开始执行前,先物化:
- `Foundation Setup`
- `Backend API`
- `Frontend App`
然后再做联调、启动脚本、验证、补丁修复。
5. `Todo demo ready for review`
这是 `milestone`,不需要独立 worktree。
### Git 视角下的理想状态
- `Foundation Setup lane` 成功后,lane 分支上至少有一笔提交
- `Backend API lane` 成功后,lane 分支上至少有一笔提交
- `Frontend App lane` 成功后,lane 分支上至少有一笔提交
- `Integration` 开始前,自己的 branch 已显式 merge 上游分支
换句话说,`Integration` 看到的应该是:
- `foundation commit`
- `backend commit`
- `frontend commit`
而不是:
- “Backend worker 说自己完成了”
- “Frontend worker 说自己完成了”
## 当前缺口
### 缺口 1:代码没有被自动提交
当前 worker 执行成功后,代码通常还停留在未提交状态。
这会导致:
- 上游 `lane` 没有稳定的可消费产物
- 下游即使要 merge,也没有明确的输入 commit
### 缺口 2:下游 lane 没有物化步骤
当前 `taskexec` 在给 worker 分配任务前,不会把依赖 lanes 的代码同步进当前 lane。
### 缺口 3merge_request 模型未接入执行主路径
系统里虽然有 `merge_requests` 表和 API,但当前并没有把它们接入 `leaderloop` / `taskexec` 的主执行链路。
### 缺口 4:lane 拆分策略过于宽松
当前 task DAG 会自动派生 lane,但不会约束:
- 哪些任务必须共用 lane
- 哪些任务值得独立 lane
- 哪些节点根本不需要独立 worktree
## 目标架构
建议引入两个新模块和一个新的持久化记录层。
### 模块 A`lanesnapshot`
建议路径:
- `inbox/internal/app/lanesnapshot/`
职责:
- 检查 lane worktree 是否有代码变更
- 过滤不应纳入版本控制的运行时垃圾
- 生成 lane 的提交快照
- 把最新提交记录回 lane 元数据
它不负责:
- 分配任务
- merge 上游 lane
- 启动容器
### 模块 B`lanematerialize`
建议路径:
- `inbox/internal/app/lanematerialize/`
职责:
- 根据 task 依赖关系识别当前 task 的上游 lanes
- 将上游 lane 的最新代码快照同步到当前 lane
- 记录 merge / 物化结果
- 冲突时产生结构化阻塞信息
它不负责:
- 选择哪个 task 该执行
- 自动 replan
- worker prompt 组装
### 模块 Clane 同步记录
建议新增表:
- `lane_syncs`
建议字段:
- `id`
- `workspace_id`
- `topic_id`
- `downstream_lane_id`
- `upstream_lane_id`
- `task_id`
- `upstream_commit`
- `merge_commit`
- `status`
- `error_message`
- `created_at`
- `updated_at`
用途:
- 保证物化过程可观测
- 避免重复 merge 同一输入时缺乏依据
- 为 dashboard / 调试 / 重试提供基础数据
## 数据模型改造
### 阶段性最小变更
在现有 `lanes` 表上新增字段:
- `head_commit TEXT NOT NULL DEFAULT ''`
说明:
- 领域名词与底层表名现在都使用 `lane / lanes`
- 先在现有 `lanes` 表上扩字段,避免一次性大迁移
### 后续可选扩展
如需更强的可观测性,可继续新增:
- `last_materialized_at`
- `last_materialized_commit`
- `last_sync_error`
但这些不是第一阶段必需项。
## 关键执行点
### 1. 成功 task 完成后,自动生成 lane snapshot
建议接入点:
- `inbox/internal/app/taskexec/service.go`
- `Complete(...)` 主路径
顺序建议:
1. worker 返回成功
2. 宿主机确认 lane runtime 与 worktree 可访问
3. `lanesnapshot` 检查并提交 worktree 代码
4. 成功后才真正把 task 标成 `succeeded`
5. 如果提交失败,则这次 run 视为失败或阻塞
原因:
- 一旦 task 已被标成成功,但代码并未形成稳定快照,下游 lane 仍然无法消费
- 所以“成功完成 task”的定义应当包含“代码产物已稳定落盘到 git”
### 2. 下游 task 被 claim 前,先做 lane materialization
建议接入点:
- `inbox/internal/app/taskexec/service.go`
- `ClaimNext(...)` 主路径
顺序建议:
1. 找到当前 lane 中下一个 ready task
2. 识别其依赖 tasks
3. 推导依赖 tasks 所属的上游 lanes
4. `lanematerialize` 将上游 lane commits 同步到当前 lane
5. 同步成功后再真正 claim task
6. 如冲突,则不 claim,改为 `blocked`
原因:
- worker 启动时就应该处于“已拿到所有上游代码输入”的环境
- 不要把 merge 责任甩给 worker
### 3. `workspaceruntime` 不做业务同步
`workspaceruntime` 继续只负责:
- ensure repository
- ensure worktree
- ensure container
- stop container
不要把以下逻辑塞进去:
- 依赖 lanes 分析
- 自动 commit
- 自动 merge
- 冲突策略判断
## 推荐 merge 策略
第一版建议使用最朴素、最可审计的策略:
- 上游 lane 产物必须先成为 git commit
- 下游 lane 物化时使用显式 `git merge`
- 保留 merge commit,不做 squash
原因:
- 依赖关系能在历史中直接看见
- 调试时容易定位是哪条 lane 带进来的改动
- 比起复制文件或 patch 应用,git merge 更符合当前 worktree 模型
### 合并顺序
建议按依赖拓扑顺序进行 merge。
`Integration And Verification` 为例,建议顺序:
1. `Foundation Setup`
2. `Backend API`
3. `Frontend App`
如果 `Backend``Frontend` 都已经是从 `Foundation` 分化出来的,并且各自分支历史完整,也可以只 merge 最终两个分支,由 git 自动通过共同祖先处理基础提交。
第一版不要求做最优优化,要求的是语义正确和行为稳定。
### 冲突处理
发生冲突时:
- 停止当前 materialization
- 记录 `lane_syncs.status = failed`
- task 标记为 `blocked`
- lane 可标记为 `blocked`
- 通过 message 或 task event 通知 leader
不要做以下行为:
- 自动硬解冲突
- 静默 fallback 为“只读摘要继续执行”
- 自动丢弃上游改动
## lane 拆分策略建议
除了代码流转,lane 数量本身也要更收敛。
### 应当独立 lane 的情况
- 明确并行价值高
- 文件边界清晰
- 预期实现时间较长
- 失败隔离有意义
- 产物可以作为其他 lane 的明确输入
### 应当共用 lane 的情况
- 前后两个 task 本质上是同一条连续实现链
- 第二个 task 高度依赖第一个 task 的局部未抽象代码
- 拆开后只会制造频繁 merge 和冲突
### 不应拥有独立代码 lane 的情况
- `milestone`
-`gate`
- 单纯汇报 / 审阅 / 总结型节点
## 分阶段实施
### 阶段 0:基线梳理与命名统一
目标:
- 明确以 `lane` 为统一术语
- 建立当前缺口的自动化验证样例
实现内容:
- 文档统一使用 `lane`
-`todo-lane-smoke-2` 这类场景补回归测试描述
- 梳理现有旧命名残留,不要求立刻全量改名
验收标准:
- 团队对外讨论统一使用 `lane`
- 有一份可复现“下游 lane 看不到上游代码”的基线案例
### 阶段 1lane snapshot 落地
目标:
- 成功的代码型 task 必须沉淀为 lane 分支上的 commit
实现内容:
-`lanes` 表新增 `head_commit`
- 新增 `lanesnapshot` 模块
- 在 task 成功完成路径中自动:
- 检查 worktree dirty 状态
- 执行 `git add`
- 执行 `git commit`
- 写回 `head_commit`
建议约束:
- 只对 `execution` / `verification` 这类代码型 task 做 snapshot
- `milestone` / `gate` 不生成代码提交
验收标准:
- `todo-lane-smoke-2``foundation` / `backend` / `frontend` 成功后,各自 lane 均有非空 `head_commit`
- worktree 不再只停留在未提交改动
### 阶段 2:下游 lane materialization
目标:
- 下游 lane 能在 worker 执行前拿到上游 lane 的真实代码输入
实现内容:
- 新增 `lanematerialize` 模块
- 新增 `lane_syncs`
-`ClaimNext(...)` 中对 candidate task 执行 materialization
- materialization 成功后才 claim task
验收标准:
- `Integration And Verification` 在执行前能看到 `Backend API``Frontend App` 的真实代码
- 回归时不再依赖 worker summary 重建实现
### 阶段 3:阻塞与 replan 闭环
目标:
- merge 冲突成为受控状态,而不是隐性失败
实现内容:
- 统一 materialization failure 到 `blocked`
- 增加 task event / message,向 leader 报告冲突
- leader 收到冲突后走 patch replan
验收标准:
- 冲突 topic 会稳定停在 `blocked`
- leader 能基于冲突信息重新编排,而不是盲目新起 lanes
### 阶段 4:lane 拆分规则收紧
目标:
- 减少不必要的 lane / worktree / container 增生
实现内容:
- 调整 leader prompt 与 lane 派生规则
- 倾向于把连续实现链保留在同一 lane
- 仅在明确并行或集成汇聚点时拆新 lane
- `milestone` / `gate` 不派生代码 lane
验收标准:
- 类似 `todo-lane-smoke-2` 的 topic 中,lane 数量与实际代码边界一致
- 不再出现“为了一个收口任务额外生成空 lane”的情况
### 阶段 5:可观测性与回收策略
目标:
- 让 lane 运行时资源与代码同步状态都可追踪、可回收
实现内容:
- dashboard 展示:
- `head_commit`
- 最新 materialization 状态
- lane sync 历史
- topic 完成后可选执行:
- 停止 lane runtime
- 延迟回收容器
- 延迟清理 worktree
说明:
- 本阶段是运维优化,不阻塞前四阶段主路径落地
验收标准:
- topic 完成后不再长期残留无意义运行中的 lane runtime
- 调试时能直接看到 lane 的代码输入输出链路
## 需要修改的主要模块
### 必改
- `inbox/internal/app/taskexec/service.go`
- `inbox/internal/store/sqlite/taskexec_store.go`
- `inbox/internal/store/sqlite/migrations/`
- `inbox/internal/domain/lane/`
### 新增建议
- `inbox/internal/app/lanesnapshot/`
- `inbox/internal/app/lanematerialize/`
- `inbox/internal/domain/lanesync/`
- `inbox/internal/store/sqlite/lane_syncs_store.go`
### 暂不建议承载业务逻辑的模块
- `inbox/internal/app/workspaceruntime/`
- `inbox/internal/httpapi/`
- `inbox/internal/app/leaderloop/`
这些模块可以消费结果,不应承担主要 git 业务逻辑。
## 测试建议
### 单元测试
- snapshot 成功创建提交
- worktree 无变化时不重复提交
- materialization 能识别依赖 lanes
- materialization 在冲突时稳定失败并写入 `blocked`
### 集成测试
`todo-lane-smoke-2` 类型流程验证:
1. `Foundation Setup` 成功后生成 commit
2. `Backend API` / `Frontend App` 都从基础快照继续开发
3. `Integration` 在启动前合并上游代码
4. `milestone` 自动完成且不新建代码 lane
### 回归重点
- 不能破坏当前单 lane 串行执行
- 不能让 `gate` / `milestone` 意外触发 git commit
- 不能把未提交脏状态错误标记成“已可下游消费”
## 开放问题
### 1. 自动提交的粒度
第一版建议:
- 每个成功 task 最多生成一笔提交
不建议第一版支持:
- 一个 task 内自动切多笔提交
### 2. merge 粒度是按 task 还是按 lane
第一版建议按 `lane` 的最新成功快照物化。
原因:
- 实现简单
- 与当前 runtime 粒度一致
- 更符合“lane 是代码演进线”的模型
### 3. 是否必须使用 merge request 模型
第一版不必强制接入 `merge_requests`
建议先把:
- 真实 commit 产物
- lane materialization
- 冲突阻塞
这三个核心闭环做完。
之后再决定是否把 `merge_requests` 升级成:
- lane 间同步记录
- 人工审批面板
- 最终 topic 合并机制
## 最小可上线版本
如果只做最小闭环,建议范围如下:
1. `lanes.head_commit`
2. 成功 task 后自动提交
3. `ClaimNext` 前 merge 上游 lane commits
4. merge 冲突时标记 `blocked`
做到这一步,系统就已经从“摘要依赖”升级成“代码依赖”。
## 结论
这次改造的本质,不是“再加一个 merge 功能”,而是修正整个 lane 执行模型的语义:
- 上游 `lane` 的产物应该是代码快照
- 下游 `lane` 的输入应该是上游代码状态
- worker summary 应回归为编排证据,而不是代码同步手段
`todo-lane-smoke-2` 这样的 DAG 来说,这不是优化,而是让系统终于按自己宣称的依赖关系去工作。
+460
View File
@@ -0,0 +1,460 @@
# Leader DAG 化实施计划
本文档定义当前 `inbox v2` 从“leader 输出 chain/task 并即时执行”升级到“更稳定的 DAG planning / gating / replan”模型的落地方案。
目标不是照搬 Astro,而是吸收其中最有价值的五个机制,并严格适配我们现有架构:
- `leader` 运行在宿主机,内嵌于 `inbox server`
- `worker` 运行在 per-chain 容器中
- `topic / chain / task / dependency / workflow_run / message` 是现有运行时主模型
- 不引入多机器调度,不引入 hosted control plane,不引入新的外部依赖
## 背景
最近几轮真实回归暴露出两个核心问题:
1. `leader` 的规划产物还不够稳定,更多是“即时创建 chain/task”,而不是明确的计划对象。
2. `worker` 失败后的 replan 缺少约束,导致同一个 topic 里反复新建 chain,造成 worktree / container 增生。
这说明当前系统已经具备“可执行”的基本能力,但还缺少“可控规划”的中间层。
本计划把这层能力拆成五个阶段,按顺序落地。
## 总目标
实施完成后,系统应具备以下能力:
- `leader` 输出稳定的 planning schema,而不是松散文本
- topic 在执行前先经过 gating,而不是盲目并发起 worker
- 任务优先按 deliverable 和 batch 拆分,而不是隐式退化成角色链
- `replan` 只能 patch 现有图,不能无限增生 chain
- milestone 成为图中的一等节点,用于收口多分支结果
## 非目标
本计划不包含以下内容:
- 多机器 / 多 runner 调度
- 容器资源感知调度
- hosted dashboard / cloud relay
- 通用 workflow DSL
- 兼容旧 pool / discovery / fixed-role 路径
---
## 阶段 1:规划产物标准化
### 目标
`leader` 的输出从“只够当前 applyLeaderOutput 消费的最小 JSON”升级为稳定的 planning schema。
### 要解决的问题
- 当前 `leader` 输出主要服务于即时建链,不利于回放、对比、重规划
- `reply_markdown``chains``tasks` 混在一起,缺少 plan 元信息
- 缺少 task 的输入、产物、验证、批次、replan 策略等显式字段
### 产出
新增一个面向 `leader` 的 planning schema,建议至少包含:
- `plan_version`
- `plan_summary_markdown`
- `execution_mode`
- `clarify`
- `plan_only`
- `plan_and_start`
- `chains[]`
- `key`
- `name`
- `purpose`
- `reuse_strategy`
- `tasks[]`
- `key`
- `chain_key`
- `title`
- `body_markdown`
- `acceptance_markdown`
- `deliverables[]`
- `depends_on[]`
- `verification`
- `batch_key`
- `replan_policy`
- `milestones[]`
- `start_nodes[]`
- `leader_reply`
### 实现范围
- `inbox/internal/app/leaderloop/service.go`
- 扩展 `leaderOutput` 结构
- 更新 `leaderOutputSchema()`
- 拆出 `plan` 相关校验函数
- 如有必要,新增:
- `inbox/internal/domain/plan/`
-`inbox/internal/domain/workflowplan/`
### 设计要求
- schema 必须可序列化进 `workflow_runs.config_snapshot_json``command_json`
- `leader` 返回的 plan 必须可重放
- plan 必须允许“仅规划不启动”
### 验收标准
- `leader` 输出可以表达:
- 只澄清
- 只规划
- 规划并启动
- 单元测试覆盖:
- 完整 plan decode
- 缺字段失败
- 非法 dependency 失败
- 非法 `start_nodes` 失败
### 风险
- 一次把字段做太多,导致 prompt 不稳定
- schema 改动过大,影响现有 `taskexec` 和 dashboard 显示
### 收口策略
- 先新增字段,不立刻让 dashboard 全部消费
- `leaderloop` 内部先把 schema 校验稳定,再逐步外显
---
## 阶段 2:前置 Gating 节点
### 目标
在真正启动 worker 之前,先通过一组 gating task 判断“是否具备执行条件”。
### 要解决的问题
- 当前 leader 规划后容易直接启动多个 chain
- 一些基础条件没满足时,worker 只会重复失败
- 失败后 leader 再次 replan,进一步放大噪音
### 典型 gating 场景
- 仓库结构是否存在
- 运行时 provider / auth 是否可用
- 基础 contract / API 边界是否已经明确
- 必要目录 / package manager / root script 是否存在
- topic 是否已具备足够信息,不需要继续澄清
### 产出
在 planning schema 中加入 gating 语义:
- `tasks[].kind`
- `gate`
- `execution`
- `verification`
- `milestone`
- `tasks[].gate_policy`
- `hard_stop`
- `ask_user`
- `leader_replan`
### 实现范围
- `leaderloop`
-`start_nodes` 解析时优先启动 gate task 所在 chain
- `taskexec`
- gate task 失败后,不自动放开后续依赖
- `dashboard`
- 在 chain/task board 上明确标记 gate 节点
### 设计要求
- gate 失败时,后续依赖节点保持 `draft``blocked`
- leader 收到 gate 失败后,应优先 patch 计划,而不是起新图
- gate task 的完成结果必须带结构化 failure reason
### 验收标准
- topic 首轮执行时,能先只启动 1 个 gating chain
- gate 失败后,其余 chain 不启动
- leader 能基于 gate failure 回复用户或 patch 任务
### 风险
- 如果 gating 太多,会让简单需求变慢
### 收口策略
- 只保留 2-3 类强 gating
- 其它检查继续留在 worker 内部做普通失败
---
## 阶段 3:按 Deliverable 与 Batch 拆分
### 目标
让 leader 优先按产物边界拆 task,而不是退化成“前端链 / 后端链 / 基础链”这类松散角色链。
### 要解决的问题
- 当前 chain 名称仍然容易被 prompt 驱动成泛化角色链
- 同一链上 task 颗粒度不稳定
- 缺少批处理并行能力
### 产出
在 leader prompt 和 schema 中强化以下约束:
- task 必须显式描述最终产物
- chain 只是执行容器,不是角色人设
- 可以使用 `batch_key` 做批次并行
建议的拆分优先级:
1. 先按 deliverable 拆
2. deliverable 太大时,再按 batch 拆
3. 最后才落到实现域(frontend/backend/integration
示例:
- `api-contract`
- `backend-crud`
- `frontend-list-create`
- `frontend-edit-filter`
- `integration-e2e`
而不是默认:
- `backend`
- `frontend`
- `foundation`
### 实现范围
- `leaderloop/buildLeaderPrompt`
- 增加强约束,要求 chain/task 以产物命名
- `leaderOutputSchema`
- 增加 `deliverables[]``batch_key`
- `dashboard`
- 在 task 卡片中展示 deliverables 与 batch
### 设计要求
- 同一 batch 内的 task 应能独立并行
- `deliverables[]` 应该能支持后续 verification 和 merge summary
### 验收标准
- 相同需求多次规划时,chain/task 命名更稳定
- leader 生成的任务里,至少 80% 有明确 deliverable 字段
- 支持一个 topic 下存在“批量任务 + 聚合 milestone”
### 风险
- prompt 改得太死,影响 leader 灵活性
### 收口策略
- 不硬编码固定 chain 名称
- 只强制“必须有 deliverable”,不强制唯一命名模板
---
## 阶段 4Replan 只允许 Patch,不允许重建图
### 目标
把 replan 从“再创建一批 chain/task”改成“在同一张图上 patch 现有节点”。
### 要解决的问题
- 这次已经真实出现过:
- worker 失败
- leader 重规划
- 同一 topic 新建 `chain-2``chain-3``chain-60`
- worktree / container 爆炸增长
### 核心原则
- topic 进入 execution 后,禁止无边界新增 chain
- replan 默认只能做:
- 更新已有 chain 状态
- 在已有 chain 下补 task
- 调整 dependencies
- 调整 `start_nodes`
- 标记 chain/task 为 superseded / blocked / cancelled
只有在极少数场景下允许新增 chain,例如:
- 用户明确要求增加新的独立工作流分支
- 原图中不存在可复用的产物链
- leader 输出显式 `replan_mode = expand_graph`
即使允许扩图,也必须通过严格限制:
- topic 级 max chain count
- replan iteration limit
- leader 必须解释新增原因
### 产出
新增 replan 策略字段:
- `plan_mode`
- `initial`
- `patch`
- `expand_graph`
- `replan_reason`
- `supersedes[]`
新增 topic 级保护:
- `max_chain_count`
- `max_replan_count`
### 实现范围
- `leaderloop/applyLeaderOutput`
- 默认复用已有 chain
- 有 existing chains 时禁止无限新建
- `taskexec`
- worker failure 回传更结构化的失败原因
- 可能需要:
- `chains` / `tasks` 新状态,例如 `superseded`
### 验收标准
- 同一 topic 上重复 worker 失败后,不再出现 chain 数量线性增长
- topic 执行 5 次失败回执后,chain 总量仍保持稳定
- 集成测试覆盖:
- 初始计划
- worker 失败
- leader patch
- 继续执行
### 风险
- patch 逻辑比“直接新增”更复杂
- 旧 topic 的脏数据可能影响验证
### 收口策略
- 先在 `leaderloop` 内部做强保护
- 旧 topic 不做兼容逻辑,直接清数据
---
## 阶段 5Milestone 成为一等节点
### 目标
把 milestone 从隐式概念提升为图中的正式节点,用来汇聚多分支结果并决定下一步推进。
### 要解决的问题
- 当前多分支的收口主要靠 leader 自己看消息判断
- 缺少“某几个 task 都完成后,再开始下一阶段”的正式表达
### 典型 milestone
- `Foundation Ready`
- `API Contract Ready`
- `Feature Complete`
- `Ready for Integration`
- `Ready for User Review`
### 产出
在 planning schema 中加入:
- `milestones[]`
- `key`
- `title`
- `depends_on[]`
- `summary_markdown`
- milestone 可被 `start_nodes` 和后续 task 依赖引用
### 实现范围
- `leaderloop`
- 允许创建 milestone 节点
- 在依赖满足时将 milestone 标记为 ready/succeeded
- `tasks` 或新表
- 若不新增表,可先将 milestone 作为特殊 task kind 存储
- `dashboard`
- graph / board 中区分 milestone 与普通执行 task
### 设计要求
- milestone 不运行容器
- milestone 只负责聚合前置状态
- milestone 可以成为 integration / verification 的 gating 前提
### 验收标准
- 一条执行链中可见 milestone 节点
- 多分支完成后 milestone 自动推进
- leader 可以基于 milestone 状态决定是否继续启动 integration
### 风险
- 如果把 milestone 单独建表,会扩大 schema 变更面
### 收口策略
- 第一版先把 milestone 存成 `task.kind = milestone`
- schema 稳定后再决定是否单独抽表
---
## 推荐实施顺序
严格按下面顺序推进,不要并行乱改:
1. 阶段 1:规划产物标准化
2. 阶段 4Replan 只允许 Patch
3. 阶段 2:前置 Gating 节点
4. 阶段 3:按 Deliverable 与 Batch 拆分
5. 阶段 5Milestone 一等节点
原因:
- 阶段 1 先固定 schema
- 阶段 4 先止住增生风险
- 阶段 2 再降低盲目执行
- 阶段 3 再提高任务质量
- 阶段 5 最后做图上的正式汇聚
## 每阶段的统一交付要求
每个阶段都必须同时完成:
- 领域模型改动
- `leaderloop` 应用逻辑改动
- 必要的 HTTP / dashboard 暴露
- 单元测试
- 至少一条真实或半真实回归路径
## 统一验收门槛
全部五阶段完成后,应满足:
- 同一 topic 不再出现失控 chain 增生
- leader 输出稳定 plan schema
- 执行前存在明确 gating
- task 以 deliverable 为核心组织
- 图中存在正式 milestone 节点
- dashboard 能看出“计划 / 执行 / 收口”的结构
## 建议的实施文档拆分
后续真正进入实现时,建议在 `.omx/plans/``docs/` 下继续细分为:
- `phase-1-plan-schema.md`
- `phase-2-gating.md`
- `phase-3-deliverable-batching.md`
- `phase-4-replan-patch-only.md`
- `phase-5-milestones.md`
本文档作为总计划,不直接承担逐字段设计。
+65
View File
@@ -0,0 +1,65 @@
# Repository Modules
This repository is organized as a small monorepo with explicit module boundaries.
## Top-Level Modules
| Module | Path | Responsibility | Public interface | Test command |
| --- | --- | --- | --- | --- |
| Inbox host | `inbox/` | Control plane: CLI, HTTP API, agent dispatch, runtime storage | CLI commands and `/api/*` HTTP endpoints | `make test-inbox` |
| Dashboard | `dashboard/` | Operations UI for the inbox host | Browser UI + calls to inbox HTTP API only | `make test-dashboard` |
| Blog backend | `apps/blog/backend/` | Sample service API | HTTP endpoints exposed by Express | `make test-blog-backend` |
| Blog frontend | `apps/blog/frontend/` | Sample client app | Browser UI + calls to blog backend HTTP API | `make test-blog-frontend` |
| Blog E2E | `apps/blog/e2e/` | Black-box integration tests | Playwright against running frontend/backend | `make test-blog-e2e` |
## Dependency Rules
### Repository-level rules
- `dashboard/` must not import code from `inbox/`.
- `apps/blog/frontend/` must not import code from `apps/blog/backend/`.
- `apps/blog/e2e/` must treat frontend and backend as black boxes.
- Runtime state, caches, worktrees, and local databases are generated artifacts and must not be committed.
### `inbox/` rules
The inbox module is the only place where repository control-plane logic should live.
- `cmd/inbox/` is the executable entrypoint only.
- `internal/store/` owns persistence and schema details.
- `internal/httpapi/` and `internal/httpservice/` own HTTP transport contracts and route wiring.
- `internal/commands/`, `internal/cli/`, and `internal/web/` own CLI/Web command behavior.
- `internal/*api`, `internal/*admin`, `internal/*flow`, and `internal/*bridge` packages are application services/adapters.
- Top-level `inbox/*.go` files should stay as composition and compatibility wiring, not as the place where new domain logic grows.
Current workflow graph data lives in the inbox module as first-class runtime state:
- `leader` planning output persists to `workflow_runs.command_json` as structured `planning`
- `chains` carry orchestration metadata such as `purpose`
- `tasks` carry execution-graph metadata such as `kind`, `gate_policy`, `deliverables`, `verification_mode`, `batch_key`, and `replan_policy`
- `task.kind = milestone` is the current milestone representation; milestone nodes do not dispatch workers
## Testing Strategy
### Fast tests
Use module-local unit tests by default:
- `make test-inbox`
- `make test-dashboard`
- `make test-blog-backend`
- `make test-blog-frontend`
### Browser/E2E tests
- `make test-dashboard-e2e`
- `make test-blog-e2e`
The blog E2E suite starts backend and frontend via Playwright `webServer` configuration, so it can run independently from the rest of the repository once dependencies are installed.
## Workflow
1. Work in one module at a time.
2. Run that module's tests before touching cross-module integration.
3. Use HTTP or CLI contracts when crossing module boundaries.
4. Prefer adding tests in the same module that owns the behavior.