@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,9 +1,202 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readFile } from "node:fs/promises";
3
+ import { z } from "zod";
3
4
  import { assertTaskContractBindingFresh } from "./task-contract-binding.js";
4
5
  import { isSafeReadOnlyPiRetryCandidate, isTransportCrashedExclusiveWriter } from "./retry-policy.js";
5
6
  import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
6
7
  import { composeDagPromptOverridePrompt, MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS, } from "../../shared/dag-prompt-override.js";
8
+ import { artifactOverrideSetV1Schema, workerContinuationAuthorityV1Schema, } from "../../shared/context-transfer.js";
9
+ import { buildArtifactBindingGraph, computeArtifactImpact, } from "./artifact-bindings.js";
10
+ import { assertValidMaterializedDagTask } from "./validate.js";
11
+ const rerunBindingsV1Schema = z
12
+ .object({
13
+ sourceBindingMatched: z.boolean(),
14
+ taskContractBindingMatched: z.boolean(),
15
+ controllerIdentityMatched: z.boolean(),
16
+ skillSnapshotVerified: z.boolean(),
17
+ workspaceMatched: z.boolean(),
18
+ })
19
+ .strict();
20
+ const hash64Schema = z.string().regex(/^[a-f0-9]{64}$/);
21
+ const highRiskContinuationAuthorityV1Schema = z
22
+ .object({
23
+ schemaVersion: z.literal(1),
24
+ workspaceFingerprint: hash64Schema,
25
+ writer: z
26
+ .object({
27
+ nodeIds: z.array(z.string().min(1)).min(1),
28
+ writeSet: z.array(z.string().min(1)).min(1),
29
+ writeSetSha256: hash64Schema,
30
+ })
31
+ .strict()
32
+ .optional(),
33
+ dynamic: z
34
+ .object({
35
+ nodeIds: z.array(z.string().min(1)).min(1),
36
+ graphSha256: hash64Schema,
37
+ })
38
+ .strict()
39
+ .optional(),
40
+ budget: z
41
+ .object({
42
+ parentLedgerSha256: hash64Schema,
43
+ inheritedConsumed: z
44
+ .object({
45
+ tokens: z.number().int().nonnegative().nullable(),
46
+ wallTimeMs: z.number().nonnegative(),
47
+ executorCalls: z.number().int().nonnegative(),
48
+ repairPasses: z.number().int().nonnegative(),
49
+ peakContextChars: z.number().int().nonnegative(),
50
+ missingTokenNodeIds: z.array(z.string()),
51
+ })
52
+ .strict(),
53
+ })
54
+ .strict()
55
+ .optional(),
56
+ })
57
+ .strict();
58
+ const rerunPlanCommonSchema = {
59
+ eligible: z.boolean(),
60
+ parentRunId: z.string().min(1),
61
+ parentLifecycle: z.enum(["completed", "dry-run", "init-only"]),
62
+ selectedNodeId: z.string().min(1),
63
+ effectiveFromNodeId: z.string().min(1).optional(),
64
+ rewriteApplied: z.boolean(),
65
+ resetNodeIds: z.array(z.string().min(1)),
66
+ importedNodeIds: z.array(z.string().min(1)),
67
+ blockingNodes: z.array(z.object({ nodeId: z.string().min(1), reasonCode: z.string().min(1) }).strict()),
68
+ reasonCodes: z.array(z.string().min(1)),
69
+ blockedReasons: z.array(z.string().min(1)),
70
+ risk: z.enum(["low", "medium", "high"]),
71
+ verificationPolicy: z
72
+ .object({
73
+ mode: z.enum(["inherited", "rerun"]),
74
+ nodeIds: z.array(z.string().min(1)),
75
+ inheritedFromRunId: z.string().min(1).optional(),
76
+ })
77
+ .strict()
78
+ .optional(),
79
+ workerAssociation: z.discriminatedUnion("kind", [
80
+ z.object({ kind: z.literal("standalone") }).strict(),
81
+ z
82
+ .object({
83
+ kind: z.literal("worker-managed"),
84
+ featureId: z.string().min(1).optional(),
85
+ taskId: z.string().min(1).optional(),
86
+ workerRunId: z.string().min(1).optional(),
87
+ })
88
+ .strict(),
89
+ ]),
90
+ estimatedExecution: z
91
+ .object({
92
+ nodeCount: z.number().int().nonnegative(),
93
+ piCallCount: z.number().int().nonnegative(),
94
+ shellNodeCount: z.number().int().nonnegative(),
95
+ avoidedNodeCount: z.number().int().nonnegative(),
96
+ avoidedPiCallCount: z.number().int().nonnegative(),
97
+ })
98
+ .strict(),
99
+ suggestedAction: z.enum([
100
+ "dag-rerun",
101
+ "standalone-task-rerun",
102
+ "worker-task-retry",
103
+ "regenerate",
104
+ "resume",
105
+ "reconcile",
106
+ "manual",
107
+ ]),
108
+ promptOverrides: z
109
+ .array(z
110
+ .object({
111
+ nodeId: z.string().min(1),
112
+ mode: z.enum(["replace", "append"]),
113
+ basePromptSha256: z.string().regex(/^[a-f0-9]{64}$/),
114
+ promptSha256: z.string().regex(/^[a-f0-9]{64}$/),
115
+ })
116
+ .strict())
117
+ .optional(),
118
+ workerContinuationAuthority: workerContinuationAuthorityV1Schema.optional(),
119
+ highRiskAuthority: highRiskContinuationAuthorityV1Schema.optional(),
120
+ };
121
+ export const dagRerunPlanV2Schema = z
122
+ .object({
123
+ schemaVersion: z.literal(2),
124
+ ...rerunPlanCommonSchema,
125
+ trigger: z.enum(["node-rerun", "prompt-override", "artifact-override"]),
126
+ artifactOverrides: artifactOverrideSetV1Schema.optional(),
127
+ impact: z
128
+ .object({
129
+ producerNodeId: z.string().min(1).optional(),
130
+ directConsumerNodeIds: z.array(z.string().min(1)),
131
+ effectiveFromNodeId: z.string().min(1),
132
+ affectedNodeIds: z.array(z.string().min(1)),
133
+ importedNodeIds: z.array(z.string().min(1)),
134
+ blockedNodeIds: z.array(z
135
+ .object({ nodeId: z.string().min(1), reasonCode: z.string().min(1) })
136
+ .strict()),
137
+ })
138
+ .strict(),
139
+ bindings: rerunBindingsV1Schema
140
+ .extend({
141
+ artifactGraphSha256: hash64Schema,
142
+ overrideSetHash: hash64Schema.optional(),
143
+ stagingRevisionSha256: hash64Schema.optional(),
144
+ parentWorkspaceFingerprint: hash64Schema.optional(),
145
+ currentWorkspaceFingerprint: hash64Schema.optional(),
146
+ budgetLedgerSha256: hash64Schema.optional(),
147
+ })
148
+ .strict(),
149
+ planHash: z.string().regex(/^[a-f0-9]{64}$/),
150
+ })
151
+ .strict()
152
+ .superRefine((plan, ctx) => {
153
+ const artifactTrigger = plan.trigger === "artifact-override";
154
+ if (artifactTrigger !== Boolean(plan.artifactOverrides)) {
155
+ ctx.addIssue({
156
+ code: z.ZodIssueCode.custom,
157
+ path: ["artifactOverrides"],
158
+ message: "artifact override trigger and override set must appear together",
159
+ });
160
+ }
161
+ if (plan.artifactOverrides &&
162
+ (plan.bindings.overrideSetHash !== plan.artifactOverrides.overrideSetHash ||
163
+ !plan.bindings.stagingRevisionSha256)) {
164
+ ctx.addIssue({
165
+ code: z.ZodIssueCode.custom,
166
+ path: ["bindings"],
167
+ message: "artifact override bindings must match the override set and revision",
168
+ });
169
+ }
170
+ if (plan.workerContinuationAuthority &&
171
+ plan.workerAssociation.kind !== "worker-managed") {
172
+ ctx.addIssue({
173
+ code: z.ZodIssueCode.custom,
174
+ path: ["workerContinuationAuthority"],
175
+ message: "Worker continuation authority requires a Worker-managed parent",
176
+ });
177
+ }
178
+ if (plan.highRiskAuthority) {
179
+ if (plan.bindings.parentWorkspaceFingerprint !==
180
+ plan.highRiskAuthority.workspaceFingerprint ||
181
+ plan.bindings.currentWorkspaceFingerprint !==
182
+ plan.highRiskAuthority.workspaceFingerprint) {
183
+ ctx.addIssue({
184
+ code: z.ZodIssueCode.custom,
185
+ path: ["highRiskAuthority", "workspaceFingerprint"],
186
+ message: "high-risk authority must bind the matching workspace fingerprints",
187
+ });
188
+ }
189
+ if (plan.highRiskAuthority.budget &&
190
+ plan.bindings.budgetLedgerSha256 !==
191
+ plan.highRiskAuthority.budget.parentLedgerSha256) {
192
+ ctx.addIssue({
193
+ code: z.ZodIssueCode.custom,
194
+ path: ["bindings", "budgetLedgerSha256"],
195
+ message: "budget ledger binding must match high-risk authority",
196
+ });
197
+ }
198
+ }
199
+ });
7
200
  const REVIEW_VERDICT_RECOVERY_NODE_ID = "review-verdict-recovery-pi";
