@tea-agent/loop-agent 0.38.1 → 0.38.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +109 -13
  2. package/dist/application/dag/generate-task-dag.js +9 -0
  3. package/dist/build-stamp.json +3 -3
  4. package/dist/commands/init.js +2 -14
  5. package/dist/executors/dag-pi-executor.js +41 -4
  6. package/dist/executors/shell-executor.js +269 -15
  7. package/dist/governance/manifest-types.js +0 -2
  8. package/dist/shared/operator/capabilities.js +87 -22
  9. package/dist/task/config-types.js +22 -0
  10. package/dist/worker/console/chat/deferred-turn.js +91 -0
  11. package/dist/worker/console/chat/pi-runtime.js +10 -5
  12. package/dist/worker/console/chat/routes.js +458 -47
  13. package/dist/worker/console/chat/semantic-activity.js +13 -13
  14. package/dist/worker/console/chat/session-store.js +146 -2
  15. package/dist/worker/console/harness-pi-model-matrix.js +2 -11
  16. package/dist/worker/console/operator-actions.js +5 -4
  17. package/dist/worker/console/pi-readiness.js +1 -0
  18. package/dist/worker/console/recovery-cta.js +4 -4
  19. package/dist/worker/console/recovery-error-copy.js +4 -4
  20. package/dist/worker/console/server.js +6 -1
  21. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Hfsot6rP.js → abnfDiagram-N423BO3Z-C4bmo3iY.js} +1 -1
  22. package/dist/worker/console/static/assets/{arc-YGwEASin.js → arc-SAD7McOS.js} +1 -1
  23. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CmOM0F92.js → architectureDiagram-T3A2C74G-DAVLUdK1.js} +1 -1
  24. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-DDLuaHC5.js → blockDiagram-VBNYF7ZC-CPPdv4vj.js} +1 -1
  25. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B0Um43W4.js → c4Diagram-5PPSVZJV-D1GRGZax.js} +1 -1
  26. package/dist/worker/console/static/assets/channel-R6oQIDkw.js +1 -0
  27. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DvBN4JZc.js → chunk-2GRJ4B5K-D_Bq2ZqK.js} +1 -1
  28. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-B1UVgW4E.js → chunk-2Q5K7J3B-CPLVtamK.js} +1 -1
  29. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Cf0GgXzX.js → chunk-5RXB4S5H-DnaZWcL3.js} +1 -1
  30. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DbNA24sj.js → chunk-5VM5RSS4-BRTmFgnq.js} +1 -1
  31. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Bi_0Be06.js → chunk-6Q2QTUOP-BRlfYZoH.js} +1 -1
  32. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C8pPGvRd.js → chunk-GF5L2VYU-CjNybD4u.js} +1 -1
  33. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BitQY8Z-.js → chunk-JWPE2WC7-D3e-74fG.js} +1 -1
  34. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-U67GCiEo.js → chunk-KBJHAD2P-Br3ir6f2.js} +1 -1
  35. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-IoJwXPBA.js → chunk-RYQCIY6F-1NgVuRbh.js} +1 -1
  36. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-YDZ_UHgE.js → chunk-XXDRQBXY-BTYuvuWN.js} +1 -1
  37. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BPXlmPWI.js +1 -0
  38. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BPXlmPWI.js +1 -0
  39. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CUZgsiRC.js → cose-bilkent-JH36ORCC-BC6Bgosf.js} +1 -1
  40. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-COmCh4pT.js → cynefin-VYW2F7L2-DV-HypfL.js} +1 -1
  41. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Ta6FSm68.js → cynefinDiagram-MW4NZA55-DhTaddO-.js} +1 -1
  42. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UdcR-VW9.js → dagre-VZM6K2ZE-BnEcOmiI.js} +1 -1
  43. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DybcO3Et.js → diagram-7IWD3JNH-xphGRVKp.js} +1 -1
  44. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-wErDFWLw.js → diagram-B4RE2ZJO-CrdD1mRG.js} +1 -1
  45. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DkVHZWqz.js → diagram-LBJQPF4R-D4YdLsaF.js} +1 -1
  46. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DdqWs7QN.js → diagram-Q27KOJAE-LCKyGZ5U.js} +1 -1
  47. package/dist/worker/console/static/assets/{diagram-UB23O5K3-C9dMVmDo.js → diagram-UB23O5K3-BeiTe7t0.js} +1 -1
  48. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-B2T2WcBK.js → ebnfDiagram-BXEA7PRR-BO2cq8GS.js} +1 -1
  49. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt9rjDFq.js → erDiagram-JOGREHBK-CmWww3Nt.js} +1 -1
  50. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-U8ic_tvi.js → flowDiagram-UKHOOZJN-C2kkUYg5.js} +1 -1
  51. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BfQJdQoO.js → ganttDiagram-PKOTCBZU-DY_JZ2iQ.js} +1 -1
  52. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CpMftH4O.js → gitGraphDiagram-DS77QQ5N-BizhCSw9.js} +1 -1
  53. package/dist/worker/console/static/assets/index-BXN89VPX.js +325 -0
  54. package/dist/worker/console/static/assets/index-CuH5CNFu.css +1 -0
  55. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DzqWXNO_.js → infoDiagram-6WML65LV-Dlo2eK_d.js} +1 -1
  56. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CJmGQ5Yp.js → ishikawaDiagram-WSZJBQD7-3EIlIIuZ.js} +1 -1
  57. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-ChPtKFLz.js → journeyDiagram-NVQOT4AX-DiMDe5Yh.js} +1 -1
  58. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-PRe7wB5u.js → kanban-definition-27J2QSJJ-sN553oIN.js} +1 -1
  59. package/dist/worker/console/static/assets/{linear-DpKX6QNp.js → linear-D9Py31Ld.js} +1 -1
  60. package/dist/worker/console/static/assets/{mermaid.core-Dnl76b7Y.js → mermaid.core-D7Occzk_.js} +5 -5
  61. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-S9PCXc3J.js → mindmap-definition-FAOFIHXS-DjDRboiR.js} +1 -1
  62. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Du8e-FSX.js → pegDiagram-VL7TDLO6-Cgc4qlzp.js} +1 -1
  63. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CY6OmHTc.js → pieDiagram-7S7Q4E2Y-DxZj8pKd.js} +1 -1
  64. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C65GPhpj.js → quadrantDiagram-CIZ2JOQS-CJ50MngR.js} +1 -1
  65. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-U6IUiTCL.js → railroadDiagram-AXF67PYL-BY-OD8Tr.js} +1 -1
  66. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-I_9Qhi.js → requirementDiagram-LRYGKXZP-CQFButfK.js} +1 -1
  67. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B57Fhed0.js → sankeyDiagram-W5VNT64P-BB_QUK_b.js} +1 -1
  68. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CJOtiw21.js → sequenceDiagram-SI44F4Z6-CqhXZjyx.js} +1 -1
  69. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-FyX8zxSh.js → sizeCapture-X5ZJPWSS-2l1U4rgJ.js} +1 -1
  70. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-eF-pbs9t.js → stateDiagram-OKZ733FA-BX1FbrhJ.js} +1 -1
  71. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DJKi0Qel.js +1 -0
  72. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CcfkA-Dq.js → swimlanes-SLNWSIFB-DOSnT7hb.js} +2 -2
  73. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-YYe7EEm5.js +8 -0
  74. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DH8FjnOK.js → timeline-definition-Z64GVDOM-DVJQA7AN.js} +1 -1
  75. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-beoTagJg.js → vennDiagram-T6HMQDX7-74uxl9gx.js} +1 -1
  76. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CQSfw4cK.js → wardleyDiagram-T6FBY63Y-DsBxlcg_.js} +1 -1
  77. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CQgzQ6jn.js → xychartDiagram-ELKLHX3M-snuW9Mjw.js} +1 -1
  78. package/dist/worker/console/static/index.html +2 -2
  79. package/dist/worker/console/static-src/app/console-types.js +0 -12
  80. package/dist/worker/console/static-src/app/useOperatorActions.js +1 -1
  81. package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
  82. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +38 -1
  83. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
  84. package/dist/worker/console/static-src/operator-chat/turn-submission.js +42 -0
  85. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
  86. package/dist/worker/console/static-src/operator-chat/useChatStream.js +204 -11
  87. package/dist/worker/console/static-src/operator-chat/useChatThread.js +23 -1
  88. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +755 -51
  89. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  90. package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
  91. package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
  92. package/dist/workflows/dag/backend-test-intake-context.js +32 -0
  93. package/dist/workflows/dag/backend-test-layout.js +133 -0
  94. package/dist/workflows/dag/backend-test-markdown-workflow.js +167 -32
  95. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  96. package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
  97. package/dist/workflows/dag/backend-test-result-contract.js +1 -1
  98. package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
  99. package/dist/workflows/dag/backend-test-scenario-partitions.js +249 -0
  100. package/dist/workflows/dag/backend-test-writer-completeness.js +100 -56
  101. package/dist/workflows/dag/contract-validator-registrations.js +2 -1
  102. package/dist/workflows/dag/convergence/controller.js +141 -0
  103. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  104. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
  105. package/dist/workflows/dag/frontend-implementation-contract.js +236 -8
  106. package/dist/workflows/dag/frontend-prewrite-gate.js +95 -35
  107. package/dist/workflows/dag/init-hybrid.js +652 -87
  108. package/dist/workflows/dag/node-execution.js +45 -18
  109. package/dist/workflows/dag/prompt.js +45 -0
  110. package/dist/workflows/dag/rerun-feedback.js +212 -0
  111. package/dist/workflows/dag/rerun-plan.js +93 -27
  112. package/dist/workflows/dag/rerun-run.js +3 -0
  113. package/dist/workflows/dag/rerun-task.js +61 -2
  114. package/dist/workflows/dag/retry-policy.js +18 -0
  115. package/dist/workflows/dag/types.js +98 -0
  116. package/docs/templates/backend-test-dag.json +60 -35
  117. package/docs/templates/harness.schema.json +0 -8
  118. package/harness.json +1 -8
  119. package/package.json +1 -1
  120. package/skills/frontend-implementation/references/node-contracts.md +2 -2
  121. package/dist/worker/console/static/assets/channel-Dp_2Aqf1.js +0 -1
  122. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL--CODy37j.js +0 -1
  123. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE--CODy37j.js +0 -1
  124. package/dist/worker/console/static/assets/index-BA5-N5ER.css +0 -1
  125. package/dist/worker/console/static/assets/index-DKPXBrdc.js +0 -325
  126. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B-mvnLEc.js +0 -1
  127. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-JtpBmIv4.js +0 -8
