@tea-agent/loop-agent 0.41.1-beta.0 → 0.42.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/CHANGELOG.md +78 -206
  2. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  3. package/dist/adapters/context-transfer/pi-session.js +61 -0
  4. package/dist/application/dag/generate-task-dag.js +19 -75
  5. package/dist/application/task-lifecycle/advance.js +5 -24
  6. package/dist/application/task-lifecycle/observe.js +17 -171
  7. package/dist/application/task-lifecycle/plan-transitions.js +7 -42
  8. package/dist/application/task-lifecycle/recommendations.js +2 -13
  9. package/dist/build-stamp.json +3 -3
  10. package/dist/cli/command-definitions.js +12 -7
  11. package/dist/cli/program.js +23 -6
  12. package/dist/commands/client-recovery.js +0 -3
  13. package/dist/commands/dag-artifact.js +284 -0
  14. package/dist/commands/dag-context.js +184 -0
  15. package/dist/commands/dag-rerun.js +206 -1
  16. package/dist/commands/init.js +1 -27
  17. package/dist/commands/task-advance.js +0 -19
  18. package/dist/executors/dag-pi-executor.js +266 -3792
  19. package/dist/executors/pi-executor.js +4 -15
  20. package/dist/executors/pi-sdk-executor.js +3 -0
  21. package/dist/executors/shell-executor.js +217 -945
  22. package/dist/executors/shell-write-guard.js +0 -7
  23. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  24. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  25. package/dist/infrastructure/console/context-export-store.js +160 -0
  26. package/dist/infrastructure/console/dir-lock.js +132 -0
  27. package/dist/infrastructure/console/operation-store.js +197 -0
  28. package/dist/infrastructure/harness/artifact-store.js +10 -1
  29. package/dist/infrastructure/harness/atomic-write.js +12 -2
  30. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  31. package/dist/shared/context-transfer.js +418 -0
  32. package/dist/shared/dag-failure-category.js +0 -12
  33. package/dist/shared/openspec-spec.js +4 -70
  34. package/dist/shared/operator/capabilities.js +14 -7
  35. package/dist/shared/operator/safe-run-summary.js +1 -0
  36. package/dist/shared/path-safety.js +93 -0
  37. package/dist/shared/preview.js +28 -4
  38. package/dist/task/config-types.js +80 -35
  39. package/dist/task/contract/adopt.js +0 -4
  40. package/dist/task/contract/import-revision.js +0 -4
  41. package/dist/task/contract/project.js +0 -3
  42. package/dist/task/contract/schema.js +1 -2
  43. package/dist/task/frontend-project-capability.js +20 -203
  44. package/dist/task/runtime.js +2 -5
  45. package/dist/task/source-prepare/build-draft.js +3 -3
  46. package/dist/task/source-prepare/fragment-inventory.js +9 -15
  47. package/dist/task/source-prepare/prepare.js +1 -89
  48. package/dist/task/source-prepare/semantic-intake.js +2 -6
  49. package/dist/task/source-references.js +1 -22
  50. package/dist/task/task-demand-routing.js +3 -0
  51. package/dist/worker/console/chat/chat-event-store.js +2 -2
  52. package/dist/worker/console/chat/pi-runtime.js +3 -2
  53. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  54. package/dist/worker/console/chat/routes.js +13 -4
  55. package/dist/worker/console/chat/semantic-activity.js +12 -11
  56. package/dist/worker/console/chat/session-stats.js +96 -0
  57. package/dist/worker/console/chat/session-store.js +1 -1
  58. package/dist/worker/console/chat/turn-process.js +28 -4
  59. package/dist/worker/console/chat/user-questions.js +1 -1
  60. package/dist/worker/console/console-update-runtime.js +1 -1
  61. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  62. package/dist/worker/console/dag-confirmation.js +1 -1
  63. package/dist/worker/console/dag-execution-receipt.js +1 -1
  64. package/dist/worker/console/doctor.js +1 -1
  65. package/dist/worker/console/draft-store.js +1 -1
  66. package/dist/worker/console/human-gate-token.js +1 -1
  67. package/dist/worker/console/index.js +3 -6
  68. package/dist/worker/console/interview/assessment.js +1 -1
  69. package/dist/worker/console/interview/session.js +1 -1
  70. package/dist/worker/console/operation-runner.js +45 -4
  71. package/dist/worker/console/operation-sse.js +1 -1
  72. package/dist/worker/console/operation-wait.js +1 -1
  73. package/dist/worker/console/operator-actions.js +107 -168
  74. package/dist/worker/console/operator-surface-health.js +1 -1
  75. package/dist/worker/console/operator-user-error.js +169 -0
  76. package/dist/worker/console/pi-plugins.js +60 -1
  77. package/dist/worker/console/routes.js +637 -8
  78. package/dist/worker/console/security.js +35 -0
  79. package/dist/worker/console/server.js +6 -6
  80. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-DZZ8m3PO.js} +1 -1
  81. package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-D6PvaVd-.js} +1 -1
  82. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-B_OTOiI8.js} +1 -1
  83. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-Bv6rqHBg.js} +1 -1
  84. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-B8eHr0oz.js} +1 -1
  85. package/dist/worker/console/static/assets/channel-BU5gOilw.js +1 -0
  86. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-DYwR0im2.js} +1 -1
  87. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-D2WPGqXt.js} +1 -1
  88. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CQISJ_I7.js} +1 -1
  89. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-C0o2Du1e.js} +1 -1
  90. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-4f8kr-U7.js} +1 -1
  91. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-D_OWvXzX.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-CasdPz5X.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-Cj8lRrla.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-CUvD1FSd.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CqLw_eqB.js} +1 -1
  96. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +1 -0
  97. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +1 -0
  98. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-ku-WqJPx.js} +1 -1
  99. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-Bq_PYDhl.js} +1 -1
  100. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-BiGZV641.js} +1 -1
  101. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-C4JRlglF.js} +1 -1
  102. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-B0dNeWYG.js} +1 -1
  103. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-Cj35YvYM.js} +1 -1
  104. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-uzQoJ2-8.js} +1 -1
  105. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-D1a-Buoz.js} +1 -1
  106. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-XjRrLRSs.js} +1 -1
  107. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-Da_O24cW.js} +1 -1
  108. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-BLJ8jrYU.js} +1 -1
  109. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-B9GrLjM0.js} +1 -1
  110. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-CeJ0TqiK.js} +1 -1
  111. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js} +1 -1
  112. package/dist/worker/console/static/assets/index-H9rFJiGL.css +1 -0
  113. package/dist/worker/console/static/assets/index-xwu9GxEc.js +451 -0
  114. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-DB26i3d8.js} +1 -1
  115. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-BOGRyeZT.js} +1 -1
  116. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-CZRoBO_6.js} +1 -1
  117. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-qlWhJyoB.js} +1 -1
  118. package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CfUiDDB3.js} +1 -1
  119. package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-BQe6fpqj.js} +5 -5
  120. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-zFzWHw64.js} +1 -1
  121. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-DFGUqjZO.js} +1 -1
  122. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-BB5i0l8Z.js} +1 -1
  123. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-obbVZ_X5.js} +1 -1
  124. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-BwdaNzc1.js} +1 -1
  125. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-CkTJaugh.js} +1 -1
  126. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-BwJ-hIgq.js} +1 -1
  127. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-BzSgmm7w.js} +1 -1
  128. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-Bn3oj5Q1.js} +1 -1
  129. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-dxhGtL8K.js} +1 -1
  130. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +1 -0
  131. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-BLpEInya.js} +2 -2
  132. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +8 -0
  133. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-ChJGYcXy.js} +1 -1
  134. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-DK-qjRer.js} +1 -1
  135. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-3qeaWAg-.js} +1 -1
  136. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CootlyP9.js} +1 -1
  137. package/dist/worker/console/static/index.html +7 -2
  138. package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
  139. package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
  140. package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
  141. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
  142. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
  143. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
  144. package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
  145. package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
  146. package/dist/worker/console/workspace-context.js +28 -112
  147. package/dist/worker/console/workspace-registry.js +2 -2
  148. package/dist/worker/continuation/worker-continuation.js +278 -0
  149. package/dist/worker/observability/read-model.js +4 -0
  150. package/dist/worker/observe/health.js +1 -1
  151. package/dist/worker/observe/node-transparency.js +572 -0
  152. package/dist/worker/observe/routes.js +51 -1
  153. package/dist/worker/observe/static/api.js +69 -5
  154. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  155. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  156. package/dist/worker/observe/static/dag-node-purpose.js +0 -5
  157. package/dist/worker/observe/static/state.js +3 -1
  158. package/dist/worker/observe/static/styles.css +1037 -126
  159. package/dist/worker/observe/static/views/dag-inspector.js +1217 -65
  160. package/dist/worker/observe/static/views/session-timeline.js +121 -25
  161. package/dist/worker/outcomes/adapters.js +19 -0
  162. package/dist/worker/outcomes/types.js +1 -0
  163. package/dist/worker/pool/attempt-lease.js +97 -66
  164. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  165. package/dist/worker/pool/reconcile.js +46 -1
  166. package/dist/worker/pool/run-store.js +2 -0
  167. package/dist/worker/pool/state-projection.js +2 -0
  168. package/dist/worker/task-spec/workflow-routing.js +8 -3
  169. package/dist/workflows/dag/artifact-bindings.js +149 -0
  170. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  171. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +99 -4
  172. package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
  173. package/dist/workflows/dag/backend-test-result-contract.js +1 -1
  174. package/dist/workflows/dag/backend-test-writer-completeness.js +45 -3
  175. package/dist/workflows/dag/budget-enforcement.js +10 -0
  176. package/dist/workflows/dag/context-receipt.js +305 -0
  177. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  178. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  179. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  180. package/dist/workflows/dag/contract-validator-registrations.js +2 -1
  181. package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
  182. package/dist/workflows/dag/frontend-plan-render.js +1 -2
  183. package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
  184. package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
  185. package/dist/workflows/dag/frontend-recovery-run.js +20 -33
  186. package/dist/workflows/dag/frontend-repair.js +432 -1
  187. package/dist/workflows/dag/frontend-review-context.js +15 -261
  188. package/dist/workflows/dag/frontend-verification-trace.js +24 -249
  189. package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
  190. package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
  191. package/dist/workflows/dag/init-hybrid.js +740 -718
  192. package/dist/workflows/dag/interrupt-request.js +0 -7
  193. package/dist/workflows/dag/node-execution.js +136 -759
  194. package/dist/workflows/dag/path-safety.js +1 -0
  195. package/dist/workflows/dag/prompt.js +30 -38
  196. package/dist/workflows/dag/report.js +1 -37
  197. package/dist/workflows/dag/rerun-feedback.js +1 -256
  198. package/dist/workflows/dag/rerun-plan.js +557 -11
  199. package/dist/workflows/dag/rerun-run.js +601 -38
  200. package/dist/workflows/dag/rerun-task.js +0 -29
  201. package/dist/workflows/dag/retry-policy.js +109 -214
  202. package/dist/workflows/dag/runner.js +142 -430
  203. package/dist/workflows/dag/scheduler.js +16 -114
  204. package/dist/workflows/dag/skill-snapshot.js +17 -0
  205. package/dist/workflows/dag/types.js +195 -246
  206. package/dist/workflows/dag/validate.js +12 -28
  207. package/docs/architecture/runtime-boundaries.md +6 -6
  208. package/docs/architecture/worker-and-feature.md +1 -1
  209. package/docs/init-surface.manifest.json +12 -30
  210. package/docs/skills/vetted-skill-registry.md +2 -4
  211. package/docs/templates/README.md +0 -2
  212. package/docs/templates/agent-dag-report.schema.json +2 -8
  213. package/docs/templates/agent-dag.schema.json +33 -1
  214. package/docs/templates/backend-test-dag.json +18 -18
  215. package/docs/templates/frontend-implementation-contract.schema.json +1 -4
  216. package/docs/templates/product-line/task.yaml +1 -1
  217. package/harness.json +3 -3
  218. package/package.json +3 -1
  219. package/skills/frontend-bounded-implement/SKILL.md +14 -15
  220. package/skills/frontend-design-review/SKILL.md +41 -22
  221. package/skills/frontend-implementation/SKILL.md +52 -0
  222. package/skills/frontend-implementation/references/code-standards.md +33 -0
  223. package/skills/frontend-implementation/references/design-spec.md +56 -0
  224. package/skills/frontend-implementation/references/node-contracts.md +31 -0
  225. package/skills/frontend-review/SKILL.md +15 -20
  226. package/skills/frontend-review/references/review-findings.md +7 -6
  227. package/skills/frontend-verification/SKILL.md +1 -1
  228. package/skills/loop-agent/references/command-reference.md +6 -1
  229. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  230. package/dist/commands/dag-follow-up.js +0 -138
  231. package/dist/executors/pi-read-budget-policy.js +0 -239
  232. package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
  233. package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
  234. package/dist/worker/console/operation-store.js +0 -389
  235. package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
  236. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
  237. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
  238. package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
  239. package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
  240. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
  241. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
  242. package/dist/worker/console/static-src/active-run-badge.js +0 -17
  243. package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
  244. package/dist/workflows/dag/dag-retry-schema.js +0 -138
  245. package/dist/workflows/dag/frontend-closeout.js +0 -221
  246. package/dist/workflows/dag/frontend-design-policy.js +0 -400
  247. package/dist/workflows/dag/frontend-human-decision.js +0 -182
  248. package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
  249. package/dist/workflows/dag/frontend-read-budgets.js +0 -12
  250. package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
  251. package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
  252. package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
  253. package/dist/workflows/dag/frontend-review-findings.js +0 -270
  254. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
  255. package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
  256. package/dist/workflows/dag/frontend-shape-facts.js +0 -409
  257. package/dist/workflows/dag/frontend-shape.js +0 -427
  258. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
  259. package/dist/workflows/dag/frontend-split-application-service.js +0 -203
  260. package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
  261. package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
  262. package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
  263. package/dist/workflows/dag/frontend-writer-admission.js +0 -285
  264. package/dist/workflows/dag/frontend-writer-status.js +0 -256
  265. package/dist/workflows/dag/recovery-lease.js +0 -80
  266. package/docs/templates/frontend-implementation-dag.json +0 -89
  267. package/docs/templates/spec-registry.schema.json +0 -45
  268. package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
  269. package/skills/frontend-contract/SKILL.md +0 -23
  270. package/skills/frontend-contract/references/contract-protocol.md +0 -34
  271. package/skills/frontend-plan/SKILL.md +0 -26
  272. package/skills/frontend-plan/references/decision-contract.md +0 -37
  273. package/skills/frontend-plan/references/design-decisions.md +0 -17
  274. package/skills/frontend-scout/SKILL.md +0 -25
  275. package/skills/frontend-scout/references/design-evidence.md +0 -16
  276. package/skills/frontend-scout/references/scout-evidence.md +0 -23
  277. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -1,13 +1,8 @@
