Files
ai-workflow/docs/inbox-v2-database.md

791 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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