package/CHANGELOG.md CHANGED
@@ -2,23 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ### 改进
6
-
7
- - Operator Chat 恢复工具对齐真实能力:系统提示改用 `dagReport` / `dagRerunPlan` / `dagRerun` / `standaloneTaskRerun` / `workerTaskRetry` 真名,不再写 `operator_*` 或把 `dagResume` 当成模型可执行项;`dagDoctor` / `dagStatus` / `workerPoolDoctor` 改为 typed `runId`/`taskId`,不再让模型猜 CLI `args[]`。ADR 0013 自定义 base prompt 上限由 1900 统一调整为 2500(2026-08-18,废止 0.8×2498 比例),预算用于写清节点续跑 / 整单重生 / Worker 重排队分流,以及多轮迭代恢复纪律(先一句话总结上一轮错误、修根因不修症状、禁止原样重交、同错两轮后停下报告)。
5
+ ### 变更
8
6
 
9
- - Operator Chat 对齐 pi-web:建会话不再传封闭 `tools` 白名单(避免 SDK 把扩展 MCP 工具滤出 registry),创建后 `bindExtensions` `session_start`,再钉激活集。扩展注册的 MCP 工具可被模型调用;`apply_patch` / `full-tools` / `shell` / `coding-chat` 仍排除。
10
- - 对话侧栏 Plugins 右侧新增只读 MCP 弹窗:展示 `mcp.json` 配置的服务,以及当前会话真正注册/激活的 MCP 工具;没有配置也没有工具时保持真实空态。
11
- - 对话侧栏原 Packages 入口改名为 Plugins,与 pi-web / Pi 用户面一致;内部仍是 package inventory。
12
- - 对话进行中底部上下文占用百分比保持常驻:token 用量事件不再冲掉上一帧的 contextUsage,并在回合中持续刷新。
13
- - 上一轮大量 bash 结束后再发新消息,旧工具条不再整批挂到新气泡下面:无主工具不再丢进最新一组,历史按完整回合裁切,空助手气泡若还挂着工具会留下来当锚点。
14
- - 侧栏文件树点击目录行直接插入 `@目录/` 引用(箭头单独负责展开/收起);输入框 @ 补全也列出目录,排在文件前面。
15
- - 对话进行中点「停止」立即安静下来(乐观停止,对齐 pi-web):不再等 POST /abort 往返;后端 abort 等待从 8s 降到 2.5s,未终结时由状态对账收敛。
16
- - 侧栏文件树实时刷新:agent 回合结束、浏览器标签页回到前台、手动点「刷新」时重拉根目录+全部已展开目录(此前手动刷新只拉根目录,子目录永远旧缓存)。
17
- - Skills / Packages / Models 面板不再把 Pi 的 warning / collision 原文堆在列表上方:改成标题条一颗摘要芯片(如「14 条提示」),点开按「需要处理 / 名称不规范 / 同名未生效」分组,文案用自然语言,路径只显示末两段;名称不规范的技能在列表旁标「提示」,详情里说明「现在这样也能用」。加载失败仍单独成组,可点「再读一次」。
18
- - Skills / Packages 双栏改为左右各自滚动;资源弹窗挂到 `document.body`,按顶部对话导航的实时高度在剩余视口居中,不再被 sticky 顶栏挡住上沿。
7
+ - npm `next` 渠道发布改为提交数节流:main push 仅当距上次 next 发布的 source commit 5 commit 时才自动发布(新增 `scripts/next-publish-gate.mjs`,读已发布 tarball build-stamp 对比 HEAD);不足阈值时 workflow 绿色跳过,手动 `workflow_dispatch` 不受限(切 release 线前强制出包 / 紧急补发)。发布文档真源 §4.1/§5.1/§7/§12 同步。
19
8
 
