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

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 (245) 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/sessions/index.js +6 -0
  30. package/dist/executors/dag-pi/sessions/plan-batches.js +280 -0
  31. package/dist/executors/dag-pi/sessions/plan-prompts.js +367 -0
  32. package/dist/executors/dag-pi/sessions/scout-parallel.js +197 -0
  33. package/dist/executors/dag-pi/sessions/segmented-plan.js +1184 -0
  34. package/dist/executors/dag-pi/sessions/writer-evidence.js +60 -0
  35. package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
  36. package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
  37. package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
  38. package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
  39. package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
  40. package/dist/executors/dag-pi-executor.js +733 -5782
  41. package/dist/executors/pi-executor.js +6 -1
  42. package/dist/executors/pi-sdk-executor.js +112 -19
  43. package/dist/executors/shell-executor.js +88 -12
  44. package/dist/executors/shell-presets.js +1 -1
  45. package/dist/executors/shell-write-guard.js +26 -3
  46. package/dist/infrastructure/console/operation-store.js +48 -14
  47. package/dist/infrastructure/evaluation/corpus-store.js +37 -12
  48. package/dist/shared/file-identity.js +23 -0
  49. package/dist/shared/git-workspace-status.js +482 -0
  50. package/dist/shared/operator/capabilities.js +10 -5
  51. package/dist/shared/path-safety.js +2 -2
  52. package/dist/shared/wizard-local-telemetry.js +51 -0
  53. package/dist/task/config-types.js +22 -8
  54. package/dist/task/contract/project.js +13 -0
  55. package/dist/task/contract/schema.js +2 -7
  56. package/dist/task/frontend-preflight.js +1 -1
  57. package/dist/task/source-prepare/parse-intent.js +124 -5
  58. package/dist/task/source-prepare/prepare.js +6 -1
  59. package/dist/task/source-prepare/semantic-intake.js +18 -3
  60. package/dist/task/source-prepare/source-execution.js +65 -0
  61. package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
  62. package/dist/task/source-prepare/source-provider-budget.js +290 -0
  63. package/dist/worker/console/chat/assistant-content.js +5 -34
  64. package/dist/worker/console/chat/chat-event-store.js +62 -10
  65. package/dist/worker/console/chat/context-insights.js +723 -0
  66. package/dist/worker/console/chat/conversation-nodes.js +383 -0
  67. package/dist/worker/console/chat/db-connections.js +854 -0
  68. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
  69. package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
  70. package/dist/worker/console/chat/pi-runtime.js +445 -36
  71. package/dist/worker/console/chat/provider-error.js +0 -12
  72. package/dist/worker/console/chat/repo-browser.js +52 -4
  73. package/dist/worker/console/chat/routes.js +577 -72
  74. package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
  75. package/dist/worker/console/chat/session-store.js +147 -19
  76. package/dist/worker/console/chat/shortcuts.js +7 -0
  77. package/dist/worker/console/chat/timeline-message-order.js +22 -0
  78. package/dist/worker/console/chat/tools.js +34 -0
  79. package/dist/worker/console/chat/turn-process.js +237 -66
  80. package/dist/worker/console/chat/user-turn-outline.js +19 -0
  81. package/dist/worker/console/console-update-runtime.js +2 -2
  82. package/dist/worker/console/dag-confirmation.js +3 -0
  83. package/dist/worker/console/dag-execution-receipt.js +3 -0
  84. package/dist/worker/console/operator-actions.js +32 -3
  85. package/dist/worker/console/operator-user-error.js +28 -1
  86. package/dist/worker/console/pi-readiness.js +4 -4
  87. package/dist/worker/console/prd-intake-bridge.js +5 -12
  88. package/dist/worker/console/recovery-cta.js +14 -14
  89. package/dist/worker/console/server.js +5 -1
  90. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
  91. package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
  92. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
  93. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
  94. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
  95. package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
  96. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
  100. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
  101. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
  102. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
  103. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
  104. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
  105. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
  106. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
  107. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
  108. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
  109. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
  110. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
  111. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
  112. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
  113. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
  114. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
  115. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
  116. package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
  117. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
  118. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
  119. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
  120. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
  121. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
  122. package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
  123. package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
  124. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
  125. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
  126. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
  127. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
  128. package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
  129. package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
  130. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
  131. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
  132. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
  133. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
  134. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
  135. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
  136. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
  137. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
  138. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
  139. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
  140. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
  141. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
  142. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
  143. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
  144. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
  145. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
  146. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
  147. package/dist/worker/console/static/index.html +2 -2
  148. package/dist/worker/console/static-src/app/console-types.js +9 -1
  149. package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
  150. package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
  151. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
  152. package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
  153. package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
  154. package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
  155. package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
  156. package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
  157. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
  158. package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
  159. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
  160. package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
  161. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
  162. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
  163. package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
  164. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
  165. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
  166. package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
  167. package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
  168. package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
  169. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
  170. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
  171. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
  172. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  173. package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
  174. package/dist/worker/console/workspace-context.js +2 -0
  175. package/dist/worker/console/workspace-initialization.js +3 -3
  176. package/dist/worker/observability/read-model.js +23 -2
  177. package/dist/worker/observe/node-context-insights.js +479 -0
  178. package/dist/worker/observe/static/console-theme.css +174 -0
  179. package/dist/worker/observe/static/console-theme.js +36 -0
  180. package/dist/worker/observe/static/operator-chrome.css +85 -0
  181. package/dist/worker/observe/static/operator-chrome.js +51 -0
  182. package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
  183. package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
  184. package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
  185. package/dist/workflows/dag/budget-enforcement.js +31 -2
  186. package/dist/workflows/dag/checkpoint.js +686 -0
  187. package/dist/workflows/dag/frontend-closeout.js +3 -1
  188. package/dist/workflows/dag/frontend-contract-facts.js +11 -8
  189. package/dist/workflows/dag/frontend-design-policy.js +6 -6
  190. package/dist/workflows/dag/frontend-durable-tools.js +90 -70
  191. package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
  192. package/dist/workflows/dag/frontend-plan-canary.js +66 -16
  193. package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
  194. package/dist/workflows/dag/frontend-plan-progress.js +92 -0
  195. package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
  196. package/dist/workflows/dag/frontend-recovery-run.js +51 -46
  197. package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
  198. package/dist/workflows/dag/frontend-review-context.js +29 -1
  199. package/dist/workflows/dag/frontend-review-scopes.js +92 -2
  200. package/dist/workflows/dag/frontend-risk.js +8 -3
  201. package/dist/workflows/dag/frontend-root-observation.js +261 -0
  202. package/dist/workflows/dag/frontend-session-budget.js +52 -39
  203. package/dist/workflows/dag/frontend-session-context.js +10 -0
  204. package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
  205. package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
  206. package/dist/workflows/dag/frontend-verification-trace.js +6 -0
  207. package/dist/workflows/dag/frontend-writer-admission.js +3 -1
  208. package/dist/workflows/dag/hybrid/sources.js +1812 -0
  209. package/dist/workflows/dag/hybrid/templates/backend-test.js +1807 -0
  210. package/dist/workflows/dag/hybrid/templates/frontend-test.js +702 -0
  211. package/dist/workflows/dag/hybrid/templates/frontend.js +1529 -0
  212. package/dist/workflows/dag/hybrid/templates/index.js +8 -0
  213. package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +449 -0
  214. package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +495 -0
  215. package/dist/workflows/dag/hybrid/templates/shared.js +101 -0
  216. package/dist/workflows/dag/hybrid/templates/standard.js +265 -0
  217. package/dist/workflows/dag/hybrid/types.js +32 -0
  218. package/dist/workflows/dag/init-hybrid.js +57 -7106
  219. package/dist/workflows/dag/node-execution.js +11 -11
  220. package/dist/workflows/dag/output-protocol.js +1 -0
  221. package/dist/workflows/dag/rerun-feedback.js +35 -4
  222. package/dist/workflows/dag/rerun-task.js +62 -66
  223. package/dist/workflows/dag/runner.js +38 -1159
  224. package/dist/workflows/dag/terminal-status.js +502 -0
  225. package/dist/workflows/dag/types.js +15 -8
  226. package/dist/workflows/loop/benchmark.js +11 -8
  227. package/docs/README.md +1 -1
  228. package/docs/architecture/dag-execution.md +1 -1
  229. package/docs/architecture/runtime-boundaries.md +1 -1
  230. package/docs/governance/harness-methodology-verification.md +11 -0
  231. package/docs/templates/backend-test-dag.json +5 -4
  232. package/docs/templates/frontend-implementation-contract.schema.json +31 -11
  233. package/package.json +6 -4
  234. package/skills/frontend-design-review/SKILL.md +8 -11
  235. package/skills/frontend-design-review/references/review-checklist.md +3 -4
  236. package/skills/frontend-review/SKILL.md +5 -1
  237. package/skills/loop-agent/references/command-reference.md +8 -0
  238. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  239. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
  240. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
  241. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
  242. package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
  243. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
  244. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
  245. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
