implementation-gap-analysis.md 24.6 KB

实现现状 vs 目标/BRD/PRD/SDD 差距分析

日期: 2026-08-03 性质: 现状快照,回应用户 "现在整个系统的实现和我们的文档的 goal 和 brd prd sdd 还有哪些差距"。基于对 git log、 docs/development-plan.md(含各 Unit 事后补记)、docs/done/build-report.md、docs/sdd/06-验收与测试锚点.md 和实际 代码(src/、supervisor/)的交叉核对得出,不是重新跑一遍验收。 结论先说: 45 个 Unit(U-001~U-043 + U-004.5/U-024.5)全部有对应实现和提交,docs/done/build-report.md 的 "全部完成"判断在"每个 Unit 都有代码落地"这个意义上是对的。但"全部完成"不等于"和文档设定的目标完全对齐"—— 差距集中在 PH5 的"自动"两个字还没完全兑现、系统还不是真正自驱动的、以及若干 SDD 契约的已知简化。逐条见下。 如何维护本文档: 这是一次性快照,会随实现推进而过时。发现某条已经补上或又出现新缺口时,直接编辑对应条目 (改状态、补日期),不要另开一份新文件——避免重蹈 development-plan.md/unit_matrix.json/build-report.md 互相之间已经出现的"哪份是最新的"混乱(见第 4 节)。


1. 目标层面:PH5 的"自动"闭环从未真正走完

  • REQ-15(Deploy Supervisor 蓝绿部署 + 自动回滚)的 happy path 只验证到真实 cutover,从没有一次交互式压测 真正等到 finalized——生产参数是 24 小时或 10 个真实任务的监控窗口,本来就不该在一次交互会话里等到。
  • 更关键:Deploy Supervisor 真正"自动"的那部分——持续轮询"有没有新的 self-target 合并等待部署"——从未 实现。supervisor/deploy_supervisor.py(U-036)只管进程生命周期(start/stop/status/PID),poll_fn 是留 给未来的钩子;supervisor/self_hosting_e2e.py(U-042)只是验收脚本的一次性编排(人工触发一条 Goal→...→ cutover 的直线调用),不是常驻发现循环。PH5 的目标"自主修改自己代码后自动安全部署",目前是"能做到"而不是 "会自己做"。
  • 回滚路径 B(监控期异常检测触发)只在 mock 测试里验证过分支逻辑,从未在真实生产数据下真正触发过。
  • (2026-08-03 自举可行性分析中新发现)部署缺互斥锁:supervisor/deploy_stage.stage_deployment() 只检查 _last_finalized_deployment()(上一次成功落地的部署是什么),不检查当前是否已有一个进行中 (staged/candidate_starting/…未到终态)的部署。如果用调度器并发派发多个 self-target 任务、且多个都 想 cutover,会互相踩——这不是原来清单里的条目,是分析"能否用调度器批量跑 gap 修复"时才暴露出来的新缺口, 建议在真正启用并发自举之前先补上。

2. "熄灯"叙事本身:没有常驻调度入口

src/scheduler.py(U-032)从未被 backend_server.py 或任何生产入口调用——backend_server.py 里只有一句注 释提到"调度器以后会长在这里"(grep 确认,无实际调用)。系统目前不存在一个真正常驻、自动扫描 queued 任务 并派发的进程;每一次"跑起来"都是脚本/测试手动调用 run_scheduler_once/run_goal_to_completion。这和"Level 5 自主研发系统"的叙事有实质差距——机制都齐了,但缺"自己跑起来"这一层。

