@tea-agent/loop-agent 0.44.0-next.1 → 0.44.0-next.10

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 (226) hide show
  1. package/CHANGELOG.md +379 -416
  2. package/dist/application/evaluation/budget.js +21 -0
  3. package/dist/application/evaluation/corpus-hash.js +10 -15
  4. package/dist/application/evaluation/corpus.js +2 -1
  5. package/dist/application/evaluation/frontend-browser-acceptance.js +77 -0
  6. package/dist/application/evaluation/frontend-corpus-browser-acceptance.js +175 -0
  7. package/dist/application/evaluation/frontend-gateway-evidence.js +226 -0
  8. package/dist/application/evaluation/frontend-pair-registration.js +143 -0
  9. package/dist/application/evaluation/frontend-paired-summary.js +150 -0
  10. package/dist/application/evaluation/frontend-run-observation.js +144 -0
  11. package/dist/application/evaluation/frontend-shared-evidence.js +105 -0
  12. package/dist/application/evaluation/types.js +45 -12
  13. package/dist/application/task-lifecycle/observe.js +26 -1
  14. package/dist/build-stamp.json +3 -3
  15. package/dist/cli/command-definitions.js +11 -0
  16. package/dist/cli/program.js +4 -0
  17. package/dist/commands/task-advance.js +3 -5
  18. package/dist/commands/task-source-prepare.js +13 -1
  19. package/dist/executors/dag-pi/guards.js +4 -0
  20. package/dist/executors/dag-pi/model-config.js +30 -0
  21. package/dist/executors/dag-pi/plan/fact-adoption.js +55 -0
  22. package/dist/{workflows/dag/frontend-plan-completeness.js → executors/dag-pi/plan/facts.js} +22 -28
  23. package/dist/executors/dag-pi/plan/finalize-tools.js +259 -0
  24. package/dist/executors/dag-pi/plan/ledger-contract.js +55 -0
  25. package/dist/executors/dag-pi/plan/record-tools.js +861 -0
  26. package/dist/executors/dag-pi/plan/schema.js +129 -0
  27. package/dist/executors/dag-pi/plan/session.js +1 -0
  28. package/dist/executors/dag-pi/plan/tool-helpers.js +16 -0
  29. package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
  30. package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
  31. package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
  32. package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
  33. package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
  34. package/dist/executors/dag-pi-executor.js +398 -3384
  35. package/dist/executors/pi-executor.js +6 -1
  36. package/dist/executors/pi-sdk-executor.js +112 -19
  37. package/dist/executors/shell-executor.js +88 -12
  38. package/dist/executors/shell-presets.js +1 -1
  39. package/dist/executors/shell-write-guard.js +26 -3
  40. package/dist/infrastructure/console/operation-store.js +48 -14
  41. package/dist/infrastructure/evaluation/corpus-store.js +37 -12
  42. package/dist/shared/file-identity.js +23 -0
  43. package/dist/shared/git-workspace-status.js +482 -0
  44. package/dist/shared/operator/capabilities.js +10 -5
  45. package/dist/shared/path-safety.js +2 -2
  46. package/dist/shared/wizard-local-telemetry.js +51 -0
  47. package/dist/task/config-types.js +22 -8
  48. package/dist/task/contract/project.js +13 -0
  49. package/dist/task/contract/schema.js +2 -7
  50. package/dist/task/frontend-preflight.js +1 -1
  51. package/dist/task/source-prepare/parse-intent.js +124 -5
  52. package/dist/task/source-prepare/prepare.js +6 -1
  53. package/dist/task/source-prepare/semantic-intake.js +18 -3
  54. package/dist/task/source-prepare/source-execution.js +65 -0
  55. package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
  56. package/dist/task/source-prepare/source-provider-budget.js +290 -0
  57. package/dist/worker/console/chat/assistant-content.js +5 -34
  58. package/dist/worker/console/chat/chat-event-store.js +62 -10
  59. package/dist/worker/console/chat/context-insights.js +723 -0
  60. package/dist/worker/console/chat/conversation-nodes.js +383 -0
  61. package/dist/worker/console/chat/db-connections.js +854 -0
  62. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
  63. package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
  64. package/dist/worker/console/chat/pi-runtime.js +445 -36
  65. package/dist/worker/console/chat/provider-error.js +0 -12
  66. package/dist/worker/console/chat/repo-browser.js +52 -4
  67. package/dist/worker/console/chat/routes.js +577 -72
  68. package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
  69. package/dist/worker/console/chat/session-store.js +147 -19
  70. package/dist/worker/console/chat/shortcuts.js +7 -0
  71. package/dist/worker/console/chat/timeline-message-order.js +22 -0
  72. package/dist/worker/console/chat/tools.js +34 -0
  73. package/dist/worker/console/chat/turn-process.js +237 -66
  74. package/dist/worker/console/chat/user-turn-outline.js +19 -0
  75. package/dist/worker/console/console-update-runtime.js +2 -2
  76. package/dist/worker/console/dag-confirmation.js +3 -0
  77. package/dist/worker/console/dag-execution-receipt.js +3 -0
  78. package/dist/worker/console/operator-actions.js +32 -3
  79. package/dist/worker/console/operator-user-error.js +28 -1
  80. package/dist/worker/console/pi-readiness.js +4 -4
  81. package/dist/worker/console/prd-intake-bridge.js +5 -12
  82. package/dist/worker/console/recovery-cta.js +14 -14
  83. package/dist/worker/console/server.js +5 -1
  84. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
  85. package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
  86. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
  87. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
  88. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
  89. package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
  90. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
  91. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
  96. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
  100. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
  101. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
  102. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
  103. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
  104. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
  105. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
  106. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
  107. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
  108. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
  109. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
  110. package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
  111. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
  112. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
  113. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
  114. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
  115. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
  116. package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
  117. package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
  118. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
  119. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
  120. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
  121. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
  122. package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
  123. package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
  124. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
  125. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
  126. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
  127. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
  128. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
  129. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
  130. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
  131. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
  132. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
  133. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
  134. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
  135. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
  136. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
  137. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
  138. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
  139. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
  140. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
  141. package/dist/worker/console/static/index.html +2 -2
  142. package/dist/worker/console/static-src/app/console-types.js +9 -1
  143. package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
  144. package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
  145. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
  146. package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
  147. package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
  148. package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
  149. package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
  150. package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
  151. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
  152. package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
  153. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
  154. package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
  155. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
  156. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
  157. package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
  158. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
  159. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
  160. package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
  161. package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
  162. package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
  163. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
  164. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
  165. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
  166. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  167. package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
  168. package/dist/worker/console/workspace-context.js +2 -0
  169. package/dist/worker/console/workspace-initialization.js +3 -3
  170. package/dist/worker/observability/read-model.js +23 -2
  171. package/dist/worker/observe/node-context-insights.js +479 -0
  172. package/dist/worker/observe/static/console-theme.css +174 -0
  173. package/dist/worker/observe/static/console-theme.js +36 -0
  174. package/dist/worker/observe/static/operator-chrome.css +85 -0
  175. package/dist/worker/observe/static/operator-chrome.js +51 -0
  176. package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
  177. package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
  178. package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
  179. package/dist/workflows/dag/budget-enforcement.js +31 -2
  180. package/dist/workflows/dag/frontend-closeout.js +3 -1
  181. package/dist/workflows/dag/frontend-contract-facts.js +11 -8
  182. package/dist/workflows/dag/frontend-design-policy.js +6 -6
  183. package/dist/workflows/dag/frontend-durable-tools.js +90 -70
  184. package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
  185. package/dist/workflows/dag/frontend-plan-canary.js +66 -16
  186. package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
  187. package/dist/workflows/dag/frontend-plan-progress.js +92 -0
  188. package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
  189. package/dist/workflows/dag/frontend-recovery-run.js +51 -46
  190. package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
  191. package/dist/workflows/dag/frontend-review-context.js +29 -1
  192. package/dist/workflows/dag/frontend-review-scopes.js +92 -2
  193. package/dist/workflows/dag/frontend-risk.js +8 -3
  194. package/dist/workflows/dag/frontend-root-observation.js +261 -0
  195. package/dist/workflows/dag/frontend-session-budget.js +52 -39
  196. package/dist/workflows/dag/frontend-session-context.js +10 -0
  197. package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
  198. package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
  199. package/dist/workflows/dag/frontend-verification-trace.js +6 -0
  200. package/dist/workflows/dag/frontend-writer-admission.js +3 -1
  201. package/dist/workflows/dag/init-hybrid.js +108 -37
  202. package/dist/workflows/dag/node-execution.js +11 -11
  203. package/dist/workflows/dag/output-protocol.js +1 -0
  204. package/dist/workflows/dag/rerun-feedback.js +35 -4
  205. package/dist/workflows/dag/rerun-task.js +62 -66
  206. package/dist/workflows/dag/runner.js +58 -12
  207. package/dist/workflows/dag/types.js +15 -8
  208. package/dist/workflows/loop/benchmark.js +11 -8
  209. package/docs/README.md +1 -1
  210. package/docs/architecture/runtime-boundaries.md +1 -1
  211. package/docs/governance/harness-methodology-verification.md +11 -0
  212. package/docs/templates/backend-test-dag.json +5 -4
  213. package/docs/templates/frontend-implementation-contract.schema.json +31 -11
  214. package/package.json +6 -4
  215. package/skills/frontend-design-review/SKILL.md +8 -11
  216. package/skills/frontend-design-review/references/review-checklist.md +3 -4
  217. package/skills/frontend-review/SKILL.md +5 -1
  218. package/skills/loop-agent/references/command-reference.md +8 -0
  219. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  220. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
  221. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
  222. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
  223. package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
  224. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
  225. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
  226. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