@@ -44,6 +44,8 @@ export const frontendCloseoutFactsSchema = z
44
44
  status: frontendCoverageStatusSchema,
45
45
  evidence: z.enum(["file-exists", "command-execution", "test-file-execution", "unavailable"]).optional(),
46
46
  behaviorCoverage: z.enum(["unconfirmed"]).optional(),
47
+ plannedEvidenceLevel: z.enum(["unit", "mounted", "real-integration", "unknown"]).optional(),
48
+ boundary: z.string().min(1).optional(),
47
49
  })
48
50
  .strict()),
49
51
  lintStatus: frontendLintStatusSchema,
@@ -145,7 +147,7 @@ function bulletList(items) {
145
147
  return items.map((item) => `- ${item}`).join("\n");
146
148
  }
147
149
  function coverageLines(facts) {
148
- const lines = facts.coverageMatrix.map((entry) => `- ${entry.verificationTargetId}: execution=${entry.status}; evidence=${entry.evidence ?? "unspecified"}; behavior-coverage=${entry.behaviorCoverage ?? "unconfirmed"}`);
150
+ const lines = facts.coverageMatrix.map((entry) => `- ${entry.verificationTargetId}: execution=${entry.status}; evidence=${entry.evidence ?? "unspecified"}; behavior-coverage=${entry.behaviorCoverage ?? "unconfirmed"}; planned-evidence=${entry.plannedEvidenceLevel ?? "unknown"}${entry.boundary ? `; boundary=${entry.boundary}` : ""}`);
149
151
  return lines.length > 0 ? lines.join("\n") : "_(none)_";
150
152
  }