20
9
  ### 新增
21
10
 
11
+ - backend-test 适配 A–D:可配置 `backendTest` 布局(`testRoot`/`markdownRel`/`scriptRel`)、用户公共前置文档引用与模块顶部 `user_shared_setup`(不写 conftest)、查询轴 Scenario Partitions 确定性展开与 fail-closed 门禁、`mode=gap-fill` 13 节点增量 DAG。Slot ID 以声明的 Partition ID 为真源(`TP-<Partition ID>-...`)。
22
12
  - 前端 plan 唯一机器契约化(JSON-only):`frontend-plan-pi` / `frontend-plan-revision-pi` 输出收敛为「一行导语 + 唯一 fenced `json` 契约 + 唯一 `openspec-citations` 引用块」,彻底移除「可选 Markdown 说明」;plan.md(`contracts/frontend-plan.md`)改由 prewrite gate 用纯函数 `renderFrontendPlanMarkdown` 确定性渲染(不新增 DAG 节点/不新增 Pi 调用)
23
13
  - 契约 schema 新增 5 个可选字段(`implementationSteps` / `stylingStrategy` / `dependencyPolicy` / `residualRisks` / `realIntegrationGap`),`schemaVersion` 保持 1;`docs/templates/frontend-implementation-contract.schema.json` 同步
24
14
  - revision 输出改为 RFC 7386 merge-patch delta + 引用块:gate 配置新增 `revisionPatch`,生效时在 `planFallbackFromNodeIds[0]` 原始契约上应用 `applyFrontendImplementationContractPatch` 后进入既有分析/规范化/物化管线;旧 DAG 缺字段按整契约处理
@@ -36,10 +26,34 @@
36
26
  - `src/shared/openspec-spec.ts` 新增 `extractTaskSourceOpenspecPaths` 纯函数(反引号内联、markdown 链接目标、裸路径 token → 归一化 → spec 过滤 → 治理子树排除 → 去重排序)
37
27
  - 生成期把 `openspecPolicy` 与候选来源元数据(`openspecCandidateSources.declared` / `taskSourceCited` / `scanStrict`)冻结进 prewrite gate 配置;`task advance` 的 advisories 说明当前模式与候选来源
38
28
 