1
1
  import { z } from "zod";
2
- import { DEFAULT_FRONTEND_SPEC_ROOTS, isFrontendSpecFilePath, isValidFrontendSpecRoot, OPENSPEC_SPEC_EXT_RE, } from "../../shared/openspec-spec.js";
2
+ import { isOpenspecSpecFilePath } from "../../shared/openspec-spec.js";
3
3
  import { campaignBudgetSchema, } from "../../application/evaluation/budget.js";
4
4
  import { assertDagPromptSourceRule } from "./prompt-source.js";
5
- // Leaf-module import: dagRetryPolicySchema must be defined by a module that
6
- // never transitively reaches types.ts during init (retry-policy does, via
7
- // frontend-recovery-capsule → frontend-recovery-lineage →
8
- // frontend-implementation-contract), or a run whose entry module initializes
9
- // retry-policy first reads an undefined schema here.
10
- import { dagRetryPolicySchema, TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE, } from "./dag-retry-schema.js";
5
+ import { dagRetryPolicySchema, TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE, } from "./retry-policy.js";
11
6
  import { dagLivenessPolicySchema, } from "./liveness-policy.js";
12
7
  import { dagOutputProtocolSchema } from "./output-protocol.js";
13
8
  export const dagComplexitySchema = z.enum(["HIGH", "MED", "LOW"]);