151
153
  function frontendStatus(facts) {
@@ -5,7 +5,8 @@ function object(value) {
5
5
  ? value
6
6
  : undefined;
7
7
  }
8
- /** Join requirement-owned identity with separately committed evidence by id.
8
+ /** Project the latest requirement per id while retaining first-seen order.
9
+ * The append-only ledger also contains earlier execution metadata revisions.
9
10
  * Later evidence declarations replace only the lanes they explicitly name. */
10
11
  export function resolveFrontendContractRequirements(facts) {
11
12
  const contractFacts = facts.map(object).filter((fact) => fact?.origin === "contract");
@@ -19,13 +20,15 @@ export function resolveFrontendContractRequirements(facts) {
19
20
  ...object(fact.evidence),
20
21
  });
21
22
  }
22
- return contractFacts.flatMap((fact) => fact.kind === "requirement" && typeof fact.id === "string"
23
- ? [{
24
- ...fact,
25
- id: fact.id,
26
- evidence: { ...object(fact.evidence), ...evidenceById.get(fact.id) },
27
- }]
28
- : []);
23
+ const requirements = new Map();
24
+ for (const fact of contractFacts) {
25
+ if (fact.kind === "requirement" && typeof fact.id === "string")
26
+ requirements.set(fact.id, fact);
27
+ }
28
+ return [...requirements].map(([id, fact]) => ({
29
+ ...fact, id,
30
+ evidence: { ...object(fact.evidence), ...evidenceById.get(id) },
31
+ }));
29
32
  }
30
33
  function names(value) {
31
34
  return Array.isArray(value)
@@ -243,9 +243,9 @@ export function checkComponentChoice(contract, componentSpecCandidatePaths, opti
243
243
  });
244
244
  return findings;
245
245
  }