29
+ ### 改进
30
+
31
+ - Operator Chat 的乐观停止现在把“视觉已停止”和 Turn ownership 分开:Abort 获得可靠终态前明确显示“正在停止”,此时可为原会话保存一条服务端持久化的“停止后发送”消息;后一次覆盖前一次,切换会话、刷新页面或 Console 重启不会丢失,旧 Turn 结束后复用正常 Turn 准入路径自动发送,并以固定 `clientRequestId` 防止重复执行。
32
+ - Operator Chat 修复快速终止时“停止后发送”与普通发送之间的准入空洞:发送路径改读实时 ownership;若 deferred 请求恰好撞上旧 Turn 终态,会先按服务端状态对账,再使用同一 `clientRequestId` 自动转为普通发送,不再要求用户刷新重试或丢失本次提交。
33
+ - `dag rerun-task` 现在会把父 run 最后一轮仍未解决的 `verify-pi` / review 反馈、证据引用和修订轮次带入新完整 DAG;planner、implementer 与 verifier/reviewer 必须修复或举证说明不再适用,不能仅因文件已存在就返回 `already-satisfied`。Console 同步区分「自动修订」「从失败节点恢复」「带反馈重新执行任务」。
34
+ - DAG 恢复与治理收敛收窄为两条最小路径:标准治理 `request-revision` 仅在 `maxFixLoops > 0` 时同 run 回到 `implement-pi`;writer 遇到 `unavailable` / `network` / `rate-limit` / `timeout` 时,`dag rerun --from-node` 重跑 writer 本身及安全下游,不再把 parent `ERROR` 改写成 `FINISHED`,任意自定义 shell 也不能仅凭 `verifyEvidence` 获得续跑资格。
35
+ - `frontend-plan-pi` 改为 runtime skeleton + editable RFC 7386 patch:`schemaVersion`、source binding、risk、writer target files 与生产 Mock 安全位由 DAG 冻结且不可被 plan/revision 覆盖;节点 validator 将补丁编译为 canonical 完整契约后再交给 review/prewrite,并在 `contracts/candidates/<nodeId>/` 保存每次 raw/extracted/normalized/validation 证据。任务提示与三类结构化重试(invalid-output / provider length 截断 / output-too-large)同步收敛为单一 patch + 唯一 `openspec-citations`;无法在声明文件中解析的可选 verification symbol 会被确定性移除并留下 normalization action,避免格式冲突、完整契约重复输出和说明性 symbol 延迟到 trace 才失败。
36
+ - Operator Chat 恢复工具对齐真实能力:系统提示改用 `dagReport` / `dagRerunPlan` / `dagRerun` / `standaloneTaskRerun` / `workerTaskRetry` 真名,不再写 `operator_*` 或把 `dagResume` 当成模型可执行项;`dagDoctor` / `dagStatus` / `workerPoolDoctor` 改为 typed `runId`/`taskId`,不再让模型猜 CLI `args[]`。ADR 0013 自定义 base prompt 上限由 1900 统一调整为 2500(2026-08-18,废止 0.8×2498 比例),预算用于写清节点续跑 / 整单重生 / Worker 重排队分流,以及多轮迭代恢复纪律(先一句话总结上一轮错误、修根因不修症状、禁止原样重交、同错两轮后停下报告)。
37
+ - Operator Chat 模型工具面收敛:`advanced` 分类的命令(workflow/loop/benchmark/knowledge-graph 等 8 个只读低频入口)不再进入模型工具白名单,模型注意力集中到高频工具;保留的 27 个 model-callable 官方 CLI 动作全部换上「做什么 + 何时用」语义描述,不再是无信息的 `Official CLI action coverage for ...` 模板;dispatcher 对官方尾部动作的识别从描述前缀匹配改为结构化 `OFFICIAL_TAIL_ACTION_IDS` 集合判断。审查收口:`knowledgeCurate` 纠正为 `mutation`/`advanced`(条件写盘,移出模型面,防经 read 通道传 `--output` 写盘);`promoteRun`/`closeoutTask` 描述去掉 dispatch 不保证的生命周期断言,如实标注为 task-advance 别名并提示先核 run/任务状态。工具清单渲染修复:promptSnippet 去掉重复工具名前缀(SDK 渲染自带 `- name:`,原实现名字出现两遍);`inspect`/`status`/`spineCheck`/`sourceInstructions` 四条占位描述换成语义描述;task-advance 家族(`newTask`/`dagRunTask`)标注与标准流 `taskAdvance` 的关系,避免模型在 5 个相似入口间选错。可调用性修复:`observeLink` 补上 4 个可选参数 schema(dagRunId/taskId/featureId/workerRunId,原空 schema + additionalProperties:false 导致模型传参必被拒、工具名存实亡);Pi 工具参数 schema 现在携带注册表里的参数描述(Type.String/Boolean/Number/Array 注入 `description`,原实现丢弃参数描述、模型只能看到参数名)。
38
+
39
+ - Operator Chat 对齐 pi-web:建会话不再传封闭 `tools` 白名单(避免 SDK 把扩展 MCP 工具滤出 registry),创建后 `bindExtensions` 发 `session_start`,再钉激活集。扩展注册的 MCP 工具可被模型调用;`apply_patch` / `full-tools` / `shell` / `coding-chat` 仍排除。
40
+ - 对话侧栏 Plugins 右侧新增只读 MCP 弹窗:展示 `mcp.json` 配置的服务,以及当前会话真正注册/激活的 MCP 工具;没有配置也没有工具时保持真实空态。
41
+ - 对话侧栏原 Packages 入口改名为 Plugins,与 pi-web / Pi 用户面一致;内部仍是 package inventory。
42
+ - 对话进行中底部上下文占用百分比保持常驻:token 用量事件不再冲掉上一帧的 contextUsage,并在回合中持续刷新。
43
+ - 上一轮大量 bash 结束后再发新消息,旧工具条不再整批挂到新气泡下面:无主工具不再丢进最新一组,历史按完整回合裁切,空助手气泡若还挂着工具会留下来当锚点。
44
+ - 侧栏文件树点击目录行直接插入 `@目录/` 引用(箭头单独负责展开/收起);输入框 @ 补全也列出目录,排在文件前面。
45
+ - 对话进行中点「停止」立即安静下来(乐观停止,对齐 pi-web):不再等 POST /abort 往返;后端 abort 等待从 8s 降到 2.5s,未终结时由状态对账收敛。
46
+ - 侧栏文件树实时刷新:agent 回合结束、浏览器标签页回到前台、手动点「刷新」时重拉根目录+全部已展开目录(此前手动刷新只拉根目录,子目录永远旧缓存)。
47
+ - Skills / Packages / Models 面板不再把 Pi 的 warning / collision 原文堆在列表上方:改成标题条一颗摘要芯片(如「14 条提示」),点开按「需要处理 / 名称不规范 / 同名未生效」分组,文案用自然语言,路径只显示末两段;名称不规范的技能在列表旁标「提示」,详情里说明「现在这样也能用」。加载失败仍单独成组,可点「再读一次」。
48
+ - Skills / Packages 双栏改为左右各自滚动;资源弹窗挂到 `document.body`,按顶部对话导航的实时高度在剩余视口居中,不再被 sticky 顶栏挡住上沿。
49
+
39
50
  ### 修复
