@tea-agent/loop-agent 0.43.0-next.9 → 0.43.0

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 (287) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +31 -2309
  3. package/README.md +7 -0
  4. package/dist/application/dag/args.js +8 -0
  5. package/dist/application/task-lifecycle/advance.js +38 -7
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/cli/command-definitions.js +2 -0
  8. package/dist/cli/program.js +3 -1
  9. package/dist/cli/update/init-surface-notifier.js +2 -0
  10. package/dist/cli/update/policy.js +2 -0
  11. package/dist/commands/client-recovery.js +20 -55
  12. package/dist/commands/cursor-prompt.js +2 -0
  13. package/dist/commands/dag-approve.js +2 -0
  14. package/dist/commands/dag-final-verification.js +2 -0
  15. package/dist/commands/dag-init-hybrid.js +2 -0
  16. package/dist/commands/dag-reconcile-tasks.js +2 -0
  17. package/dist/commands/dag-reject.js +2 -0
  18. package/dist/commands/dag-report.js +2 -0
  19. package/dist/commands/dag-rerun.js +2 -0
  20. package/dist/commands/dag-resume.js +2 -0
  21. package/dist/commands/dag-workflow-compile.js +2 -0
  22. package/dist/commands/dag-workflow-plan.js +2 -0
  23. package/dist/commands/dag-workflow-validate.js +2 -0
  24. package/dist/commands/delegate.js +2 -0
  25. package/dist/commands/examples.js +2 -0
  26. package/dist/commands/import-prd.js +2 -0
  27. package/dist/commands/init-model-catalog.js +2 -0
  28. package/dist/commands/init.js +19 -13
  29. package/dist/commands/instructions.js +2 -0
  30. package/dist/commands/knowledge.js +2 -0
  31. package/dist/commands/loop-benchmark.js +2 -0
  32. package/dist/commands/pi-prompt.js +2 -0
  33. package/dist/commands/pi-reuse-benchmark.js +2 -0
  34. package/dist/commands/promote-run.js +2 -0
  35. package/dist/commands/stats.js +1 -1
  36. package/dist/commands/study-init.js +2 -0
  37. package/dist/commands/task-advance.js +34 -0
  38. package/dist/commands/task-source-prepare.js +2 -0
  39. package/dist/commands/worktree-create.js +2 -0
  40. package/dist/commands/worktree-remove.js +2 -0
  41. package/dist/executors/dag-pi-executor.js +1650 -198
  42. package/dist/executors/pi-executor.js +85 -3
  43. package/dist/executors/pi-sdk-executor.js +18 -0
  44. package/dist/executors/shell-executor.js +93 -7
  45. package/dist/executors/shell-verification.js +3 -0
  46. package/dist/executors/shell-write-guard.js +27 -32
  47. package/dist/governance/exec-plans.js +6 -3
  48. package/dist/infrastructure/console/app-data.js +6 -0
  49. package/dist/shared/artifacts-core.js +2 -0
  50. package/dist/shared/backend-dogfood-preflight.js +47 -0
  51. package/dist/shared/dag-failure-category.js +3 -0
  52. package/dist/shared/git-progress.js +2 -0
  53. package/dist/shared/one-shot-prompt-args.js +2 -0
  54. package/dist/shared/operator/capabilities.js +180 -0
  55. package/dist/shared/package-metadata.js +6 -4
  56. package/dist/shared/pi-context-pressure/extension.js +26 -16
  57. package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
  58. package/dist/shared/pi-provider-recovery.js +182 -0
  59. package/dist/shared/pi-retry-settings.js +40 -0
  60. package/dist/shared/reference-context.js +2 -0
  61. package/dist/task/config-types.js +29 -0
  62. package/dist/task/contract/project.js +9 -0
  63. package/dist/task/contract/recover.js +6 -0
  64. package/dist/task/contract/schema.js +17 -0
  65. package/dist/task/source-prepare/artifact-meta.js +9 -2
  66. package/dist/task/source-prepare/build-draft.js +60 -0
  67. package/dist/task/source-prepare/fragment-inventory.js +4 -1
  68. package/dist/task/source-prepare/parse-intent.js +36 -11
  69. package/dist/task/task-demand-routing.js +5 -2
  70. package/dist/worker/console/chat/browser-automation.js +523 -0
  71. package/dist/worker/console/chat/browser-policy.js +6 -36
  72. package/dist/worker/console/chat/browser-routes.js +188 -56
  73. package/dist/worker/console/chat/explore-tools.js +5 -2
  74. package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
  75. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
  76. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +184 -0
  77. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
  78. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
  79. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +94 -0
  80. package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
  81. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
  82. package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
  83. package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
  84. package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
  85. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
  86. package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
  87. package/dist/worker/console/chat/pi-runtime.js +208 -1138
  88. package/dist/worker/console/chat/routes.js +27 -7
  89. package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
  90. package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
  91. package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
  92. package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
  93. package/dist/worker/console/chat/session-mode-view.js +5 -4
  94. package/dist/worker/console/chat/session-mode.js +18 -12
  95. package/dist/worker/console/chat/session-store.js +42 -9
  96. package/dist/worker/console/chat/tool-preview.js +115 -0
  97. package/dist/worker/console/chat/tools.js +10 -0
  98. package/dist/worker/console/chat/turn-order.js +13 -0
  99. package/dist/worker/console/chat/turn-process.js +32 -30
  100. package/dist/worker/console/console-update-and-init.js +1 -19
  101. package/dist/worker/console/console-update-runtime.js +5 -20
  102. package/dist/worker/console/operator-actions.js +63 -1
  103. package/dist/worker/console/prd-intake-bridge.js +54 -1
  104. package/dist/worker/console/routes.js +8 -0
  105. package/dist/worker/console/scheduled-goal-host.js +98 -0
  106. package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
  107. package/dist/worker/console/server.js +24 -1
  108. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D_fNbWc_.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
  109. package/dist/worker/console/static/assets/{arc-_HuqLW6m.js → arc-eoQiMvuk.js} +1 -1
  110. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-Djy2qY1m.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
  111. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-bB-1TSyY.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
  112. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-C5Hmghrz.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
  113. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
  114. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Duyl39oy.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
  115. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-x6mdnSyf.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
  116. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-WlBuFj7W.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
  117. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CTF429HX.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
  118. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Dwd6r-SF.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
  119. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C-Br82D1.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
  120. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CuGp0DeE.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
  121. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CJhFX6Lh.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
  122. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DpGOa_jT.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
  123. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DXJaTynd.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
  124. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
  125. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
  126. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CRBZWjE4.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
  127. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BdXcxtlb.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
  128. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-07UKsOts.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
  129. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-D38p9__S.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
  130. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-nSiBNNjH.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
  131. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BFOTot5D.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
  132. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DypKaZ6k.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
  133. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-B6yoOQeW.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
  134. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BHYS6viX.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
  135. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-D2VD5Snj.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
  136. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-E98mWxN1.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
  137. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CT1zxhqP.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
  138. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-6GcngNcy.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
  139. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DCL6NvKe.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
  140. package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
  141. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +469 -0
  142. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C0627e-9.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
  143. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C_u3P6Ik.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
  144. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DPDPv9HL.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
  145. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-D3OHdWiO.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
  146. package/dist/worker/console/static/assets/{linear-DI88zD7n.js → linear-BDZ9riWi.js} +1 -1
  147. package/dist/worker/console/static/assets/{mermaid.core-p2l8IrWY.js → mermaid.core-7pKqYtpZ.js} +5 -5
  148. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-B1bAX-oS.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
  149. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DwOnWxdD.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
  150. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-xRkTXnJC.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
  151. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-_9UC5Zio.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
  152. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CgEUirWJ.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
  153. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Czbpwiyz.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
  154. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-_j7_KiqY.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
  155. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BNJa6Hyi.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
  156. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DedpKtOc.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
  157. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BBvthr7c.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
  158. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
  159. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Ds7umgEM.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
  160. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
  161. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKa-p1nF.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
  162. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-LoUNR56o.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
  163. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bqbl6ym7.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
  164. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B8dUg4lf.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
  165. package/dist/worker/console/static/index.html +2 -2
  166. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
  167. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
  168. package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
  169. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
  170. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
  171. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
  172. package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
  173. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +92 -38
  174. package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
  175. package/dist/worker/console/static-src/operator-chat/useChatThread.js +38 -55
  176. package/dist/worker/console/static-src/operator-chat/useComposer.js +182 -69
  177. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
  178. package/dist/worker/console/static-src/prd-file-import.js +3 -1
  179. package/dist/worker/console/workspace-context.js +22 -0
  180. package/dist/worker/console/workspace-initialization.js +209 -0
  181. package/dist/worker/delivery/git-transaction.js +2 -0
  182. package/dist/worker/feature/fullstack-validate.js +2 -0
  183. package/dist/worker/loop-agent/loop-agent-client.js +8 -2
  184. package/dist/worker/materialize/harness-task-materializer.js +4 -1
  185. package/dist/worker/metrics/projector.js +1 -1
  186. package/dist/worker/observability/event-store.js +4 -1
  187. package/dist/worker/observability/read-model.js +4 -1
  188. package/dist/worker/observe/node-transparency.js +23 -1
  189. package/dist/worker/observe/routes.js +4 -0
  190. package/dist/worker/observe/static/operator-chrome.js +3 -1
  191. package/dist/worker/observe/static/styles.css +7 -4
  192. package/dist/worker/observe/static/views/dag-inspector.js +5 -19
  193. package/dist/worker/outcomes/declared-artifacts.js +2 -0
  194. package/dist/worker/preflight.js +3 -0
  195. package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
  196. package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
  197. package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
  198. package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
  199. package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
  200. package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
  201. package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
  202. package/dist/worker/task-spec/validate.js +4 -1
  203. package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
  204. package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
  205. package/dist/workflows/dag/dag-retry-schema.js +11 -0
  206. package/dist/workflows/dag/decision-envelope.js +2 -0
  207. package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
  208. package/dist/workflows/dag/frontend-closeout.js +3 -1
  209. package/dist/workflows/dag/frontend-committed-facts.js +461 -0
  210. package/dist/workflows/dag/frontend-durable-tools.js +15 -3
  211. package/dist/workflows/dag/frontend-implementation-contract.js +369 -12
  212. package/dist/workflows/dag/frontend-plan-canary.js +53 -0
  213. package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
  214. package/dist/workflows/dag/frontend-plan-render.js +0 -2
  215. package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
  216. package/dist/workflows/dag/frontend-recovery-run.js +57 -0
  217. package/dist/workflows/dag/frontend-repair.js +55 -76
  218. package/dist/workflows/dag/frontend-review-context.js +42 -68
  219. package/dist/workflows/dag/frontend-review-scopes.js +2 -2
  220. package/dist/workflows/dag/frontend-risk.js +92 -10
  221. package/dist/workflows/dag/frontend-session-budget.js +117 -3
  222. package/dist/workflows/dag/frontend-shape.js +11 -55
  223. package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
  224. package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
  225. package/dist/workflows/dag/frontend-verification-trace.js +33 -54
  226. package/dist/workflows/dag/frontend-writer-admission.js +3 -47
  227. package/dist/workflows/dag/frontend-writer-status.js +0 -23
  228. package/dist/workflows/dag/init-hybrid.js +47 -19
  229. package/dist/workflows/dag/interrupt-request.js +2 -0
  230. package/dist/workflows/dag/lifecycle.js +11 -2
  231. package/dist/workflows/dag/node-execution.js +50 -13
  232. package/dist/workflows/dag/reconcile-run.js +2 -0
  233. package/dist/workflows/dag/repair-artifact.js +3 -1
  234. package/dist/workflows/dag/report.js +2 -0
  235. package/dist/workflows/dag/rerun-plan.js +10 -0
  236. package/dist/workflows/dag/rerun-task.js +79 -1
  237. package/dist/workflows/dag/retry-policy.js +18 -0
  238. package/dist/workflows/dag/scheduler.js +2 -4
  239. package/dist/workflows/dag/types.js +44 -6
  240. package/dist/workflows/dag/validate.js +4 -0
  241. package/docs/README.md +1 -0
  242. package/docs/architecture/runtime-boundaries.md +3 -3
  243. package/docs/governance/README.md +1 -0
  244. package/docs/init-surface.manifest.json +1 -0
  245. package/docs/operations/README.md +2 -0
  246. package/docs/templates/README.md +1 -1
  247. package/docs/templates/agent-dag.schema.json +2 -2
  248. package/docs/templates/backend-test-dag.json +14 -10
  249. package/docs/templates/frontend-implementation-contract.schema.json +0 -7
  250. package/package.json +9 -3
  251. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  252. package/skills/frontend-contract/references/contract-protocol.md +3 -1
  253. package/skills/frontend-plan/SKILL.md +8 -6
  254. package/skills/frontend-plan/references/decision-contract.md +3 -3
  255. package/skills/frontend-review/SKILL.md +18 -28
  256. package/skills/frontend-review/references/review-findings.md +8 -3
  257. package/dist/commands/new-task.js +0 -5
  258. package/dist/commands/status.js +0 -57
  259. package/dist/commands/task-contract.js +0 -269
  260. package/dist/shared/prompts.js +0 -26
  261. package/dist/task/config.js +0 -2
  262. package/dist/task/contract/validate-draft.js +0 -106
  263. package/dist/task/goal.js +0 -3
  264. package/dist/task/index.js +0 -12
  265. package/dist/task/lifecycle.js +0 -1
  266. package/dist/task/operator/capabilities.js +0 -6
  267. package/dist/task/operator/envelope.js +0 -2
  268. package/dist/task/operator/index.js +0 -5
  269. package/dist/task/operator/registry.js +0 -2
  270. package/dist/task/operator/types.js +0 -1
  271. package/dist/task/paths.js +0 -1
  272. package/dist/task/source-state.js +0 -1
  273. package/dist/task/subagent-guidance.js +0 -1
  274. package/dist/worker/console/chat/sift-bridge.js +0 -1
  275. package/dist/worker/console/index.js +0 -20
  276. package/dist/worker/console/static/assets/channel-C0A6UEIa.js +0 -1
  277. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DzguNppy.js +0 -1
  278. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DzguNppy.js +0 -1
  279. package/dist/worker/console/static/assets/index-24jp3aBh.js +0 -468
  280. package/dist/worker/console/static/assets/index-CY6y6I8M.css +0 -1
  281. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BABVQnfM.js +0 -1
  282. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D0qsLaxR.js +0 -8
  283. package/dist/worker/scheduler/index.js +0 -25
  284. package/dist/workflows/dag/facts.js +0 -4
  285. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
  286. package/dist/workflows/dag/index.js +0 -6
  287. package/dist/workflows/dynamic/index.js +0 -7
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { mkdir, rename, rm, writeFile } from "node:fs/promises";
2
+ import { mkdir, readdir, rename, rm, readFile, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { readFrontendToolProgress } from "./frontend-durable-tools.js";
5
5
  import { frontendModelCapabilities } from "../../shared/frontend-execution-policy.js";
@@ -229,7 +229,7 @@ export async function observeFrontendSession(input, execute) {
229
229
  await waitForWrite(save());
230
230
  return result;
231
231
  }
232
- export function summarizeFrontendSessions(receipts) {
232
+ function uniqueFrontendSessions(receipts) {
233
233
  const byId = new Map();
234
234
  for (const receipt of receipts) {
235
235
  const previous = byId.get(receipt.identity.sessionId);
@@ -240,10 +240,124 @@ export function summarizeFrontendSessions(receipts) {
240
240
  if (!previous || difference > 0)
241
241
  byId.set(receipt.identity.sessionId, receipt);
242
242
  }
243
- const unique = [...byId.values()];
243
+ return [...byId.values()];
244
+ }
245
+ export function summarizeFrontendSessions(receipts) {
246
+ const unique = uniqueFrontendSessions(receipts);
244
247
  return { sessions: unique.length, failedSessions: unique.filter(r => r.terminal?.ok === false).length, incompleteSessions: unique.filter(r => r.completion === "incomplete").length,
245
248
  failureLayers: Object.fromEntries([...new Set(unique.flatMap(r => r.terminal && r.terminal.failureLayer !== "none" ? [r.terminal.failureLayer] : []))].sort().map(layer => [layer, unique.filter(r => r.terminal?.failureLayer === layer).length])),
246
249
  progress: Object.fromEntries(["durableCommittedDelta", "durableSubmissions", "durableReplacements", "failedRecords", "duplicateRecords", "successfulReadBytes", "duplicateReadBytes"].map(key => [key, sumKnown(unique.map(r => r.progress[key]))])),
247
250
  totalTokens: sumKnown(unique.flatMap(r => r.attempts.length ? r.attempts.map(a => a.actual?.totalTokens ?? null) : [r.actual.totalTokens])),
248
251
  sessionDurationSumMs: sumKnown(unique.map(r => r.terminal?.durationMs ?? null)) };
249
252
  }
253
+ /** Read the persisted session receipts for one Plan node. This is deliberately
254
+ * an observation-only projection: missing or malformed receipts remain
255
+ * missing, never zero, so a canary cannot manufacture an improvement. */
256
+ export async function readFrontendSessionBudgetReceipts(runDir, nodeId = "frontend-plan-pi") {
257
+ const root = path.join(runDir, nodeId);
258
+ const files = [];
259
+ const unreadable = [];
260
+ const visit = async (dir, insideBudget = false) => {
261
+ let entries;
262
+ try {
263
+ entries = await readdir(dir, { withFileTypes: true });
264
+ }
265
+ catch (error) {
266
+ if (error.code !== "ENOENT")
267
+ unreadable.push(dir);
268
+ return;
269
+ }
270
+ for (const entry of entries) {
271
+ const absolute = path.join(dir, entry.name);
272
+ if (entry.isDirectory() && (insideBudget || entry.name === "session-budget" || entry.name === "parallel" || /^coverage-\d+$/.test(entry.name)))
273
+ await visit(absolute, insideBudget || entry.name === "session-budget");
274
+ else if (insideBudget)
275
+ files.push(absolute);
276
+ }
277
+ };
278
+ await visit(root);
279
+ const receipts = [];
280
+ for (const file of [...files, ...unreadable].sort()) {
281
+ try {
282
+ const value = JSON.parse(await readFile(file, "utf8"));
283
+ if (!isCanaryReceipt(value))
284
+ throw new Error("Invalid session receipt");
285
+ receipts.push(value);
286
+ }
287
+ catch {
288
+ // Keep a visible incomplete receipt so canary aggregation cannot turn a
289
+ // truncated artifact into an apparent zero-cost improvement.
290
+ receipts.push({
291
+ schemaVersion: 1,
292
+ policyVersion: "frontend-session-observation-v1",
293
+ identity: { sessionId: `invalid:${file}`, phase: "unknown", scopeIds: [], taskId: null, runId: null, nodeId, attempt: null, sourceDigest: null, contractDigest: null },
294
+ startedAt: new Date(0).toISOString(), finishedAt: null, completion: "incomplete",
295
+ planned: { prompt: { sha256: "", chars: 0, bytes: 0, estimatedTokens: 0 }, userMessage: { sha256: "", chars: 0, bytes: 0, estimatedTokens: 0 }, toolSchemas: null, estimatorVersion: "unknown", capabilities: { ...frontendModelCapabilities(undefined), model: null }, unmeasuredComponents: [] },
296
+ actual: { ...collectFrontendUsage([]) }, attempts: [],
297
+ progress: { memoryCommittedDelta: null, durableCommittedDelta: null, failedRecords: null, duplicateRecords: null, durableSubmissions: null, durableReplacements: null, successfulReadBytes: null, duplicateReadBytes: null }, terminal: null,
298
+ });
299
+ }
300
+ }
301
+ return receipts;
302
+ }
303
+ export function summarizeFrontendPlanCanary(receipts) {
304
+ const unique = uniqueFrontendSessions(receipts);
305
+ const complete = unique.length > 0 && unique.every(receipt => isCanaryReceipt(receipt) && receipt.completion === "complete");
306
+ const known = (values, sum) => {
307
+ if (!complete || values.length === 0)
308
+ return null;
309
+ const present = values.filter((value) => value !== null && value !== undefined);
310
+ return present.length === values.length ? sum(present) : null;
311
+ };
312
+ return {
313
+ planModelRequests: known(unique.map((receipt) => receipt.actual.requestCount), (items) => items.reduce((total, value) => total + value, 0)),
314
+ planSessions: complete ? unique.length : null,
315
+ // Receipt envelope includes gaps and overlapping sessions only once. The
316
+ // run reader below replaces this with the authoritative node interval.
317
+ planWallClockMs: complete ? Math.max(...unique.map(r => Date.parse(r.finishedAt))) - Math.min(...unique.map(r => Date.parse(r.startedAt))) : null,
318
+ planTokens: known(unique.map((receipt) => receipt.actual.totalTokens), (items) => items.reduce((total, value) => total + value, 0)),
319
+ toolRejections: known(unique.map((receipt) => receipt.progress.failedRecords), (items) => items.reduce((total, value) => total + value, 0)),
320
+ retries: complete ? unique.reduce((total, receipt) => total + Math.max(0, receipt.attempts.length - 1), 0) : null,
321
+ };
322
+ }
323
+ /** Validate the fields consumed by aggregation before accessing nested values.
324
+ * Valid JSON with the wrong shape is missing evidence too. */
325
+ function isCanaryReceipt(value) {
326
+ const r = record(value), identity = record(r.identity), actual = record(r.actual), progress = record(r.progress);
327
+ const nullableCount = (v) => v === null || (typeof v === "number" && Number.isFinite(v) && Number.isInteger(v) && v >= 0);
328
+ return r.schemaVersion === 1 && r.policyVersion === "frontend-session-observation-v1"
329
+ && typeof identity.sessionId === "string" && identity.sessionId.length > 0
330
+ && typeof r.startedAt === "string" && Number.isFinite(Date.parse(r.startedAt))
331
+ && (r.completion === "incomplete" || (r.completion === "complete" && typeof r.finishedAt === "string" && Date.parse(r.finishedAt) >= Date.parse(r.startedAt)))
332
+ && Array.isArray(r.attempts) && r.attempts.every(a => a !== null && typeof a === "object")
333
+ && nullableCount(actual.requestCount) && nullableCount(actual.totalTokens) && nullableCount(progress.failedRecords);
334
+ }
335
+ /** Read one frozen canary run. The optional artifact may add only run-level
336
+ * timing/review/recovery observations; Plan session counts, retries, tokens,
337
+ * request usage and tool rejections always come from session receipts. */
338
+ export async function readFrontendPlanCanaryRunMetrics(runDir, nodeId = "frontend-plan-pi") {
339
+ const sessionMetrics = summarizeFrontendPlanCanary(await readFrontendSessionBudgetReceipts(runDir, nodeId));
340
+ let supplemental = {};
341
+ try {
342
+ const parsed = JSON.parse(await readFile(path.join(runDir, "frontend-plan-canary-metrics.json"), "utf8"));
343
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
344
+ supplemental = parsed;
345
+ }
346
+ }
347
+ catch {
348
+ // Missing or malformed supplemental evidence remains unavailable.
349
+ }
350
+ const optionalNumber = (key) => {
351
+ const value = supplemental[key];
352
+ return typeof value === "number" && Number.isFinite(value) && value >= 0
353
+ ? value
354
+ : null;
355
+ };
356
+ return {
357
+ ...sessionMetrics,
358
+ dagWallClockMs: optionalNumber("dagWallClockMs"),
359
+ designReviewRejectRate: optionalNumber("designReviewRejectRate"),
360
+ finalReviewDefectRate: optionalNumber("finalReviewDefectRate"),
361
+ recoveryCount: optionalNumber("recoveryCount"),
362
+ };
363
+ }
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { createHash } from "node:crypto";
11
11
  import { z } from "zod";
12
+ import { collectFrontendSharedSignals, isNarrowProductSurface } from "./frontend-risk.js";
12
13
  /** Fixture-only node counts. They are NOT the authoritative 15/13/11
13
14
  * topology: split-required has no count. */
14
15
  export const FRONTEND_SHAPE_NODE_COUNTS = {
@@ -17,16 +18,6 @@ export const FRONTEND_SHAPE_NODE_COUNTS = {
17
18
  standard: 11,
18
19
  high: 11,
19
20
  };
20
- const SHAPE_ESCALATION_PATTERNS = [
21
- { id: "auth-permission", re: /\bauth\b|\bpermission\b|\brbac\b|登录|权限|\bjwt\b|\bsession\b/i },
22
- { id: "sensitive-action", re: /\bpayment\b|delete\s+account|支付|删除用户|\bdestructive\b/i },
23
- { id: "ssr-boundary", re: /\bssr\b|\brsc\b|server\s+component|\bhydration\b|server-client/i },
24
- { id: "new-route", re: /new\s+route|新增路由|createBrowserRouter|add\s+route/i },
25
- { id: "shared-public-api", re: /public\s+api|shared\s+component|公共组件|破坏性|breaking\s+change/i },
26
- { id: "schema-api-change", re: /\bopenapi\b|graphql\s+schema|api\s+contract|接口变更|schema\s+change/i },
27
- { id: "new-dependency", re: /new\s+dependency|add\s+dependency|npm\s+install|新增依赖/i },
28
- { id: "cross-domain-paths", re: /多个领域|cross-module|multiple\s+packages/i },
29
- ];
30
21
  const SHAPE_SMALL_PATTERNS = [
31
22
  { id: "single-component", re: /single\s+component|单一组件|局部样式|style\s+tweak|css\s+only|文案|copy\s+change/i },
32
23
  { id: "no-remote", re: /no\s+api|无\s*api|无\s*接口|本地状态|local\s+state\s+only|pure\s+ui/i },
@@ -59,48 +50,13 @@ function pathSpreadScore(paths) {
59
50
  return tops.size;
60
51
  }
61
52
  function collectEscalationSignals(blob, supervised) {
62
- const signals = [];
63
- for (const pattern of SHAPE_ESCALATION_PATTERNS) {
64
- if (hasAffirmativeShapeSignal(blob, pattern))
65
- signals.push(pattern.id);
66
- }
67
- if (supervised)
68
- signals.push("supervised-profile");
53
+ const signals = collectFrontendSharedSignals(blob, supervised);
69
54
  if (SHAPE_SPLIT_REQUIRED_PATTERN.test(blob) &&
70
55
  !SHAPE_SPLIT_REQUIRED_NEGATION.test(blob)) {
71
56
  signals.push("large-requirement-split");
72
57
  }
73
58
  return [...new Set(signals)];
74
59
  }
75
- function isExplicitShapeExclusion(clause, pattern) {
76
- const match = new RegExp(pattern.re.source, "i").exec(clause);
77
- if (!match)
78
- return false;
79
- const prefix = clause.slice(0, match.index).trim();
80
- const suffix = clause.slice(match.index + match[0].length).trim();
81
- const actionInSignal = ["new-route", "new-dependency"].includes(pattern.id);
82
- if (suffix && !(actionInSignal && /^no$/i.test(prefix) && /^is\s+(?:required|needed)$/i.test(suffix)))
83
- return false;
84
- if (/^(?:不涉及|不(?:修改|变更|调整|改变|新增|添加)|无需(?:修改|变更|调整|改变|新增|添加)|no\s+changes?\s+to|(?:do\s+not|don't)\s+(?:change|modify))$/i.test(prefix))
85
- return true;
86
- return actionInSignal && /^(?:不|禁止|无需|无须|不需要|no|without|do\s+not|don't)$/i.test(prefix);
87
- }
88
- /** Only a complete, independent scope exclusion can suppress a risk signal.
89
- * Nested negation, conditions and trailing consequences remain risk evidence;
90
- * an excluded occurrence never cancels another occurrence elsewhere. */
91
- function hasAffirmativeShapeSignal(blob, pattern) {
92
- for (const sentence of blob.split(/[\n。.;;!?!?]/)) {
93
- if (!pattern.re.test(sentence))
94
- continue;
95
- // Commas can attach a condition to an exclusion. A comma-separated list
96
- // is independent only when every item is itself an explicit exclusion.
97
- const clauses = sentence.split(/[,,、]/).map(part => part.trim().replace(/^(?:[-*+]|\d+\))\s+/, ""));
98
- if (clauses.every(clause => SHAPE_ESCALATION_PATTERNS.some(candidate => isExplicitShapeExclusion(clause, candidate))))
99
- continue;
100
- return true;
101
- }
102
- return false;
103
- }
104
60
  /** Negation applies to the API mention itself, never to unrelated Mock policy. */
105
61
  function hasFrontendRemoteSignal(blob) {
106
62
  const remaining = blob
@@ -126,7 +82,7 @@ function resolveBaselineFrontendTaskShape(input) {
126
82
  }
127
83
  const paths = input.allowedPaths ?? [];
128
84
  const spread = pathSpreadScore(paths);
129
- const concentrated = spread <= 1 && paths.length <= 4;
85
+ const concentrated = paths.length > 0 && spread <= 1 && paths.length <= 4;
130
86
  const hasApiSignal = hasFrontendRemoteSignal(blob);
131
87
  const complexity = (input.complexity ?? "").toLowerCase();
132
88
  // split-required wins over everything: it is a termination/escalation
@@ -172,17 +128,17 @@ function resolveBaselineFrontendTaskShape(input) {
172
128
  reason: "micro scope: trivial single-file change without remote API",
173
129
  };
174
130
  }
175
- // small: no escalation, no supervised, small keywords, concentrated paths,
176
- // no remote API signal.
177
- if (smallHits.length > 0 &&
178
- concentrated &&
179
- !hasApiSignal &&
180
- (complexity === "small" || complexity === "low")) {
131
+ // small: no escalation, no supervised, narrow single-domain delivery
132
+ // surface, no remote API signal, and not an explicitly large task. Keyword
133
+ // and explicit "small/low" complexity hints are no longer required — they
134
+ // were a fragile proxy for "this is genuinely a small change" and blocked
135
+ // genuinely-small tasks whose authors used different wording.
136
+ if (isNarrowProductSurface(paths) && !hasApiSignal && complexity !== "large") {
181
137
  return {
182
138
  shape: "small",
183
- signals: smallHits,
139
+ signals: smallHits.length > 0 ? smallHits : ["structured-small"],
184
140
  rejectedSignals: [],
185
- reason: `small signals without escalation: ${smallHits.join(", ")}`,
141
+ reason: "narrow single-domain delivery surface without remote API or escalation; small topology",
186
142
  };
187
143
  }
188
144
  const rejected = smallHits.map((id) => `small:${id}:blocked-by-${hasApiSignal ? "api-signal" : "path-spread"}`);
@@ -1,21 +1,18 @@
1
1
  import path from "node:path";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { z } from "zod";
4
- /** Observe supported frozen Vitest commands without changing their selection. */
5
- export async function withFrontendTestReporter(input) {
6
- // Compound commands/wrappers need their own adapter: never append flags to
7
- // a different shell command, change selection, or override a user reporter.
8
- if (/[;&|<>`\n\r$]/.test(input.command) || /--(?:reporter|outputFile)/.test(input.command))
9
- return undefined;
10
- const direct = /^(?:(?:npx|pnpm exec|yarn exec)\s+)?vitest\s+run(?:\s|$)/.test(input.command.trim());
11
- const npm = /^npm\s+(?:run\s+([\w:-]+)|(test))(?=\s|$)(.*)$/.exec(input.command.trim());
4
+ /** Decide whether a bare vitest/npm-test command is observable and, if so,
5
+ * return the command with the separator (" --") a reporter would need. */
6
+ async function adaptPlainTestCommand(command, cwd) {
7
+ const direct = /^(?:(?:npx|pnpm exec|yarn exec)\s+)?vitest\s+run(?:\s|$)/.test(command.trim());
8
+ const npm = /^npm\s+(?:run\s+([\w:-]+)|(test))(?=\s|$)(.*)$/.exec(command.trim());
12
9
  let separator = "";
13
10
  if (!direct) {
14
11
  if (!npm)
15
12
  return undefined;
16
13
  let manifest;
17
14
  try {
18
- manifest = JSON.parse(await readFile(path.join(input.cwd, "package.json"), "utf8"));
15
+ manifest = JSON.parse(await readFile(path.join(cwd, "package.json"), "utf8"));
19
16
  }
20
17
  catch {
21
18
  return undefined;
@@ -27,8 +24,36 @@ export async function withFrontendTestReporter(input) {
27
24
  return undefined;
28
25
  separator = npm[3]?.trim() ? "" : " --";
29
26
  }
27
+ return `${command}${separator}`;
28
+ }
29
+ /** Observe supported frozen Vitest commands without changing their selection. */
30
+ export async function withFrontendTestReporter(input) {
31
+ // Compound commands/wrappers need their own adapter: never append flags to
32
+ // a different shell command, change selection, or override a user reporter.
33
+ if (/[;&|<>`\n\r$]/.test(input.command) || /--(?:reporter|outputFile)/.test(input.command))
34
+ return undefined;
35
+ // Frozen verify commands are env-prefixed bash wrappers —
36
+ // `HARNESS_ALLOW_ACTIVE_DAG_RUNS=1 'bash' '-lc' 'npm test'` — so the JSON
37
+ // reporter must be injected INSIDE the quoted payload; appending it to the
38
+ // wrapper would target bash's own CLI and produce no test report at all
39
+ // (smoke r54: every behavior verification target then failed the trace
40
+ // gate with "test execution report unavailable").
41
+ const bashWrapped = /^((?:[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|[^\s'"]+)\s+)*)'bash'\s+'-lc'\s+'([^']*)'\s*$/.exec(input.command);
42
+ if (bashWrapped) {
43
+ const inner = bashWrapped[2];
44
+ if (/[;&|<>`\n\r$]/.test(inner) || /--(?:reporter|outputFile)/.test(inner))
45
+ return undefined;
46
+ const adaptedInner = await adaptPlainTestCommand(inner, input.cwd);
47
+ if (!adaptedInner)
48
+ return undefined;
49
+ const quote = (value) => `'${value.replace(/'/g, `'"'"'`)}'`;
50
+ return input.command.replace(`'${inner}'`, `'${adaptedInner} --reporter=json --outputFile=${quote(input.reportPath)}'`);
51
+ }
52
+ const adapted = await adaptPlainTestCommand(input.command, input.cwd);
53
+ if (!adapted)
54
+ return undefined;
30
55
  const quote = (value) => `'${value.replace(/'/g, `'"'"'`)}'`;
31
- return `${input.command}${separator} --reporter=json --outputFile=${quote(input.reportPath)}`;
56
+ return `${adapted} --reporter=json --outputFile=${quote(input.reportPath)}`;
32
57
  }
33
58
  const resultSchema = z.object({
34
59
  testResults: z.array(z.object({
@@ -31,18 +31,11 @@ export const TYPED_EVENT_FACT_KINDS = [
31
31
  "provider-ended-without-terminal",
32
32
  "read-only-fact-channel",
33
33
  ];
34
- /**
35
- * M2 shadow-only fact catalog (AC-001 shadow dual write). These facts are
36
- * audit-only observations derived from the authoritative text/JSON outputs of
37
- * the frontend-contract-pi / frontend-scout-pi / frontend-plan-pi nodes. They
38
- * are never branch-control signals and never participate in writer admission.
39
- *
40
- * Kept deliberately separate from `TYPED_EVENT_FACT_KINDS`: widening the shadow
41
- * catalog must never widen the provider capability matrix (which stays frozen
42
- * at its seven kinds). `target-surface` is shared by the scout and plan
43
- * origins, so the union of the two catalogs is 12 unique kinds.
44
- */
45
- export const SHADOW_FACT_KINDS = [
34
+ /** Frontend DAG decision facts. These are committed runtime facts, not a
35
+ * secondary text channel; Policy, Admission and Plan compilation read them
36
+ * directly. Kept separate from provider capability facts so the provider
37
+ * matrix remains frozen. */
38
+ export const FRONTEND_DAG_FACT_KINDS = [
46
39
  "requirement",
47
40
  "constraint",
48
41
  "open-question",
@@ -56,11 +49,26 @@ export const SHADOW_FACT_KINDS = [
56
49
  "design-deviation",
57
50
  "dependency",
58
51
  ];
52
+ /** Experimental decision Plan fact catalog. The decision tools emit semantic
53
+ * decisions (module placement, reuse, state ownership, Mock boundary, behavior
54
+ * verification focus); `data-flow` and `dependency` reuse the DAG catalog above.
55
+ * Kept as a dedicated catalog so widening the decision surface never widens the
56
+ * frozen provider / event / review / design catalogs. */
57
+ export const FRONTEND_DECISION_FACT_KINDS = [
58
+ "module-placement",
59
+ "reuse-decision",
60
+ "state-ownership",
61
+ "api-mock-boundary",
62
+ "verification-focus",
63
+ ];
64
+ /** Terminal fact for the experimental decision Plan path. Kept separate from the
65
+ * decision fact catalog so it never gets collected as a decision entry. */
66
+ export const DECISION_TERMINAL_FACT_KINDS = ["finalize_decision"];
59
67
  /**
60
68
  * M5 review terminal fact catalog (AC-001). These two committed typed tools are
61
69
  * the authoritative terminal verdict of `frontend-review-pi`. They are kept in
62
70
  * a dedicated catalog so widening the review terminal surface never widens the
63
- * frozen 7/7/12 provider/shadow/event catalogs.
71
+ * frozen provider/event catalogs.
64
72
  */
65
73
  export const REVIEW_TERMINAL_FACT_KINDS = [
66
74
  "approve_review",
@@ -72,7 +80,7 @@ export const REVIEW_TERMINAL_FACT_KINDS = [
72
80
  * `approve_design` / `request_design_changes` instead of the legacy first-line
73
81
  * `VERDICT: pass|request-revision` text protocol. Kept in a dedicated catalog
74
82
  * so widening the design terminal surface never widens the frozen provider /
75
- * shadow / event / review catalogs.
83
+ * event / review catalogs.
76
84
  */
77
85
  export const DESIGN_TERMINAL_FACT_KINDS = [
78
86
  "approve_design",
@@ -84,7 +92,7 @@ export const DESIGN_TERMINAL_FACT_KINDS = [
84
92
  * open-question/split-proposal facts plus the `contract-finalized` terminal
85
93
  * fact carrying the terminal disposition (`ready | ready-with-assumptions |
86
94
  * blocked` + optional `blockingOwner`). Kept in a dedicated catalog so
87
- * widening the contract surface never widens the frozen provider / shadow /
95
+ * widening the contract surface never widens the frozen provider /
88
96
  * event / review / design catalogs.
89
97
  */
90
98
  export const CONTRACT_FACT_KINDS = [
@@ -142,8 +150,8 @@ export const FRONTEND_ISSUE_CATEGORY_VALUES = [
142
150
  ];
143
151
  export const frontendIssueCategorySchema = z.enum(FRONTEND_ISSUE_CATEGORY_VALUES);
144
152
  /** Runtime control facts committed at frontend shape boundaries. Kept outside
145
- * the frozen provider/shadow catalogs so adding control authority cannot widen
146
- * either legacy capability surface. */
153
+ * the frozen provider catalog so adding control authority cannot widen that
154
+ * capability surface. */
147
155
  export const FRONTEND_SHAPE_FACT_KINDS = ["frontend-shape"];
148
156
  export const typedEventPhaseSchema = z.enum([
149
157
  "staged",
@@ -153,7 +161,9 @@ export const typedEventPhaseSchema = z.enum([
153
161
  const ALL_TYPED_EVENT_FACT_KIND_VALUES = [
154
162
  ...new Set([
155
163
  ...TYPED_EVENT_FACT_KINDS,
156
- ...SHADOW_FACT_KINDS,
164
+ ...FRONTEND_DAG_FACT_KINDS,
165
+ ...FRONTEND_DECISION_FACT_KINDS,
166
+ ...DECISION_TERMINAL_FACT_KINDS,
157
167
  ...REVIEW_TERMINAL_FACT_KINDS,
158
168
  ...DESIGN_TERMINAL_FACT_KINDS,
159
169
  ...FRONTEND_SHAPE_FACT_KINDS,
@@ -166,7 +176,7 @@ const ALL_TYPED_EVENT_FACT_KIND_VALUES = [
166
176
  export const typedEventFactKindSchema = z.enum(ALL_TYPED_EVENT_FACT_KIND_VALUES);
167
177
  /** A typed event fact is a `kind`-discriminated record. The schema stays
168
178
  * permissive beyond `kind` (arbitrary JSON-safe payload) so provider/model
169
- * shadow facts can carry their own fields, but `kind` is always validated. */
179
+ * provider facts can carry their own fields, but `kind` is always validated. */
170
180
  export const typedEventFactSchema = z
171
181
  .record(z.unknown())
172
182
  .superRefine((value, ctx) => {
@@ -178,8 +188,7 @@ export const typedEventFactSchema = z
178
188
  });
179
189
  }
180
190
  });
181
- /** A single review finding shared by both typed terminal verdicts. Mirrors the
182
- * legacy JSON review finding shape so typed↔JSON equivalence stays total. */
191
+ /** A single review finding shared by both typed terminal verdicts. */
183
192
  export const reviewFindingSchema = z
184
193
  .object({
185
194
  severity: z.enum(["Critical", "Important", "Minor", "Info"]),
@@ -190,8 +199,7 @@ export const reviewFindingSchema = z
190
199
  })
191
200
  .strict();
192
201
  /** `approve_review` terminal fact payload. Approval may carry informational
193
- * findings (Minor/Info) but never Critical/Important — the same rule the legacy
194
- * JSON verdict applies to `verdict: "pass"`. */
202
+ * findings (Minor/Info) but never Critical/Important. */
195
203
  export const approveReviewFactSchema = z
196
204
  .object({
197
205
  kind: z.literal("approve_review"),
@@ -221,8 +229,7 @@ export const requestReviewChangesFactSchema = z
221
229
  })
222
230
  .strict();
223
231
  /** `approve_design` terminal fact payload. Approval may carry informational
224
- * findings (Minor/Info) but never Critical/Important — the same rule the legacy
225
- * text protocol applies to `VERDICT: pass`. */
232
+ * findings (Minor/Info) but never Critical/Important. */
226
233
  export const approveDesignFactSchema = z
227
234
  .object({
228
235
  kind: z.literal("approve_design"),
@@ -170,6 +170,8 @@ export function extractFrontendTestTitles(content) {
170
170
  }
171
171
  for (let index = 0; index < tokens.length; index += 1) {
172
172
  const token = tokens[index];
173
+ if (token === undefined)
174
+ continue;
173
175
  if (token?.type !== "identifier" ||
174
176
  !["describe", "it", "test"].includes(token.value) ||
175
177
  (tokens[index - 1]?.type === "punctuation" &&
@@ -183,15 +185,16 @@ export function extractFrontendTestTitles(content) {
183
185
  while (tokens[cursor]?.type === "punctuation" &&
184
186
  tokens[cursor]?.value === "." &&
185
187
  tokens[cursor + 1]?.type === "identifier") {
186
- if (tokens[cursor + 1]?.type === "identifier" &&
187
- configurationModifiers.has(tokens[cursor + 1].value)) {
188
+ const modifier = tokens[cursor + 1];
189
+ if (modifier === undefined)
190
+ break;
191
+ if (configurationModifiers.has(modifier.value)) {
188
192
  hasConfigurationModifier = true;
189
- if (["runIf", "skipIf"].includes(tokens[cursor + 1].value)) {
190
- conditionalModifier = tokens[cursor + 1].value;
193
+ if (["runIf", "skipIf"].includes(modifier.value)) {
194
+ conditionalModifier = modifier.value;
191
195
  }
192
196
  }
193
- if (tokens[cursor + 1]?.type === "identifier" &&
194
- nonExecutingModifiers.has(tokens[cursor + 1].value)) {
197
+ if (nonExecutingModifiers.has(modifier.value)) {
195
198
  hasNonExecutingModifier = true;
196
199
  }
197
200
  cursor += 2;
@@ -221,54 +224,23 @@ export function extractFrontendTestTitles(content) {
221
224
  index = closingParenIndex(cursor) ?? tokens.length;
222
225
  continue;
223
226
  }
224
- if (tokens[cursor + 1]?.type !== "string")
227
+ const titleToken = tokens[cursor + 1];
228
+ if (titleToken?.type !== "string")
225
229
  continue;
226
- titles.push(tokens[cursor + 1].value);
230
+ titles.push(titleToken.value);
227
231
  }
228
232
  return titles;
229
233
  }
230
- function traceKeyMatchesTitle(traceKey, title) {
231
- const escaped = traceKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
232
- return new RegExp(`(?:^|[^A-Za-z0-9_-])${escaped}(?:$|[^A-Za-z0-9_-])`).test(title);
233
- }
234
- function formatObservedTestTitles(titles) {
235
- if (titles.length === 0)
236
- return "(none)";
237
- const limit = 8;
238
- const preview = titles
239
- .slice(0, limit)
240
- .map((title) => title.length > 120 ? `${title.slice(0, 117)}...` : title)
241
- .join(" | ");
242
- return titles.length > limit
243
- ? `${preview} | ... (${titles.length - limit} more)`
244
- : preview;
245
- }
246
234
  async function evaluateTargetFileTrace(input) {
247
235
  const fileIssues = await assertFileAndSymbol({
248
236
  workspaceRoot: input.workspaceRoot,
249
237
  file: input.target.file,
250
238
  });
251
- if (fileIssues.length || input.target.mode === "static") {
252
- return { issues: fileIssues };
253
- }
254
- const absolute = path.resolve(input.workspaceRoot, input.target.file);
255
- const content = await readFile(absolute, "utf8");
256
- const observedTestTitles = extractFrontendTestTitles(content);
257
- if (observedTestTitles.some((title) => traceKeyMatchesTitle(input.target.id, title))) {
258
- return { issues: [], observedTestTitles };
259
- }
260
- const observed = formatObservedTestTitles(observedTestTitles);
261
- return {
262
- issues: [
263
- `trace key not found in test title: ${input.target.id} in ${input.target.file}; observed test titles: ${observed}`,
264
- ],
265
- observedTestTitles,
266
- };
239
+ return { issues: fileIssues };
267
240
  }
268
241
  /**
269
- * Lightweight writer-boundary check. It validates file existence and stable
270
- * behavior trace tokens only; frozen commands remain the final trace gate's
271
- * responsibility after command execution.
242
+ * Lightweight writer-boundary check. It validates declared file existence;
243
+ * frozen commands remain the final trace gate's responsibility after execution.
272
244
  */
273
245
  export async function evaluateFrontendWriterTracePreflight(input) {
274
246
  const targets = [];
@@ -287,8 +259,9 @@ export async function evaluateFrontendWriterTracePreflight(input) {
287
259
  commandLabel: target.commandLabel,
288
260
  file: target.file,
289
261
  ...(target.symbol ? { symbol: target.symbol } : {}),
290
- ...(target.mode !== "static" ? { traceKey: target.id } : {}),
291
262
  status: fileTrace.issues.length ? "failed" : "ok",
263
+ evidence: fileTrace.issues.length ? "unavailable" : "file-exists",
264
+ behaviorCoverage: "unconfirmed",
292
265
  matchedNodeIds: [],
293
266
  issues: fileTrace.issues,
294
267
  });
@@ -298,8 +271,8 @@ export async function evaluateFrontendWriterTracePreflight(input) {
298
271
  /**
299
272
  * Pure verification-target evaluation shared by the trace gate and the repair
300
273
  * assess gate. It derives, for each contract verification target, whether the
301
- * target bound to frozen evidence (commandLabel owners), and whether the file
302
- * and stable behavior-target id exist in a real test title. It performs no
274
+ * target bound to frozen evidence (commandLabel owners), whether its file exists,
275
+ * and whether the declared test file was exercised by the observed runner. It performs no
303
276
  * writes and throws nothing: failures are returned as `hardIssues` plus
304
277
  * per-target `status`.
305
278
  */
@@ -325,10 +298,13 @@ export async function evaluateVerificationTargets(input) {
325
298
  });
326
299
  issues.push(...fileTrace.issues);
327
300
  const executed = input.executedTests?.get(target.commandLabel);
328
- if (target.mode !== "static" && executed !== undefined) {
301
+ if (target.mode !== "static" && executed === undefined) {
302
+ issues.push(`test execution report unavailable for behavior target: ${target.id}`);
303
+ }
304
+ else if (target.mode !== "static" && executed !== undefined) {
329
305
  // safePath permits "./" prefixes; reporter files are cwd-relative.
330
306
  const targetFile = path.posix.normalize(target.file.replace(/\\/g, "/"));
331
- const matching = executed.filter((test) => test.file === targetFile && traceKeyMatchesTitle(target.id, test.title));
307
+ const matching = executed.filter((test) => test.file === targetFile);
332
308
  if (matching.length === 0 || matching.some((test) => test.status !== "passed")) {
333
309
  issues.push(`behavior target was not fully executed and passed: ${target.id} in ${target.file}`);
334
310
  }
@@ -342,8 +318,11 @@ export async function evaluateVerificationTargets(input) {
342
318
  commandLabel: target.commandLabel,
343
319
  file: target.file,
344
320
  symbol: target.symbol,
345
- ...(target.mode !== "static" ? { traceKey: target.id } : {}),
346
321
  status,
322
+ evidence: issues.length
323
+ ? "unavailable"
324
+ : target.mode === "static" ? "command-execution" : "test-file-execution",
325
+ behaviorCoverage: "unconfirmed",
347
326
  matchedNodeIds: owners ?? [],
348
327
  issues,
349
328
  });
@@ -353,16 +332,16 @@ export async function evaluateVerificationTargets(input) {
353
332
  /**
354
333
  * Legacy deterministic symbol resolvability check retained by canonical
355
334
  * normalization (`dropUnresolvedVerificationSymbols`). New plan tools do not
356
- * expose symbol and runtime trace binding uses the stable target id. This
335
+ * expose symbol and runtime trace binding uses the command and test file. This
357
336
  * helper still handles old `all describe blocks`, `describe('title')`, and
358
337
  * slash-delimited labels so existing run artifacts normalize consistently.
359
338
  */
360
339
  export function verificationSymbolMatchesContent(symbol, content) {
361
340
  if (symbol.trim().toLowerCase() === "all describe blocks")
362
341
  return content.includes("describe(");
342
+ const separatorHead = symbol.split(/[((]/, 1)[0];
363
343
  const symbols = symbol.includes(" / ")
364
- ? symbol
365
- .split(/[((]/, 1)[0]
344
+ ? (separatorHead ?? symbol)
366
345
  .split("/")
367
346
  .map((item) => item.trim())
368
347
  .filter(Boolean)
@@ -462,7 +441,7 @@ export async function runFrontendVerificationTraceGate(input) {
462
441
  const mockCommandLabels = input.evidence?.mock?.commandLabels ?? [];
463
442
  const mockCommandNodeId = input.evidence?.mock?.nodeId;
464
443
  const mockCommandTexts = input.evidence?.mock?.commandTexts ?? [];
465
- if (input.evidence && contract.mockApi.strategy !== "not-needed" && mockCommandLabels.length === 0) {
444
+ if (input.evidence && contract.verificationTargets.some((target) => target.mode === "behavior") && contract.mockApi.strategy !== "not-needed" && mockCommandLabels.length === 0) {
466
445
  throw new Error("trace: selected Mock strategy requires successful Mock verification commands");
467
446
  }
468
447
  if (input.evidence) {
@@ -550,7 +529,7 @@ export function formatFrontendVerificationTraceStdout(result) {
550
529
  `Browser: ${result.browserStatus}`,
551
530
  `Visual: ${result.visualStatus}`,
552
531
  `Targets: ${result.targets.length}`,
553
- ...result.targets.map((target) => `- ${target.id}: ${target.status} label=${JSON.stringify(target.commandLabel)} file=${target.file}`),
532
+ ...result.targets.map((target) => `- ${target.id}: execution=${target.status} evidence=${target.evidence} behavior-coverage=${target.behaviorCoverage} label=${JSON.stringify(target.commandLabel)} file=${target.file}`),
554
533
  ];
555
534
  if (result.artifactPath) {
556
535
  lines.push(`Artifact: ${result.artifactPath}`);