246
- /** 5. Every implementation target and every non-static verification target
247
- * must fall inside the declared writeSet patterns. */
248
- export function checkTargetPaths(contract, writeSetPatterns) {
246
+ /** 5. Implementation targets require write permission. Existing, safely
247
+ * resolved verification files may remain read-only outside that writeSet. */
248
+ export function checkTargetPaths(contract, writeSetPatterns, existingVerificationPaths = []) {
249
249
  const findings = [];
250
250
  // No frozen writeSet patterns (legacy / unconfigured run) → nothing to
251
251
  // check; an empty pattern set must not report every file as out of scope.
@@ -275,7 +275,7 @@ export function checkTargetPaths(contract, writeSetPatterns) {
275
275
  for (const target of contract.verificationTargets) {
276
276
  if (target.mode === "static")
277
277
  continue;
278
- if (!matches(target.file)) {
278
+ if (!matches(target.file) && !existingVerificationPaths.includes(target.file)) {
279
279
  findings.push({
280
280
  code: "target-path-outside-write-set",
281
281
  message: `verification target "${target.id}" file is outside the writeSet patterns: ${target.file}`,
@@ -442,7 +442,7 @@ export function checkRequiredDeliverables(contract) {
442
442
  return [];
443
443
  const planned = new Set([
444
444
  ...contract.targets.files,
445
- ...contract.requirements.flatMap((requirement) => requirement.implementationTargets),
445
+ ...contract.requirements.flatMap((requirement) => [...requirement.implementationTargets, ...(requirement.deliverableTargets ?? [])]),
446
446
  ...contract.verificationTargets.map((target) => target.file),
447
447
  ]);
448
448
  return required
@@ -464,7 +464,7 @@ export function evaluateFrontendDesignPolicy(input) {
464
464
  ...checkComponentChoice(input.contract, input.componentSpecCandidatePaths, input.hasFreshPathEvidence
465
465
  ? { hasFreshPathEvidence: input.hasFreshPathEvidence }
466
466
  : undefined),
467
- ...checkTargetPaths(input.contract, input.writeSetPatterns),
467
+ ...checkTargetPaths(input.contract, input.writeSetPatterns, input.existingVerificationPaths),
468
468
  ...checkDependencies(input.contract, input.allowedDependencies),
469
469
  ...checkUiStateAttribution(input.contract),
470
470
  ...checkUxNameCollisions(input.contract),
@@ -1,3 +1,4 @@
1
+ import { frontendPlanSemanticDigest } from "./frontend-plan-progress.js";
1
2
  import { mkdir, open, rename, rm } from "node:fs/promises";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import path from "node:path";
@@ -12,7 +13,7 @@ export function readFrontendToolProgress(tools) {
12
13
  const counters = [...new Set((tools ?? []).flatMap(tool => tool && typeof tool === "object" && progressByTool.has(tool) ? [progressByTool.get(tool)] : []))];
13
14
  if (!counters.length)
14
15
  return undefined;
15
- return counters.reduce((sum, item) => ({ failedRecords: sum.failedRecords + item.failedRecords, duplicateRecords: sum.duplicateRecords + item.duplicateRecords, durableSubmissions: sum.durableSubmissions + item.durableSubmissions, durableReplacements: sum.durableReplacements + item.durableReplacements }), { failedRecords: 0, duplicateRecords: 0, durableSubmissions: 0, durableReplacements: 0 });
16
+ return counters.reduce((sum, item) => ({ finalizeRejections: sum.finalizeRejections + item.finalizeRejections, failedRecords: sum.failedRecords + item.failedRecords, duplicateRecords: sum.duplicateRecords + item.duplicateRecords, durableSubmissions: sum.durableSubmissions + item.durableSubmissions, durableReplacements: sum.durableReplacements + item.durableReplacements }), { finalizeRejections: 0, failedRecords: 0, duplicateRecords: 0, durableSubmissions: 0, durableReplacements: 0 });
16
17
  }
17
18
  async function exclusive(key, work) {
18
19
  const previous = queues.get(key) ?? Promise.resolve();
@@ -61,7 +62,8 @@ export async function createDurableFrontendTools(input) {
61
62
  return { ...tool, parameters: { ...schema, properties: { ...schema.properties, replace: { type: "boolean", description: "Explicitly revise the current fact with the same identity. Omit for new facts or identical replay." } } } };
62
63
  });
63
64
  const bindingSha256 = hash({ version: 1, attemptId: input.attemptId, binding: input.binding, schemas: tools.map(t => ({ name: t.name, parameters: t.parameters })) });
64
- const publish = (store) => { input.store.records = store.records; input.store.revision = store.revision; input.setWorkingStore(input.store); };
65
+ const publish = (store) => { input.store.records = store.records; input.store.revision = store.revision; input.setWorkingStore(input.store); if (hasCommittedTerminalEvent(store, input.attemptId))
66
+ input.planControl?.acceptTerminal(); };
65
67
  const validate = async (records) => {
66
68
  let revision = 0;
67
69
  let terminal = false;
@@ -89,81 +91,99 @@ export async function createDurableFrontendTools(input) {
89
91
  throw ledgerError("FRONTEND_LEDGER_MISSING", "frontend durable ledger disappeared or became empty; refusing an unproven acknowledgement");
90
92
  };
91
93
  await exclusive(file, restore);
92
- const progress = { failedRecords: 0, duplicateRecords: 0, durableSubmissions: 0, durableReplacements: 0 };
94
+ const progress = { finalizeRejections: 0, failedRecords: 0, duplicateRecords: 0, durableSubmissions: 0, durableReplacements: 0 };
93
95
  const replay = (receipt) => { progress.duplicateRecords++; return receipt; };
94
96
  const wrapped = tools.map(tool => ({ ...tool, execute: (...args) => exclusive(file, async () => {
95
- const [callId, params] = args;
96
- const valid = Check(tool.parameters, params);
97
- if (!valid) {
98
- const errors = [...Errors(tool.parameters, params)];
99
- return failure("TOOL_SCHEMA_INVALID", `${tool.name}: ${errors.slice(0, 8).map(e => `${e.instancePath || "/"}: ${e.message}`).join("; ")}${errors.length > 8 ? `; ${errors.length - 8} more fields` : ""}`);
100
- }
101
- try {
102
- await restore();
103
- const paramsSha256 = hash(params);
104
- const singleton = new Set(["record_route_selection", "record_state_registry", "record_mock_api", "record_data_flow", "record_dependency", "record_design_deviation", "record_required_deliverables", "record_target_surface", "finalize_contract", "finalize_plan", "finalize_decision"]);
105
- const naturalId = params?.id ?? params?.scopeId ?? params?.entry?.id ?? params?.requirementId ?? params?.choice?.purpose ?? params?.entry?.purpose ?? params?.entry?.state ?? params?.entry?.interaction ?? params?.entry?.boundary ?? (tool.name === "record_openspec_selection" ? params?.path : undefined) ?? (params?.endpoint ? `${params.endpoint.method}:${params.endpoint.path}` : undefined) ?? (singleton.has(tool.name) ? "singleton" : undefined);
106
- const identity = `${tool.name}:${typeof naturalId === "string" ? naturalId : "incremental"}`;
107
- const submissions = input.store.records.flatMap(r => r.submission?.receipt ? [r.submission] : []);
108
- const previousCall = submissions.find(s => s.callId === callId);
109
- if (previousCall)
110
- return previousCall.tool === tool.name && previousCall.paramsSha256 === paramsSha256 ? replay(previousCall.receipt) : failure("REQUEST_ID_REUSE_CONFLICT", `${tool.name}: call id already acknowledged with different parameters`);
111
- const previousFact = submissions.filter(s => s.identity === identity).at(-1);
112
- if (tool.name !== "record_plan_group_coverage" && previousFact?.paramsSha256 === paramsSha256)
113
- return replay(previousFact.receipt);
114
- // A committed finalize terminal carries no domain payload in its params
115
- // (r15: the model acknowledged with {replace:false}, the executor's
116
- // auto-finalize sends {}) — both are acknowledgements of the same durable
117
- // terminal, so any later finalize call replays the receipt instead of
118
- // demanding a params match no caller can reproduce. Other singletons
119
- // (state registry, data flows, ...) carry domain payloads and keep the
120
- // replace:true correction path.
121
- if (FINALIZE_TERMINAL_TOOLS.has(tool.name) && previousFact?.receipt)
122
- return replay(previousFact.receipt);
123
- if (previousFact && naturalId !== undefined && params?.replace !== true && !["record_evidence_expectation", "record_required_deliverables", "record_data_flow"].includes(tool.name))
124
- return failure("FACT_IDENTITY_CONFLICT", `${identity}: changing a saved fact requires explicit replacement (replace:true)`);
125
- if (input.store.records.some(r => ["contract-finalized", "finalize_plan", "finalize_decision", "approve_review", "request_review_changes", "approve_design", "request_design_changes"].includes(String(r.fact.kind))))
126
- return failure("TERMINAL_CONFLICT", "A successful terminal is already durable; only identical acknowledged submissions may be replayed");
127
- const draft = structuredClone(input.store);
128
- input.setWorkingStore(draft);
129
- let result;
97
+ const beforeRevision = input.store.revision;
98
+ const execute = async () => {
99
+ const stopped = input.planControl?.readStop();
100
+ if (stopped)
101
+ return failure(stopped.code, stopped.error);
102
+ const [callId, params] = args;
103
+ const valid = Check(tool.parameters, params);
104
+ if (!valid) {
105
+ const errors = [...Errors(tool.parameters, params)];
106
+ return failure("TOOL_SCHEMA_INVALID", `${tool.name}: ${errors.slice(0, 8).map(e => `${e.instancePath || "/"}: ${e.message}`).join("; ")}${errors.length > 8 ? `; ${errors.length - 8} more fields` : ""}`);
107
+ }
130
108
  try {
131
- // Protocol controls must not be spread into domain facts by permissive callers.
132
- const original = input.tools.find(t => t.name === tool.name);
133
- const domainParams = { ...params };
134
- if (!original.parameters.properties?.replace)
135
- delete domainParams.replace;
136
- result = await tool.execute(args[0], domainParams, ...args.slice(2));
109
+ await restore();
110
+ const isReviewFinding = ["record_review_finding", "record_design_finding"].includes(tool.name);
111
+ const paramsSha256 = hash(isReviewFinding ? { ...params, replace: undefined } : params);
112
+ const singleton = new Set(["record_route_selection", "record_state_registry", "record_mock_api", "record_data_flow", "record_dependency", "record_design_deviation", "record_required_deliverables", "record_target_surface", "finalize_contract", "finalize_plan", "finalize_decision"]);
113
+ const naturalId = params?.id ?? params?.scopeId ?? params?.entry?.id ?? params?.requirementId ?? params?.choice?.purpose ?? params?.entry?.purpose ?? params?.entry?.state ?? params?.entry?.interaction ?? params?.entry?.boundary ?? (tool.name === "record_openspec_selection" ? params?.path : undefined) ?? (params?.endpoint ? `${params.endpoint.method}:${params.endpoint.path}` : undefined) ?? (singleton.has(tool.name) ? "singleton" : undefined);
114
+ const identity = `${tool.name}:${typeof naturalId === "string" ? naturalId : "incremental"}`;
115
+ const submissions = input.store.records.flatMap(r => r.submission?.receipt ? [r.submission] : []);
116
+ const previousCall = submissions.find(s => s.callId === callId);
117
+ if (previousCall)
118
+ return previousCall.tool === tool.name && previousCall.paramsSha256 === paramsSha256 ? replay(previousCall.receipt) : failure("REQUEST_ID_REUSE_CONFLICT", `${tool.name}: call id already acknowledged with different parameters`);
119
+ const previousFact = submissions.filter(s => s.identity === identity).at(-1);
120
+ if (isReviewFinding && !previousFact && params?.replace === true)
121
+ return failure("FACT_REPLACEMENT_NOT_FOUND", `${identity}: replacement requires an existing finding; omit replace to create it`);
122
+ if (tool.name !== "record_plan_group_coverage" && previousFact?.paramsSha256 === paramsSha256)
123
+ return replay(previousFact.receipt);
124
+ // A committed finalize terminal carries no domain payload in its params
125
+ // (r15: the model acknowledged with {replace:false}, the executor's
126
+ // auto-finalize sends {}) — both are acknowledgements of the same durable
127
+ // terminal, so any later finalize call replays the receipt instead of
128
+ // demanding a params match no caller can reproduce. Other singletons
129
+ // (state registry, data flows, ...) carry domain payloads and keep the
130
+ // replace:true correction path.
131
+ if (FINALIZE_TERMINAL_TOOLS.has(tool.name) && previousFact?.receipt)
132
+ return replay(previousFact.receipt);
133
+ if (previousFact && naturalId !== undefined && params?.replace !== true && !["record_evidence_expectation", "record_required_deliverables", "record_data_flow"].includes(tool.name))
134
+ return failure("FACT_IDENTITY_CONFLICT", `${identity}: changing a saved fact requires explicit replacement (replace:true)`);
135
+ if (input.store.records.some(r => ["contract-finalized", "finalize_plan", "finalize_decision", "approve_review", "request_review_changes", "approve_design", "request_design_changes"].includes(String(r.fact.kind))))
136
+ return failure("TERMINAL_CONFLICT", "A successful terminal is already durable; only identical acknowledged submissions may be replayed");
137
+ const draft = structuredClone(input.store);
138
+ input.setWorkingStore(draft);
139
+ let result;
140
+ try {
141
+ // Protocol controls must not be spread into domain facts by permissive callers.
142
+ const original = input.tools.find(t => t.name === tool.name);
143
+ const domainParams = { ...params };
144
+ if (!original.parameters.properties?.replace)
145
+ delete domainParams.replace;
146
+ result = await tool.execute(args[0], domainParams, ...args.slice(2));
147
+ }
148
+ finally {
149
+ input.setWorkingStore(input.store);
150
+ }
151
+ if (result.details?.ok !== true)
152
+ return result;
153
+ const newRecords = draft.records.filter(r => r.phase === "committed" && r.revision > input.store.revision);
154
+ if (!newRecords.length)
155
+ return replay(result);
156
+ for (const record of draft.records) {
157
+ record.submission ??= { version: 1, bindingSha256, tool: "bootstrap", callId: record.requestId, identity: record.eventId, paramsSha256: record.payloadSha256 };
158
+ }
159
+ for (const record of newRecords)
160
+ record.submission = { version: 1, bindingSha256, tool: tool.name, callId, identity, paramsSha256 };
161
+ newRecords.at(-1).submission.receipt = JSON.parse(JSON.stringify(result));
162
+ const committed = draft.records.filter(r => r.phase === "committed");
163
+ await validate(committed);
164
+ await persist(file, committed);
165
+ publish(draft);
166
+ progress.durableSubmissions++;
167
+ if (previousFact)
168
+ progress.durableReplacements++;
169
+ return result;
137
170
  }
138
- finally {
171
+ catch (error) {
139
172
  input.setWorkingStore(input.store);
173
+ return failure(typeof error?.code === "string" ? error.code : "FRONTEND_COMMIT_FAILED", error instanceof Error ? error.message : String(error));
140
174
  }
141
- if (result.details?.ok !== true)
142
- return result;
143
- const newRecords = draft.records.filter(r => r.phase === "committed" && r.revision > input.store.revision);
144
- if (!newRecords.length)
145
- return replay(result);
146
- for (const record of draft.records) {
147
- record.submission ??= { version: 1, bindingSha256, tool: "bootstrap", callId: record.requestId, identity: record.eventId, paramsSha256: record.payloadSha256 };
148
- }
149
- for (const record of newRecords)
150
- record.submission = { version: 1, bindingSha256, tool: tool.name, callId, identity, paramsSha256 };
151
- newRecords.at(-1).submission.receipt = JSON.parse(JSON.stringify(result));
152
- const committed = draft.records.filter(r => r.phase === "committed");
153
- await validate(committed);
154
- await persist(file, committed);
155
- publish(draft);
156
- progress.durableSubmissions++;
157
- if (previousFact)
158
- progress.durableReplacements++;
159
- return result;
160
- }
161
- catch (error) {
162
- input.setWorkingStore(input.store);
163
- return failure(typeof error?.code === "string" ? error.code : "FRONTEND_COMMIT_FAILED", error instanceof Error ? error.message : String(error));
175
+ };
176
+ const result = await execute();
177
+ if (result.details?.ok !== true) {
178
+ progress.failedRecords++;
179
+ if (FINALIZE_TERMINAL_TOOLS.has(tool.name))
180
+ progress.finalizeRejections++;
164
181
  }
165
- }).then(result => { if (result.details?.ok !== true)
166
- progress.failedRecords++; return result; }) }));
182
+ const note = input.planControl?.observe(tool.name, args[1], result.details, input.store.revision > beforeRevision, input.planControl ? frontendPlanSemanticDigest(input.store.records) : undefined);
183
+ if (note)
184
+ return { ...result, content: [...(Array.isArray(result.content) ? result.content : []), { type: "text", text: JSON.stringify(note) }], details: { ...result.details, planProgress: note } };
185
+ return result;
186
+ }) }));
167
187
  for (const tool of wrapped)
168
188
  progressByTool.set(tool, progress);
169
189
  return {