40
51
 
52
+ - Console 对话中的 Markdown 表格只约束横向滚动,不再截断纵向滚动链;鼠标或触控板停在表格上时仍可继续上下滚动对话。
53
+ - scenario-param 确定性 rewrite 的 Python 语法校验:Windows 优先 `python`,并把 Microsoft Store `python3` 空壳(非 0 且无语法诊断)视为解释器不可用后回退,避免误回滚合法 rewrite。
41
54
  - `frontend-plan-revision-pi` 节点级校验与 merge-patch 输出协议矛盾修复:新增 schemaId `frontend-implementation-contract-revision-patch-v1` 与格式级宽松校验 `validateFrontendRevisionPatchNodeOutput`(至少一个 `openspec-citations` 块 + 剥离引用块后恰好一个 JSON 对象,不做完整契约 schema 校验),revision 节点 `structuredContractOutput.schemaId` 切换(plan-pi 仍为 v1);node-execution 的 invalid-output retry 指令按 schemaId 分支,patch 协议提示 merge-patch delta + 引用块,消除「prompt 要求 patch、retry 要求完整契约」的 retryable 死循环;gate 仍是唯一权威合并/校验点(零改动)。ADR 0015 补「节点级校验」决策 7
42
55
  - `frontend-implementation-contract.schema.json` 的 `uiComponentChoices` items 补 `if/then` 决策语义(`specified` ⇒ `specReference` 必填非 null;`new` ⇒ `specReference` 为 null/缺失);`frontend-plan-render.ts` 空 `section` 不再渲染尾随 `#`;ADR 0016 增补「门禁边界」小节
56
+ - 前端 gate 内容驱动 revisionPatch 兼容 + verdict 归一化统一:旧 DAG(缺 `revisionPatch` 字段)但 revision 节点按协议输出 merge-patch 时,gate 按内容判定自动合并原始契约并走既有校验(不再因 `missing targets.files` 进入 retryable 死循环);patch 形态但无法合并(fallback 缺失/自身、原始节点不可读或非完整契约、apply 失败)→ `blocked` + 新增 `revision-patch-unmergeable`(错误信息含重新生成 DAG 指引);`normalizeVerdictCandidateLine` 增强 backtick 强调剥离,gate 的 `firstNonEmptyVerdictLine` 复用共享 normalize,runIf / output-protocol / gate 三处一致
43
57
 
44
58
  ### 文档
45
59
 
@@ -47,6 +61,33 @@
47
61
  - 新增 `docs/decisions/0015-frontend-plan-json-only-contract.md`,并同步 `docs/runtime/frontend-implementation-workflow.md`、`docs/templates/frontend-implementation-contract.schema.json`、`skills/frontend-implementation/references/node-contracts.md`
48
62
  - 新增 `docs/decisions/0014-openspec-gate-claim-verification.md`,并同步 `docs/runtime/frontend-implementation-workflow.md`、`docs/templates/frontend-task-constraints.md`、`docs/templates/frontend-design-contract.md`、`skills/frontend-implementation/references/node-contracts.md`
49
63
 
