-
## 起因 e2e-demo 真实任务卡住:org-planner-sdd 自己判 input_underspecified 并点名 org-planner-prd,但这张图里 sdd 没有任何 fail 出边——精确匹配、 兜底边两条路都接不住,节点直接 blocked,靠人工点了「重试」才续上。 ## 根因 fail-fallback-edge 校验(27-P3/P4 §三.7 第五项)只在节点已经画了别的 fail 边时才检查兜底边齐不齐。一条 fail 边都没有的节点完全不受约束—— workgraph-planning/agent.md 原文明说这是「合法设计」。但系统里所有本体 共用 _base 的四段式协议,VERIFY 阶段任何 agent_call 节点都可能自判 失败,不止专职判词节点。四个骨架模板审计后发现 6 个中间节点存在同一个 漏洞(sdd 只是撞上的那一个)。 ## 决策 除图的唯一入口节点外,每个节点必须留一条兜底边(判据是 forward 入度, 不是节点名字模式)。org 层暂不动——node 全是 subgraph 类型,失败来源 机制不同,且套用同一条规则会让三个 org 模板立刻不合法,属于 27-p3b 「验收9」还没收口的链路,留给那边一起做。 中途发现无条件收紧会炸 46 个用例/13 个文件(createWorkGraph 被大量 测试当成建最小图的通用工具)。改法是加 strict 开关,默认关闭(行为 不变),只在 workgraph-planning 真实提交图落库的那个唯一入口 (createWorkGraphSafely)打开。 ## 改动 - work-graph-db.js / graph-engine.js:新规则 + strict 开关 - planner.json / executor-fanout.json:补 6 条兜底边 - workgraph-planning/agent.md:约束三等四处措辞同步 - 新增单测:strict 模式 7 例 + 真实链路回归用例(真实引擎代码+真实 DB+只换最外层 CLI 子进程,复现 sdd 自判失败后引擎自动路由到 prd, 全程零 retryNode 调用)——比重跑一次真实任务更严格,可回归、进 CI 978/978 单测通过。需求与决策记录见 docs/todo/system-design/31-work-graph-fail-fallback-coverage.md。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
28-followups §3.1。**审计结论与那条待办原来的写法不一样,两列要分开处理。** ## source_kind:删 与 origin 一一映射(global_agent↔builtin / company_hired↔created), 实查生产库 76 行零例外。全仓唯一读侧是 migrate() 里那条 origin 回填, 而那是给「origin 列出现之前写下的行」用的一次性迁移。 顺序:**先回填、再删列**,两步都用 hasColumn 守着(幂等)。反过来做不报错 —— 库能开、测试能过,只是那几行 origin 悄悄是错的。守卫 source-kind-drop-migration.test.js 走存量库重启那条路,专门钉这个顺序, 反向验过:弄反立刻红。 写侧散在 3 处生产 INSERT + 35 个测试文件。批量改的时候正则误伤了 4 处 (吃掉了 JS 默认参数、位置占位符、ON CONFLICT 的赋值行),逐个修回并复查了 diff —— **这种跨几十个文件的机械改动,靠「全量跑一遍」兜底比靠正则写得对更可靠**。 ## legacy_path:不能删,它还没死 原待办说「所有读取点都已改成从 id 推导」——那半句对新行成立,对存量行不成立: · 新行 legacy_path 恒等于 system/agents/{id}(确实冗余); · **存量行里存着批次 4 之前的公司侧路径**,而 graph-seed-service 判「悬空行 / 孤儿行」 靠的正是这个路径指向的目录还在不在。从 id 推导出来的路径永远指向 system/agents/, 那条判据会整个失效。 实查生产库:76 行里还有 2 行是老路径(org-planner / org-coder 指着已不存在的 data/companies/mycompany/agents/)—— 正是这条判据要认出来的那种行。 退出条件写进了 graph-db.js 的建表注释(不只留在文档里): 库里不再有 legacy_path <> 'system/agents/' || id 的行。 ## 顺带记一条:getDb() 会就地迁移生产库 删列那次,生产库的列是被一条**查状态用的**一行 node -e 删掉的 —— 服务当时没重启,人也没打算迁移。结果是对的(幂等;与备份逐行核对 76 行一致、 origin 全部按映射回填正确),但「读一下状态」不该顺手改 schema 这件事值得知道。 闸门只挡测试进程,故意不挡普通脚本(挡了服务自己也起不来)。 注释里给了只读的正确姿势。 全量:140 文件 / 1299 测试全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> -
## 验收 4:从「静态成立」变成真的跑过一次 原状态是 🟡「静态成立,未真跑」—— 人读代码把 fixture verdict → FAILING_VERDICTS → readNodeProduct → callAgentNode 判 fail 这条链读通了,但没有任何一次真跑或真链路证据。 现在 graph-real-chain-smoke ⑥ 走完整条:fixture 产出 verdict="attributed" → 节点 fail、fail_reason=input_underspecified、沿 fail 边点亮下游、 判词落在**下游节点**行上(判词的载体是「点亮它的那次状态转换」,不是失败节点自己)。 反向验过:verdict 改成 passed 立刻红。 写 fixture 时踩到一条值得记的:fail_reason 是封闭域 ATTRIBUTION_REASONS, 第一版编了个 contract_invalid,被**按设计降级成 null** 并留一条 timeline —— 引擎不认识的归因一律不信。判据没错,是 fixture 错。 ## 验收 2 / 8:由 2026-08-20 那次真跑验掉(§六ter) · 2 —— 19 次真 agent 调用之后 `git status --porcelain` 完全为空,交付物都在项目根内。 这正是 §六bis「后半被并行 agent 打破」那条的干净环境重验。 · 8 —— 判据取「它引用了只可能来自提示词的内部 id」:triager 判词原文写着 「org-coder-impl 私有目录 rn_96a8cd696dcc 下只有 _research.md」, rn_96a8cd696dcc 是 graph_run_nodes.id,agent 没有别的途径知道它。 ## 验收 9:产出端通了,消费端没等到那个形状 真跑里 triager **确实产出了两半地址**(orgNode + workNode)—— 这一条本身有分量:27-P3/P4 §八.22 第 13 处记的正是「引擎四环全通, 但产出方 agent.md 里根本没有这个键」,那半条现在通了。 紧接着「忽略跨层地址」那条**不是缺陷,是判据在正常工作**:这次回退发生在 work 层内部,edge.to 本身就是 agent_call 节点,已是最细粒度,没有跨层可言。 缺的是**那个形状**:一次 org 层回退边、且目标是 subgraph 节点。 别把「机制没验」和「机制没有」混为一谈 —— 定点重置的逻辑侧有 graph-cross-layer-address.test.js 三例注入式覆盖,缺的只是真跑里发生过一次。 ## 顺带 · template-verdicts 的样本非空那条补 ENOENT 容忍。它扫**活目录**, 而 graph-real-chain-smoke 会在 system/agents/{aid}/{cid}/ 下建了又清运行态 —— 文件在「列出名字」和「读它」之间消失是正常的。walk() 与 violations() 早就容忍了, 只有这一处漏了,实测偶发红过一次。**扫活目录的守卫,每一处读都要容忍 ENOENT。** · 真跑 timeline 里发现一条未修的:每个节点的产物索引写入都撞 UNIQUE idx_graph_files_path 失败并被降级成日志(重试时同路径再写一次)。 后果是 files.json 产物索引在有重试的 run 上静默不全 —— 归 P4 一起处理,记在 §六ter。 全量:139 文件 / 1297 测试全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> -
agent_call 与 subgraph 是**两段不同的默认实现**(defaultAgentExecutor / defaultPlanWorkGraph),各有各的 onAcpEvent 累加、各有各的守卫开关分支。 上一轮只覆盖了前者,「规划子图」那半条链仍然是零真实覆盖。 现在走完:defaultPlanWorkGraph → AgentManager → ClaudeCliProtocol(mock) → extractPlannedGraph 解析回复 → createWorkGraphSafely → 建子 run。 断言两处假 planner 天然绕开的接线: · 子 run 的定义确实来自 fixture 那张图(节点名 / systemAgentId 逐个对) · 规划那一轮烧的 token 也进了预算 —— 走的是 defaultPlanWorkGraph 自己那条 onAcpEvent,与 agent_call 那条不是同一段代码 ③ 的守卫断言补上 subgraph 分支的正反对照。 新增 fixture workgraph-planning.json:图 JSON 同时放进 chunks 与 result (mock 下 output 取哪一半由 AgentManager 决定,不该由 fixture 来赌)。 反向验过:抽掉 fixture 里的图 JSON、或把它的 usage 归零,对应用例分别变红。 **覆盖边界写在用例注释里**:跳过 resolvePlanningAgentName 里的按需 hire (预塞一行 company_agents)—— hire 要一条真实公司记录,那条路径另有 company-hire-org-layer.test.js 盯着;这条 smoke 验的是规划链路,不是招聘。 全量:139 文件 / 1296 测试全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
## 一、W7「零边图」不是待办 —— 它早修了,是注释在骗人 `company-service.js` 的判据注释仍写着「判据用**有没有边**」,并留着一段 「已知残留:单 org node 的最小图没有边,判据③不生效……真跑到那一步再看」。 **那个残留在 `58a2865`(08-17)就修掉了**:`orgGraphMembership` 现在的主判据是 `authored_by='planner'` 的节点存在(「orggraph-planning 提交过一张图」的直接证据, 零边也认),边数降为存量行的回落判据;守卫 `company-hire-org-layer.test.js` 的 `CO_ZEROEDGE` + `W7-fix` 两条盯着。 注释没跟着改,于是它把读的人**引向一个已经修好的缺陷**去重修一遍 —— 我就是这么被 带偏的:读到那段注释就没再往 `orgGraphMembership` 里跟,还据此往待办清单里写了一条。 这是「指路牌失效」那一族,和 CLAUDE.md 死链同型,只是方向相反(那边指向不存在的东西, 这边描述已不存在的洞)。 判据的措辞也改了:**「成型」的判据在 graph-service.orgGraphMembership(),不在这里**, 免得下一个人照注释理解。P3B 文档与 docs/README.md 的对应条目一并更正。 ## 二、还上「真实路径 smoke 进 CI」那笔欠账的一半(27-P3/P4 §十 硬规矩 2) 原话是「凡是靠注入点验的能力,都要有一条真实路径的 smoke 进 CI 才算验收完成 —— 这条欠账仍未还」。此前唯一走真实路径的 `scripts/real-pm-smoke.js` 要真 API 额度、 人工跑、而且验的是 DevLoop 不是图执行。 做法是把「真 LLM」和「真代码路径」拆开:**只把最外层的 CLI 子进程换成 fixture 回放**, setAgentExecutor / setPlanWorkGraph 一个都不调。链路完整走 advanceOne → defaultAgentExecutor → AgentManager → ClaudeCliProtocol → onAcpEvent → 落盘 → readNodeProduct → settleNode → spendBudget → 边界守卫。 三条断言各盯一处**假 executor 天然绕开**的接线: · ① mock/files 真的落到节点产物目录(假 executor 直接 return,这一环整个不存在) · ② token 用量真的从 session/result.usage 数进预算 —— **就是 §六 验收 10 卡了两轮的那一维** · ③ boundaryGuardApplicable 在这条路径上为真(注入假 executor 时它恒为假, 那条唯一有实际后果的分支实际零覆盖);带反向对照,注入后必须变假 反向验过:断掉①的落盘、断掉②的累加,对应用例分别变红。 为此改了一处 mock:session/result 的 usage 原来硬编码 {input:0,output:0}, 现在 fixture 可指定(缺省仍是 0,既有 fixture 行为一个字不变)。 **不改这一处,token 那一维在 CI 里永远产不出数** —— 验收 10 当初就是卡在这儿。 **没还清的部分写在文档里,别当成还清了**:真 LLM 仍进不了 CI; subgraph 那一支(defaultPlanWorkGraph)仍只有注入式覆盖; 多节点 / 回退边 / 熔断没进这条 smoke,它只推一个节点。 afterAll 清掉真实链路建出的公司态目录(gitignore 挡着不进库,但留着会让别的 用例遍历 system/agents/ 时多扫一份)。 全量:139 文件 / 1295 测试全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>