@@ -85,7 +80,10 @@ export const dagShellVerifyEvidenceSchema = z.object({
85
80
  selectionReasons: z.array(z.string()).default([]),
86
81
  /** Non-executing generation-time cwd/local-script validation evidence. */
87
82
  preflight: z
88
- .array(z.object({ label: z.string(), status: z.enum(["ok", "future-delivery-script"]) }))
83
+ .array(z.object({
84
+ label: z.string(),
85
+ status: z.enum(["ok", "future-delivery-script"]),
86
+ }))
89
87
  .default([]),
90
88
  /** Effective timeout applied independently to each shell command. */
91
89
  commandTimeoutMs: z.number().int().positive().optional(),
@@ -190,16 +188,7 @@ const dagFrontendNodeIdSchema = z
190
188
  .regex(/^[a-z][a-z0-9-]*$/, "frontend node id must be kebab-case");
191
189
  const openspecCandidatePathSchema = z
192
190
  .string()
193
- .refine((candidate) => !candidate.includes("\\") &&
194
- !candidate.startsWith("/") &&
195
- !candidate.split("/").includes("..") &&
196
- OPENSPEC_SPEC_EXT_RE.test(candidate), "openspec candidate must be a repo-relative supported specification file");
197
- const openspecCandidateSnapshotSchema = z
198
- .object({
199
- path: openspecCandidatePathSchema,
200
- sha256: z.string().regex(/^[a-f0-9]{64}$/),
201
- })
202
- .strict();
191
+ .refine((candidate) => !candidate.includes("\\") && isOpenspecSpecFilePath(candidate), "openspec candidate must be a repo-relative supported file under openspec/schemas/, openspec/project-specs/, or ai_workspace/");
203
192
  export const dagFrontendPrewriteGateSchema = z.object({
204
193
  schemaVersion: z.literal(1),
205
194
  planFromNodeId: dagFrontendNodeIdSchema,
@@ -218,24 +207,7 @@ export const dagFrontendPrewriteGateSchema = z.object({
218
207
  outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
219
208
  requireSourceFreshness: z.literal(true),
220
209
  implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
221
- /**
222
- * Generation-frozen roots that bound custom normative directories. Omitted
223
- * legacy DAGs retain the canonical OpenSpec + ai_workspace roots.
224
- */
225
- openspecSpecRoots: z
226
- .array(z.string().refine((root) => isValidFrontendSpecRoot(root), "openspec spec root must be a normalized repo-relative directory"))
227
- .optional(),
228
210
  openspecCandidatePaths: z.array(openspecCandidatePathSchema).default([]),
229
- /** Read-only selector output; omitted for scan-strict and legacy DAGs. */
230
- openspecSelectionNodeId: dagFrontendNodeIdSchema.optional(),
231
- /** Contract-declared and source-explicit paths that cannot be downgraded. */
232
- openspecMandatoryPaths: z.array(openspecCandidatePathSchema).optional(),
233
- /**
234
- * Content fingerprints frozen with the generation-time OpenSpec scope. New
235
- * DAGs use these to reject a plan whose cited specification changed after it
236
- * was read; legacy DAGs without the field retain existence-only drift checks.
237
- */
238
- openspecCandidateSnapshots: z.array(openspecCandidateSnapshotSchema).optional(),
239
211
  /**
240
212
  * Generation-frozen component/theme specification bucket. Derived from
241
213
  * `designEvidence.classified.component ∪ theme ∪ rule.components` at DAG
@@ -280,28 +252,14 @@ export const dagFrontendPrewriteGateSchema = z.object({
280
252
  scanStrict: z.array(openspecCandidatePathSchema).default([]),
281
253
  })
282
254
  .optional(),
283
- }).superRefine((gate, context) => {
284
- const roots = gate.openspecSpecRoots ?? DEFAULT_FRONTEND_SPEC_ROOTS;
285
- const candidateGroups = [
286
- ["openspecCandidatePaths", gate.openspecCandidatePaths],
287
- ["openspecMandatoryPaths", gate.openspecMandatoryPaths],
288
- ["componentSpecCandidatePaths", gate.componentSpecCandidatePaths],
289
- ["openspecCandidateSources.declared", gate.openspecCandidateSources?.declared],
290
- ["openspecCandidateSources.taskSourceCited", gate.openspecCandidateSources?.taskSourceCited],
291
- ["openspecCandidateSources.scanStrict", gate.openspecCandidateSources?.scanStrict],
292
- ];
293
- for (const [field, paths] of candidateGroups) {
294
- for (const [index, candidate] of (paths ?? []).entries()) {
295
- if (!isFrontendSpecFilePath(candidate, roots)) {
296
- context.addIssue({
297
- code: z.ZodIssueCode.custom,
298
- path: [...field.split("."), index],
299
- message: "openspec candidate must be a supported file under the generation-frozen openspec spec roots",
300
- });
301
- }
302
- }
303
- }
304
255
  });
256
+ export const dagFrontendLintBaselineSchema = z
257
+ .object({
258
+ schemaVersion: z.literal(1),
259
+ lintCommands: z.array(z.string().min(1)).min(1),
260
+ lintEvidence: dagShellVerifyEvidenceSchema,
261
+ })
262
+ .strict();
305
263
  export const dagFrontendVerificationBundleSchema = z
306
264
  .object({
307
265
  schemaVersion: z.literal(1),
@@ -315,10 +273,6 @@ export const dagFrontendVerificationBundleSchema = z
315
273
  behaviorEvidence: dagShellVerifyEvidenceSchema,
316
274
  lintBaselineNodeId: dagFrontendNodeIdSchema.optional(),
317
275
  writerNodeIds: z.array(dagFrontendNodeIdSchema).optional(),
318
- /** "initial" = first-pass assess bundle; "repair" = the convergence
319
- * loop's post-repair reverify, which re-runs the same frozen commands
320
- * and fails on any failure (still no same-run repair branch inside the
321
- * node itself). */
322
276
  mode: z.enum(["initial", "repair"]),
323
277
  })
324
278
  .superRefine((bundle, context) => {
@@ -394,135 +348,6 @@ export const dagFrontendReviewContextSchema = z.object({
394
348
  schemaVersion: z.literal(1),
395
349
  requireBaseline: z.literal(true),
396
350
  });
397
- /**
398
- * Deterministic frontend contract-import shell (micro topology). Re-validates
399
- * the generation-frozen managed Contract's source/binding/schema freshness and
400
- * emits a digest. It never invokes a model and never rewrites requirements —
401
- * micro requires a pre-validated managed Contract, not natural-language intake.
402
- */
403
- export const dagFrontendContractImportSchema = z.object({
404
- schemaVersion: z.literal(1),
405
- artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
406
- outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
407
- requireSourceFreshness: z.literal(true),
408
- });
409
- /**
410
- * Deterministic frontend design-policy shell (M8). Materializes the canonical
411
- * implementation contract from the plan node and evaluates the design policy
412
- * (Mock strategy, source freshness, component spec candidates, writeSet
413
- * patterns, allowed dependencies). Runs BEFORE frontend-design-review-pi, so it
414
- * never consumes a design verdict; the verdict is enforced by the writer
415
- * admission shell instead. Replaces the prewrite-gate authorization path.
416
- */
417
- export const dagFrontendDesignPolicySchema = z
418
- .object({
419
- schemaVersion: z.literal(1),
420
- planFromNodeId: dagFrontendNodeIdSchema,
421
- requiredRequirementIds: z
422
- .array(z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/))
423
- .default([]),
424
- allowedMockStrategies: z
425
- .array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"]))
426
- .min(1),
427
- /** Frozen Mock verification command labels the DAG will actually execute. */
428
- mockCommandLabels: z.array(z.string()).default([]),
429
- artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
430
- outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
431
- requireSourceFreshness: z.literal(true),
432
- implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
433
- /** Generation-frozen roots that bound custom normative directories. */
434
- openspecSpecRoots: z
435
- .array(z.string().refine((root) => isValidFrontendSpecRoot(root), "openspec spec root must be a normalized repo-relative directory"))
436
- .optional(),
437
- openspecCandidatePaths: z.array(openspecCandidatePathSchema).default([]),
438
- /** Read-only selector output; omitted for scan-strict and legacy DAGs. */
439
- openspecSelectionNodeId: dagFrontendNodeIdSchema.optional(),
440
- /** Contract-declared and source-explicit paths that cannot be downgraded. */
441
- openspecMandatoryPaths: z.array(openspecCandidatePathSchema).optional(),
442
- openspecCandidateSnapshots: z
443
- .array(openspecCandidateSnapshotSchema)
444
- .optional(),
445
- /**
446
- * Generation-frozen component/theme specification bucket. Derived from
447
- * `designEvidence.classified.component ∪ theme ∪ rule.components` at DAG
448
- * generation. When non-empty and the canonical contract has UI-visible
449
- * work, the design policy requires a non-empty `uiComponentChoices` and
450
- * cross-checks each `specReference.path` against this set + the effective
451
- * plan openspec-citations block.
452
- */
453
- componentSpecCandidatePaths: z.array(openspecCandidatePathSchema).optional(),
454
- /** OpenSpec gate policy (cited | scan-strict); generation always writes it. */
455
- openspecPolicy: z.enum(["cited", "scan-strict"]).optional(),
456
- /** Generation-frozen candidate source metadata (diagnostic). */
457
- openspecCandidateSources: z
458
- .object({
459
- declared: z.array(openspecCandidatePathSchema).default([]),
460
- taskSourceCited: z.array(openspecCandidatePathSchema).default([]),
461
- scanStrict: z.array(openspecCandidatePathSchema).default([]),
462
- })
463
- .optional(),
464
- /** Dependency identifiers authorized at generation time (empty = none). */
465
- allowedDependencies: z.array(z.string()).default([]),
466
- })
467
- .superRefine((gate, context) => {
468
- const roots = gate.openspecSpecRoots ?? DEFAULT_FRONTEND_SPEC_ROOTS;
469
- const candidateGroups = [
470
- ["openspecCandidatePaths", gate.openspecCandidatePaths],
471
- ["openspecMandatoryPaths", gate.openspecMandatoryPaths],
472
- ["componentSpecCandidatePaths", gate.componentSpecCandidatePaths],
473
- ["openspecCandidateSources.declared", gate.openspecCandidateSources?.declared],
474
- ["openspecCandidateSources.taskSourceCited", gate.openspecCandidateSources?.taskSourceCited],
475
- ["openspecCandidateSources.scanStrict", gate.openspecCandidateSources?.scanStrict],
476
- ];
477
- for (const [field, paths] of candidateGroups) {
478
- for (const [index, candidate] of (paths ?? []).entries()) {
479
- if (!isFrontendSpecFilePath(candidate, roots)) {
480
- context.addIssue({
481
- code: z.ZodIssueCode.custom,
482
- path: [...field.split("."), index],
483
- message: "openspec candidate must be a supported file under the generation-frozen openspec spec roots",
484
- });
485
- }
486
- }
487
- }
488
- });
489
- /**
490
- * Deterministic frontend writer-admission shell (M8). Runs after
491
- * frontend-design-review-pi: freezes the writer baseline, derives the concrete
492
- * writeSet, folds the design-policy result + design verdict into admission,
493
- * and writes contracts/frontend-writer-admission-result.json
494
- * (schemaId frontend-writer-admission-shell-v1).
495
- */
496
- export const dagFrontendWriterAdmissionShellSchema = z
497
- .object({
498
- schemaVersion: z.literal(1),
499
- /** Design verdict source (frontend-design-review-pi); pass is enforced.
500
- * Omitted in small topology, which has no design review node. */
501
- designReviewFromNodeId: dagFrontendNodeIdSchema.optional(),
502
- /** Frozen command labels the verification bundle will execute. */
503
- frozenCommandLabels: z.array(z.string()).default([]),
504
- allowedMockStrategies: z
505
- .array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"]))
506
- .min(1),
507
- /** Writer-preceding lint commands; baseline is materialized here (R5). */
508
- lintCommands: z.array(z.string().min(1)).optional(),
509
- lintEvidence: dagShellVerifyEvidenceSchema.optional(),
510
- })
511
- .superRefine((admission, context) => {
512
- if ((admission.lintCommands?.length ?? 0) > 0 && !admission.lintEvidence) {
513
- context.addIssue({
514
- code: z.ZodIssueCode.custom,
515
- path: ["lintEvidence"],
516
- message: "lintEvidence is required when lintCommands are present",
517
- });
518
- }
519
- });
520
- /** Deterministic frontend closeout shell (M8). Renders the closeout from
521
- * committed facts only (review context + typed review terminal + trace). */
522
- export const dagFrontendCloseoutSchema = z.object({
523
- schemaVersion: z.literal(1),
524
- reviewFromNodeId: dagFrontendNodeIdSchema,
525
- });
526
351
  export const ENV_VAR_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
527
352
  export const dagVersionSchema = z
528
353
  .union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)])
@@ -570,14 +395,7 @@ export const dagWriteGuardPolicySchema = z.enum(["full", "tools-only", "filesyst
570
395
  */
571
396
  export const dagWriterOutcomePolicySchema = z
572
397
  .object({
573
- /**
574
- * Protocol face: `implementation-outcome-v1` keeps the legacy
575
- * IMPLEMENTATION_OUTCOME first-line protocol (backend/README/module
576
- * writers + frontend-repair-pi); `frontend-facts-v1` derives the status
577
- * from mechanical facts (write-tool events / run delta / write guard /
578
- * requirement coverage / focused-check) and never reads the first line.
579
- */
580
- type: z.enum(["implementation-outcome-v1", "frontend-facts-v1"]),
398
+ type: z.literal("implementation-outcome-v1"),
581
399
  /** Generation writers may forbid already-satisfied/no-diff completion. */
582
400
  requireChangedFiles: z.boolean().optional(),
583
401
  })
@@ -710,13 +528,10 @@ export const dagShellConfigSchema = z.object({
710
528
  .optional(),
711
529
  requirementCoverageGate: dagRequirementCoverageGateSchema.optional(),
712
530
  jsonArtifactGate: dagJsonArtifactGateSchema.optional(),
713
- frontendDesignPolicy: dagFrontendDesignPolicySchema.optional(),
714
- frontendWriterAdmission: dagFrontendWriterAdmissionShellSchema.optional(),
715
531
  frontendPrewriteGate: dagFrontendPrewriteGateSchema.optional(),
532
+ frontendLintBaseline: dagFrontendLintBaselineSchema.optional(),
716
533
  frontendVerificationBundle: dagFrontendVerificationBundleSchema.optional(),
717
534
  frontendReviewContext: dagFrontendReviewContextSchema.optional(),
718
- frontendContractImport: dagFrontendContractImportSchema.optional(),
719
- frontendCloseout: dagFrontendCloseoutSchema.optional(),
720
535
  frontendTestCaseChecklist: dagFrontendTestCaseChecklistSchema.optional(),
721
536
  frontendBrowserToolPreflight: dagFrontendBrowserToolPreflightSchema.optional(),
722
537
  frontendTestStandardScenarios: dagFrontendTestStandardScenariosSchema.optional(),
@@ -922,7 +737,58 @@ export const structuredContractOutputSchemaIds = [
922
737
  "frontend-implementation-contract-plan-patch-v1",
923
738
  "frontend-implementation-contract-revision-patch-v1",
924
739
  ];
925
- export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
740
+ export const dagArtifactIdentitySchema = z
741
+ .string()
742
+ .regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/, "artifact id must be stable lower-case dot/kebab syntax");
743
+ const dagArtifactRelativePathSchema = z
744
+ .string()
745
+ .min(1)
746
+ .refine((value) => !value.includes("\0"), "artifact path must not contain NUL")
747
+ .refine((value) => !value.includes("\\"), "artifact path must use POSIX separators")
748
+ .refine((value) => !value.startsWith("/") && !/^[A-Za-z]:/.test(value), "artifact path must be run-relative")
749
+ .refine((value) => value.split("/").every((segment) => segment !== "" && segment !== ".."), "artifact path must not escape the run");
750
+ export const dagProducedArtifactSchema = z
751
+ .object({
752
+ artifactId: dagArtifactIdentitySchema,
753
+ kind: z.enum(["structured", "assistant", "stdout"]),
754
+ path: dagArtifactRelativePathSchema,
755
+ mediaType: z.string().min(1),
756
+ schemaId: z.string().min(1).optional(),
757
+ revisionPolicy: z.enum(["immutable", "replace-file"]),
758
+ })
759
+ .strict()
760
+ .superRefine((value, ctx) => {
761
+ if (value.kind === "structured" && !value.schemaId) {
762
+ ctx.addIssue({
763
+ code: z.ZodIssueCode.custom,
764
+ path: ["schemaId"],
765
+ message: "structured artifact requires schemaId",
766
+ });
767
+ }
768
+ if (value.kind !== "structured" && value.schemaId) {
769
+ ctx.addIssue({
770
+ code: z.ZodIssueCode.custom,
771
+ path: ["schemaId"],
772
+ message: "only structured artifacts may declare schemaId",
773
+ });
774
+ }
775
+ if (value.revisionPolicy === "replace-file" &&
776
+ value.kind !== "structured") {
777
+ ctx.addIssue({
778
+ code: z.ZodIssueCode.custom,
779
+ path: ["revisionPolicy"],
780
+ message: "MVP revisions are limited to structured artifacts",
781
+ });
782
+ }
783
+ });
784
+ export const dagConsumedArtifactSchema = z
785
+ .object({
786
+ artifactId: dagArtifactIdentitySchema,
787
+ required: z.boolean().default(true),
788
+ })
789
+ .strict();
790
+ export const dagTaskSchema = z.object({
791
+ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
926
792
  depends_on: z.array(z.string()).default([]),
927
793
  /**
928
794
  * How SKIPPED upstreams affect readiness.
@@ -955,19 +821,6 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
955
821
  role: dagRoleSchema.optional(),
956
822
  skills: z.array(z.string()).optional(),
957
823
  toolProfile: dagToolProfileSchema.optional(),
958
- /** Bounded read-only discovery budget for nodes that inspect a repository. */
959
- readBudget: z
960
- .object({
961
- maxFiles: z.number().int().positive(),
962
- maxBytes: z.number().int().positive(),
963
- maxMs: z.number().int().positive().optional(),
964
- /** Maximum read/grep/find/ls calls. */
965
- maxOperations: z.number().int().positive().optional(),
966
- /** On any read budget exhaustion, return concise completion guidance instead of failing the node. */
967
- onExhaustion: z.enum(["return-guidance"]).optional(),
968
- })
969
- .strict()
970
- .optional(),
971
824
  /**
972
825
  * Explicit Pi extension allowlist (backend implementation DAGs only):
973
826
  * frozen short ids resolved at run time against the Pi settings package
@@ -998,6 +851,15 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
998
851
  shell: dagShellConfigSchema.optional(),
999
852
  static: dagStaticConfigSchema.optional(),
1000
853
  outputContract: z.string().optional(),
854
+ /**
855
+ * Frozen producer declarations for run-owned artifacts. These declarations,
856
+ * together with consumesArtifacts, are the only authority for artifact
857
+ * lineage and impact analysis. Prompt/outputContract/path heuristics are
858
+ * intentionally forbidden.
859
+ */
860
+ producesArtifacts: z.array(dagProducedArtifactSchema).optional(),
861
+ /** Frozen logical artifact inputs consumed by this node. */
862
+ consumesArtifacts: z.array(dagConsumedArtifactSchema).optional(),
1001
863
  outputMode: dagOutputModeSchema.optional(),
1002
864
  firstProtocolLine: z.string().min(1).optional(),
1003
865
  /**
@@ -1026,8 +888,7 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
1026
888
  })
1027
889
  .strict()
1028
890
  .superRefine((value, ctx) => {
1029
- if (value.schemaId ===
1030
- "frontend-implementation-contract-plan-patch-v1" &&
891
+ if (value.schemaId === "frontend-implementation-contract-plan-patch-v1" &&
1031
892
  !value.skeleton) {
1032
893
  ctx.addIssue({
1033
894
  code: "custom",
@@ -1082,6 +943,121 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
1082
943
  dynamicCondition: dagDynamicConditionSchema.optional(),
1083
944
  dynamicLoopUntil: dagDynamicLoopUntilSchema.optional(),
1084
945
  });
946
+ function hasTransitiveDependency(tasksById, consumerNodeId, producerNodeId) {
947
+ const queue = [...(tasksById.get(consumerNodeId)?.depends_on ?? [])];
948
+ const seen = new Set();
949
+ while (queue.length > 0) {
950
+ const nodeId = queue.shift();
951
+ if (nodeId === producerNodeId)
952
+ return true;
953
+ if (seen.has(nodeId))
954
+ continue;
955
+ seen.add(nodeId);
956
+ queue.push(...(tasksById.get(nodeId)?.depends_on ?? []));
957
+ }
958
+ return false;
959
+ }
960
+ function validateFrozenArtifactBindings(tasks, ctx) {
961
+ const tasksById = new Map(tasks.map((task) => [task.id, task]));
962
+ const producers = new Map();
963
+ const paths = new Map();
964
+ for (const [taskIndex, task] of tasks.entries()) {
965
+ const localIds = new Set();
966
+ for (const [artifactIndex, artifact] of (task.producesArtifacts ?? []).entries()) {
967
+ if (localIds.has(artifact.artifactId) ||
968
+ producers.has(artifact.artifactId)) {
969
+ ctx.addIssue({
970
+ code: z.ZodIssueCode.custom,
971
+ path: [
972
+ "tasks",
973
+ taskIndex,
974
+ "producesArtifacts",
975
+ artifactIndex,
976
+ "artifactId",
977
+ ],
978
+ message: `artifact ${artifact.artifactId} must have exactly one producer`,
979
+ });
980
+ }
981
+ else {
982
+ producers.set(artifact.artifactId, {
983
+ nodeId: task.id,
984
+ taskIndex,
985
+ artifactIndex,
986
+ path: artifact.path,
987
+ });
988
+ }
989
+ localIds.add(artifact.artifactId);
990
+ const foldedPath = artifact.path
991
+ .normalize("NFC")
992
+ .toLocaleLowerCase("en-US");
993
+ const existingArtifactId = paths.get(foldedPath);
994
+ if (existingArtifactId && existingArtifactId !== artifact.artifactId) {
995
+ ctx.addIssue({
996
+ code: z.ZodIssueCode.custom,
997
+ path: [
998
+ "tasks",
999
+ taskIndex,
1000
+ "producesArtifacts",
1001
+ artifactIndex,
1002
+ "path",
1003
+ ],
1004
+ message: `artifact path collides with ${existingArtifactId} under case folding`,
1005
+ });
1006
+ }
1007
+ else {
1008
+ paths.set(foldedPath, artifact.artifactId);
1009
+ }
1010
+ }
1011
+ }
1012
+ for (const [taskIndex, task] of tasks.entries()) {
1013
+ const consumed = new Set();
1014
+ for (const [artifactIndex, binding] of (task.consumesArtifacts ?? []).entries()) {
1015
+ if (consumed.has(binding.artifactId)) {
1016
+ ctx.addIssue({
1017
+ code: z.ZodIssueCode.custom,
1018
+ path: [
1019
+ "tasks",
1020
+ taskIndex,
1021
+ "consumesArtifacts",
1022
+ artifactIndex,
1023
+ "artifactId",
1024
+ ],
1025
+ message: `artifact ${binding.artifactId} is consumed more than once by ${task.id}`,
1026
+ });
1027
+ }
1028
+ consumed.add(binding.artifactId);
1029
+ const producer = producers.get(binding.artifactId);
1030
+ if (!producer) {
1031
+ ctx.addIssue({
1032
+ code: z.ZodIssueCode.custom,
1033
+ path: [
1034
+ "tasks",
1035
+ taskIndex,
1036
+ "consumesArtifacts",
1037
+ artifactIndex,
1038
+ "artifactId",
1039
+ ],
1040
+ message: `artifact ${binding.artifactId} has no declared producer`,
1041
+ });
1042
+ continue;
1043
+ }
1044
+ if (producer.nodeId === task.id ||
1045
+ !hasTransitiveDependency(tasksById, task.id, producer.nodeId)) {
1046
+ ctx.addIssue({
1047
+ code: z.ZodIssueCode.custom,
1048
+ path: [
1049
+ "tasks",
1050
+ taskIndex,
1051
+ "consumesArtifacts",
1052
+ artifactIndex,
1053
+ "artifactId",
1054
+ ],
1055
+ message: `consumer ${task.id} must be downstream of producer ${producer.nodeId}`,
1056
+ });
1057
+ }
1058
+ }
1059
+ }
1060
+ }
1085
1061
  export const dagModelsSchema = z
1086
1062
  .object({
1087
1063
  HIGH: z.string().optional(),
@@ -1111,9 +1087,7 @@ export const dagBudgetSchema = campaignBudgetSchema;
1111
1087
  export const dagSourceBindingSourceSchema = z.object({
1112
1088
  kind: z.enum(["requirement", "constraint", "reference"]),
1113
1089
  path: z.string().min(1),
1114
- sha256: z
1115
- .string()
1116
- .regex(/^[a-f0-9]{64}$/, "sha256 must be lowercase hex"),
1090
+ sha256: z.string().regex(/^[a-f0-9]{64}$/, "sha256 must be lowercase hex"),
1117
1091
  });
1118
1092
  /**
1119
1093
  * DagSourceBinding v1:无 ledger 字段(schemaVersion=1,.strict() 拒绝 ledger
@@ -1215,22 +1189,6 @@ export const dagSpecSchema = z
1215
1189
  taskContractBinding: dagTaskContractBindingSchema.optional(),
1216
1190
  /** Optional unresolved parent-run feedback for a full task rerun. */
1217
1191
  rerunFeedback: dagRerunFeedbackSchema.optional(),
1218
- /** Frontend recovery continuation quota (frozen at generation time from
1219
- * task.json frontendRecovery.maxContinuations). Bounds how many times a
1220
- * failed frontend run may auto-recover via M6 recovery operations before
1221
- * the run stays failed and needs human intervention. */
1222
- frontendRecovery: z
1223
- .object({
1224
- maxContinuations: z
1225
- .number()
1226
- .int()
1227
- .min(1)
1228
- .max(5)
1229
- .optional()
1230
- .default(1),
1231
- })
1232
- .strict()
1233
- .optional(),
1234
1192
  /** Backend-test-only workspace mutation control; absent means Git. */
1235
1193
  backendTestWorkspaceControl: z.enum(["git", "filesystem-only"]).optional(),
1236
1194
  /** Frozen backend-test artifact layout (plan A); absent = historical default testcase/ layout. */
@@ -1280,6 +1238,7 @@ export const dagSpecSchema = z
1280
1238
  })
1281
1239
  .superRefine((spec, ctx) => {
1282
1240
  const supportsV3Fields = spec.version === 3 || spec.version === 4;
1241
+ validateFrozenArtifactBindings(spec.tasks, ctx);
1283
1242
  if (spec.evaluation && !supportsV3Fields) {
1284
1243
  ctx.addIssue({
1285
1244
  code: z.ZodIssueCode.custom,
@@ -1356,9 +1315,6 @@ export const FRONTEND_RECOVERY_RESULT_SCHEMA_VERSION = 1;
1356
1315
  * Phase 3a parents converge first, so `rollback-pending` is not produced and
1357
1316
  * was removed by AC-1; the phase starts at `child-staging` and ends at
1358
1317
  * `settled`.
1359
- *
1360
- * @deprecated Read-only compatibility only; the M6 operation controller no
1361
- * longer uses this phase as the authoritative recovery entry point.
1362
1318
  */
1363
1319
  export const frontendRecoveryPhaseSchema = z.enum([
1364
1320
  "child-staging",
@@ -1378,9 +1334,6 @@ export const frontendRecoveryPhaseSchema = z.enum([
1378
1334
  * runner's `(existing?.revision ?? 0) + 1` CAS write (the schema only bounds it
1379
1335
  * non-negative). `childRunId` may be absent through `child-activating` and must
1380
1336
  * be non-empty from `child-running` onward.
1381
- *
1382
- * @deprecated Read-only compatibility only; the runner no longer writes this
1383
- * intent (the M6 operation controller supervises recovery instead).
1384
1337
  */
1385
1338
  export const frontendRecoveryStateSchema = z
1386
1339
  .object({
@@ -1452,10 +1405,6 @@ export const frontendRecoveryEvidenceRefSchema = z
1452
1405
  * stays permissive (failureClass optional, evidenceRefs may be empty) so it
1453
1406
  * accepts the runner's actual phase-3a products, which do not always carry a
1454
1407
  * failure class or evidence reference.
1455
- *
1456
- * @deprecated Read-only compatibility only; the M6 operation controller writes
1457
- * `recovered`/`auto-recovery-blocked` results directly and never uses this
1458
- * schema as the authoritative recovery settlement.
1459
1408
  */
1460
1409
  export const frontendRecoveryResultSchema = z
1461
1410
  .object({
@@ -3,7 +3,7 @@ import { resolveShellCommands } from "../../executors/shell-executor.js";
3
3
  import { pathMatchesPattern } from "../../shared/git-progress.js";
4
4
  import { resolveRepairTaskForGate } from "./repair-artifact.js";
5
5
  import { topoSortToRanks } from "./topo.js";
6
- import { isSafeReadOnlyPiRetryCandidate, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, CONTEXT_OVERFLOW_RETRY_CATEGORY, } from "./retry-policy.js";
6
+ import { isSafeReadOnlyPiRetryCandidate, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, } from "./retry-policy.js";
7
7
  import { isBackendTestCompletenessRetryCandidate } from "./backend-test-writer-completeness.js";
8
8
  import { defaultFrontendTestLayout, frontendTestLayoutFromSpec, } from "./frontend-test-layout.js";
9
9
  const GOVERNANCE_WARNING_TYPES = new Set([
@@ -464,9 +464,7 @@ function validateShellTaskConfig(task, spec, issues) {
464
464
  if (commands.length === 0 &&
465
465
  !shell.jsonArtifactGate &&
466
466
  !shell.backendTestPipeline &&
467
- !shell.frontendDesignPolicy &&
468
- !shell.frontendWriterAdmission &&
469
- !shell.frontendCloseout &&
467
+ !shell.frontendPrewriteGate &&
470
468
  !shell.frontendBrowserToolPreflight &&
471
469
  !shell.frontendTestStandardScenarios &&
472
470
  !shell.frontendTestEnvironmentProbe &&
@@ -539,26 +537,18 @@ function validateShellTaskConfig(task, spec, issues) {
539
537
  }
540
538
  }
541
539
  }
542
- if (shell.frontendDesignPolicy) {
543
- const sourceNodeIds = [shell.frontendDesignPolicy.planFromNodeId];
544
- for (const sourceNodeId of new Set(sourceNodeIds)) {
545
- if (!task.depends_on.includes(sourceNodeId)) {
546
- issues.push({
547
- type: "missing-dependency",
548
- message: `shell task ${task.id} frontendDesignPolicy source ${sourceNodeId} must be a direct dependency`,
549
- });
550
- }
551
- }
552
- }
553
- if (shell.frontendWriterAdmission) {
554
- const sourceNodeIds = shell.frontendWriterAdmission.designReviewFromNodeId
555
- ? [shell.frontendWriterAdmission.designReviewFromNodeId]
556
- : [];
540
+ if (shell.frontendPrewriteGate) {
541
+ const sourceNodeIds = [
542
+ shell.frontendPrewriteGate.planFromNodeId,
543
+ ...shell.frontendPrewriteGate.planFallbackFromNodeIds,
544
+ shell.frontendPrewriteGate.reviewFromNodeId,
545
+ ...shell.frontendPrewriteGate.reviewFallbackFromNodeIds,
546
+ ];
557
547
  for (const sourceNodeId of new Set(sourceNodeIds)) {
558
548
  if (!task.depends_on.includes(sourceNodeId)) {
559
549
  issues.push({
560
550
  type: "missing-dependency",
561
- message: `shell task ${task.id} frontendWriterAdmission source ${sourceNodeId} must be a direct dependency`,
551
+ message: `shell task ${task.id} frontendPrewriteGate source ${sourceNodeId} must be a direct dependency`,
562
552
  });
563
553
  }
564
554
  }
@@ -656,18 +646,12 @@ function validateRetryPolicyTaskConfig(task, issues) {
656
646
  }
657
647
  if (isWriterTransportCandidate) {
658
648
  const categories = new Set(task.retryPolicy.retryCategories);
659
- const allowedWriterTransportCategories = new Set([
660
- WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
661
- CONTEXT_OVERFLOW_RETRY_CATEGORY,
662
- ]);
663
- const allAllowed = [...categories].every((category) => allowedWriterTransportCategories.has(category));
664
649
  if (task.retryPolicy.maxAttempts !== 2 ||
665
- categories.size === 0 ||
666
- !allAllowed ||
650
+ categories.size !== 1 ||
667
651
  !categories.has(WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY)) {
668
652
  issues.push({
669
653
  type: "invalid-retry-policy",
670
- message: `task ${task.id} writer transport retryPolicy must use exactly two total attempts and only writer-clean-timeout (plus optional context-overflow)`,
654
+ message: `task ${task.id} writer transport retryPolicy must use exactly two total attempts and only ${WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY}`,
671
655
  });
672
656
  }
673
657
  }