64
+ ## [0.37.1-beta.0] - 2026-08-16
65
+
66
+ ### 重点更新
67
+
68
+ - backend-test live campaign R115–R141 收口:全新 R141 达到 readiness PASS、23/23 correspondence exact、146/146 Test Points、77/77 scenario MATCH、traceability PASS,并唯一执行 82 个真实 API pytest body
69
+ - 强化 backend-test correspondence、scenario-param、Markdown/pytest writer、collection repair、日志审计和 Windows pytest argfile 执行链,稳定区分 controller TestBug、generated TestBug 与真实 API ProductBug
70
+
71
+ ### 改进
72
+
73
+ - backend-test correspondence 只分析目标请求实际绑定的 body/payload,忽略 `scenario` 元数据与 setup-only payload;对 `scenario["body"]` 保留字段 shape,并把 intentional invalid enum 交由 scenario-param 门禁判断;同时识别下划线前缀 pytest 参数集合与带说明的 `Payload Contract: none(…)`,避免零 eligible 的 payload/parameter 误阻断
74
+ - backend-test correspondence 继续解析模块内 transport wrapper 的位置 payload 与 wrapper 构造 body,并清理 `Payload Enum` 值后的中文说明,避免 PUT/lifecycle 合法载荷误报;Markdown/pytest writer 同步禁止臆测响应 ID 类型,并在路径 ID 类型未声明时使用 create-delete 派生的合法 missing ID,避免把测试资产缺陷误分类为产品 400/404 缺陷
75
+ - backend-test correspondence 可还原 `_apply_patch(_base_body(...), patch)` 等 transport-body 组合表达式,并把 setup wrapper payload 限定为有 body 契约时的 fallback;traceability 接受经递归脱敏和有界输出的结构化 request/response 日志,scenario-param 支持带显式例值的 string wrong-type;pytest writer/repair 同步禁止把 path/query/control `id` 混入 DTO patch
76
+ - backend-test correspondence 识别模块级 `_post_case`/`_put_case` 参数工厂、factory TP 字面量与通过 `payload["field"] = value` 构造的 DTO shape,并避免空列表全局变量吞并后续参数集合;writer/repair 要求每个变体展开为直接 `pytest.param(..., id="TP-...")`,scenario readiness 不再依赖隐藏的动态参数行;traceability 允许窄 `urllib.error.URLError`/`httpx.RequestError` DELETE cleanup
77
+ - backend-test correspondence 继续还原 `payload = _base_note_payload()` 形式的 builder-return dataflow,并把 intentional invalid enum 交给 scenario-param 而非 Case 聚合 payload gate;Markdown review 明确 GET/DELETE setup DTO 不得改写目标 no-body contract,pytest writer/repair 禁止把返回 scalar 的 helper 当 tuple 解包
78
+ - backend-test collection repair 把 upstream 的 `Repair paths: testcase/...` 直接视为完整权威路径证据并读取/编辑该文件;禁止猜测根级 `contracts/**` 或 DAG run 目录,也禁止 read tool 已成功返回文件后反事实声称路径不存在并提前 blocked
79
+ - backend-test 的 REPAIRABLE collection repair 现在强制 non-empty diff,禁止 `already-satisfied` 制造假修复;Markdown review 增加 `### 测试点` 与三类 binding union 的 exact-set 校验并删除 legacy alias;no-body Case 的 payload correspondence 忽略同一 journey 的 direct setup POST/PUT DTO
80
+ - backend-test generated-local collection repair 从 HIGH 路由降为 MED,在保留 write guard 与 requireChangedFiles 的同时避开连续多轮 grok repair hallucinated blocked/changed-without-write,改由稳定 writer 路由执行有界单文件修复
81
+ - writer outcome 协议确定性归一化显式中文 `实现结果:已变更|已满足|已阻塞` 到 canonical `IMPLEMENTATION_OUTCOME`,仍拒绝无标签 prose、未知值和冲突值,不根据 diff 猜测语义
82
+ - scenario-param 识别 `"x" * _TITLE_MAX_LENGTH` 等长度表达式与 helper 常量名相乘形式
83
+ - correspondence 忽略 pytest payload 中的 `expect`/`echo_*` 辅助键,避免把 DTO 外 helper 字段误判为 PAYLOAD_SHAPE_MISMATCH
84
+ - scenario-param 解析 `bound=LEN100` 机器行、解引用 `_STATUS_ACTIVE` 等 Python 字符串常量,并把 `NOMINAL` 长度样本从 min/max 纠正为 nominal
85
+
86
+ - scenario-param 观测识别 `_OMIT`/omit sentinel 为合法 `missing`,跳过 `"2xx"`/`"400"` 期望码位置参数,解码 Python `\t`/`\n` 空白字面量,识别 `_TITLE_LEN1` 等长度 helper 名,支持 `custom-literal:whitespace-only|whitespace-padded` 语义标签,`enum-invalid` 接受具体非法字面量,并在 `min`/`LEN1` 意图下优先读取 TP 编码长度与 `minLength` 边界
87
+ - scenario-param 兼容 `empty` 意图下的 omit/`missing-key`,规范化 `custom-literal` 外包引号与可见空白符,并把 `max+1`/`min`/`max` 的裸数字位置参数按长度 token 比较(含 oversized 数字本身);`custom-literal:trim` 视为 padded whitespace 语义标签
88
+ - backend-test writer 合同明确:`enum-invalid` 必须使用具体非法字面量且禁止 `_OMIT`/missing-key;`custom-literal:trim|ACTIVE|ARCHIVED` 必须使用真实 padded/enum 字面量,禁止 `filter-active` 类描述性伪值
89
+ - scenario-param 识别 `_*_MAX_LENGTH` / `_*_OVER_LENGTH` 等长度 helper 名,明确 `custom-literal:trim` 不能是全空白,并把无 pytest.param 的 request-level/health nominal 记为 MATCH 以免污染 eligibility
90
+
50
91
  ## [0.36.4-beta.0] - 2026-08-16
51
92
 
52
93
  > 前端 DAG 规范上下文确定性落地(beta,发布到 `beta` dist-tag)。
@@ -441,6 +482,61 @@
441
482
  - 会话创建期间侧栏与 Landing 页共享 busy 状态反馈,抑制双击导致的重复创建
442
483
  - 删除正在初始化的会话不会被迟到的后台初始化任务复活,初始化失败统一返回 HTTP 503 错误码
443
484
 
