graphlemon 开发计划
版本: v1.0
日期: 2026-07-29
输入来源: graphlemon_architecture.md(v0.11)+ docs/sdd/(00-06 + sdd-mvcp-contract.json v1.6.0)
方法论: 复用本地 Hermes planning profile 的 project-unit-decomposition(Development Unit 分解 + 依赖 DAG)+ engineering-work-planning(Phase/Sub-phase/Unit 三层分类:Epic 级/Feature 级/Task 级,垂直切片优先于技术分层)
0. 怎么读这份文档
- Phase(大阶段,对应 Epic 级):有自己的验收标准和"完成"定义,多个 Sub-phase 组成。
- Sub-phase(小阶段,对应 Feature 级):一个可独立验收的能力切片,多个 Unit 组成。
- Unit(子任务,对应 Task 级,即 Development Unit):一个明确的工程产物 + 完成标准,映射到至少一条 SDD 条目(M/V/C/P/REQ),标注风险分类:
- [Value Path] 主线价值切片,直接对应用户/系统可感知的能力
- [Risk Gate] 高风险校验/回路/护栏,不做会在生产阶段暴露风险
- [Enabler] 使能型基础设施,本身不直接产出用户可见价值,但阻塞其他 Unit
- 每个 Unit 标注
依赖(DAG 上游)和Done 标准(可观察、可验证,优先复用 06-验收与测试锚点.md 已有的 REQ 用例,不重新发明验收口径)。 - 机器可读版本见
docs/sdd/unit_matrix.json(Unit ID、依赖、REQ/M/V/C/P 覆盖、风险分类,供后续 Coder Agent 或调度器直接消费)。
关于阶段排序的说明:Phase 0 是纯 Enabler(无独立用户价值),Phase 1 起才是垂直切片(贪吃蛇场景端到端跑通)。这不违反"避免按技术分层切故事"的原则——Phase 0 内部的确按技术层(数据模型→后端集成→控制台骨架)排列,但这是因为它们是共同阻塞后续所有价值切片的地基工作,不是把一个本该完整的价值切片强行拆成"先做数据库、再做 API、再做前端"三个假装独立的"Phase"。真正的价值切片从 Phase 1 开始,一路贯穿 Org Graph→Work Graph→端到端验收,不按技术层拆分。
1. Phase 总览
| Phase | 目标 | 类型 | 验收锚点 |
|---|---|---|---|
| PH0 | 基础设施与脚手架就绪 | Enabler | 能创建目标项目、Git 仓库结构符合约定、15 个实体可建表 |
| PH1 | MVP 全链路闭环跑通(贪吃蛇场景) | Value Path | 架构文档 §11 验收标准第 1-3 条;REQ-01/02/03/07 |
| PH2 | 质量闭环:回路、Skill 迭代、护栏、断点恢复 | Risk Gate | 架构文档 §11 验收标准第 4-5 条;REQ-04/05/06/08/10/14 |
| PH3 | 人机交互控制台 | Value Path | REQ-11/12/13 |
| PH4 | 全量验收与固化 | Risk Gate | 06 节全部 14 条 REQ 完整回归通过一次 |
| PH5(2026-07-30 新增) | 自举能力:Deploy Supervisor + 蓝绿部署自动回滚 | Risk Gate | docs/supervisor-auto-rollback-design.md 状态机全部路径(health_check_failed / rolled_back / rollback_failed / finalized)至少各复现一次;self-development-feasibility.md 9 步走查场景真实跑通一次 |
依赖顺序:PH0 → PH1 → PH2,PH3 可在 PH1 完成后与 PH2 并行(控制台是对已产生数据的可视化层,不阻塞回路/护栏逻辑本身的开发),PH4 收口需要 PH1/PH2/PH3 全部完成。PH5 依赖 PH4 收口(自我修改的安全网必须建立在"系统本身已通过全量验收、行为可信"的前提上,对已知不稳定的系统做自动回滚没有意义),且 PH5 不阻塞、不改变 PH0-PH4 的开发方式——PH0-PH4 期间 graphlemon 自身仍是由人类 + Claude Code 交互式开发(这是任何自举系统的必然前提,见 self-development-feasibility.md §1),PH5 只在"PH0-PH4 都跑通之后,graphlemon 开始被允许对自己的代码做自主修改"这个新增能力上生效。
PH0:基础设施与脚手架(Enabler)
Goal:为后续所有开发建立可靠地基,本身不产出用户可见能力。
Phase 验收:给定一个空 git 仓库路径,能自动初始化出完整的 graphlemon 目录骨架;15 个数据实体全部建表成功并通过约束校验;claude --agent planner 之类的 subagent 调用能跑通最简单的一次往返。
PH0-S1 数据模型与仓库骨架
可与 PH0-S3 并行。
U-001 [Enabler] 落地 01-M 全部 15 个实体的物理数据库 schema(MVP 单机场景可用 SQLite),含全部枚举约束、外键、唯一索引
- 依赖:无(起点)
- Done:15 张表全部建表成功;
project.target_folder_path唯一约束、work_node_execution.status枚举约束等关键约束有单测覆盖 - 覆盖:01-M 全部实体
- 源码位置:graphlemon 仓库根目录
src/(见 05 节 §5.7"源码目录约定",与 U-002 在目标项目里生成的src/是两回事)
U-002 [Enabler] 目标项目 Git 仓库目录骨架初始化逻辑:只生成
.graphlemon/子目录(含runs/<task_id>/,架构文档 §13.2——01-Mproject.graphlemon_subdir_initialized字段本身指向的就是这节,不是 §7.3)- 2026-07-31 订正:development-plan.md 这行原先误引用了架构文档 §7.3 的旧目录树(
.claude/agents/、.claude/agent-memory/、.claude/workflows/、skills/、顶层runs/)。但 §13.1("已确认"决策,晚于 §7.3)明确写着这些是 graphlemon 平台自己仓库的资产("graphlemon 平台自己的仓库只保留平台资产:Org/Work Graph 引擎、Skill 库、Agent 人设...不含任何目标项目的业务代码"),属于 U-003 范围,不是目标项目、不是 U-002。§7.3 是"graphlemon 只服务自己开发自己"阶段(自举模式)的旧设计,已被 §13"目标项目是独立代码空间"这个决策取代。目标项目自己的src/不需要 graphlemon 创建或规定结构("目标项目自己原有的源码目录,graphlemon 不规定具体结构,尊重目标项目原有约定")。用户确认,2026-07-31。 - 依赖:U-001(
project.graphlemon_subdir_initialized字段需要有实体支撑) - Done:给定一个空 git 仓库,能生成
.graphlemon/子目录骨架并产生一次初始化 commit;非 git 仓库时按 02-V 2.1 节的确认态流程走(不静默git init) - 覆盖:01-M
project;03-Cc_project_create
- 2026-07-31 订正:development-plan.md 这行原先误引用了架构文档 §7.3 的旧目录树(
U-003 [Enabler] graphlemon 平台自身的仓库/运行时目录结构(与目标项目仓库物理分离;本地非 Git 的原始日志目录,见架构文档 §13.2)
- 依赖:无
- Done:平台目录和任意目标项目目录可以是完全独立的两个路径,互不干扰;日志目录不纳入平台自身 Git 历史
PH0-S2 Claude Code 后端集成
依赖 PH0-S1。这是全项目风险最集中的 Enabler 子阶段,直接决定 A-1(已 confirmed)之后的下一个高风险假设 A-2 能否成立。
U-004 [Risk Gate] Hermes profile 内容迁移到
.claude/agents/{planner,coder,validator}.md(对应 SDD 00 节 A-2 假设的实际验证动作)- 依赖:U-002
- Done:三个 subagent 定义文件可被 Claude Code 发现,
claude --agent planner -p "..."能正常启动且系统提示词内容正确;skills:/memory: project/tools:/model:frontmatter 字段配置完整(见 05-实施约束与依赖.md §5.1) - 覆盖:架构文档 §7.2/§9;05 节 §5.1;A-2 假设
- 注意(2026-07-31 已验证,结果:部分成立):Planner/Validator 的 SOUL.md 基本原样迁移;Coder 的 SOUL.md 需要实质性重写(原文件硬编码了一个真实飞书 Base API token、整段认领机制强绑定
lark-cli/Base 状态机、且指示自己再跑一次claude --dangerously-skip-permissions,与 05 §5.3.1 的auto权限模式直接冲突)。真实claude --agent planner -p调用(--permission-mode auto,成本 $0.05)验证通过,返回内容准确复述了迁移后的身份与红线。A-2 记为部分成立,按本条预先注册的判定标准处理,不是 confirmed;详见 00-SDD-生成记录.md 假设表和agents/{planner,coder,validator}/SOUL.md各自文件头的迁移说明。U-005/U-011 沿用现有.claude/agents/*.md内容即可,不需要因为这条重新做 A-2 spike
U-004.5(2026-07-30 新增)[Enabler] 搭建
RUN_REAL_AGENT_E2E门控测试 harness(真实 shell out 到claude的端到端测试默认跳过,需显式环境变量开启;镜像CLAUDE.mdTesting 约束里点名的 graphloop 先例,避免 U-005/U-006/U-010 等后续 Unit 各自发明一套)- 依赖:U-004
- Done:仓库里有一个可复用的 skip 装饰器/测试基类(例如
skip_unless_env("RUN_REAL_AGENT_E2E"));日常python3 -m unittest默认跳过任何真实调用claude子进程的测试;显式设置该环境变量时,对 U-004 落地的某个 subagent 做一次真实-p往返调用能跑通 - 覆盖:
CLAUDE.mdTesting 约束
U-005 [Enabler]
c_org_node_trigger的claude --agent <name> -p子进程调用封装(骨架,不含完整状态机)- 依赖:U-004, U-004.5
- Done:手工调用能触发三个 Org Agent 之一并拿到文本响应
U-006 [Enabler] Work Graph 执行层调用封装(workflow 脚本写入 +
claude -p调用,直接复用spikes/a1-dynamic-workflows/已验证的代码路径,见 03-C 3.5 节实现契约)- 依赖:U-004, U-004.5
- Done:封装成可被服务端函数调用的接口,单测通过,
--permission-mode auto生效(不是--bare,不是bypassPermissions,见 05 节 §5.3.1)
PH0-S3 控制台骨架
可与 PH0-S1/S2 并行,无相互依赖。
U-007 [Enabler] 本地后端进程骨架(纯 stdlib Python
http.server/socketserver实现 HTTP API + WebSocket/SSE,不引入 Flask/FastAPI 等第三方包,见 05 节 §5.7)+c_pick_folder的 macOS 实现(osascript,Windows/Linux 留到 PH3 补齐)- 依赖:无
- Done:后端进程可启停;macOS 上
/api/system/pick-folder能真实弹出系统对话框并返回路径;后端代码零第三方依赖(pip list/import检查可验证) - 覆盖:03-C
c_pick_folder;05 节 §5.4.1, §5.7
U-008 [Enabler] 前端骨架(本地 Web 应用,原生 HTML/CSS/JS,无构建步骤,无 npm 依赖,见 05 节 §5.7)
- 依赖:U-007
- Done:浏览器访问
localhost能看到路由跳转正常的空壳应用;前端不依赖 npm install/构建工具链
PH1:MVP 全链路闭环(Value Path,贪吃蛇场景)
Goal:端到端跑通一次从"提出目标"到"任务完成"的完整闭环,不要求 UI,可以用手工 API 调用驱动。 Phase 验收:REQ-01/02/03/07 的 happy path 全部通过;架构文档 §11 验收标准第 1-3 条达成。
PH1-S1 Org Graph 层单点触发
U-009 [Value Path]
c_task_create实现,task状态机(p_org_task_lifecycle)落地到queued态(c_project_create已经在 U-002 完整实现,这里不重做)- 依赖:U-001, U-002
- Done(2026-07-31 订正:原来写"REQ-01 happy/boundary/failure 三个用例全部通过"不准确——happy 用例("依次触发 planner/coder/validator 三个 org_node,task.status 按状态机正确流转")需要
c_org_node_trigger完整实现,U-009 本身不碰这个,做不到;U-010 的 Done 标准原话几乎就是这句 happy 用例。boundary(并发上限不阻塞任务创建本身)和 failure(INVALID_PARAM)两条在 U-009 范围内是真实可测的):boundary/failure 两个用例通过;happy 用例随 U-010 落地后累计满足,不在本 Unit 单独打勾 - 覆盖:01-M
task;03-Cc_task_create;04-Pp_org_task_lifecycle;REQ-01(boundary/failure)
U-010 [Value Path]
c_org_node_trigger完整实现(含TASK_NOT_READY/CONCURRENCY_LIMIT_REACHED/USAGE_LIMIT_REACHED错误处理)- 依赖:U-005, U-009
- Done:手工依次触发 planner/coder/validator 三个 Org Agent,
task.status按状态机正确流转(2026-07-31 订正引用:原来写"对应架构文档 §11'手工调用验证'里程碑",但架构文档 §11 里根本没有这个短语;真正出处是 05 节 §5.5"上线顺序与兼容性"第 2 步:"再落地 Org Graph 层接口...此时可先用手工调用验证 Planner/Coder/Validator 三个 Org Agent 分别可被触发"——注意是"分别",即逐个验证能被触发,不是要求一次串联跑通带真实 Artifact 校验的完整链路,那是 05 节 §5.5 第 3 步"Work Graph 层集成"之后的事,此处不做) - 范围说明(2026-07-31,实现后补充):本 Unit 只落地
p_org_task_lifecycle(04-P)里由单次c_org_node_trigger调用直接触发的三段前向状态迁移——queued→planning_brd_prd_sdd(触发 planner)、planning_brd_prd_sdd→coding(触发 coder,需要 BRD/PRD/SDD/Benchmark 四类 Artifact 均validation_status=passed)、coding→validating(触发 validator,需要code_diff/test_report均validation_status=passed)。planning_failed/coding_failed重试、Validator PASS/FAIL 判定与bug_record/c_org_loop_route路由、blocked_pending_human升级,都需要真实 Work Graph 产出(U-011 起)或 Org-level Loop(U-015/U-016,PH2)才能实现,本 Unit 不做,也做不到——由于没有真实 Work Graph 产出 Artifact,测试里的 Artifact 校验前置条件是直接 seed 数据进去的,不是跑出来的,这是意料之中的(上游契约还没实现,消费方先按契约测) - 覆盖:03-C
c_org_node_trigger
PH1-S2 Work Graph 层集成(本项目风险最高的 Sub-phase)
U-011 [Value Path]
c_work_graph_plan实现(Planning Skill 调用,输出graph_manifest)- 依赖:U-004, U-010
- Done(2026-07-31 订正:原来写"REQ-02 happy 用例通过(Planner 产出 BRD/PRD/SDD/Benchmark 四类 artifact 且血缘完整)",但 03-C §3.4 里
c_work_graph_plan自己的契约明确写着"不执行,只规划"——它只产出graph_manifest(结构/计划),不产出真实 Artifact 内容;REQ-02 happy 用例要求的"四类 artifact 且血缘完整"需要真正执行(U-012/U-013)加上c_artifact_write(见下方缺口),单靠 U-011 做不到,也不该做,这是同一类 Done 标准过头的问题,U-009/U-010 已经出现过两次):c_work_graph_plan真实调用claude --agent planner -p产出一个通过结构校验的graph_manifest(四个节点对应 brd/prd/sdd/benchmark,依赖链 sdd→prd→brd 正确),落一条work_graph_run(status='planning')记录;REQ-02 happy 用例整体随 U-012/U-013 落地后累计满足 - 已知缺口(2026-07-31,实现时发现):
c_artifact_write(03-C §3.7)是 REQ-02 happy/failure 两个用例都直接依赖的控制器(failure 用例原话:"c_artifact_write应拒绝写入"),但翻遍 development-plan.md 全部 43 个 Unit,没有任何一个 Unit 的"覆盖"字段提到c_artifact_write——不是分散在别处,是真的没人认领。等做到 U-012/U-013(或不管哪个 Unit 第一个需要"把 Agent 写的文件登记成 artifact 行")时需要显式补上,不要假设它已经存在 - 技术发现(2026-07-31):
--agent和--json-schema同时使用时,--json-schema会被静默忽略(structured_output完全不出现),已记录到 05 节 §5.4.1.1,plan_work_graph改成从result里解析```json围栏代码块 - 覆盖:01-M
artifact/work_graph_run;03-Cc_work_graph_plan;REQ-02(happy 用例的"计划"部分,非全部)
U-012 [Value Path]
c_work_graph_execute完整实现(auto权限模式、USAGE_LIMIT_REACHED退避重试p_usage_limit_wait、CLASSIFIER_BLOCKED处理)- 依赖:U-006, U-011
- Done(2026-07-31 订正:原来写"REQ-07 全部用例通过",但 REQ-07 happy 用例原话是"Claude Code ACP 后端下完整跑通 REQ-01~06 全部场景"——这需要 REQ-04/05/06(Validator 根因归属、Org-level Loop、断点恢复)全部就绪,那是 PH2 的事,U-012 单靠 U-006+U-011 做不到,是同一类 Done 标准过头问题第四次出现):REQ-07 boundary(不加
--bare,冷启动开销可接受——U-006 已满足,本 Unit 未改变这点)和 failure(CLASSIFIER_BLOCKED检测正确路由)通过;happy 用例随 PH2 收口后累计满足。REQ-14 happy 用例通过(boundary/failure 场景在 PH2-S3/U-020 补齐完整覆盖)——这条原来就写对了,不用改 - 范围说明(2026-07-31,实现后补充):
p_usage_limit_wait退避重试落在整个execute_work_graph调用级别,不是逐个work_node_execution记录级别——U-013(c_work_node_run,真正拥有work_node_execution行)还没做,节点级别的额度等待追踪要等 U-013 才有意义。CLASSIFIER_BLOCKED判定用单次-p调用自带的permission_denials数组长度 ≥3 做近似(03-C 原话"连续 3 次或累计 20 次",跨调用的真正累计计数需要持久化,也是等 U-013) - 覆盖:03-C
c_work_graph_execute;04-Pp_work_graph_execution/p_usage_limit_wait;REQ-07(boundary/failure)/REQ-14(happy)
U-013 [Value Path]
c_work_node_run+git_worktree隔离(并行写代码节点)- 依赖:U-012
- Done(2026-07-31 订正:原来写"REQ-03 happy/boundary 通过",但 happy 用例("产出 code_diff 与 test_report...unit+integration test 全部通过")需要真实 Coder 会话写代码 +
c_artifact_write(U-011 已记录的缺口,至今没有 Unit 认领),U-013 单靠 U-012 做不到——第五次出现同一类问题):REQ-03 boundary 通过(两个独立节点各自用git worktree add隔离,run_fn各写一个文件并 commit,merge --no-ff合并回主分支,git log确认两次提交都在,且work_node_execution.git_worktree_ref各不相同);happy 用例随c_artifact_write落地后累计满足 - 精度提升(2026-07-31,实现后补充):03-C §3.6 给出了
USAGE_LIMIT_REACHED的精确探测正则/hit your (session|weekly|Opus) limit/i,比 U-010/U-012 当时用的短语近似匹配更准——U-013 按这条正则实现,且真正写回work_node_execution.quota_wait_started_at/quota_limit_type(U-010/U-012 阶段这两张表字段还没有真实归属)。c_work_node_run是内部接口(03-C 原话"不对外暴露 HTTP,供 c_work_graph_execute 调用"),退避重试从 U-012 的整图级别下沉到这里的单节点级别 - 覆盖:01-M
work_node_execution;03-Cc_work_node_run;REQ-03(boundary)
PH1-S3 端到端验收(收口)
- U-014 [Value Path] 完整跑通"实现贪吃蛇游戏"验收场景,从
goal_text到task.status=done- 依赖:U-011, U-012, U-013
- Done:架构文档 §11 MVP 验收标准第 1-3 条全部达成(Planner 出四件套,Coder 自测通过,Validator 归因产出 bug list)
- 实现补充(2026-07-31):这是前面 U-009~U-013 五次"Done 标准过头"里被推迟的 happy 用例真正兑现的地方。落地时顺手把 U-011 记录的
c_artifact_write(03-C §3.7)缺口补上了——src/artifact_write.py,之前没有任何 Unit 认领。编排代码是src/e2e_pipeline.py:plan_documents(真实调用 planner 依次产出 BRD→PRD→SDD→Benchmark,各自用c_artifact_write落盘+提交+建血缘)、implement_code(真实调用 coder,用会话前后的git diff作为code_diff、coder 自己的回复文本作为test_report)、validate_implementation(真实调用 validator,解析约定的VERDICT: PASS|FAIL|INCONCLUSIVE行)、run_end_to_end(串联全部四步) - 真实验证的范围(2026-07-31):
plan_documents这一段(4 次真实 planner 调用产出真实 BRD/PRD/SDD/Benchmark,血缘链验证)成本和之前几个 Unit 同量级,已经真实跑过。run_end_to_end的 Coder/Validator 那一半——真实写代码、真实跑测试——量级完全不同(可能是分钟级、真金白银的开销,而且结果不确定),没有不请示直接跑,等用户决定要不要真的跑一次完整闭环
PH2:质量闭环(Risk Gate,回路与护栏)
Goal:系统出错时能自主定位、自主修复,或安全地转人工,不会无限空转也不会裸奔超支。 Phase 验收:完整跑通一次 Org-level Loop(人为植入 bug → 定位 → 修复);REQ-04/05/06/08/09/10/14 全部通过。
PH2-S1/S2/S3/S4 之间除标注的依赖外相互独立,可并行推进。
PH2-S1 Validator 回路与根因溯源
U-015 [Risk Gate]
p_validator_root_cause_trace实现(三态判定 PASS/FAIL/INCONCLUSIVE,双向可追溯矩阵)- 依赖:U-013
- Done:REQ-04 happy/boundary/failure 通过
- 实现补充(2026-07-31):这是这几轮里少有的一次不用订正 Done 标准的 Unit——REQ-04 的三个用例都只依赖"给定 lineage 上下文后的多视角判定",没有牵扯到 U-015 自身依赖之外的能力。"血缘读取"(读 SDD/PRD/BRD/Coder 实现记录)刻意设计成调用方职责(
perspective_fn闭包自带上下文),judge_root_cause本身只管三视角≥2/3 多数表决。"双向可追溯矩阵"是 Validator 人设自己的内部推理框架(qa-task-validationskill + 迁移后的 AGENTS.md "claim—风险—实现—测试—证据矩阵",U-004 时已经迁移),不是本 Unit 需要单独建的后端能力 - 已知缺口,2026-07-31 U-018 已解决:
root_cause_category=skill_defect时 03-C §3.10 要求"必须同时创建 skill_iteration_suggestion",当时skill_iteration表要求的before_git_commit_sha/after_git_commit_sha还没有真实来源。做 U-018 时按 04-P §4.3 本身的状态表重新确认:bug_recorded(c_bug_record_create单独达成)和skill_iteration_triggered(触发c_skill_update)是两个先后独立的状态迁移,不是同一次调用——"必须同时创建"读成"同一条流程线里最终要有"而不是"同一次 API 调用里同步造出来",bug_record.skill_iteration_suggestion_id从 null 起步,c_skill_update跑完后回填。已放开bug_record.py接受skill_defect - 覆盖:01-M
bug_record;03-Cc_bug_record_create;04-Pp_validator_root_cause_trace;REQ-04
U-016 [Risk Gate]
c_org_loop_route+fast_lane/full_replan快速通道- 依赖:U-015
- Done:REQ-05/REQ-09 全部用例通过(含 3 轮上限 →
blocked_pending_human) - 实现补充(2026-07-31):顺手补上了
validating→done(validator_pass 分支,04-P 原话动作是"关闭任务",没有挂具体c_*控制器名字,之前也没有任何代码把task.status真正写成过done)——close_task_done(),和c_org_loop_route是同一个"validating 状态怎么退出"决策的另一半,REQ-05 happy 用例要求"最终 task.status=done",不补这个到不了终点 - 简化,如实标注(2026-07-31):04-P 表格把 fast_lane/full_replan 拆成两跳(
validating→routed_fast_lane→coding),但c_org_loop_route自己的 03-C 契约直接输出最终态{new_status, new_assigned_org_node},且没有第二跳的独立控制器名字——实现上把两跳合并成一次原子调用,routed_fast_lane/routed_full_replan这两个task.status枚举值实际不会被落库,改成写一条execution_event(event_type=loop_routed)记录路由决策本身。REQ-05/REQ-09 的验收文字都只看最终落点,不检查这个中间态,不影响 Done;以后 PH3 看板如果要真的展示这个瞬时状态,需要回头改 - 覆盖:03-C
c_org_loop_route;REQ-05/REQ-09
U-017 [Risk Gate] 人为植入 bug 完整闭环验证(架构文档 §11 验收标准第 4 条)
- 依赖:U-016
- Done:引入一个人为植入的 bug,系统自主定位根因并驱动 Planner/Coder 完成修复闭环
- 实现补充(2026-07-31):
src/org_level_loop.py的run_fix_loop把 U-015(三视角判定)+ U-016(路由)串成一个闭环:判定→建bug_record→路由→真实触发 Coder/Planner 修复→真实触发 Validator 复核→复核通过则close_task_done。category=coder→fast_lane、category=planning→full_replan是这里定的固定映射规则(bug_record.suggested_route本来是 Validator 独立判定的字段,但 REQ-05/架构文档 §11 的验收场景里两者天然对应,没有再额外调一次 Agent 单独决定 route)。真实验证复用了 U-015 那个撞墙误判场景,但这次真的让 Coder 编辑仓库里的collision.py文件、真的让 Validator 读文件复核,不是只判定不动手
PH2-S2 Skill 自主迭代
- U-018 [Risk Gate]
c_skill_update+c_skill_rollback+ Git commit 留痕- 依赖:U-015(需要
root_cause_category=skill_defect触发) - Done:REQ-08 全部用例通过
- 实现补充(2026-07-31):Skill 文件物理落在 graphlemon 平台自己仓库(
skills/<skill_id>/,架构文档 §13.1 平台资产,和 U-004 时agents/的归属判断一致),不是目标项目——repo_path参数没有默认值,强制调用方每次显式传,避免测试不小心忘记覆盖就误提交进 graphlemon 自己的真实仓库(写代码时先犯过这个错,当场改掉了)。c_skill_rollback不是历史重写,是"读回滚前那个 commit 的内容,作为一次新的 commit + 新的skill_iteration记录"提交,回滚记录的triggered_by_bug_id复用被回滚那次迭代原本的 bug_id(c_skill_rollback输入契约本来就没有单独给 bug_id 的位置,skill_iteration.triggered_by_bug_id又是非空 FK,只有这一个来源可用) - 已知缺口,不是本 Unit 的事:Hermes 迁移过来的 Skill 内容本身还没有真正搬过来(05§5.1 只列了要迁移的技能名字,
agent_profile.py的skills:frontmatter 目前只是引用名字,skills/<skill_id>/在仓库里还没有真实文件)——测试里跟 U-010/U-011 对没建成的上游产物的处理方式一样,自己 seed 一个占位 Skill 文件 - 覆盖:01-M
skill/skill_iteration;03-Cc_skill_update/c_skill_rollback;REQ-08
- 依赖:U-015(需要
PH2-S3 护栏双层机制
U-019 [Risk Gate]
budget_guardrail_config+c_budget_check(人工配置层)- 依赖:U-012
- Done:REQ-10 happy/boundary 通过
- 实现补充(2026-07-31):boundary 用例(调高额度后
paused态任务恢复调度)需要的"改配置+恢复调度"能力,本 Unit 只建了最小可用版本update_budget_limit_and_resume——完整设置页控制器c_budget_config_update(含完整字段校验、UI)是 U-030 的事;这里直接复用 U-016 的manual_resume(org_loop_route.route_org_loop)做实际的状态恢复,没有重新实现一遍。c_budget_check超限时直接抛BudgetExceededError并把blocked_reason设成budget_exceeded(不是返回allowed=false让调用方自己处理),对齐 03-C 原文"触发后对应任务转 blocked_pending_human"这句话描述的是c_budget_check自己的副作用 - 覆盖:01-M
budget_guardrail_config;03-Cc_budget_check;04-Pp_budget_guardrail;REQ-10
U-020 [Risk Gate]
p_usage_limit_wait完整验收(session/weekly/opus三种quota_limit_type+ 超时兜底)- 依赖:U-012(骨架已在 U-012 实现,本 Unit 补齐边界/失败场景)
- Done:REQ-14 全部用例通过(含超时转
blocked_pending_human, blocked_reason='usage_limit_wait_timeout') - 实现补充(2026-07-31):这次不只是补测试——重新对照 REQ-14 boundary 用例原文发现,U-013 时
work_node_run.py的 opus 处理只做到"不退避",没做到"直接降级到非 Opus 模型重试"(原来是探测到 opus 额度直接抛错,从没真正重试过)。补上了:run_fn签名改成run_fn(cwd, model=None),探测到 opus 信号后立即(不等待)用opus_downgrade_model(默认claude-sonnet-5)重试一次;如果降级后还是收到 opus 信号(不合理,非 opus 模型不该撞 opus 专属额度),直接判超时,不会无限重试。顺手把之前漏测的weekly额度类型(探测恢复 + 9 天超时)也补齐了。work_graph_execute.py(U-012)里同名的 opus 立即失败逻辑没有跟着改——REQ-14 覆盖列表只写了c_work_node_run/c_org_node_trigger,没有c_work_graph_execute,而且那条路径是多 Agent 动态工作流,没有一个单一的"the model"可以整体降级,属于更深的 A-1 动态工作流问题,本 Unit 不动 - 覆盖:04-P
p_usage_limit_wait;REQ-14
PH2-S4 断点恢复
- U-021 [Risk Gate] Graph Engine 持久化状态 + 断点恢复
- 依赖:U-012, U-013
- Done:REQ-06 happy/boundary/failure 通过(执行中途中断进程后从持久化状态恢复,不重跑已完成节点)
- 实现补充(2026-07-31):核心问题是 U-013
run_work_node的now默认值是time.monotonic——单调时钟在进程重启后没有固定的墙钟含义,REQ-06 恰恰要求"进程重启后继续计算 quota 等待已经过去多久",两者根本不兼容。改法:now默认改回time.time(墙钟),新增_parse_sqlite_timestamp()把 SQLiteCURRENT_TIMESTAMP的 UTC 字符串转成可比的 epoch 值;run_work_node一开始就读出该 node_exec_id 已持久化的status/retry_count/quota_wait_started_at/git_worktree_ref,status='done'直接返回存量output_payload(不重跑run_fn),retrying/waiting_for_quota从持久化的retry_count/quota_wait_started_at续算而不是清零重来,needs_worktree=True时若已存在对应 worktree 目录则复用而不是重新git worktree add(会直接失败)。新增resume_work_graph_run(conn, run_id):返回一次work_graph_run里还没done的node_exec_id列表,供调用方决定重新调度谁;对内部不一致的持久化状态(无效的work_graph_run.status/节点status,status='done'但output_payload为空,status='waiting_for_quota'但quota_wait_started_at为空)统一抛CorruptPersistedStateError,不会返回一份看似正常但实际有问题的恢复计划。这几类"状态不一致"用正常写入路径其实到不了(各字段都有CHECK约束),测试里用PRAGMA ignore_check_constraints=ON模拟外部损坏的行来触发。 - 覆盖:01-M
work_graph_run/work_node_execution;04-Pp_work_graph_execution;REQ-06
PH3:人机交互控制台(Value Path,可观测性与操作入口)
Goal:人可以通过浏览器创建项目、提出目标、反馈 bug、实时看到运行过程、管理 Skill 库。
Phase 验收:REQ-11/12/13 全部通过。
并行关系:可在 PH1 完成后与 PH2 并行开工(控制台是对已产生数据的可视化/操作层,不阻塞回路/护栏本身的开发逻辑)。PH3-S2(实时看板)分两级:U-024(推送链路本身)只需 U-012/U-013 落地即可开工,比原计划更早;REQ-12 的完整验收挪到 U-024.5,需要排在 PH2 大部分 Unit 完成、execution_event 各类场景都已产生真实数据之后再联调。
PH3-S1 项目与目标入口
U-022 [Value Path]
v_project_setup+c_pick_folder全平台补齐(WindowsPowerShell/Linuxzenity/kdialog)- 依赖:U-007, U-008
- Done:REQ-11 happy/boundary/failure 通过
- 实现补充(2026-07-31):Done 行字面写的是"REQ-11 全部通过",但 REQ-11 happy 用例完整走完还需要
v_goal_composer/c_task_create创建 task 那一步——那是 U-023 的范围,本 Unit 依赖只到 U-007/U-008,不可能单独满足。本 Unit 实际完成的是 REQ-11 里v_project_setup/c_pick_folder/c_project_create那部分:①pick_folder()补齐 Windows(PowerShellFolderBrowserDialog脚本)/Linux(zenity优先,缺失则退回kdialog,都不在则DIALOG_UNAVAILABLE);②c_project_create(POST /api/projects)此前只有 U-002 写好的project_scaffold.create_project()函数本体、从未接到 HTTP 层(backend_server.py因此第一次需要持有一个 sqlite 连接——ThreadingHTTPServer按请求起线程,给schema.connect()加了check_same_thread=False参数 + 一把server.conn_lock避免并发请求踩到同一个连接);③frontend/app.js补上#/setup真实交互(选文件夹 → 校验 Git 仓库 → 非 Git 仓库时二次确认 → 创建 → 跳转仪表盘),DOM class 命名对齐docs/wireframes/v_project_setup.html。REQ-11 的完整 happy 用例(含创建 task 那一步)等 U-023 落地后再验收。浏览器扩展在本次会话不可用,UI 部分改为对着真实运行的后端做完整 HTTP 级冒烟(curl走一遍选文件夹之外的全部接口调用序列,node --check过一遍app.js语法),不是真人点击浏览器验证过的。 - 覆盖:02-V
v_project_setup;03-Cc_pick_folder;05 节 §5.4.1;REQ-11
U-023 [Value Path]
v_goal_composer+v_bug_feedback- 依赖:U-022
- Done:能通过 UI 创建
task和人工发起的bug_record(c_bug_report_create) - 实现补充(2026-07-31):①
c_task_create(U-009 已实现的task_create.create_task())第一次接上POST /api/tasks;②c_bug_report_create(03-C §3.1.1)此前完全没有实现,本 Unit 新增bug_record.create_bug_report()+POST /api/bug-reports。发现并解决一个 03-C 内部小缺口:c_bug_report_create的input_contract只列了{description, repro_steps, related_task_id},没有project_id;但execution_event.project_id是 NOT NULL(01-M 1.9),bug_record表自己又没有project_id列可以在related_task_id留空时兜底推导——"留空则视为新发现的独立问题"(02-V §2.4)这条路径字面上无法满足execution_event的约束。判断这是文档遗漏而非架构决策点(同类判断依据见 U-022 的c_project_createHTTP 接线、U-014 的c_artifact_write),直接让project_id和c_task_create一样成为必填参数,未停下来单独确认。③ 控制台目前没有"当前项目"这个概念的载体(v_dashboard是 U-025 才做),v_goal_composer/v_bug_feedback需要的project_id暂存到localStorage(v_project_setup创建成功时写入),没有选中项目时两个页面直接引导回#/setup。④related_task_id在 02-V 原型里是下拉选择器,但c_task_list(列出当前项目下的任务供选择)没有被任何 Unit 认领,本 Unit 简化成纯文本 task_id 输入框,避免顺手做一个不属于自己范围的接口;等 U-025 补上任务列表后再换成真下拉。⑤ 提交成功后 02-V 写的是"跳转v_task_detail",但那个页面是 U-025 才建,本 Unit 改成在原页面展示一条带 task_id/bug_id 的确认信息 + 返回仪表盘链接,等 U-025 落地后再切回真跳转。⑥ 对着真实运行的后端做端到端冒烟时发现一个真 bug:create_bug_report()一开始没校验project_id/related_task_id是否存在,传入不存在的project_id会在execution_event插入时触发未捕获的sqlite3.IntegrityError,请求直接 500(无干净 JSON 错误体)——补了存在性校验,改为清晰的INVALID_PARAM/400,并加了回归测试。 - 覆盖:02-V
v_goal_composer/v_bug_feedback;03-Cc_task_create/c_bug_report_create
PH3-S2 实时看板
U-024 [Value Path]
c_ws_subscribe+execution_event实时推送(最小连通版)- 依赖:U-012, U-013(work graph 执行、work node run 是最早、最稳定产出
execution_event的两个来源,足够验证推送链路本身是否通) - Done:
c_ws_subscribe对真实execution_event做实时推送,≤2 秒延迟达成;REQ-12 happy 用例通过(boundary/failure——尤其是断线重连拉快照在多样事件场景下的表现——留给 U-024.5 补齐完整覆盖) - 实现补充(2026-07-31):Python stdlib 没有 WebSocket 支持,
src/ws_subscribe.py手写了 RFC 6455 握手(Sec-WebSocket-Accept用 hashlib+base64 算)+ 帧编解码(socket原始读写,服务端帧不加掩码,客户端帧强制解掩码),没有引入websockets之类的第三方库(05 §5.7)。推送机制是轮询,不是写路径 hook——execution_event的写入分散在org_node_trigger/work_graph_execute/work_node_run/task_create/bug_record等十几个调用点,没有单一写入路径可以插桩,Unit 名字里的"最小连通版"本身也在暗示这个方向;_handle_ws_subscribe用ThreadingHTTPServer劫持连接后每 0.5 秒查一次execution_event(按rowid增量,project_id/可选task_id过滤),轻松满足 REQ-12 的 ≤2 秒目标(该数字本身写的是"建议"而非硬 SLA)。03-C §3.14 自己写明断线重连走 REST 快照补齐、不在 WebSocket 层做消息重放缓冲,所以新连接只看"连接那一刻起"的新事件,不做历史重放。给ThreadingHTTPServer加了daemon_threads = True,避免长连接的 WS 线程阻塞进程退出。端到端验证:src/test_backend_server.py用原始 socket 走真实握手+推送延迟断言;另外用 Node 原生WebSocket客户端(跨实现的真实互操作性验证,不是同一套代码自己测自己)连真实运行的服务器,从建连到收到c_task_create触发的真实task_status_changed推送全程走通。 - 覆盖:01-M
execution_event;03-Cc_ws_subscribe;REQ-12(部分)
- 依赖:U-012, U-013(work graph 执行、work node run 是最早、最稳定产出
U-024.5(2026-07-30 新增)[Value Path] REQ-12 完整验收(事件多样性联调)
- 依赖:U-024, U-015, U-016, U-018, U-019, U-020, U-021(需要 PH2 大部分 Unit 已上线,事件类型覆盖足够丰富才能真正验证 boundary/failure 场景——例如 usage-limit 等待、护栏拦截、Skill 回滚各自产生的
execution_event都要真实出现过) - Done:REQ-12 全部 happy/boundary/failure 用例通过;PH3 Phase 验收里的 REQ-12 以本 Unit 为准,U-024 本身只解决"链路通不通"
- 覆盖:REQ-12(完整)
- 依赖:U-024, U-015, U-016, U-018, U-019, U-020, U-021(需要 PH2 大部分 Unit 已上线,事件类型覆盖足够丰富才能真正验证 boundary/failure 场景——例如 usage-limit 等待、护栏拦截、Skill 回滚各自产生的
U-025 [Value Path]
v_dashboard+v_task_board+v_task_detail- 依赖:U-024
- Done:三个页面可正常展示实时数据(原文档缺 Done/覆盖行,2026-07-31 补上)
- 实现补充(2026-07-31):这三个页面需要的读接口此前完全不存在,本 Unit 顺带把它们建了:①
c_task_list/c_task_get(03-C §3.2,此前无 Unit 认领)新增src/task_query.py,c_task_get的返回比 03-C 字面写的"task + artifact 列表 + bug_record 列表"更完整——额外带上work_graph_run(含解析后的graph_manifest)+ 每个 run 的work_node_execution列表,因为v_task_detail的 to-do 树(01-M 1.3 补充说明、02-V §2.6 2026-07-30 新增)就是要渲染这份数据,字面契约低估了这个视图真正需要的东西;②c_org_loop_route(03-C §3.11,U-016 已实现route_org_loop()但从未接 HTTP)第一次接上POST /api/tasks/:task_id/route,v_task_detail的"人工干预(manual_resume)"按钮需要它;③ 预算护栏没有任何读接口(03-C §3.13 只写了 PUT,读是 U-030 配置页的隐含前提但没人建),新增budget_guardrail.list_budget_configs()+GET /api/settings/budget给仪表盘的预算条用。前端:frontend/app.js补上真实的#/(org graph 三节点卡片、当前任务概要、预算条、事件时间线,全部通过c_ws_subscribe实时刷新)、#/tasks(关键字/状态/org_node 过滤 + 点击行跳详情)、#/tasks/:task_id(task 字段、to-do 树、关联 artifact/bug_record、blocked_pending_human告警条 + manual_resume 按钮);路由改成对象形式支持挂载态清理(registerRouteCleanup),每次切页面先关掉上一个页面开的 WebSocket,避免连接堆积。v_task_detail里v_graph_visualizer(DAG 视图)和v_log_viewer(原始日志)两个子模块明确标成"随 U-026/U-027 补上"的占位提示,不是假造内容。顺带把 U-023 里因为c_task_list当时还不存在而简化成纯文本输入的related_task_id换回了真下拉(v_goal_composer/v_bug_feedback提交成功后也从"页面内展示确认信息"改回 02-V 原文写的"跳转v_task_detail",这个页面现在是真的了)。浏览器扩展本次会话不可用,验证方式:全部新增 HTTP 路由的真实请求/响应断言(src/test_backend_server.py)+ 对着真实运行的服务器用curl/Node 原生WebSocket/fetch走一遍数据形状(尤其是 to-do 树需要的嵌套 JSON 结构,逐字段核对过 JS 渲染逻辑会怎么消费它)+node --check过语法,不是真人点击浏览器验证过的。 - 覆盖:02-V
v_dashboard/v_task_board/v_task_detail;03-Cc_task_list/c_task_get/c_org_loop_route(HTTP 接线)
U-026 [Value Path]
v_graph_visualizer(Org Graph + Work Graph 双层可视化,含waiting_for_quota信息态展示)- 依赖:U-024
- Done:Org/Work Graph 双层可视化含
waiting_for_quota信息态(原文档缺 Done/覆盖行,2026-07-31 补上) - 实现补充(2026-07-31):纯前端 Unit,不需要新接口——数据已经在 U-025 的
c_task_get里(work_graph_run.graph_manifest+work_node_execution列表),只补了两个 hover tooltip 要用但 U-025 当时没取的字段(input_schema_ref/output_schema_ref/quota_limit_type/quota_wait_started_at,见task_query._work_nodes_for_run)。frontend/app.js新增computeGraphLayers():按graph_manifest节点的depends_on边做拓扑分层(没有依赖的节点在第 0 层,每个节点的层号 = 1 + 依赖节点里最大层号),渲染成按执行层级排布的行(work-row),fan-out/fan-in 都能正确分层——用真实运行的服务器 + 一个 fan-out/fan-in 混合的graph_manifest(n1→[n2,n3]→n4)验证过,直接从app.js里eval出computeGraphLayers本体(不是重新实现一份逻辑再测)跑真实拿到的 JSON,分层结果[["n1"],["n2","n3"],["n4"]]完全正确。节点按 03-C §3.6 的六态配色(done/running/reflecting|retrying/waiting_for_quota/blocked|failed/pending),waiting_for_quota额外挂data-quota-limit-type/data-quota-wait-since,hover tooltip 展示 skill/quota_limit_type/已等待时长(客户端用quota_wait_started_at和Date.now()算,服务端只存原始时间戳不算相对时长)。02-V §2.7 原描述还包含一份 Org Graph 层,但v_task_detail(U-025)页面顶部已经有等价的org-strip面板(同款 class/data 结构),紧接着 to-do 树下面再画一份完全一样的东西纯属重复滚动,所以这里只做了 Work Graph DAG 层,Org Graph 复用页面已有的那个——不是漏做,是判断为无意义重复后跳过,写在这里说明。嵌入位置:v_task_detail里原来"随 U-026 补上"的占位提示换成了真实的 DAG 面板。浏览器扩展本次会话不可用,验证方式同 U-025:真实服务器 + 真实数据 + 直接跑生产代码里的分层函数,不是真人点击浏览器验证过的。 - 覆盖:02-V
v_graph_visualizer;03-C 3.6 节六态配色语义
PH3-S3 日志与 Artifact/Skill 管理
U-027 [Value Path]
execution_log_file+c_execution_log_get+c_ws_log_tail+v_log_viewer- 依赖:U-024
- Done:REQ-13 happy/boundary/failure 通过
- 实现补充(2026-07-31):
execution_log_file的行生命周期(active→closed)U-013 早就管好了,但从来没有代码真的往日志文件里写过内容——src/execution_log.py新增append_log()补上这条缺失的写路径,设计成run_fn(真正干活的那个函数)在执行过程中随时可调用,不关心节点在做什么,和work_node_run.py自己对run_fn划的边界一致。get_log()按 offset/limit 分页读,download=true时走原始文件流。c_ws_log_tail(WS /api/ws/work-nodes/:node_exec_id/log)复用 U-024 手写的 RFC 6455 机制,同样是轮询execution_event(event_type=log_appended);按 03-C §3.6.2 原文要求,节点一旦转终态(done/blocked/failed)服务端主动发关闭帧断开连接,不是让客户端一直轮询下去。p_log_retention_cleanup(04-P §4.5)实现成一个纯函数run_log_retention_cleanup()——过期扫描 +max_total_size_mb超限时按最旧优先追加候选 + 单个文件删除失败只记录不阻塞其余项,全部按 04-P 原文逻辑写;没有配调度器/cron(那不在本 Unit 覆盖范围内,c_execution_log_get/c_ws_log_tail才是),留给未来的调度进程周期调用。过程中意外抓到一个真的并发 bug:backend_server.py的_Server.server_close()关闭共享 sqlite 连接时原来没拿conn_lock,如果这时候正好有个 WS 轮询线程在另一边持锁执行 SQL,两个线程无同步地并发操作同一个 C 级 sqlite3 连接对象,实测直接把解释器崩了(exit code 139,SIGSEGV)——不是新代码引入的,U-024 就已经有这个隐患,只是这次高频率的 WS 测试把它的时序窗口撞开了;修好后连续跑几十次全部稳定通过。前端:frontend/app.js的v_log_viewer挂在v_task_detail每个 to-do 节点的展开详情里(不是页面底部一个孤立面板,node_exec_id对应哪个节点这份日志就该长在哪个节点旁边),首次展开才建连接(懒加载),运行中态走c_ws_log_tail实时滚动、终态走c_execution_log_get一次性静态拉取;活跃的 log-tail 连接会在任务级刷新/离开页面时统一关闭,避免连接泄漏。真实服务器 + Node 原生WebSocket/fetch端到端验证过实时追加与终态自动断开的完整链路(静态回填先拿到已有内容,WS 只推增量,二者拼起来等于最终静态内容)。 - 覆盖:01-M
execution_log_file/log_retention_config;02-Vv_log_viewer;03-Cc_execution_log_get/c_ws_log_tail;REQ-13
U-028 [Value Path]
v_artifact_browser- 依赖:U-011
- Done:Artifact 列表+预览+血缘链可用(原文档缺 Done/覆盖行,2026-07-31 补上)
- 实现补充(2026-07-31):
c_artifact_get在机器可读的sdd-mvcp-contract.json里其实定义过(GET /api/artifacts/:artifact_id),只是 03-C 的正文一直没写出来;c_artifact_list连 MVCP JSON 里都没有 operation 定义,纯粹是v_artifact_browser自己的过滤字段(artifact_type_filter/task_id_filter)倒逼出来的缺口,和 U-025 补c_task_list是同一类"读接口没人建"问题。新增src/artifact_query.py:list_artifacts()返回裸数组(跟list_tasks()保持同款风格,不跟 MVCP JSON 里c_task_list的{items,total}包一层——本地单项目 MVP 不需要分页,跟自己代码库已有约定保持一致比跟一份机器生成的镜像文件保持一致更重要);get_artifact()顺带把血缘链(lineage_chain(),按depends_on_artifact_ids往回走,多个依赖时只走第一个——原型图和这条流水线的实际产出都只有线性 BRD→PRD→SDD 链,没有真正的多依赖分叉场景)和文件内容(artifact表本身没有 content 列,内容活在磁盘上,走project.target_folder_path+artifact.file_path拼出绝对路径现读)一起返回,预览面板一次请求就够。前端v_artifact_browser的预览统一用<pre>纯文本展示,不做 Markdown 渲染/代码高亮——02-V 原文想要那个效果,但这需要引入第一个前端第三方库(图表/高亮/markdown 解析器),和这个控制台"纯 vanilla JS、无构建步骤"的一贯做法(05 §5.7 精神延伸到前端)冲突,判断为有意简化而非漏做。顺手把frontend/index.html的常驻导航栏加上了"产物"链接(之前只有 Dashboard/Tasks/Settings 三个占位项)。真实服务器验证过三级血缘链(BRD→PRD→SDD)返回顺序正确。 - 覆盖:02-V
v_artifact_browser;03-Cc_artifact_get(正文订正补全)+ 新增c_artifact_list
U-029 [Value Path]
v_skill_library+v_skill_audit_log- 依赖:U-018
PH3-S4 配置页
- U-030 [Value Path]
v_concurrency_settings+v_budget_settings+v_log_retention_settings- 依赖:U-019(预算配置)、U-021 涉及的
log_retention_config(U-001 已建表,此处只是配置页 UI)
- 依赖:U-019(预算配置)、U-021 涉及的
PH4:全量验收与固化(Risk Gate,收口)
Goal:确认 MVP 验收标准(架构文档 §11)全部达成,系统可以长期无人值守运行。 Phase 验收:06-验收与测试锚点.md 全部 14 条 REQ 的全部用例(happy/boundary/failure)完整回归通过一次。
U-031 [Risk Gate] REQ-01~14 全量自动化回归套件
- 依赖:PH1/PH2/PH3 全部 Unit
U-032 [Risk Gate] 多 Goal 并发(
org_concurrency_config)+ 长时间运行稳定性压测- 依赖:U-031
- 覆盖:01-M
org_concurrency_config;架构文档 §7.4"多任务并发"决策
U-033 [Risk Gate] A-2 假设收口:Hermes SOUL.md 迁移效果复核,确认
confirmed或记录偏差- 依赖:U-004(此时已有足够真实运行数据判断人设迁移是否真的"不需要大改")
PH5:自举能力(Risk Gate,Deploy Supervisor + 蓝绿部署自动回滚)
命名说明:本 Phase 的"Deploy Supervisor"和架构文档里"Supervisor(调度子 Agent,Coder Work Graph 内部监控节点)"是两个不同概念,详见 supervisor-auto-rollback-design.md 开头的命名说明,不要混用。
Goal:graphlemon 具备安全地自主修改自己代码(含 Org Agent 身份文件、调度器/执行器核心逻辑)的能力——出问题时自动回滚,不需要人工评审代码内容把关。
Phase 验收:docs/supervisor-auto-rollback-design.md 状态机的四个终态(health_check_failed、rolled_back、rollback_failed → blocked_pending_human、finalized)都能真实触发一次;self-development-feasibility.md 第 3 节的 9 步走查场景改成真实执行(不是模拟),从"人类发现 bug"到"Deploy Supervisor 自动 finalize"全程无需人工介入代码评审。
设计依据:docs/supervisor-design-comparison.md(方案选型)+ docs/supervisor-auto-rollback-design.md(状态机/数据模型/健康检查/回滚流程/MVP 参数)。
PH5-S1 数据模型与治理规则
U-034 [Enabler] 01-M 扩展:新增
deployment、deploy_health_signal两个实体建表;bug_record.root_cause_category新增core_engine枚举值- 依赖:U-001
- Done:两张新表建表成功并通过约束校验;
bug_record现有记录/查询逻辑对新枚举值向后兼容(不破坏 U-015/U-018 已有测试) - 覆盖:
supervisor-auto-rollback-design.md§3;self-development-feasibility.md缺口 3
U-035 [Risk Gate] self-target 场景 worktree 无条件强制规则(缺口 1)+
root_cause_category=core_engine命中时路由到蓝绿部署流程而非fast_lane(c_org_loop_route扩展)- 依赖:U-013(worktree 机制)、U-016(
c_org_loop_route)、U-034(core_engine枚举) - Done:
target_folder_path == graphlemon 自身安装路径时,即使是单节点串行修改也强制走 worktree(不再要求"仅并行时才强制");root_cause_category=core_engine的 bug 被c_org_loop_route正确路由到 PH5-S2/S3 的蓝绿流程;且改动落地后必须重跑一次 U-013/REQ-03(worktree 隔离)现有回归用例全部通过——这是 PH4 全量收口之后第一个真正 touch 生产核心执行路径的 Unit,不能假设"没测出问题就是没问题",否则等于在 PH5 一开工就悄悄打破 PH4 刚建立起来的信任前提 - 覆盖:REQ-03(回归)
- 依赖:U-013(worktree 机制)、U-016(
PH5-S2 Deploy Supervisor 核心机制
U-036 [Enabler]
supervisor/isolation/四模块补齐单元测试(当前从 graphloop 复制过来的isolated_workspace.py/workspace_security.py/change_manifest.py/selective_apply.py只做过一次手工冒烟测试,没有正式unittest套件)+ Deploy Supervisor 进程骨架(独立操作系统进程,纯 stdlib,不依赖任何 LLM CLI,PID/状态文件约定参考 graphloophermes_watchdog.py的原子写模式)- 依赖:无(代码已存在,是补测试和补骨架,不是从零开发)
- Done:四模块各自有对应
test_*.py(可直接照搬/改造 graphloop 的test_change_manifest.py/test_selective_apply.py/test_workspace_security.py等,逻辑未改动,测试也基本不用改);Deploy Supervisor 骨架能独立启停,不受 Live/Candidate 进程崩溃影响 - 待定(2026-07-31 订正):此前这里写的是"
supervisor/目录该归入 U-002 的目标项目树(.claude/、skills/、src/那套)还是 U-003 的平台目录"——但 U-002 的范围已经订正为只创建.graphlemon/(见该 Unit 2026-07-31 订正说明),.claude/、skills/从来就不是目标项目的东西,是 graphlemon 平台自己仓库的资产。所以这个选择题已经不成立,supervisor/只可能归属"platform 自身工具"(当前的实际存放位置,supervisor/isolation/),不存在真正的"归入目标项目"选项;保留这条记录是为了让后来者理解为什么这里曾经有过一次误判
U-037 [Risk Gate]
c_deploy_supervisor_stage:候选实例拉起(全新目录 checkout,不git pull原地改)+ 一次性 DB 副本机制(候选实例验证期间绝不接触生产库)- 依赖:U-034, U-036
- Done:给定一个已合并到主分支的 commit,能生成
deployment记录(status=staged)并拉起一个监听临时端口、连接一次性 DB 副本的候选进程
U-038 [Risk Gate]
c_deploy_supervisor_health_check:process_alive/http_health/canary_task三项 pre-cutover 检查- 依赖:U-037
- Done:三项检查全部通过 →
status=health_check_passed;任一失败/超时 →status=health_check_failed,Live 全程未受影响(对应回滚路径 A)
U-039 [Risk Gate]
c_deploy_supervisor_cutover:蓝绿切换本体(Live 停止接受新触发 → 候选切换到生产端口/生产库 → 旧 Live checkout 保留为previous_instance_dir)- 依赖:U-038
- Done:健康检查通过后能完成真实切换,切换过程中在途
work_node_execution能在重启后从持久化状态恢复(复用 U-021 断点恢复机制),previous_instance_dir在监控窗口结束前不被清理
PH5-S3 监控与回滚
U-040 [Risk Gate]
c_deploy_supervisor_monitor:post-cutover 监控窗口内四类信号采样(process_alive/http_health/canary_task/validator_approval_rate)- 依赖:U-039
- Done:
validator_approval_rate信号能从execution_event/work_node_execution正确计算滚动窗口内 Validator 的 approve 占比,人为把该占比推到偏离基线阈值之外时能触发anomaly_detected
U-041 [Risk Gate]
c_deploy_supervisor_rollback:回滚路径 A(pre-cutover,终止候选+git revert+发bug_record)与路径 B(post-cutover,换回previous_instance_dir+健康检查+git revert)完整实现,含"回滚本身失败"兜底转blocked_pending_human- 依赖:U-038, U-040
- Done:两条回滚路径各自跑通一次;人为制造"旧版本也起不来"的场景能正确终态到
blocked_pending_human而不是无限重试或静默失败;git revert冲突场景(supervisor-auto-rollback-design.md§10 开放问题 3)有明确处理,不能是本 Unit 唯一遗留的未定义行为
U-042 [Risk Gate] 自举闭环端到端验收:真实复现
self-development-feasibility.md第 3 节的 9 步场景(修复 graphlemon 自己p_org_task_lifecycle状态机里的一个真实植入 bug),走完整"部署→健康检查→cutover→监控→finalize"或"检测异常→自动回滚"两条路径中至少一条- 依赖:U-035, U-041
- Done:全程无需人工评审代码内容;参照 graphloop
RUN_REAL_AGENT_E2E=1的先例,真实拉起两个 graphlemon 实例的端到端用例默认跳过,需要显式环境变量开启,日常回归只跑 mock 版本
PH5-S4 可观测性(非阻塞,可延后)
- U-043 [Value Path] 控制台展示
deployment状态与健康信号(02-V 新增视图)- 依赖:U-024, U-040
- Done:能在控制台看到当前是否有 deployment 在进行中、处于状态机的哪个状态、最近一次健康信号采样结果
PH5 已知交叉缺口(不阻塞设计定稿,实现前需要留意)
supervisor/isolation/现有代码目前只做过一次手工冒烟测试(本次会话里跑的 isolate → diff → apply 往返验证),不是正式unittest套件——U-036 里已列为待办,暂不算"已完成"。supervisor/目录归属 U-002 还是 U-003 的问题目前只在 U-036 里留了一个临时决定,还没有真正的架构决策支撑,PH0 真正实现 U-002/U-003 时应该回头确认这个临时决定是否仍然成立。- U-001 的"15 张表"这个数字会在 PH5 落地后变成 17 张(
deployment/deploy_health_signal),后续如果有人只看 PH0 的验收标准会觉得和实际情况对不上,属于文档层面需要注意的地方,不是设计缺陷。 supervisor-auto-rollback-design.md里的参数草案值(健康检查超时、监控窗口时长等)明确标注"工程经验值,需要实测校准",U-036~U-042 实现时不要把这些数字当成已经验证过的常量直接硬编码进测试断言里,要留出可调整的空间。
2. Unit 分类汇总
| 分类 | Unit 数量 | Unit 列表 |
|---|---|---|
| Value Path(主线) | 17 | U-009~U-014, U-022~U-030, U-024.5, U-043 |
| Risk Gate(高风险校验/护栏/收口) | 18 | U-004, U-015~U-021, U-031~U-033, U-035, U-037~U-042 |
| Enabler(使能基础设施) | 10 | U-001~U-003, U-004.5, U-005~U-008, U-034, U-036 |
(PH0-PH4 原有 33 个 Unit 分类不变,PH5 新增 10 个:U-034~U-043,其中 Value Path +1、Risk Gate +7、Enabler +2。
2026-07-30 计划评审追加 2 个 Unit:U-004.5[Enabler](RUN_REAL_AGENT_E2E harness 归属)、U-024.5[Value Path](U-024 依赖分级后的 REQ-12 完整验收),不改变原有编号顺序,只插入在对应 Unit 之后)
3. 关键路径(Critical Path)
风险最高、返工代价最大的一条依赖链(PH0-PH4 主线):
U-001 → U-002 → U-004(A-2 假设验证)→ U-004.5(RUN_REAL_AGENT_E2E harness)→ U-005/U-006 → U-010 → U-011 → U-012(A-1 已 confirmed,实现契约已验证)→ U-013 → U-014(PH1 收口)
U-004 是全链路里唯一还未经实测验证的假设节点(A-2 pending)。建议 U-004 完成后立即跑一次 U-005(最小触发验证),而不是等到 U-010/U-011 完整实现后才发现人设迁移有问题——这是应用 03-C/05 节里对 A-1 采用的"前置 spike 验证"同一原则。U-004.5 的插入不改变关键路径的性质,只是把"门控 e2e 测试模式怎么搭"这个原本会摊到 U-005/U-006/U-010 各自解决的隐性工作,显式化成一个前置节点,避免重复发明。
PH5 关键路径(与上面这条主线不合并——PH5 依赖 PH4 全部收口,是完全独立的第二条链):
U-034 → U-035 → U-036 → U-037 → U-038 → U-039 → U-040 → U-041 → U-042(PH5 收口)
U-041(回滚路径实现,含 §10 提到的 git revert 冲突处理)是 PH5 里返工风险最集中的一环,因为"回滚"本身失败没有再下一层的自动兜底,只能转人工——建议 U-041 完成后立即用人为制造的"旧版本也起不来"场景验证兜底路径,而不是假设它"大概率不会发生"就跳过测试。
4. 与 06-验收与测试锚点.md 的对应关系
本文档不重新定义验收标准,Unit 的 Done 标准直接引用 06 节已有的 REQ-01~14 用例(happy/boundary/failure)。如果后续 06 节的验收用例发生变更,本文档对应 Unit 的 Done 标准需要同步更新,避免两份文档各说各话。