@@ -1,3 +1,4 @@
1
+ import { sha256OfCanonicalJson } from "../../task/contract/hash.js";
1
2
  import { access, mkdir, readFile } from "node:fs/promises";
2
3
  import { createHash } from "node:crypto";
3
4
  import path from "node:path";
@@ -28,7 +29,7 @@ export function parseDagRerunTaskArgs(args) {
28
29
  for (let index = 0; index < args.length; index += 1) {
29
30
  const arg = args[index];
30
31
  if (arg === undefined)
31
- break;
32
+ throw new Error("dag rerun-task argument is missing");
32
33
  if (arg === "--run-id")
33
34
  parentRunId = args[++index];
34
35
  else if (arg.startsWith("--run-id="))
@@ -103,6 +104,9 @@ async function readFrontendRecoveryDescriptor(runDir, parentRunId, requestId) {
103
104
  plan.schemaVersion !== 1 ||
104
105
  plan.parentRunId !== parentRunId ||
105
106
  plan.requestId !== requestId ||
107
+ plan.failureSource !== raw.failureSource ||
108
+ plan.failureOwner !== raw.failureOwner ||
109
+ plan.resetRootNodeId !== raw.resetRootNodeId ||
106
110
  !Array.isArray(plan.resetNodeIds) ||
107
111
  !Array.isArray(plan.importedNodeIds)) {
108
112
  throw new Error("frontend recovery descriptor is invalid or unbound");
@@ -126,6 +130,8 @@ function buildFrontendRecoveryIntent(input) {
126
130
  attemptIndex: input.parentRecovery?.attemptIndex ?? 0,
127
131
  continuationCount: input.parentRecovery?.continuationCount ?? 0,
128
132
  failureSource: input.failureSource,
133
+ ...(input.failureOwner ? { failureOwner: input.failureOwner } : {}),
134
+ ...(input.resetRootNodeId ? { resetRootNodeId: input.resetRootNodeId } : {}),
129
135
  revision: 0,
130
136
  };
131
137
  }
@@ -391,6 +397,8 @@ async function executeFrontendRecoveryContinuation(input) {
391
397
  parentRunId: input.parentRunId,
392
398
  requestId: input.requestId,
393
399
  failureSource: descriptor.failureSource,
400
+ failureOwner: descriptor.failureOwner,
401
+ resetRootNodeId: descriptor.resetRootNodeId,
394
402
  parentRecovery: parentState.frontendRecoveryState,
395
403
  }),
396
404
  recoveryPlan: descriptor.plan,
@@ -404,75 +412,63 @@ async function executeFrontendRecoveryContinuation(input) {
404
412
  // these through its own durable tools (normal commit entry, child binding)
405
413
  // and the model only corrects the rows the design review flagged; the
406
414
  // parent ledger is never rewritten and the finalize terminal is not
407
- // inherited. Best-effort: a parent without plan facts plans fresh.
408
- try {
415
+ // inherited. Only an absent parent ledger permits a fresh Plan.
416
+ if (descriptor.plan.resetRootNodeId === "frontend-plan-pi") {
409
417
  const parentPlanLedger = path.join(located.runDir, "frontend-plan-pi", "plan-decision-facts.jsonl");
410
- const parentRaw = await readFile(parentPlanLedger, "utf8");
411
- const parentRecords = await readTypedEventStoreFromJsonl(parentPlanLedger);
412
- // Collapse superseded records first: the parent ledger is append-only
413
- // (replace:true appends a corrected row), and replaying both the original
414
- // and its correction would trip FACT_IDENTITY_CONFLICT in the child.
415
- const { collapseDecisionEntriesByKind } = await import("./frontend-plan-decision-contract.js");
416
- const byKind = new Map();
417
- for (const record of parentRecords) {
418
- const kind = String(record.fact.kind);
419
- if (record.phase !== "committed" || kind === "finalize_decision")
420
- continue;
421
- const entry = record.fact.entry;
422
- if (typeof entry !== "object" || entry === null || Array.isArray(entry))
423
- continue;
424
- const bucket = byKind.get(kind) ?? [];
425
- bucket.push(entry);
426
- byKind.set(kind, bucket);
427
- }
428
- let parentCanonicalSha256;
429
- try {
430
- const parentCanonical = await readFile(path.join(located.runDir, "contracts", "frontend-implementation-contract.json"), "utf8");
431
- parentCanonicalSha256 = createHash("sha256").update(parentCanonical).digest("hex");
432
- }
433
- catch {
434
- // Parent canonical unavailable → the identical-plan guard is skipped.
435
- }
436
- // Carry the design findings that blocked the parent run: the child's
437
- // finalize runs a per-finding closure check against them.
438
- let designFindings = [];
439
- try {
440
- const designFacts = await readTypedEventStoreFromJsonl(path.join(located.runDir, "frontend-design-review-pi", "design-typed-facts.jsonl"));
441
- for (const record of designFacts) {
442
- const fact = record.fact;
443
- if (record.phase !== "committed" || fact.kind !== "design-finding")
418
+ const parentRaw = await readFile(parentPlanLedger, "utf8").catch((error) => {
419
+ if (error.code === "ENOENT")
420
+ return undefined;
421
+ throw error;
422
+ });
423
+ if (parentRaw !== undefined) {
424
+ const parentRecords = await readTypedEventStoreFromJsonl(parentPlanLedger);
425
+ // Collapse superseded records first: the parent ledger is append-only
426
+ // (replace:true appends a corrected row), and replaying both the original
427
+ // and its correction would trip FACT_IDENTITY_CONFLICT in the child.
428
+ const { collapseDecisionEntriesByKind } = await import("./frontend-plan-decision-contract.js");
429
+ const byKind = new Map();
430
+ for (const record of parentRecords) {
431
+ const kind = String(record.fact.kind);
432
+ if (record.phase !== "committed" || kind === "finalize_decision")
444
433
  continue;
445
- const finding = (fact.finding ?? {});
446
- designFindings.push({
447
- severity: typeof finding.severity === "string" ? finding.severity : "Important",
448
- issue: typeof finding.issue === "string" ? finding.issue : "",
449
- requiredChange: typeof finding.requiredChange === "string" ? finding.requiredChange : "",
450
- });
434
+ const entry = record.fact.entry;
435
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
436
+ continue;
437
+ const bucket = byKind.get(kind) ?? [];
438
+ bucket.push(entry);
439
+ byKind.set(kind, bucket);
451
440
  }
441
+ let parentCanonicalSha256;
442
+ try {
443
+ const parentCanonical = await readFile(path.join(located.runDir, "contracts", "frontend-implementation-contract.json"), "utf8");
444
+ parentCanonicalSha256 = createHash("sha256").update(parentCanonical).digest("hex");
445
+ }
446
+ catch {
447
+ // Parent canonical unavailable → the identical-plan guard is skipped.
448
+ }
449
+ const { readCurrentFrontendRepairFindings } = await import("./rerun-feedback.js");
450
+ const feedback = await readCurrentFrontendRepairFindings(located.runDir);
451
+ const designFindings = feedback?.verdict === "request" ? feedback.findings : [];
452
+ const parentSpec = await readDagRunSpec(located.runDir);
453
+ const snapshot = {
454
+ schemaVersion: 1,
455
+ schemaId: "frontend-plan-parent-decision-snapshot-v1",
456
+ parentRunId: input.parentRunId,
457
+ parentLedgerSha256: createHash("sha256").update(parentRaw).digest("hex"),
458
+ sourceBindingSha256: sha256OfCanonicalJson(parentSpec.sourceBinding ?? null),
459
+ ...(feedback ? { feedbackBinding: { sourceNodeId: feedback.sourceNodeId, ledgerSha256: feedback.ledgerSha256, terminalEventId: feedback.terminalEventId } } : {}),
460
+ ...(parentCanonicalSha256 ? { parentCanonicalSha256 } : {}),
461
+ designFindings,
462
+ facts: [...byKind.entries()].flatMap(([kind, entries]) => collapseDecisionEntriesByKind(kind, entries).map((entry) => ({
463
+ kind,
464
+ entry,
465
+ }))),
466
+ };
467
+ await mkdir(path.join(locatedChild.runDir, "frontend-plan-pi"), {
468
+ recursive: true,
469
+ });
470
+ await writeJsonAtomic(path.join(locatedChild.runDir, "frontend-plan-pi", "parent-decision-snapshot.json"), snapshot);
452
471
  }
453
- catch {
454
- // No readable design findings → the closure check has no assertions.
455
- }
456
- const snapshot = {
457
- schemaVersion: 1,
458
- schemaId: "frontend-plan-parent-decision-snapshot-v1",
459
- parentRunId: input.parentRunId,
460
- parentLedgerSha256: createHash("sha256").update(parentRaw).digest("hex"),
461
- ...(parentCanonicalSha256 ? { parentCanonicalSha256 } : {}),
462
- designFindings,
463
- facts: [...byKind.entries()].flatMap(([kind, entries]) => collapseDecisionEntriesByKind(kind, entries).map((entry) => ({
464
- kind,
465
- entry,
466
- }))),
467
- };
468
- await mkdir(path.join(locatedChild.runDir, "frontend-plan-pi"), {
469
- recursive: true,
470
- });
471
- await writeJsonAtomic(path.join(locatedChild.runDir, "frontend-plan-pi", "parent-decision-snapshot.json"), snapshot);
472
- }
473
- catch {
474
- // No parent plan ledger (or unreadable) → the child plans fresh,
475
- // exactly like a run without recovery lineage.
476
472
  }
477
473
  const childSpec = await readDagRunSpec(locatedChild.runDir);
478
474
  const childState = await readDagRunState(locatedChild.runDir);
@@ -1,3 +1,7 @@
1
+ import { assertFrontendPairExecution } from "../../application/evaluation/frontend-pair-registration.js";
2
+ import { sourceBudgetContractIdentity } from "../../application/evaluation/budget.js";
3
+ import { summarizeFrozenSourceUsage } from "../../task/source-prepare/source-provider-budget.js";
4
+ import { sha256OfCanonicalJson } from "../../task/contract/hash.js";
1
5
  import { readdir, readFile } from "node:fs/promises";
2
6
  import { createHash } from "node:crypto";
3
7
  import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
@@ -244,6 +248,17 @@ export function createInitialRunState(spec, opts, ranks, runId = opts.runId ?? "
244
248
  : {}),
245
249
  };
246
250
  initRunBudgetLedger(state, spec.budget);
251
+ if (spec.sourceProviderBudget) {
252
+ if (sha256OfCanonicalJson(spec.sourceProviderBudget.contract ?? null) !== sha256OfCanonicalJson(sourceBudgetContractIdentity(spec.taskContractBinding) ?? null) || !state.budgetLedger || spec.sourceProviderBudget.ledger.taskId !== spec.sourceBinding?.taskId || spec.sourceProviderBudget.ledger.maxProviderRequests !== state.budgetLedger.limits.maxProviderRequests || spec.sourceProviderBudget.sourceBindingSha256 !== sha256OfCanonicalJson(spec.sourceBinding))
253
+ throw Error("FRONTEND_PROVIDER_BUDGET_INVALID: source snapshot does not match DAG");
254
+ state.sourceProviderBudget = structuredClone(spec.sourceProviderBudget);
255
+ state.budgetLedger.consumed.providerRequests = spec.sourceProviderBudget.ledger.reservedRequests;
256
+ const sourceUsage = summarizeFrozenSourceUsage(spec.sourceProviderBudget);
257
+ if (sourceUsage.complete)
258
+ state.budgetLedger.consumed.tokens = sourceUsage.totalTokens;
259
+ else if (spec.sourceProviderBudget.ledger.reservedRequests > 0)
260
+ state.budgetLedger.consumed.missingTokenNodeIds.push(`task-budget:${spec.sourceProviderBudget.ledger.budgetId}`);
261
+ }
247
262
  return state;
248
263
  }
249
264
  export function assertFrozenEvaluationBinding(spec, state) {
@@ -280,6 +295,12 @@ export async function runDag(spec, opts) {
280
295
  if (!runningIdentity) {
281
296
  throw new Error("running controller identity could not be resolved; refuse to create an unpinned DAG run");
282
297
  }
298
+ if (spec.evaluation?.frontendPair)
299
+ await assertFrontendPairExecution({
300
+ repoRoot: opts.cwd, binding: spec.evaluation,
301
+ controllerFingerprint: runningIdentity.packageFingerprint.value,
302
+ startedAt: new Date().toISOString(),
303
+ });
283
304
  assertRuntimeContractCompatible(spec, {
284
305
  ...DAG_CONTROLLER_CAPABILITIES,
285
306
  controllerVersion: runningIdentity.packageVersion,
@@ -453,6 +474,12 @@ export async function resumeDagRun(opts) {
453
474
  if (!runningIdentity) {
454
475
  throw new Error("running controller identity could not be resolved; refuse to resume an unpinned DAG run");
455
476
  }
477
+ if (spec.evaluation?.frontendPair)
478
+ await assertFrontendPairExecution({
479
+ repoRoot: opts.cwd, binding: spec.evaluation,
480
+ controllerFingerprint: runningIdentity.packageFingerprint.value,
481
+ startedAt: state.startedAt,
482
+ });
456
483
  assertRuntimeContractCompatible(spec, {
457
484
  ...DAG_CONTROLLER_CAPABILITIES,
458
485
  controllerVersion: runningIdentity.packageVersion,
@@ -602,8 +629,12 @@ async function executeDagCheckpoint(input) {
602
629
  };
603
630
  const onSigTerm = () => shutdownOnSignal("SIGTERM");
604
631
  const onSigInt = () => shutdownOnSignal("SIGINT");
605
- process.once("SIGTERM", onSigTerm);
606
- process.once("SIGINT", onSigInt);
632
+ // Keep listeners installed until persistence completes. Pi dependencies use
633
+ // signal-exit, which recounts listeners during signal dispatch and re-sends
634
+ // the signal if a once-listener has already removed itself. terminalSignal
635
+ // above makes repeated signals idempotent; normal cleanup removes listeners.
636
+ process.on("SIGTERM", onSigTerm);
637
+ process.on("SIGINT", onSigInt);
607
638
  // Exit-time diagnostics (silent-death forensics): write an `armed` line at
608
639
  // checkpoint start and append process-exit / uncaught-exception /
609
640
  // unhandled-rejection lines with a live state snapshot so an abnormal death
@@ -862,7 +893,12 @@ async function executeDagCheckpoint(input) {
862
893
  trigger: recoveryTrigger,
863
894
  deps: input.recovery,
864
895
  });
865
- if (created.kind === "conflict") {
896
+ if (created.kind === "blocked") {
897
+ state.status = "failed";
898
+ state.terminalReason = created.reason;
899
+ state.frontendRecoveryResult = buildRecoveryResultForTrigger(state, recoveryTrigger, "auto-recovery-blocked");
900
+ }
901
+ else if (created.kind === "conflict") {
866
902
  // Same clientRequestId with a different payload → fail closed; never
867
903
  // create a second recovery operation (AC-004).
868
904
  state.status = "failed";
@@ -1276,7 +1312,7 @@ export async function finalizeTerminalRunStatus(state, taskCount, runDir, cwd, f
1276
1312
  // The rejected design is a plan input defect. Reset from the
1277
1313
  // planner so the next child can incorporate the committed
1278
1314
  // findings before design review runs again.
1279
- failureSource: "frontend-plan-pi",
1315
+ failureSource: "frontend-design-review-pi",
1280
1316
  failureOwner: classifyReviewIssueCategoryFailureOwner(designRequestFact.issueCategory),
1281
1317
  protocolFailureReason: `frontend design review request_design_changes (issueCategory=${designRequestFact.issueCategory})`,
1282
1318
  recoveryMode: "new-dag",
@@ -1477,6 +1513,20 @@ export async function finalizeTerminalRunStatus(state, taskCount, runDir, cwd, f
1477
1513
  */
1478
1514
  async function createRecoveryOperation(input) {
1479
1515
  const { cwd, runDir, spec, state, trigger } = input;
1516
+ const { computeFrontendRecoveryPlanForSource } = await import("./frontend-recovery-plan.js");
1517
+ let plan;
1518
+ try {
1519
+ plan = computeFrontendRecoveryPlanForSource({
1520
+ spec,
1521
+ parentRunId: state.runId,
1522
+ requestId: trigger.requestId,
1523
+ failureSource: trigger.failureSource,
1524
+ failureOwner: trigger.failureOwner,
1525
+ });
1526
+ }
1527
+ catch (error) {
1528
+ return { kind: "blocked", reason: error instanceof Error ? error.message : String(error) };
1529
+ }
1480
1530
  const recoveryLease = await acquireDagRecoveryLease({
1481
1531
  cwd,
1482
1532
  parentRunId: state.runId,
@@ -1502,13 +1552,6 @@ async function createRecoveryOperation(input) {
1502
1552
  observeOnly = true;
1503
1553
  }
1504
1554
  }
1505
- const { computeFrontendRecoveryPlanForSource } = await import("./frontend-recovery-plan.js");
1506
- const plan = computeFrontendRecoveryPlanForSource({
1507
- spec,
1508
- parentRunId: state.runId,
1509
- requestId: trigger.requestId,
1510
- failureSource: trigger.failureSource,
1511
- });
1512
1555
  const planHash = createHash("sha256")
1513
1556
  .update(JSON.stringify(plan))
1514
1557
  .digest("hex");
@@ -1518,6 +1561,8 @@ async function createRecoveryOperation(input) {
1518
1561
  parentRunId: state.runId,
1519
1562
  requestId: trigger.requestId,
1520
1563
  failureSource: trigger.failureSource,
1564
+ failureOwner: plan.failureOwner,
1565
+ resetRootNodeId: plan.resetRootNodeId,
1521
1566
  recoveryMode: trigger.recoveryMode,
1522
1567
  planHash,
1523
1568
  plan,
@@ -1529,7 +1574,8 @@ async function createRecoveryOperation(input) {
1529
1574
  parentRunId: state.runId,
1530
1575
  requestId: trigger.requestId,
1531
1576
  failureSource: trigger.failureSource,
1532
- failureOwner: trigger.failureOwner,
1577
+ failureOwner: plan.failureOwner,
1578
+ resetRootNodeId: plan.resetRootNodeId,
1533
1579
  protocolFailureReason: trigger.protocolFailureReason,
1534
1580
  recoveryMode: trigger.recoveryMode,
1535
1581
  planHash,
@@ -1,7 +1,12 @@
1
+ import { frontendPairExecutionBindingSchema } from "../../application/evaluation/frontend-pair-registration.js";
2
+ import { sourceBudgetContractIdentity } from "../../application/evaluation/budget.js";
3
+ import { sha256OfCanonicalJson } from "../../task/contract/hash.js";
4
+ import { frontendTestObservationSchema } from "./frontend-test-execution-evidence.js";
5
+ import { capabilityBoundarySchema } from "../../task/config-types.js";
1
6
  import { z } from "zod";
2
7
  import { frontendExecutionPolicySchema } from "../../shared/frontend-execution-policy.js";
3
8
  import { DEFAULT_FRONTEND_SPEC_ROOTS, isFrontendSpecFilePath, isValidFrontendSpecRoot, OPENSPEC_SPEC_EXT_RE, } from "../../shared/openspec-spec.js";
4
- import { campaignBudgetSchema, } from "../../application/evaluation/budget.js";
9
+ import { campaignBudgetSchema, sourceProviderBudgetBindingSchema, } from "../../application/evaluation/budget.js";
5
10
  import { assertDagPromptSourceRule } from "./prompt-source.js";
6
11
  // Leaf-module import: dagRetryPolicySchema must be defined by a module that
7
12
  // never transitively reaches types.ts during init (retry-policy does, via
@@ -89,6 +94,7 @@ export const dagShellVerifyEvidenceSchema = z.object({
89
94
  .array(z.object({
90
95
  label: z.string(),
91
96
  status: z.enum(["ok", "future-delivery-script", "future-test-target"]),
97
+ testObservation: frontendTestObservationSchema.optional(),
92
98
  }))
93
99
  .default([]),
94
100
  /** Effective timeout applied independently to each shell command. */
@@ -463,13 +469,7 @@ export const dagFrontendDesignPolicySchema = z
463
469
  .optional(),
464
470
  /** Generation-frozen test capability boundary composed into the
465
471
  * canonical dependencyPolicy by the runtime. */
466
- capabilityBoundary: z
467
- .object({
468
- allowed: z.array(z.string().min(1)).optional(),
469
- forbidden: z.array(z.string().min(1)).optional(),
470
- })
471
- .strict()
472
- .optional(),
472
+ capabilityBoundary: capabilityBoundarySchema.optional(),
473
473
  /** Generation-frozen interaction id vocabulary (plan finalize check). */
474
474
  interactionIds: z.array(z.string().min(1)).optional(),
475
475
  /** Generation-frozen roots that bound custom normative directories. */
@@ -1320,6 +1320,7 @@ export const dagEvaluationBindingSchema = z
1320
1320
  seed: z.number().int().nonnegative(),
1321
1321
  split: z.enum(["public", "private", "held_out"]).optional(),
1322
1322
  taskRef: z.string().min(1).optional(),
1323
+ frontendPair: frontendPairExecutionBindingSchema.optional(),
1323
1324
  })
1324
1325
  .strict();
1325
1326
  /** Eval Lab Campaign Budget (W3.4); enforced by runner ledger when mode=hard. */
@@ -1424,6 +1425,7 @@ export const dagSpecSchema = z
1424
1425
  evaluation: dagEvaluationBindingSchema.optional(),
1425
1426
  /** Optional hard/record-only budget; requires version 3 or 4. */
1426
1427
  budget: dagBudgetSchema.optional(),
1428
+ sourceProviderBudget: sourceProviderBudgetBindingSchema.optional(),
1427
1429
  frontendExecutionPolicy: frontendExecutionPolicySchema.optional(),
1428
1430
  sourceBinding: dagSourceBindingSchema.optional(),
1429
1431
  /** v4 managed Task Contract binding; only valid on version 4. */
@@ -1494,6 +1496,9 @@ export const dagSpecSchema = z
1494
1496
  tasks: z.array(dagTaskSchema).min(1),
1495
1497
  })
1496
1498
  .superRefine((spec, ctx) => {
1499
+ if (spec.sourceProviderBudget && (!spec.sourceBinding || spec.sourceProviderBudget.ledger.taskId !== spec.sourceBinding.taskId || sha256OfCanonicalJson(spec.sourceProviderBudget.contract ?? null) !== sha256OfCanonicalJson(sourceBudgetContractIdentity(spec.taskContractBinding) ?? null) || spec.budget?.mode !== "hard" || spec.sourceProviderBudget.ledger.maxProviderRequests !== spec.budget?.limits.maxProviderRequests || spec.sourceProviderBudget.sourceBindingSha256 !== sha256OfCanonicalJson(spec.sourceBinding))) {
1500
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["sourceProviderBudget"], message: "source request budget must match the frozen source and request limit" });
1501
+ }
1497
1502
  const supportsV3Fields = spec.version === 3 || spec.version === 4;
1498
1503
  validateFrozenArtifactBindings(spec.tasks, ctx);
1499
1504
  if (spec.evaluation && !supportsV3Fields) {
@@ -1618,6 +1623,8 @@ export const frontendRecoveryStateSchema = z
1618
1623
  * frontend-plan-revision-pi / frontend-prewrite-gate-shell /
1619
1624
  * frontend-implement-pi (writer partial write). */
1620
1625
  failureSource: z.string().min(1).optional(),
1626
+ failureOwner: z.enum(["contract", "scout", "plan", "implementation", "verification-config", "environment", "scope", "unknown"]).optional(),
1627
+ resetRootNodeId: z.string().min(1).optional(),
1621
1628
  revision: z.number().int().nonnegative(),
1622
1629
  })
1623
1630
  .strict()
@@ -40,7 +40,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
40
40
  regressionPause: false,
41
41
  changedPaths: 2,
42
42
  repairPasses: 1,
43
- }, ["./test/dag-runner.test.ts"], "Single repair exhausts before the third successful hard verification."),
43
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Single repair exhausts before the third successful hard verification."),
44
44
  result("3-pass-convergence", {
45
45
  pass: true,
46
46
  fullVerifyPass: true,
@@ -51,7 +51,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
51
51
  regressionPause: false,
52
52
  changedPaths: 2,
53
53
  repairPasses: 3,
54
- }, ["./test/dag-runner.test.ts"], "Convergence re-enters supervisor, repair, and hard verify until pass 3."),
54
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Convergence re-enters supervisor, repair, and hard verify until pass 3."),
55
55
  result("3-pass-convergence+quota", {
56
56
  pass: true,
57
57
  fullVerifyPass: true,
@@ -64,6 +64,9 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
64
64
  repairPasses: 3,
65
65
  }, [
66
66
  "./test/dag-runner.test.ts",
67
+ "./test/dag-runner-convergence.test.ts",
68
+ "./test/dag-runner-artifacts.test.ts",
69
+ "./test/dag-runner-decisions.test.ts",
67
70
  "./test/verify-step.test.ts",
68
71
  ], "Intermediate verification uses quota while final hard gate remains full."),
69
72
  ]),
@@ -78,7 +81,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
78
81
  regressionPause: false,
79
82
  changedPaths: 3,
80
83
  repairPasses: 1,
81
- }, ["./test/dag-runner.test.ts"], "Single repair ends failed without enough evidence to classify regression."),
84
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Single repair ends failed without enough evidence to classify regression."),
82
85
  result("3-pass-convergence", {
83
86
  pass: false,
84
87
  fullVerifyPass: false,
@@ -89,7 +92,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
89
92
  regressionPause: true,
90
93
  changedPaths: 3,
91
94
  repairPasses: 2,
92
- }, ["./test/dag-runner.test.ts"], "Convergence pauses instead of retrying blindly after regression signal."),
95
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Convergence pauses instead of retrying blindly after regression signal."),
93
96
  result("3-pass-convergence+quota", {
94
97
  pass: false,
95
98
  fullVerifyPass: false,
@@ -100,7 +103,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
100
103
  regressionPause: true,
101
104
  changedPaths: 3,
102
105
  repairPasses: 2,
103
- }, ["./test/dag-runner.test.ts"], "Quota reduces intermediate cost but still pauses on regression."),
106
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Quota reduces intermediate cost but still pauses on regression."),
104
107
  ]),
105
108
  sample("LB-03", "write guard hard failure must not retry", "dag-runtime", [
106
109
  result("single-repair", {
@@ -113,7 +116,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
113
116
  regressionPause: false,
114
117
  changedPaths: 1,
115
118
  repairPasses: 1,
116
- }, ["./test/dag-runner.test.ts"], "Write-guard failure remains terminal."),
119
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Write-guard failure remains terminal."),
117
120
  result("3-pass-convergence", {
118
121
  pass: false,
119
122
  fullVerifyPass: false,
@@ -124,7 +127,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
124
127
  regressionPause: false,
125
128
  changedPaths: 1,
126
129
  repairPasses: 1,
127
- }, ["./test/dag-runner.test.ts"], "Non-retry class is not re-entered."),
130
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Non-retry class is not re-entered."),
128
131
  result("3-pass-convergence+quota", {
129
132
  pass: false,
130
133
  fullVerifyPass: false,
@@ -135,7 +138,7 @@ export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
135
138
  regressionPause: false,
136
139
  changedPaths: 1,
137
140
  repairPasses: 1,
138
- }, ["./test/dag-runner.test.ts"], "Quota strategy does not change write-guard retry policy."),
141
+ }, ["./test/dag-runner.test.ts", "./test/dag-runner-convergence.test.ts", "./test/dag-runner-artifacts.test.ts", "./test/dag-runner-decisions.test.ts"], "Quota strategy does not change write-guard retry policy."),
139
142
  ]),
140
143
  sample("LB-04", "quota final gate must stay full", "loop-runtime", [
141
144
  result("single-repair", {
package/docs/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `docs/` 是 loop-agent 源仓的治理文档根目录。新读者不需要先浏览所有文件,先按任务选择入口,再由目录 README 或 `docs/document-catalog.json` 判断文档身份。
4
4
 
5
- **维护日:2026-08-16** — 对齐 `@tea-agent/loop-agent@0.35.3`。版本事实以根 `CHANGELOG.md` 与 `package.json` 为准;能力短摘要见 `reports/current-capability-summary.md`。
5
+ **维护日:2026-09-16** — 对齐 `@tea-agent/loop-agent@0.41.1-beta.0`。版本事实以根 `CHANGELOG.md` 与 `package.json` 为准;能力短摘要见 `reports/current-capability-summary.md`。
6
6
 
7
7
  ## 我现在要做什么?
8
8
 
@@ -180,7 +180,7 @@ Runner / Loop ──(迁移中)──> 逐步改为仅经 Store / Appli
180
180
  | Script | 检查内容 | 失败条件 |
181
181
  | -------- | ---------- | ---------- |
182
182
  | `scripts/check-architecture-boundaries.sh` | workflow/executor forbidden import,及 Worker → CLI/commands/application import | 新的未 allowlist violation |
183
- | `scripts/check-command-registry-drift.sh` | `command-reference.md` 中的 top-level command vs `src/cli/catalog.ts` | 文档引用未注册 command |
183
+ | `scripts/check-command-registry-drift.sh` | 五处真源的 top-level command 一致性:`src/cli/command-definitions.ts`(权威)、`src/shared/operator/capabilities.ts` 覆盖表、`skills/loop-agent/references/command-reference.md`、`website/docs/reference/cli.md`、`test/cli-contract.test.ts` 的 `LEGACY_SUPPORTED_COMMANDS`(逐项且同序) | 任一真源引用未注册 command、未覆盖已注册 command,或契约测试清单与 registry 不一致 |
184
184
  | `scripts/check-exec-plan-index-sync.sh` | `ai_workspace/loop-agent/exec-plans/{active,completed}` 目录文件 vs 对应 `README.md` 索引 | 任一 plan 文件未在索引登记 |
185
185
  | `scripts/check-skill-entry.sh` | `loop-agent` / `agent-worker` 的 frontmatter、required references、入口行数与 operator trigger vocabulary | 任一公共 skill 缺失、reference/trigger 漂移或入口超过 hard limit |
186
186
 
@@ -19,9 +19,20 @@
19
19
  | 行为有覆盖 | `npm test` |
20
20
  | 完整本地交付有效 | `bash scripts/ci.sh` |
21
21
 
22
+ ## 代理与拦截层:校验状态码分布,不是请求条数
23
+
24
+ 当验证经过中间层(网关 shim、mock server、反向代理、录制层)时,「请求到了中间层」不等于「中间层在起作用」:
25
+
26
+ - 只看条数会把 **404/5xx 也算成命中**。真实的失败模式是「流量根本没经过」或「经过了但立刻被拒」,两者都产出请求记录。
27
+ - 必须核对 **状态码分布**(如 `200×99` 而非 `200×1 + 404×42`),以及流量是否落在**目标时间窗口**内。
28
+ - 用一条已知成功的直接调用(curl)建立参照,并确认它的路径形态与被测链路的路径形态**一致**——baseUrl 拼接差异(如 `/v1` 重复)会造成静默 404,而外层 runner 可能仍产出看似完整的产物。
29
+ - 尤其警惕「未触发阈值」这类结论:它既可能是真的余量充足,也可能是**根本没走到阈值判断**。两者的证据形态完全不同,不能只凭「无拦截记录」下结论。
30
+
22
31
  ## Red Flags
23
32
 
24
33
  - 凭意图声明完成
25
34
  - 依赖陈旧命令输出
26
35
  - 用窄检查支撑宽声明
27
36
  - 跳过失败命令的细节
37
+ - 用计数(请求数/命中数)代替状态码与时间窗口证据
38
+ - 把「没有触发拦截」直接读成「余量充足」,未确认流量确实经过了拦截点