485
+ ## [0.33.7-beta.2] - 2026-08-13
486
+
487
+ ### Fixed
488
+
489
+ - scenario-param 解析 `bound=LEN100`、解引用 `_STATUS_ACTIVE` 等 Python 字符串常量,识别 `"x" * _TITLE_MAX_LENGTH` 长度表达式
490
+ - correspondence 忽略 pytest payload 中的 `expect`/`echo_*` 辅助键,避免假 PAYLOAD_SHAPE_MISMATCH
491
+
492
+ ### Changed
493
+
494
+ - my-webapp 五轮 backend-test 全流程 live campaign 归档(R1–R5 均 finished;R5 correspondence PASS)
495
+
496
+ ## [0.33.7-beta.1] - 2026-08-12
497
+
498
+ ### Fixed
499
+
500
+ - scenario-param 识别 `_*_MAX_LENGTH` / `_*_OVER_LENGTH` 等长度 helper 名,明确 `custom-literal:trim` 不能是全空白,并把无 pytest.param 的 request-level/health nominal 记为 MATCH 以免污染 eligibility
501
+ - backend-test writer 合同明确:`enum-invalid` 必须使用具体非法字面量且禁止 `_OMIT`/missing-key;`custom-literal:trim|ACTIVE|ARCHIVED` 必须使用真实 padded/enum 字面量;max/min/max+1 应用长度表达式/helper
502
+
503
+ ### Changed
504
+
505
+ - 归档 R108–R110 live campaign 证据与 residual 收紧结果
506
+
507
+ ## [0.33.7-beta.0] - 2026-08-11
508
+
509
+ ### 重点更新
510
+
511
+ - backend-test 收口 source-fidelity P0:绑定 source coverage、有界 Markdown/pytest 同步、item-level eligibility 与单次 eligible 业务执行;my-webapp R107 16 节点 finished
512
+
513
+ ### 新增
514
+
515
+ - backend-test 新增绑定 source references 的来源覆盖事实、Markdown 一轮增量同步和 pytest correspondence 一轮有界修复,在业务测试体执行前保留 initial/effective/residual 证据
516
+ - backend-test 新增 item-level execution eligibility:只授权 payload-safe 的 pytest items,excluded items 与真实业务失败分开展示
517
+
518
+ ### 改进
519
+
520
+ - 将 backend-test Markdown/pytest map 的默认总 token 预算从 `600000` 提升到 `3000000`,降低多模块生成时过早触发 `dynamic map token budget exhausted` 的概率;仍保持预算耗尽 fail-closed
521
+ - Coverage Matrix 接受 `R400001` 等错误码 Rule Key,并保留单元格内 `ACTIVE|ARCHIVED` 未转义枚举管道符,避免合法行被拆成 10 列后整表丢弃
522
+ - scenario-param 按 TP 精确匹配多行 `场景意图`,保留 `ACTIVE` 大小写字面量,识别 `"a" * N` 长度表达式与空白 `empty`,request-level `nominal` 允许 `None` 查询过滤;避免把合法 payload 误判为 MISMATCH 并错误排除 eligible items
523
+ - backend-test 支持 evidence-only 元用例:Markdown 显式声明 `脚本/primary symbol=无` 且仅绑定 assertion/cross-cutting TP 时,不再强制 1:1 pytest 符号,也不再把其算作 MISSING_PYTEST/SCRIPT_MISMATCH
524
+ - generated pytest 内的 `ScopeMismatch`/setup fixture 失败若完全归因于 `testcase/**` 映射脚本,则归为 REPAIRABLE 并进入唯一一轮 collection repair,不再直接 BLOCKED
525
+ - scenario-param 观测跳过 `pytest.param("TP-...", value, id="TP-...")` 的前导 TP 标签,并支持长度意图用数字位置参数表达
526
+
527
+ ### 修复
528
+
529
+ - 修复 readiness 错误信息截断与无效 Coverage Matrix 状态语义,避免超长错误或 advisory 矩阵状态误阻断后续节点
530
+ - 修复 scenario 排除项未绑定 payload 字段、request-level 不确定性被误升级为硬失败的问题
531
+
532
+ ## [0.33.6-beta.0] - 2026-08-11
533
+
534
+ ### 修复
535
+
536
+ - 修复 backend-test correspondence scanner 将下一 Case 的分隔注释误归入前一个 pytest function region,造成虚假的 `MULTIPLE_PYTEST`、script mismatch 和额外 Test Point;现在按函数实际缩进边界隔离 body,并只从精确 `Case-ID:` 元数据或 primary symbol 绑定 Case
537
+ - 修复 backend-test 将 README 其他区域的 Markdown 链接误提取为业务模块、允许 `a401606` 等 opaque/hash-like stem 和异常多模块进入 map 的问题;现在只解析唯一 `## Module Index`,非法、mixed 或超过 8 个模块时 README-only 有界重试并由 manifest fail-closed
538
+ - 修复 backend-test map 总 token 预算耗尽后只生成部分 Markdown/pytest 资产但 parent 仍可能显示成功的问题;budget-blocked child 现在使 backend map 明确 ERROR,并保留 expected/finished/blocked/token 事实
539
+
444
540
  ## [0.33.6] - 2026-08-11
445
541
 
446
542
  ### 重点更新