8
201
  const SAFE_SHELL_GATE_KEYS = [
9
202
  "verdictGate",
@@ -117,6 +310,68 @@ function isWriterTask(task, spec) {
117
310
  const writePolicy = resolveWritePolicy(task, spec);
118
311
  return writePolicy === "exclusive" || writePolicy === "docs-only";
119
312
  }
313
+ function passesWriterBoundaryValidation(task, parentSpec) {
314
+ try {
315
+ assertValidMaterializedDagTask(task, parentSpec);
316
+ return true;
317
+ }
318
+ catch {
319
+ return false;
320
+ }
321
+ }
322
+ function isBoundedArtifactContinuationWriter(task, parentSpec) {
323
+ return (task.executor === "pi" &&
324
+ task.toolProfile === "write" &&
325
+ task.writePolicy === "exclusive" &&
326
+ (task.writeSet?.length ?? 0) > 0 &&
327
+ (task.allowedPaths?.length ?? 0) > 0 &&
328
+ (task.forbiddenPaths?.length ?? 0) > 0 &&
329
+ passesWriterBoundaryValidation(task, parentSpec));
330
+ }
331
+ function isDynamicWriterTemplate(task) {
332
+ return (task.toolProfile === "write" ||
333
+ task.writePolicy === "exclusive" ||
334
+ task.writePolicy === "docs-only");
335
+ }
336
+ function isBoundedDynamicWriterTemplate(task, parentSpec) {
337
+ if (!(task.executor === "pi" &&
338
+ task.toolProfile === "write" &&
339
+ task.writePolicy === "exclusive" &&
340
+ (task.writeSet?.length ?? 0) > 0 &&
341
+ (task.allowedPaths?.length ?? 0) > 0 &&
342
+ (task.forbiddenPaths?.length ?? 0) > 0))
343
+ return false;
344
+ return passesWriterBoundaryValidation({
345
+ id: "continuation-dynamic-writer-template",
346
+ depends_on: [],
347
+ executor: task.executor,
348
+ role: task.role,
349
+ complexity: task.complexity,
350
+ subtask_prompt: task.subtaskPromptTemplate,
351
+ toolProfile: task.toolProfile,
352
+ writePolicy: task.writePolicy,
353
+ allowedPaths: task.allowedPaths,
354
+ forbiddenPaths: task.forbiddenPaths,
355
+ writeSet: task.writeSet,
356
+ outputContract: task.outputContract,
357
+ }, parentSpec);
358
+ }
359
+ function collectDynamicWriterTemplates(task) {
360
+ const writers = [];
361
+ const expansionChild = task.dynamicExpansion?.childTask;
362
+ if (expansionChild && isDynamicWriterTemplate(expansionChild)) {
363
+ writers.push({ id: `${task.id}#child`, task: expansionChild });
364
+ }
365
+ for (const bodyTask of task.dynamicLoopUntil?.bodyTasks ?? []) {
366
+ if (isDynamicWriterTemplate(bodyTask)) {
367
+ writers.push({ id: `${task.id}#${bodyTask.id}`, task: bodyTask });
368
+ }
369
+ }
370
+ return writers;
371
+ }
372
+ function hasOnlyBoundedDynamicWriters(task, parentSpec) {
373
+ return collectDynamicWriterTemplates(task).every(({ task: writer }) => isBoundedDynamicWriterTemplate(writer, parentSpec));
374
+ }
120
375
  function hasDynamicRuntime(task) {
121
376
  return Boolean(task.dynamicExpansion ||
122
377
  task.dynamicReduction ||
@@ -160,16 +415,12 @@ function isCanonicalGeneratedVerifyShell(task) {
160
415
  return false;
161
416
  if (shell.projectGovernanceGate)
162
417
  return false;
163
- if (shell.frontendDesignPolicy)
164
- return false;
165
- if (shell.frontendWriterAdmission)
418
+ if (shell.frontendPrewriteGate)
166
419
  return false;
167
420
  if (shell.frontendVerificationBundle)
168
421
  return false;
169
422
  if (shell.frontendReviewContext)
170
423
  return false;
171
- if (shell.frontendCloseout)
172
- return false;
173
424
  if (shell.backendTestPipeline)
174
425
  return false;
175
426
  if (shell.nonZeroExitPolicy === "record")
@@ -178,6 +429,34 @@ function isCanonicalGeneratedVerifyShell(task) {
178
429
  return false;
179
430
  return true;
180
431
  }
432
+ /**
433
+ * Frozen frontend workflow shells are deterministic runtime adapters. They may
434
+ * materialize run-owned evidence or run the already-frozen verification
435
+ * commands, but they do not receive a generic shell-continuation exemption.
436
+ */
437
+ function isCanonicalFrontendArtifactContinuationShell(task) {
438
+ if (task.executor !== "shell" || task.writePolicy !== "read-only" || !task.shell) {
439
+ return false;
440
+ }
441
+ const shell = task.shell;
442
+ if (shell.envAllowlist?.length || shell.nonZeroExitPolicy === "record")
443
+ return false;
444
+ switch (task.id) {
445
+ case "frontend-prewrite-gate-shell":
446
+ return Boolean(shell.frontendPrewriteGate) && (shell.commands?.length ?? 0) === 0;
447
+ case "frontend-lint-baseline-shell":
448
+ return Boolean(shell.frontendLintBaseline &&
449
+ shell.commands?.length === shell.frontendLintBaseline.lintCommands.length &&
450
+ shell.commands.every((command, index) => command === shell.frontendLintBaseline.lintCommands[index]));
451
+ case "frontend-verify-assess-shell":
452
+ case "frontend-reverify-shell":
453
+ return Boolean(shell.frontendVerificationBundle) && (shell.commands?.length ?? 0) === 0;
454
+ case "frontend-review-context-shell":
455
+ return Boolean(shell.frontendReviewContext) && (shell.commands?.length ?? 0) === 0;
456
+ default:
457
+ return false;
458
+ }
459
+ }
181
460
  function isSafeGateOnlyShell(task) {
182
461
  if (task.executor !== "shell")
183
462
  return false;
@@ -195,16 +474,12 @@ function isSafeGateOnlyShell(task) {
195
474
  return false;
196
475
  if (shell.preset)
197
476
  return false;
198
- if (shell.frontendDesignPolicy)
199
- return false;
200
- if (shell.frontendWriterAdmission)
477
+ if (shell.frontendPrewriteGate)
201
478
  return false;
202
479
  if (shell.frontendVerificationBundle)
203
480
  return false;
204
481
  if (shell.frontendReviewContext)
205
482
  return false;
206
- if (shell.frontendCloseout)
207
- return false;
208
483
  if (shell.backendTestPipeline)
209
484
  return false;
210
485
  if (shell.verifyEvidence)
@@ -331,6 +606,10 @@ export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
331
606
  continue;
332
607
  }
333
608
  if (isWriterTask(task, spec)) {
609
+ if (options.artifactContinuation === true &&
610
+ isBoundedArtifactContinuationWriter(task, spec)) {
611
+ continue;
612
+ }
334
613
  if (nodeId === options.allowedTransportWriterNodeId)
335
614
  continue;
336
615
  if (nodeId === options.allowedExclusiveShellFromNodeId &&
@@ -353,6 +632,10 @@ export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
353
632
  continue;
354
633
  }
355
634
  if (hasDynamicRuntime(task)) {
635
+ if (options.artifactContinuation === true &&
636
+ hasOnlyBoundedDynamicWriters(task, spec)) {
637
+ continue;
638
+ }
356
639
  blockingNodes.push({
357
640
  nodeId,
358
641
  reasonCode: "dynamic-rerun-unsupported",
@@ -375,7 +658,9 @@ export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
375
658
  }
376
659
  if (task.executor === "shell") {
377
660
  if (!isSafeGateOnlyShell(task) &&
378
- !isCanonicalGeneratedVerifyShell(task)) {
661
+ !isCanonicalGeneratedVerifyShell(task) &&
662
+ !(options.artifactContinuation === true &&
663
+ isCanonicalFrontendArtifactContinuationShell(task))) {
379
664
  blockingNodes.push({
380
665
  nodeId,
381
666
  reasonCode: "restart-subgraph-contains-unsafe-shell",
@@ -488,6 +773,267 @@ export function computePlanHash(planWithoutHash) {
488
773
  const canonical = JSON.stringify(sortKeysDeep(planWithoutHash));
489
774
  return createHash("sha256").update(canonical, "utf8").digest("hex");
490
775
  }
776
+ function hashCanonicalValue(value) {
777
+ return createHash("sha256")
778
+ .update(JSON.stringify(sortKeysDeep(value)), "utf8")
779
+ .digest("hex");
780
+ }
781
+ function collectHighRiskContinuationAuthority(input) {
782
+ const tasks = taskById(input.parentSpec);
783
+ const writerNodeIds = [];
784
+ const writeSet = new Set();
785
+ const dynamicNodeIds = [];
786
+ for (const nodeId of input.affectedNodeIds) {
787
+ const task = tasks.get(nodeId);
788
+ if (!task)
789
+ continue;
790
+ if (isBoundedArtifactContinuationWriter(task, input.parentSpec)) {
791
+ writerNodeIds.push(nodeId);
792
+ for (const pattern of task.writeSet ?? [])
793
+ writeSet.add(pattern);
794
+ }
795
+ if (hasDynamicRuntime(task)) {
796
+ dynamicNodeIds.push(nodeId);
797
+ for (const writer of collectDynamicWriterTemplates(task)) {
798
+ writerNodeIds.push(writer.id);
799
+ for (const pattern of writer.task.writeSet ?? [])
800
+ writeSet.add(pattern);
801
+ }
802
+ }
803
+ }
804
+ const ledger = input.parentState.budgetLedger;
805
+ const hasHardBudget = (input.parentSpec.budget?.mode ?? input.parentState.budget?.mode) === "hard";
806
+ const highRisk = writerNodeIds.length > 0 || dynamicNodeIds.length > 0 || hasHardBudget;
807
+ if (!highRisk || !input.workspaceFingerprint)
808
+ return undefined;
809
+ const normalizedWriters = [...new Set(writerNodeIds)].sort();
810
+ const normalizedWriteSet = [...writeSet].sort();
811
+ const normalizedDynamic = [...new Set(dynamicNodeIds)].sort();
812
+ return {
813
+ schemaVersion: 1,
814
+ workspaceFingerprint: input.workspaceFingerprint,
815
+ ...(normalizedWriters.length > 0
816
+ ? {
817
+ writer: {
818
+ nodeIds: normalizedWriters,
819
+ writeSet: normalizedWriteSet,
820
+ writeSetSha256: hashCanonicalValue(normalizedWriteSet),
821
+ },
822
+ }
823
+ : {}),
824
+ ...(normalizedDynamic.length > 0
825
+ ? {
826
+ dynamic: {
827
+ nodeIds: normalizedDynamic,
828
+ graphSha256: hashCanonicalValue(normalizedDynamic.map((nodeId) => tasks.get(nodeId))),
829
+ },
830
+ }
831
+ : {}),
832
+ ...(hasHardBudget && ledger
833
+ ? {
834
+ budget: {
835
+ parentLedgerSha256: hashCanonicalValue(ledger),
836
+ inheritedConsumed: structuredClone(ledger.consumed),
837
+ },
838
+ }
839
+ : {}),
840
+ };
841
+ }
842
+ export function evaluateArtifactOverrideRerunPlan(input) {
843
+ const graph = buildArtifactBindingGraph(input.parentSpec);
844
+ const declaration = graph.artifacts.find((artifact) => artifact.artifactId === input.artifactId);
845
+ const impact = computeArtifactImpact(input.parentSpec, input.artifactId);
846
+ const reasonCodes = [];
847
+ const blockedReasons = [];
848
+ const blockingNodes = [];
849
+ const workerAssociation = input.workerAssociation ?? { kind: "standalone" };
850
+ const block = (code, nodeId) => {
851
+ reasonCodes.push(code);
852
+ blockedReasons.push(code);
853
+ if (nodeId)
854
+ blockingNodes.push({ nodeId, reasonCode: code });
855
+ };
856
+ if (!graph.available)
857
+ block("ARTIFACT_BINDING_UNAVAILABLE");
858
+ if (!declaration)
859
+ block("ARTIFACT_IDENTITY_AMBIGUOUS");
860
+ if (!impact)
861
+ block("ARTIFACT_CONSUMER_UNKNOWN", declaration?.producerNodeId);
862
+ if (input.parentLifecycle !== "completed")
863
+ block("parent-lifecycle-ineligible");
864
+ const overrideRevisionId = input.artifactOverrides.overrides[0]?.revisionId;
865
+ const authority = input.workerContinuationAuthority;
866
+ if (workerAssociation.kind === "worker-managed") {
867
+ if (!authority) {
868
+ block("WORKER_MANAGED_CONTINUATION_UNSUPPORTED");
869
+ }
870
+ else if (!workerAssociation.workerRunId ||
871
+ !workerAssociation.featureId ||
872
+ !workerAssociation.taskId ||
873
+ authority.parentWorkerRunId !== workerAssociation.workerRunId ||
874
+ authority.featureId !== workerAssociation.featureId ||
875
+ authority.taskId !== workerAssociation.taskId ||
876
+ authority.revisionId !== overrideRevisionId) {
877
+ block("WORKER_CONTINUATION_AUTHORITY_MISMATCH");
878
+ }
879
+ }
880
+ else if (authority) {
881
+ block("WORKER_CONTINUATION_AUTHORITY_MISMATCH");
882
+ }
883
+ if (input.parentSpec.convergence?.enabled || input.parentState.convergence?.enabled) {
884
+ block("convergence-rerun-unsupported");
885
+ }
886
+ if (input.parentSpec.evaluation || input.parentState.evaluation) {
887
+ block("evaluation-rerun-unsupported");
888
+ }
889
+ const hardBudget = (input.parentSpec.budget?.mode ?? input.parentState.budget?.mode) === "hard";
890
+ if (hardBudget) {
891
+ const ledger = input.parentState.budgetLedger;
892
+ if (!ledger) {
893
+ block("budget-lineage-missing");
894
+ }
895
+ else if (ledger.status === "breached" || ledger.breaches.length > 0) {
896
+ block("budget-lineage-breached");
897
+ }
898
+ else if (ledger.mode !== "hard" ||
899
+ JSON.stringify(ledger.limits) !==
900
+ JSON.stringify(input.parentSpec.budget?.limits ?? input.parentState.budget?.limits)) {
901
+ block("budget-lineage-drift");
902
+ }
903
+ }
904
+ for (const [name, matched] of Object.entries(input.bindings)) {
905
+ if (!matched)
906
+ block(`${name}-drift`);
907
+ }
908
+ const override = input.artifactOverrides.overrides[0];
909
+ const parentFact = declaration
910
+ ? input.parentState.nodes[declaration.producerNodeId]?.declaredArtifacts?.find((fact) => fact.artifactId === input.artifactId)
911
+ : undefined;
912
+ if (!declaration ||
913
+ declaration.revisionPolicy !== "replace-file" ||
914
+ parentFact?.status !== "materialized" ||
915
+ parentFact.revisionEligible !== true ||
916
+ !parentFact.sha256) {
917
+ block("ARTIFACT_NOT_REVISION_ELIGIBLE", declaration?.producerNodeId);
918
+ }
919
+ if (!override ||
920
+ override.parentArtifact.runId !== input.parentRunId ||
921
+ override.parentArtifact.nodeId !== declaration?.producerNodeId ||
922
+ override.parentArtifact.path !== declaration?.path ||
923
+ override.parentArtifact.sha256 !== parentFact?.sha256 ||
924
+ override.supersedes !== parentFact?.sha256 ||
925
+ override.replacement.sha256 !== input.stagingRevisionSha256) {
926
+ block("ARTIFACT_REVISION_DRIFT", declaration?.producerNodeId);
927
+ }
928
+ const affectedNodeIds = impact?.affectedNodeIds ?? [];
929
+ const importedNodeIds = impact?.importedNodeIds ?? [];
930
+ const closureSafety = assessResetClosureSafety(input.parentSpec, affectedNodeIds, {
931
+ parentNodes: input.parentState.nodes,
932
+ artifactContinuation: true,
933
+ });
934
+ for (const entry of closureSafety.blockingNodes) {
935
+ blockingNodes.push(entry);
936
+ block("ARTIFACT_IMPACT_UNSAFE");
937
+ }
938
+ for (const nodeId of importedNodeIds) {
939
+ const importedTask = taskById(input.parentSpec).get(nodeId);
940
+ if (importedTask?.decisionGate?.enabled) {
941
+ const code = "restart-subgraph-contains-decision-gate";
942
+ blockingNodes.push({ nodeId, reasonCode: code });
943
+ block(code);
944
+ }
945
+ const status = input.parentState.nodes[nodeId]?.status;
946
+ if (status !== "FINISHED" && status !== "SKIPPED") {
947
+ block("parent-facts-invalid", nodeId);
948
+ }
949
+ }
950
+ const highRiskAuthority = collectHighRiskContinuationAuthority({
951
+ parentSpec: input.parentSpec,
952
+ parentState: input.parentState,
953
+ affectedNodeIds,
954
+ workspaceFingerprint: input.parentTerminalWorkspaceFingerprint,
955
+ });
956
+ const highRiskExpected = affectedNodeIds.some((nodeId) => {
957
+ const task = taskById(input.parentSpec).get(nodeId);
958
+ return Boolean(task && (isWriterTask(task, input.parentSpec) || hasDynamicRuntime(task)));
959
+ }) || hardBudget;
960
+ if (highRiskExpected) {
961
+ if (!input.parentTerminalWorkspaceFingerprint ||
962
+ !input.currentWorkspaceFingerprint) {
963
+ block("workspace-checkpoint-missing");
964
+ }
965
+ else if (input.parentTerminalWorkspaceFingerprint !==
966
+ input.currentWorkspaceFingerprint) {
967
+ block("workspace-drift");
968
+ }
969
+ else if (!highRiskAuthority) {
970
+ block("HIGH_RISK_AUTHORITY_MISSING");
971
+ }
972
+ }
973
+ const uniqueReasons = [...new Set(reasonCodes)];
974
+ const uniqueBlocked = [...new Set(blockedReasons)];
975
+ const eligible = uniqueBlocked.length === 0;
976
+ const shellNodeIds = affectedNodeIds.filter((nodeId) => taskById(input.parentSpec).get(nodeId)?.executor === "shell");
977
+ const piCallCount = affectedNodeIds.filter((nodeId) => taskById(input.parentSpec).get(nodeId)?.executor === "pi").length;
978
+ const planWithoutHash = {
979
+ schemaVersion: 2,
980
+ eligible,
981
+ parentRunId: input.parentRunId,
982
+ parentLifecycle: input.parentLifecycle,
983
+ selectedNodeId: declaration?.producerNodeId ?? input.artifactId,
984
+ effectiveFromNodeId: impact?.effectiveFromNodeId,
985
+ rewriteApplied: false,
986
+ resetNodeIds: affectedNodeIds,
987
+ importedNodeIds,
988
+ blockingNodes,
989
+ reasonCodes: uniqueReasons,
990
+ blockedReasons: uniqueBlocked,
991
+ risk: !eligible ? "high" : affectedNodeIds.length <= 2 ? "low" : "medium",
992
+ bindings: {
993
+ ...input.bindings,
994
+ artifactGraphSha256: graph.artifactGraphSha256,
995
+ overrideSetHash: input.artifactOverrides.overrideSetHash,
996
+ stagingRevisionSha256: input.stagingRevisionSha256,
997
+ ...(input.parentTerminalWorkspaceFingerprint
998
+ ? { parentWorkspaceFingerprint: input.parentTerminalWorkspaceFingerprint }
999
+ : {}),
1000
+ ...(input.currentWorkspaceFingerprint
1001
+ ? { currentWorkspaceFingerprint: input.currentWorkspaceFingerprint }
1002
+ : {}),
1003
+ ...(highRiskAuthority?.budget
1004
+ ? { budgetLedgerSha256: highRiskAuthority.budget.parentLedgerSha256 }
1005
+ : {}),
1006
+ },
1007
+ ...(shellNodeIds.length > 0
1008
+ ? { verificationPolicy: { mode: "rerun", nodeIds: shellNodeIds } }
1009
+ : {}),
1010
+ workerAssociation,
1011
+ ...(authority ? { workerContinuationAuthority: authority } : {}),
1012
+ ...(highRiskAuthority ? { highRiskAuthority } : {}),
1013
+ estimatedExecution: {
1014
+ nodeCount: affectedNodeIds.length,
1015
+ piCallCount,
1016
+ shellNodeCount: shellNodeIds.length,
1017
+ avoidedNodeCount: importedNodeIds.length,
1018
+ avoidedPiCallCount: importedNodeIds.filter((nodeId) => taskById(input.parentSpec).get(nodeId)?.executor === "pi").length,
1019
+ },
1020
+ suggestedAction: eligible ? "dag-rerun" : workerAssociation.kind === "worker-managed" ? "worker-task-retry" : "manual",
1021
+ trigger: "artifact-override",
1022
+ artifactOverrides: input.artifactOverrides,
1023
+ impact: {
1024
+ ...(impact ? { producerNodeId: impact.producerNodeId } : {}),
1025
+ directConsumerNodeIds: impact?.directConsumerNodeIds ?? [],
1026
+ effectiveFromNodeId: impact?.effectiveFromNodeId ?? declaration?.producerNodeId ?? input.artifactId,
1027
+ affectedNodeIds,
1028
+ importedNodeIds,
1029
+ blockedNodeIds: blockingNodes,
1030
+ },
1031
+ };
1032
+ return dagRerunPlanV2Schema.parse({
1033
+ ...planWithoutHash,
1034
+ planHash: computePlanHash(planWithoutHash),
1035
+ });
1036
+ }
491
1037
  function countExecutorNodes(spec, nodeIds, executor) {
492
1038
  const tasks = taskById(spec);
493
1039
  return nodeIds.filter((nodeId) => tasks.get(nodeId)?.executor === executor)