3. SDD 契约与实现之间已知的简化/偏差

  • Skill 内容从未真正迁移:.claude/agents/*.md 的 skills: frontmatter 只是引用名字(如 code-task-development),仓库里没有 skills/<skill_id>/ 真实文件,测试靠 seed 占位内容绕过—— src/skill_update.py 自己的注释也承认这点。
  • Coder 阶段没有真正的多模块 Work Graph 规划:c_work_graph_plan 目前只服务 Planner 的固定四节点线性图 (brd→prd→sdd→benchmark);Coder 永远走一个合成的单节点 manifest,"规划一个真正的多文件代码变更图"这个能 力从未被要求也没实现。
  • ✅ 已修复(2026-08-03)普通 target project 从未部署过 Agent 定义: agent_profile.deploy_agent_definitions() 存在,但没有任何生产代码路径调用它——project_scaffold.create_project() 建 project 时不会调,org_node_trigger.py 自己的注释也如实写着"this function does not deploy it"。self-target 场景"恰好"没事,是因为 .claude/agents/*.md 本来就是 graphlemon 自己仓库里已提交的文件;但任何新建的普通 target project,第一次触发 Planner 时 claude --agent planner 会因为 cwd 下找不到 agent 定义而非零退出, 抛出 OrgNodeTriggerError——这个异常不在 scheduler._ROUTABLE_EXCEPTIONS 里,被兜底成笼统的 blocked_reason='execution_error',真实报错内容不会被持久化,控制台上完全看不出原因。真实复现:项目 LemonGraphFront(target_folder_path=.../graphlemon/frontend/)的任务从 queued 直接跳到 blocked_pending_human,零 artifact 产出。 修复:project_scaffold.create_project() 现在会在 .graphlemon/ 初始化提交时一并调用 agent_profile.deploy_agent_definitions() 并 commit(同一个 commit,没有多引入一次提交,也顺带解决了下面 worktree 隔离那条 gap);scheduler._block_task()/task_execution._mark_blocked() 新增 detail 参数,把真实 异常信息写进 execution_event.payload,不再是不可诊断的裸字符串(blocked_reason 本身值不变, budget_guardrail 等地方靠精确匹配它的值,没有改)。新增/扩展了 3 处回归测试 (test_project_scaffold.py/test_scheduler.py/test_task_execution.py),src/ 全量 498 个测试 + supervisor/ 123 个测试全绿。真实验证:给 LemonGraphFront 项目手动补部署+提交 agent 定义后,task_id=7400d876-… 走完 了完整闭环——BRD/PRD/SDD/Benchmark/code_diff/test_report 六个 artifact 全部 validation_status=passed,中途 Validator 第一轮 FAIL 还真实触发了一次 Org-level Loop 的 full_replan 路由、重新规划后第二轮通过,最终 task.status=done。是通过这次交互式会话直接改代码修的,不是走 graphlemon 自己的 Planner→Coder→Validator 自举出来的——对应第 6 节任务清单顺序 1(原描述是"补 commit",实际根因和修复范围比原描述更大,一并记在这里)。
  • 普通 target project 的并行 worktree 隔离没被真正踩过:needs_worktree 目前只有 self-target 场景被 work_graph_orchestrate.py 强制打开;给普通项目手动开启时,原先 agent_profile.deploy_agent_definitions() 只 写 .claude/agents/*.md 文件、不 commit,而 git worktree add 只认已提交内容——这条路径理论上会失败。 随上面那条一起修复:create_project() 现在会把部署的 agent 定义提交,新建项目从一开始就有已提交的 .claude/agents/*.md,这个阻塞点已解除;但"手动对一个已存在、之前没有 agent 定义的项目开启 worktree"这种 场景仍未专门测试过,不算完全清零。
  • v_artifact_browser(U-028)有意不做 Markdown 渲染/代码高亮(02-V 原型期望有),因为要避免引入第一个前端 第三方库,判断为有意简化而非漏做。

4. 文档本身滞后于实现(这本身就是一种"差距")

  • development-plan.md/unit_matrix.json 从写就没有随实现更新"done"状态,只能靠 git log 和散落在各 Unit 条目下的事后补记("实现补充"/"真实 bug")判断真实进度。
  • docs/done/build-report.md 里"⚠️ 尚未真实执行的一项"那一段(称 RUN_REAL_SELF_HOSTING_E2E 从未跑过)本身也已 经过时——development-plan.md U-042 条目下 2026-08-03 的记录显示之后确实做了 13 次真实自举压测(git log 里那一长串 graphlemon: auto-commit/merge/artifact 提交就是证据),只是仍未到达 finalized。连"总结性 收口文档"都会在自举持续运行的过程中被甩在后面——这是个会持续放大的风险,不是一次性的疏漏。

5. 验证方法上的缺口

  • 几乎所有 PH3 前端 Unit(U-022~U-030)的验证方式都写着"浏览器扩展本次会话不可用",靠 curl/Node 原生 WebSocket/node --check 语法检查代替,没有一次是真人在浏览器里点击验证过的——功能逻辑上大概率没问 题,但这是个反复出现、被如实记录但从未补上的验证缺口。
  • supervisor-auto-rollback-design.md 里健康检查超时、监控窗口时长等参数明确标注"工程经验值,需要实测校 准",还没有真实生产数据校准过(PH5 已知交叉缺口第 4 条,development-plan.md 第 422-427 行)。
  • ✅ 已修复(2026-08-03)健康检查 canary 探测永久泄漏 DB 记录:supervisor/deploy_health_check.py 的 check_canary_task() 每次健康检查都会真实调候选进程的 POST /api/projects+POST /api/tasks 验证 HTTP+DB 写路径,但只清理了临时 git 目录,从未清理通过 API 真实创建的 project/task 行——每跑一次真实健康检查, 候选进程当时用的数据库里就永久多两条垃圾记录。真实发现:在真实的 ~/.graphlemon/graphlemon.db 里发现 5 个 deploy-canary 项目,全部指向早已被系统清掉的 /var/folders/.../deploy-canary-* 临时目录,任务永远卡 在 queued,是之前某次真实健康检查验证(08-03 凌晨 05:50 那几次)留下的。已手动归档这 5 条历史记录 (PUT /api/projects/:id {status:'archived'},跟控制台"归档"按钮走的是同一条路径——没用硬删除,因为 task/artifact 表有外键引用 project_id,硬删会破坏历史)。修复:check_canary_task() 的 finally 块里新增:只要 project 行被真实创建过(不管后续 task 创建/校验成功还是失败),就调用同一条归档 API 清理掉 自己建的 project(最佳努力——归档调用本身失败不覆盖真实检查结果,避免把"没能清理自己"误判成"健康检查失 败")。新增 2 个回归测试:一次成功的 canary 检查后 project 应变成 archived;task 创建失败时 project 也 不应该泄漏。supervisor/ 全量 125 个测试 + src/ 513 个测试全绿。

6. 用 graphlemon 自举修复以上 gap 的可行性,及任务拆解清单

结论:机制上可行且已被验证过(13 次真实自举压测已经走通 Planner→Coder→Validator→Org-level Loop→Deploy Supervisor 全链路),但不能整体当成"黑灯工厂"一次性丢给它——原因就是第 2 节本身:没有常驻调度器,每一次 执行仍需人工手动触发(run_goal_to_completion() 或门控测试),这是当前系统的真实边界,不是这批任务的缺陷。

下表把上述 gap 拆成可以逐个手动触发的 self-target task,标注风险等级和建议顺序。"手动触发"指人工调用一次 scheduler.run_goal_to_completion(...)(或未来对应的 API),不是让它们排队后无人值守跑完;每个任务完成、 Deploy Supervisor 走到 cutover 前,仍按现有习惯人工确认一次。

顺序 Gap(对应章节) 建议 goal_text 方向 风险等级 备注
1 ✅ 已修复(2026-08-03) 普通 target project 从未部署 Agent 定义 + worktree 隔离未提交(§3) 让 agent_profile.deploy_agent_definitions() 在写完 .claude/agents/*.md 后自动 git add+commit 低 实际根因比预想的更基础(根本没调用点,不只是没 commit);这次是人工直接改代码修的,不是走自举流程验证的——留作顺序 2 之后再真正试一次"用 graphlemon 自己修 graphlemon"的对照
2 ✅ 已修复(2026-08-04,真正走 graphlemon 自举流程验证) 文档滞后(§4) 让 Planner/Coder 核对 development-plan.md/unit_matrix.json 与 git log 的实际进度,补齐/订正"done"状态字段 低 这是本次会话第一次真正用 graphlemon 自己的 Planner→Coder→Validator 闭环修复自己(不是人工直接改代码,对照顺序 1)。真实注册 graphlemon (self) project → scheduler.run_goal_to_completion() 后台跑约 40 分钟 → Coder 找出 6 处真实文档/machine-readable 不一致并订正(含主动发现"另一会话"两个功能提交在 unit_matrix.json 里缺 Unit 条目,补建 U-044/U-045)→ Validator 独立重跑全量 513 测试 + 逐条核对 git commit 后判 PASS,未直接信任 Coder 报告。过程中触发了一次真实 ensure_worktree_committed 主仓库漂移自动提交,机制本身也顺带得到一次真实验证。详见 git log(43dc7333 相关提交链,3bebe3b起)。
3 ✅ 已修复(2026-08-04,真正走 graphlemon 自举流程验证) Skill 内容未真正迁移(§3) 把 ~/.hermes/profiles/{code,planning,qa}/skills/ 下 frontmatter 引用到的 Skill 真实内容迁移进仓库 skills/<skill_id>/ 中 启动前先人工核实了 12 个 skill_id 的真实源路径(8 个比预想深一层,藏在分类子目录里)并把精确映射直接写进 goal_text,避免 Coder 会话浪费真实 token 去搜索——原先担心的"读取仓库外路径权限"最后证明不是问题,Bash/Read 能正常读 ~/.hermes/...。真实跑的路上还顺带遇到并修好两个操作性问题:① 一次真实 API 网络瞬断(ENOTFOUND)导致任务阻塞,直接推动修了 manual_resume 不会真的续跑这个 bug(见下方"顺带修复");② 我自己用一次性脚本调 start_task_execution_in_background() 导致 daemon 线程随脚本退出被误杀,改用 nohup 持久进程后正常跑完。结果:12 个 Skill 目录全部真实迁移(SKILL.md+references/templates/scripts),Validator 做了逐字节 diff -rq 核对(9/12 完全一致,3/12 只差一行——主动发现并脱敏了源文件里一个真实泄漏的飞书 API token,跟仓库已有先例一致,不是内容改写),agent_profile.py 的 FRONTMATTER 与新目录名逐一核对一致。Validator 顺带报过一次 supervisor/test_deploy_rollback.RollbackPathBFailureTest 失败并称"确认是迁移前就有的";事后独立复核(单独跑 + 全量跑 supervisor/)没能复现,大概率是这个套件已知的孤儿进程/资源竞争类偶发失败,不算新缺口,先记录观察。顺带修复:src/backend_server.py 的 manual_resume 之前只对 user_paused/user_stopped 自动续跑,execution_error(网络瞬断等)resume 后卡死——改成除 budget_exceeded/loop_limit_reached 外都自动续跑,见 commit db62570。
4 部署缺互斥锁(本节新发现,§1) 给 deploy_stage.stage_deployment() 加"当前是否已有进行中部署"的检查,拒绝/排队重复 staging 中 应该在启用并发自举(顺序 6 之后)之前完成,否则后续任务并发跑时有真实冲突风险
5 PH3 前端从未被真人验证(§5) 不适合作为自动化 Coder 任务——这是"人用浏览器点一遍"的验证缺口,不是代码缺陷;列在这里是为了不让它被误当成可自举任务 不适用 需要人(或 claude-in-chrome 之类的浏览器自动化)介入,不建议塞进 Planner→Coder 循环
6 调度器接入生产入口(§2) 给某个常驻进程(backend_server.py 或独立进程)接入 scheduler.run_scheduler_once() 的轮询循环 高 改变系统自己的自主权边界,建议单独一批、额外人工审查 diff,不要和其他 gap 混跑;完成并 cutover 后,理论上后续 gap 才谈得上真正黑灯
7 Deploy Supervisor 缺 poll_fn(§1) 实现"发现待部署的 self-target 合并"逻辑,接入 supervisor/deploy_supervisor.py 的 poll_fn 钩子 高 依赖顺序 6 先完成(两者共同构成"真正自驱动");且 SDD 里"什么条件触发自部署"本身有文档内部冲突(设计文档 vs 规范正文),需要人工先拍板这个设计决策,不能让 Planner 自己悄悄选一种理解
— Coder 阶段的多模块 Work Graph 规划(§3) 不建议列入这批"修 gap"任务 不适用 这是被明确判断为"超出范围的新能力",不是缺陷;如果要做,应该走正常的新功能立项,不该包装成"修复"塞进自动循环

执行建议:先跑 1→2(验证流程本身),再跑 3→4(补齐已知风险点),5 留给人工/浏览器自动化处理,6→7 作为 独立的、审查最严格的一批,完成后再评估是否真的要把后续维护交给自动调度。整个过程每一步仍需人工确认后触发, 不建议现在就配置成无人值守连续跑——待 6/7 完成并观察一段时间稳定后再考虑。

顺序 1 验证过程中额外发现的一个非代码问题,记一笔:org_node_lifecycle._check_concurrency() 统计"活跃任务 数"是跨全部 project 全局统计的,不分项目;/tmp/graphlemon-visual-check/graphlemon.db 这个测试库里另一个 project 下有条历史演示任务一直停在 coding(非终态但没人真的在跑),加上 org_concurrency_config 表当时是空 的(兜底 max_concurrent_goals=1),导致新任务永远被并发闸门挡住,和 agent 定义那个 gap 完全无关,纯粹是这个 临时测试库里的历史数据问题(已经通过 PUT /api/settings/concurrency 调高上限绕过,没有改代码)。不算一个新 gap,但全局而非按项目的并发统计是不是有意为之,值得找个时间单独确认一下——如果以后真的多项目并行用,这 个设计会让不相关项目的任务互相卡对方的并发额度。


7. 控制台 UI Bug 批次(2026-08-04/05,用户直接提供的 9 条清单)

跟第 6 节的系统性 gap 不同,这批是用户实际用控制台时记录的具体 UI/体验问题。逐条排查代码现状后,分成三类处理:

7.1 已修复(3 条,全部真实走 graphlemon 自举流程,非人工改代码)

  • Bug1+Bug6(合并成一个任务,同根因):Graph 页面日志区域永远显示"暂无日志内容";下载完整日志报错跳 到不存在的页面。根因:execution_log.append_log() 函数本身完整,但生产代码从来没有调用过它,execution_log_file 磁盘文件从未被真正写过内容——前端显示空是对空数据的正确反应,下载报错是 _send_file_download() 没接 FileNotFoundError。修复:把 append_log() 接入 work_graph_orchestrate.py 的 _make_run_fn()(conn 一路传进去),节点开始/完成/失败/限流各写一条真实日志事件;下载接口补上干净的 404 处理。真实验证:修复 合并后新起的任务,实时查询 GET /api/work-nodes/:id/log 真的能读到 {"event": "node_start", ...} 这类真实内 容(不是靠 Validator 报告,是我自己在这批任务运行过程中直接查询确认的)。
  • Bug8:http://.../#/skills 页面点击 skill 只显示"当前没有迭代记录",真实内容不显示。根因:skill_query.py 只读 DB 里的 skill/skill_iteration 表(迭代记录,本来就是空的没错),从没有函数读 skills/<skill_id>/SKILL.md 这批磁盘上的真实内容(就是本文档 6.2 节顺序 3 那次迁移进来的)。修复:新增 get_skill_content(),两层路 径穿越防护(realpath + commonpath containment 双重校验,防止 skill_id 里带 ../ 跳出 skills/ 目录); Validator 用真实编码攻击字符串(%2e%2e%2f、..%2f..%2f 等)对着一个真实放在 skills/ 外面的"秘密文件"验 证过防护确实拦得住;前端新增的"Skill 内容"面板跟已有的"迭代记录"面板并列展示,不是二选一。
  • Bug2:Planner 产出总览的文件/代码列表项点击没反应。根因:renderArtifactSummaryList() 渲染纯 <li>,零 点击事件。修复:列表项加 data-artifact-id + 点击跳转,复用已有的 v_artifact_browser(c_artifact_get), 没有重新建一套内容展示机制;"查看全部"跳未过滤列表的既有行为原样保留。Validator 自己搭了一个 DOM/fetch 模拟 环境做端到端验证,中途发现自己搭的验证环境本身有 bug(不是被测代码问题),先修好验证工具再继续判定。

7.2 明确不建任务、需要先讨论设计(2 条,按用户要求保留)

  • Bug3:用户想把 Coder/Validator 的 Work Graph 规划提前统一到 Planner 阶段一并产出(brd-prd-sdd-benchmark- workgraph for other org agent)。代码现状核实:e2e_pipeline.py 的 implement_code() 目前永远用一个写死的 单节点合成 manifest 调用 Coder,work_graph_plan.py 的 Prompt/依赖链模板硬编码死了 Planner 自己那固定的四节 点链,没有任何机制能扩展到覆盖其他 Agent;Validator 完全不用图。这跟第 3 节"Coder 阶段的多模块 Work Graph 规划"是同一个此前就被判断为"超出范围的新能力"的口子,这次用户的诉求更进一步(不只是 Coder 自己会规划,而 是 Planner 统一规划全部三个 Agent 的图)——没有创建 task,属于设计决策,需要先讨论方案。
  • Bug9:Org-level Loop 的迭代(Validator FAIL → 重新规划/修复)应该怎么在图上展示。代码现状核实:并非从零 开始——task.work_graph_runs 本来就是一个 run 一个独立 DAG 面板,多轮 loop 天然会产生多个面板;task.route 会渲染一条"重试环"的标注边。缺的是跨轮次面板之间的视觉连接,以及标注边只反映当前单一 route 值、没有按轮 次的历史。没有创建 task,属于设计决策,需要先讨论怎么呈现比较合理。

7.3 尚未处理(Bug4、Bug5、Bug7,本次会话未排)

  • Bug4(Org Graph 节点不可点)、Bug5(运行历史点击跳错地方,缺一整个运行详情/日志页含实时 tail -f)、Bug7 (导航栏缺项目名+任务看板小 dashboard)——代码层面已经排查过根因(见本节对应研究记录),规模从"中"到"中 大"不等,Bug5 是真的缺一整个新页面,不只是接线问题。留待下一批处理。

7.4 这一批顺带修复的两个基础设施 bug(不在原始 9 条清单里,运行过程中真实遇到才发现)

  • manual_resume 之前只对用户主动暂停/停止生效:一次真实网络瞬断(ENOTFOUND)导致任务 execution_error 阻塞,resume 后只是状态翻回去、没有真的续跑,/execute 又要求 status=queued——resume 完两边都够不着,卡死。 改成除 budget_exceeded/loop_limit_reached(两者各有专属续跑路径)外,其余原因 resume 后都真的会自动续跑。
  • scheduler.run_goal_to_completion() 对不可路由异常没有兜底:一次真实"API Response stalled mid-stream" 错误(WorkGraphExecuteError,不在 _ROUTABLE_EXCEPTIONS 里)直接把调用进程崩溃,任务卡在一个既不运行也不 是 blocked_pending_human 的真空状态,blocked_reason 是空的,控制台上没有任何入口能恢复它。补了一个跟 task_execution._mark_blocked() 对称的兜底:任何未路由异常现在都会落成 blocked_pending_human + execution_error + 真实错误详情,不会再无声卡死。

7.5 test_deploy_rollback.RollbackPathBFailureTest 的偶发失败,证据升级:第 6 节顺序 3 时第一次被 Validator 报告失败、我本地复现不了;这次顺序 B 又被独立报告了一次失败(且这次 Validator 自己检出了 diff 前的父 commit 确认那个版本上同样失败,证实与本次改动无关)。累计三次不同的真实会话报告失败,我自己手动重跑始终 3/3 通 过——不再当成"偶发、先不管",升级成"确认存在、待专门排查",很可能还是这个套件已知的孤儿进程/资源竞争类 问题,但没有再深挖根因。


最值得关注的三条

  1. 调度器没接入生产入口,系统还不是真正自驱动的(第 2 节)。
  2. Deploy Supervisor 缺"自动发现待部署改动"的 poll_fn,PH5 的"自动"两个字还没完全兑现(第 1 节)。
  3. 文档(development-plan/unit_matrix/build-report)本身持续滞后于自举产生的新提交,这个滞后会越滚越大, 建议找个时间点做一次文档收口,而不是继续靠事后补记维持可读性(第 4 节)。