@@ -279,6 +279,7 @@ export async function generateTaskDagUseCase(input) {
279
279
  const candidateResult = await initHybridDagFromTask(repoRoot, parsed.taskId, {
280
280
  outputPath: parsed.outputPath,
281
281
  template: "standard-dag",
282
+ rerunFeedback: parsed.rerunFeedback,
282
283
  });
283
284
  const candidateValidateSummary = await validateDagUseCase(buildValidateInput(repoRoot, candidateResult.outputPath, parsed));
284
285
  const profileRouting = await resolveProfileRouting(repoRoot, parsed, candidateValidateSummary.governanceProfile ?? {
@@ -324,8 +325,16 @@ export async function generateTaskDagUseCase(input) {
324
325
  : await initHybridDagFromTask(repoRoot, parsed.taskId, {
325
326
  outputPath: parsed.outputPath,
326
327
  template: profileRouting.selectedTemplate,
328
+ rerunFeedback: parsed.rerunFeedback,
327
329
  });
328
330
  const outputPath = initResult.outputPath;
331
+ if (parsed.expectedRerunFeedbackDigest) {
332
+ const generatedSpec = parseDagSpec(JSON.parse(await readFile(outputPath, "utf8")));
333
+ if (generatedSpec.rerunFeedback?.digest !==
334
+ parsed.expectedRerunFeedbackDigest) {
335
+ throw new Error(`rerun feedback drift: expected ${parsed.expectedRerunFeedbackDigest}, got ${generatedSpec.rerunFeedback?.digest ?? "missing"}`);
336
+ }
337
+ }
329
338
  const validateSummary = profileRouting.selectedTemplate === candidateResult.template
330
339
  ? candidateValidateSummary
331
340
  : await validateDagUseCase(buildValidateInput(repoRoot, outputPath, parsed));
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.38.1",
4
- "gitSha": "0b7518c6f009d1fcf8776f757775bb447fd006ee",
5
- "builtAt": "2026-08-18T22:33:47.281Z"
3
+ "version": "0.38.2",
4
+ "gitSha": "ba66152b71609e67330471e5325c1efddd200412",
5
+ "builtAt": "2026-08-20T07:31:23.829Z"
6
6
  }
@@ -1362,13 +1362,7 @@ async function readExistingSurfaceState(repoRoot) {
1362
1362
  }
1363
1363
  /** The IDE schema reference remains a structural update signal. */
1364
1364
  const HARNESS_STRUCTURAL_VALUE_KEYS = new Set(["$schema"]);
1365
- const PI_MODEL_ROUTING_KEYS = new Set([
1366
- "LOW",
1367
- "MED",
1368
- "HIGH",
1369
- "defaultModel",
1370
- "modelOptions",
1371
- ]);
1365
+ const PI_MODEL_ROUTING_KEYS = new Set(["LOW", "MED", "HIGH", "defaultModel"]);
1372
1366
  function isPiModelRoutingPath(path) {
1373
1367
  return path.length === 2 && path[0] === "executors" && path[1] === "pi";
1374
1368
  }
@@ -1733,13 +1727,7 @@ function assessHarnessModelMigration(harness) {
1733
1727
  const hasDefaultModel = "defaultModel" in pi;
1734
1728
  const hasTier = PI_MODEL_TIERS.some((tier) => tier in pi);
1735
1729
  if (["requiresApiKey", "provider", "model"].some((field) => field in pi) ||
1736
- Object.keys(pi).some((field) => ![
1737
- "description",
1738
- "enabled",
1739
- "defaultModel",
1740
- "modelOptions",
1741
- ...PI_MODEL_TIERS,
1742
- ].includes(field))) {
1730
+ Object.keys(pi).some((field) => !["description", "enabled", "defaultModel", ...PI_MODEL_TIERS].includes(field))) {
1743
1731
  return {
1744
1732
  kind: "ambiguous",
1745
1733
  reason: "executors.pi contains unknown or deprecated model semantics",
@@ -10,6 +10,7 @@ import { redactPromptForLog, truncateOutput, } from "../shared/output-truncation
10
10
  import { GitStatusUnavailableError, pathsChangedDuringRun, readGitStatusPorcelain, recoverRootNulArtifact, snapshotGitStatusPathFingerprints, snapshotGitStatusPorcelain, validateShellWriteGuard, } from "./shell-write-guard.js";
11
11
  import { isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, } from "../workflows/dag/retry-policy.js";
12
12
  import { assessBackendTestMdPlanCompleteness, assessBackendTestMdWriterCompleteness, assessBackendTestPytestPlanCompleteness, assessBackendTestPytestWriterCompleteness, assessBackendTestShardChildCompleteness, backendTestWriterProgressRoleForTask, classifyBackendTestWriterCompletenessFailure, isBackendTestCompletenessRetryCandidate, isBackendTestMdPlanTask, isBackendTestPytestPlanTask, isBackendTestShardChildTask, writeBackendTestWriterProgressArtifacts, } from "../workflows/dag/backend-test-writer-completeness.js";
13
+ import { resolveBackendTestLayout } from "../workflows/dag/backend-test-layout.js";
13
14
  import { redactSecrets, truncateUtf8Preview } from "../shared/preview.js";
14
15
  /**
15
16
  * Writer classification for a length-stopped thinking-only attempt: the model
@@ -808,6 +809,13 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
808
809
  isBackendTestCompletenessRetryCandidate(input.task)) {
809
810
  try {
810
811
  const writerRole = backendTestWriterProgressRoleForTask(input.task.id);
812
+ const backendLayout = resolveBackendTestLayout(meta.spec.backendTestLayout
813
+ ? {
814
+ testRoot: meta.spec.backendTestLayout.testRoot,
815
+ markdownRel: meta.spec.backendTestLayout.markdownRel,
816
+ scriptRel: meta.spec.backendTestLayout.scriptRel,
817
+ }
818
+ : undefined);
811
819
  const progress = isBackendTestShardChildTask(input.task)
812
820
  ? await assessBackendTestShardChildCompleteness({
813
821
  workspaceRoot: input.cwd,
@@ -815,12 +823,12 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
815
823
  writeSet: input.task.writeSet ?? [],
816
824
  })
817
825
  : isBackendTestMdPlanTask(input.task)
818
- ? await assessBackendTestMdPlanCompleteness(input.cwd)
826
+ ? await assessBackendTestMdPlanCompleteness(input.cwd, backendLayout)
819
827
  : isBackendTestPytestPlanTask(input.task)
820
- ? await assessBackendTestPytestPlanCompleteness(input.cwd)
828
+ ? await assessBackendTestPytestPlanCompleteness(input.cwd, backendLayout)
821
829
  : writerRole === "pytest-generate"
822
- ? await assessBackendTestPytestWriterCompleteness(input.cwd)
823
- : await assessBackendTestMdWriterCompleteness(input.cwd);
830
+ ? await assessBackendTestPytestWriterCompleteness(input.cwd, backendLayout)
831
+ : await assessBackendTestMdWriterCompleteness(input.cwd, backendLayout);
824
832
  await writeBackendTestWriterProgressArtifacts({
825
833
  runDir: meta.runDir,
826
834
  progress,
@@ -977,6 +985,15 @@ function parseWriterImplementationOutcome(text) {
977
985
  const lines = text.split(/\r?\n/);
978
986
  const candidates = [];
979
987
  for (const [lineIndex, line] of lines.entries()) {
988
+ const localizedOutcome = localizedWriterImplementationOutcome(line);
989
+ if (localizedOutcome) {
990
+ candidates.push({
991
+ lineIndex,
992
+ value: localizedOutcome,
993
+ outcome: localizedOutcome,
994
+ });
995
+ continue;
996
+ }
980
997
  const nextLine = lines[lineIndex + 1];
981
998
  const normalized = normalizeProtocolLine(line, WRITER_OUTCOME_PROTOCOL_LINE, nextLine);
982
999
  if (normalized === undefined)
@@ -1005,6 +1022,26 @@ function parseWriterImplementationOutcome(text) {
1005
1022
  candidates,
1006
1023
  };
1007
1024
  }
1025
+ function localizedWriterImplementationOutcome(line) {
1026
+ const normalized = normalizedProtocolValue(line);
1027
+ const match = /^(?:实现|实施)结果\s*[::]\s*(已变更|已更改|已修改|已满足|无需变更|已阻塞|阻塞)$/u.exec(normalized);
1028
+ if (!match)
1029
+ return undefined;
1030
+ switch (match[1]) {
1031
+ case "已变更":
1032
+ case "已更改":
1033
+ case "已修改":
1034
+ return "changed";
1035
+ case "已满足":
1036
+ case "无需变更":
1037
+ return "already-satisfied";
1038
+ case "已阻塞":
1039
+ case "阻塞":
1040
+ return "blocked";
1041
+ default:
1042
+ return undefined;
1043
+ }
1044
+ }
1008
1045
  function isWriterImplementationOutcome(value) {
1009
1046
  return (value === "changed" || value === "already-satisfied" || value === "blocked");
1010
1047
  }