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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +319 -1
  3. package/dist/application/dag/generate-task-dag.js +75 -19
  4. package/dist/application/dag/run-dag.js +41 -0
  5. package/dist/application/task-lifecycle/advance.js +24 -5
  6. package/dist/application/task-lifecycle/observe.js +171 -17
  7. package/dist/application/task-lifecycle/plan-transitions.js +42 -7
  8. package/dist/application/task-lifecycle/recommendations.js +13 -2
  9. package/dist/build-stamp.json +3 -3
  10. package/dist/cli/command-definitions.js +7 -0
  11. package/dist/cli/program.js +6 -1
  12. package/dist/commands/client-recovery.js +3 -0
  13. package/dist/commands/dag-follow-up.js +138 -0
  14. package/dist/commands/dag-rerun-task.js +2 -0
  15. package/dist/commands/init.js +27 -1
  16. package/dist/commands/task-advance.js +19 -0
  17. package/dist/executors/dag-pi-executor.js +3976 -76
  18. package/dist/executors/pi-executor.js +15 -4
  19. package/dist/executors/pi-read-budget-policy.js +239 -0
  20. package/dist/executors/shell-executor.js +978 -188
  21. package/dist/executors/shell-write-guard.js +7 -0
  22. package/dist/infrastructure/console/operation-store.js +226 -6
  23. package/dist/shared/dag-failure-category.js +12 -0
  24. package/dist/shared/openspec-spec.js +70 -4
  25. package/dist/shared/operator/capabilities.js +7 -0
  26. package/dist/task/config-types.js +105 -5
  27. package/dist/task/contract/adopt.js +4 -0
  28. package/dist/task/contract/import-revision.js +4 -0
  29. package/dist/task/contract/project.js +3 -0
  30. package/dist/task/contract/schema.js +2 -1
  31. package/dist/task/frontend-project-capability.js +203 -20
  32. package/dist/task/runtime.js +5 -2
  33. package/dist/task/source-prepare/build-draft.js +3 -3
  34. package/dist/task/source-prepare/fragment-inventory.js +64 -25
  35. package/dist/task/source-prepare/prepare.js +126 -21
  36. package/dist/task/source-prepare/semantic-intake.js +6 -2
  37. package/dist/task/source-references.js +22 -1
  38. package/dist/worker/console/chat/chat-ui-policy.js +4 -2
  39. package/dist/worker/console/chat/model-resolver.js +7 -4
  40. package/dist/worker/console/chat/pi-runtime.js +258 -11
  41. package/dist/worker/console/chat/repo-browser.js +10 -3
  42. package/dist/worker/console/chat/routes.js +165 -71
  43. package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
  44. package/dist/worker/console/chat/session-catalog.js +2 -0
  45. package/dist/worker/console/chat/session-store.js +159 -70
  46. package/dist/worker/console/chat/shortcuts.js +19 -3
  47. package/dist/worker/console/chat/turn-process.js +111 -56
  48. package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
  49. package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
  50. package/dist/worker/console/index.js +3 -0
  51. package/dist/worker/console/operator-actions.js +169 -7
  52. package/dist/worker/console/pi-readiness.js +3 -2
  53. package/dist/worker/console/server.js +23 -1
  54. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DZZ8m3PO.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
  55. package/dist/worker/console/static/assets/{arc-D6PvaVd-.js → arc-COptKq2S.js} +1 -1
  56. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-B_OTOiI8.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
  57. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bv6rqHBg.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
  58. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8eHr0oz.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
  59. package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
  60. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DYwR0im2.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D2WPGqXt.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CQISJ_I7.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C0o2Du1e.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-4f8kr-U7.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D_OWvXzX.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
  66. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CasdPz5X.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
  67. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Cj8lRrla.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
  68. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CUvD1FSd.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
  69. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CqLw_eqB.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
  70. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
  71. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
  72. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ku-WqJPx.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
  73. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bq_PYDhl.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
  74. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BiGZV641.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
  75. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C4JRlglF.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
  76. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B0dNeWYG.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
  77. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Cj35YvYM.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
  78. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-uzQoJ2-8.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
  79. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D1a-Buoz.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
  80. package/dist/worker/console/static/assets/{diagram-UB23O5K3-XjRrLRSs.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
  81. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Da_O24cW.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
  82. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BLJ8jrYU.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
  83. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B9GrLjM0.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
  84. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CeJ0TqiK.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
  85. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
  86. package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
  87. package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
  88. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DB26i3d8.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
  89. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BOGRyeZT.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
  90. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CZRoBO_6.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
  91. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-qlWhJyoB.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
  92. package/dist/worker/console/static/assets/{linear-CfUiDDB3.js → linear-D59YJ9kB.js} +1 -1
  93. package/dist/worker/console/static/assets/{mermaid.core-BQe6fpqj.js → mermaid.core-Bl13LpNa.js} +5 -5
  94. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-zFzWHw64.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
  95. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DFGUqjZO.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
  96. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BB5i0l8Z.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
  97. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-obbVZ_X5.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
  98. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BwdaNzc1.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
  99. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CkTJaugh.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
  100. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BwJ-hIgq.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
  101. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BzSgmm7w.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
  102. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Bn3oj5Q1.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
  103. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-dxhGtL8K.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
  104. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
  105. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BLpEInya.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
  106. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
  107. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ChJGYcXy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
  108. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DK-qjRer.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
  109. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3qeaWAg-.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
  110. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CootlyP9.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
  111. package/dist/worker/console/static/index.html +2 -2
  112. package/dist/worker/console/static-src/active-run-badge.js +17 -0
  113. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  114. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
  115. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  116. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
  117. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
  118. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
  119. package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
  120. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
  121. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
  122. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
  123. package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
  124. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  125. package/dist/worker/console/workspace-context.js +115 -1
  126. package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
  127. package/dist/worker/observe/routes.js +4 -0
  128. package/dist/worker/observe/static/dag-helpers.js +4 -2
  129. package/dist/worker/observe/static/dag-node-purpose.js +5 -0
  130. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  131. package/dist/worker/observe/static/kpi.js +2 -2
  132. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  133. package/dist/worker/observe/static/operator-chrome.js +37 -23
  134. package/dist/worker/observe/static/relations.js +2 -2
  135. package/dist/worker/observe/static/router.d.ts +12 -1
  136. package/dist/worker/observe/static/router.js +48 -6
  137. package/dist/worker/observe/static/run-processing.js +4 -2
  138. package/dist/worker/observe/static/shell-chrome.js +2 -2
  139. package/dist/worker/observe/static/state.js +5 -0
  140. package/dist/worker/observe/static/styles.css +95 -0
  141. package/dist/worker/observe/static/views/dag-inspector.js +14 -7
  142. package/dist/worker/observe/static/views/dag.js +10 -2
  143. package/dist/worker/observe/static/views/dags.js +3 -1
  144. package/dist/worker/observe/static/views/dashboard.js +14 -6
  145. package/dist/worker/observe/static/views/pool.js +7 -2
  146. package/dist/worker/observe/static/views/run.js +12 -3
  147. package/dist/worker/observe/static/views/session-timeline.js +69 -10
  148. package/dist/worker/observe/static/views/task.js +7 -2
  149. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
  150. package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
  151. package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
  152. package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
  153. package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
  154. package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
  155. package/dist/workflows/dag/contract-validator-registrations.js +1 -2
  156. package/dist/workflows/dag/dag-retry-schema.js +138 -0
  157. package/dist/workflows/dag/frontend-closeout.js +221 -0
  158. package/dist/workflows/dag/frontend-design-policy.js +400 -0
  159. package/dist/workflows/dag/frontend-human-decision.js +182 -0
  160. package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
  161. package/dist/workflows/dag/frontend-plan-render.js +2 -1
  162. package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
  163. package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
  164. package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
  165. package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
  166. package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
  167. package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
  168. package/dist/workflows/dag/frontend-recovery-run.js +166 -34
  169. package/dist/workflows/dag/frontend-repair.js +1 -432
  170. package/dist/workflows/dag/frontend-review-context.js +261 -15
  171. package/dist/workflows/dag/frontend-review-findings.js +270 -0
  172. package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
  173. package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
  174. package/dist/workflows/dag/frontend-shape-facts.js +419 -0
  175. package/dist/workflows/dag/frontend-shape.js +435 -0
  176. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
  177. package/dist/workflows/dag/frontend-split-application-service.js +203 -0
  178. package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
  179. package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
  180. package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
  181. package/dist/workflows/dag/frontend-verification-trace.js +252 -25
  182. package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
  183. package/dist/workflows/dag/frontend-writer-admission.js +319 -0
  184. package/dist/workflows/dag/frontend-writer-status.js +256 -0
  185. package/dist/workflows/dag/init-hybrid.js +1028 -562
  186. package/dist/workflows/dag/interrupt-request.js +7 -0
  187. package/dist/workflows/dag/node-execution.js +828 -4
  188. package/dist/workflows/dag/prompt.js +70 -3
  189. package/dist/workflows/dag/recovery-lease.js +170 -0
  190. package/dist/workflows/dag/report.js +37 -1
  191. package/dist/workflows/dag/rerun-feedback.js +315 -1
  192. package/dist/workflows/dag/rerun-plan.js +17 -6
  193. package/dist/workflows/dag/rerun-run.js +44 -6
  194. package/dist/workflows/dag/rerun-task.js +251 -13
  195. package/dist/workflows/dag/retry-policy.js +219 -104
  196. package/dist/workflows/dag/runner.js +580 -124
  197. package/dist/workflows/dag/scheduler.js +133 -20
  198. package/dist/workflows/dag/types.js +244 -16
  199. package/dist/workflows/dag/validate.js +28 -12
  200. package/docs/examples/README.md +5 -0
  201. package/docs/init-surface.manifest.json +30 -12
  202. package/docs/skills/vetted-skill-registry.md +4 -2
  203. package/docs/templates/README.md +2 -0
  204. package/docs/templates/agent-dag-report.schema.json +8 -2
  205. package/docs/templates/agent-dag.schema.json +1 -1
  206. package/docs/templates/backend-test-dag.json +23 -20
  207. package/docs/templates/frontend-implementation-contract.schema.json +4 -1
  208. package/docs/templates/frontend-implementation-dag.json +89 -0
  209. package/docs/templates/spec-registry.schema.json +45 -0
  210. package/harness.json +1 -1
  211. package/package.json +4 -3
  212. package/skills/frontend-bounded-implement/SKILL.md +15 -14
  213. package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
  214. package/skills/frontend-contract/SKILL.md +23 -0
  215. package/skills/frontend-contract/references/contract-protocol.md +34 -0
  216. package/skills/frontend-design-review/SKILL.md +22 -41
  217. package/skills/frontend-plan/SKILL.md +26 -0
  218. package/skills/frontend-plan/references/decision-contract.md +37 -0
  219. package/skills/frontend-plan/references/design-decisions.md +17 -0
  220. package/skills/frontend-review/SKILL.md +20 -15
  221. package/skills/frontend-review/references/review-findings.md +6 -7
  222. package/skills/frontend-scout/SKILL.md +25 -0
  223. package/skills/frontend-scout/references/design-evidence.md +16 -0
  224. package/skills/frontend-scout/references/scout-evidence.md +23 -0
  225. package/skills/frontend-verification/SKILL.md +1 -1
  226. package/skills/loop-agent/references/command-reference.md +1 -0
  227. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  228. package/dist/worker/console/static/assets/channel-BU5gOilw.js +0 -1
  229. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +0 -1
  230. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +0 -1
  231. package/dist/worker/console/static/assets/index-H9rFJiGL.css +0 -1
  232. package/dist/worker/console/static/assets/index-xwu9GxEc.js +0 -451
  233. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +0 -1
  234. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +0 -8
  235. package/skills/frontend-implementation/SKILL.md +0 -52
  236. package/skills/frontend-implementation/references/code-standards.md +0 -33
  237. package/skills/frontend-implementation/references/design-spec.md +0 -56
  238. package/skills/frontend-implementation/references/node-contracts.md +0 -31
@@ -415,12 +415,16 @@ function isCanonicalGeneratedVerifyShell(task) {
415
415
  return false;
416
416
  if (shell.projectGovernanceGate)
417
417
  return false;
418
- if (shell.frontendPrewriteGate)
418
+ if (shell.frontendDesignPolicy)
419
+ return false;
420
+ if (shell.frontendWriterAdmission)
419
421
  return false;
420
422
  if (shell.frontendVerificationBundle)
421
423
  return false;
422
424
  if (shell.frontendReviewContext)
423
425
  return false;
426
+ if (shell.frontendCloseout)
427
+ return false;
424
428
  if (shell.backendTestPipeline)
425
429
  return false;
426
430
  if (shell.nonZeroExitPolicy === "record")
@@ -444,15 +448,18 @@ function isCanonicalFrontendArtifactContinuationShell(task) {
444
448
  switch (task.id) {
445
449
  case "frontend-prewrite-gate-shell":
446
450
  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-design-policy-shell":
452
+ return Boolean(shell.frontendDesignPolicy) && (shell.commands?.length ?? 0) === 0;
453
+ case "frontend-writer-admission-shell":
454
+ return Boolean(shell.frontendWriterAdmission) && (shell.commands?.length ?? 0) === 0;
451
455
  case "frontend-verify-assess-shell":
452
456
  case "frontend-reverify-shell":
457
+ case "frontend-verify-shell":
453
458
  return Boolean(shell.frontendVerificationBundle) && (shell.commands?.length ?? 0) === 0;
454
459
  case "frontend-review-context-shell":
455
460
  return Boolean(shell.frontendReviewContext) && (shell.commands?.length ?? 0) === 0;
461
+ case "frontend-closeout-shell":
462
+ return Boolean(shell.frontendCloseout) && (shell.commands?.length ?? 0) === 0;
456
463
  default:
457
464
  return false;
458
465
  }
@@ -474,12 +481,16 @@ function isSafeGateOnlyShell(task) {
474
481
  return false;
475
482
  if (shell.preset)
476
483
  return false;
477
- if (shell.frontendPrewriteGate)
484
+ if (shell.frontendDesignPolicy)
485
+ return false;
486
+ if (shell.frontendWriterAdmission)
478
487
  return false;
479
488
  if (shell.frontendVerificationBundle)
480
489
  return false;
481
490
  if (shell.frontendReviewContext)
482
491
  return false;
492
+ if (shell.frontendCloseout)
493
+ return false;
483
494
  if (shell.backendTestPipeline)
484
495
  return false;
485
496
  if (shell.verifyEvidence)
@@ -13,6 +13,7 @@ import { prepareActiveRunDir, writeNodeRecord, writeRunSpec, writeRunState, } fr
13
13
  import { cloneParentSkillSnapshotForContinuation, readSkillSnapshot, } from "./skill-snapshot.js";
14
14
  import { topoSortToRanks } from "./topo.js";
15
15
  import { captureWorkspaceCheckpoint, readWorkspaceCheckpoint, WORKSPACE_CHECKPOINT_START_REL, WORKSPACE_CHECKPOINT_TERMINAL_REL, writeWorkspaceCheckpoint, } from "./workspace-checkpoint.js";
16
+ import { acquireDagRecoveryLease, bindDagRecoveryLeaseExecution, } from "./recovery-lease.js";
16
17
  import { captureWriterBaseline, readJournalFile, recordWriterAttemptFromCurrent, restoreJournal, writeJournalFile, } from "./frontend-writer-rollback.js";
17
18
  export const RERUN_PLAN_REL_PATH = ".runtime/rerun-plan.json";
18
19
  export const IMPORT_MANIFEST_REL_PATH = ".runtime/import-manifest.json";
@@ -87,11 +88,18 @@ const operatorRequestEnvelopeSchema = z
87
88
  })
88
89
  .strict();
89
90
  async function readExistingOperatorRequest(cwd, requestId) {
91
+ const requestPath = operatorRequestPath(cwd, requestId);
90
92
  try {
91
- const raw = JSON.parse(await readFile(operatorRequestPath(cwd, requestId), "utf-8"));
93
+ const raw = JSON.parse(await readFile(requestPath, "utf-8"));
92
94
  const parsed = operatorRequestEnvelopeSchema.safeParse(raw);
93
- if (!parsed.success)
94
- return undefined;
95
+ if (!parsed.success) {
96
+ throw new Error("REQUEST_ID_REUSE_CONFLICT: persisted operator request envelope is invalid or belongs to a different request kind");
97
+ }
98
+ if (parsed.data.requestId !== requestId ||
99
+ parsed.data.parentRunId !== parsed.data.result.plan.parentRunId ||
100
+ parsed.data.newRunId !== parsed.data.result.newRunId) {
101
+ throw new Error("REQUEST_ID_REUSE_CONFLICT: persisted operator request envelope binding mismatch");
102
+ }
95
103
  const result = parsed.data.result;
96
104
  return {
97
105
  ...result,
@@ -99,10 +107,18 @@ async function readExistingOperatorRequest(cwd, requestId) {
99
107
  idempotentReplay: true,
100
108
  };
101
109
  }
102
- catch {
103
- // not found or unreadable
110
+ catch (error) {
111
+ if (error &&
112
+ typeof error === "object" &&
113
+ "code" in error &&
114
+ error.code === "ENOENT") {
115
+ return undefined;
116
+ }
117
+ if (error instanceof Error && error.message.startsWith("REQUEST_ID_REUSE_CONFLICT:")) {
118
+ throw error;
119
+ }
120
+ throw new Error(`REQUEST_ID_REUSE_CONFLICT: invalid persisted operator request envelope: ${error instanceof Error ? error.message : String(error)}`);
104
121
  }
105
- return undefined;
106
122
  }
107
123
  /**
108
124
  * Idempotent replay of a previously persisted operator request, if any.
@@ -970,6 +986,20 @@ export async function executeDagRerun(input) {
970
986
  }
971
987
  const parentSpec = await readDagRunSpec(located.runDir);
972
988
  const parentState = await readDagRunState(located.runDir);
989
+ const recoveryLease = await acquireDagRecoveryLease({
990
+ cwd: input.cwd,
991
+ parentRunId: input.parentRunId,
992
+ requestId: input.requestId,
993
+ kind: "rerun-from-node",
994
+ });
995
+ if (recoveryLease.kind === "held") {
996
+ return {
997
+ ok: false,
998
+ plan,
999
+ errorCode: "RECOVERY_LEASE_HELD",
1000
+ message: `parent run recovery is already claimed by ${recoveryLease.lease.kind} request ${recoveryLease.lease.requestId}`,
1001
+ };
1002
+ }
973
1003
  const staged = await stageContinuationRun({
974
1004
  cwd: input.cwd,
975
1005
  parentRunDir: located.runDir,
@@ -981,6 +1011,14 @@ export async function executeDagRerun(input) {
981
1011
  requestId: input.requestId,
982
1012
  promptOverrides: input.promptOverrides,
983
1013
  });
1014
+ // Stage is non-executing; bind the child identity immediately before handing
1015
+ // it to the runner so no recovery DAG can start without durable parent
1016
+ // lineage ownership.
1017
+ await bindDagRecoveryLeaseExecution({
1018
+ leasePath: recoveryLease.leasePath,
1019
+ lease: recoveryLease.lease,
1020
+ executionRef: staged.state.runId,
1021
+ });
984
1022
  const continuationOptions = {
985
1023
  cwd: input.cwd,
986
1024
  spec: staged.spec,
@@ -1,14 +1,19 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
+ import { createHash } from "node:crypto";
2
3
  import path from "node:path";
3
4
  import { generateTaskDagUseCase } from "../../application/dag/generate-task-dag.js";
5
+ import { buildDefaultFrontendRecoveryDeps, } from "../../application/dag/run-dag.js";
6
+ import { runDagContinuation, } from "./runner.js";
7
+ import { stageRecoveryChild } from "./frontend-recovery-run.js";
4
8
  import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
5
9
  import { DAG_RUNS_DIR, isTerminalDagRunStatus, locateDagRun, readDagRunSpec, readDagRunState, } from "./lifecycle.js";
6
10
  import { deriveDagRerunFeedback } from "./rerun-feedback.js";
11
+ import { acquireDagRecoveryLease, bindDagRecoveryLeaseExecution, } from "./recovery-lease.js";
7
12
  import { parseDagSpec, } from "./types.js";
8
13
  export const RERUN_TASK_LINEAGE_REL_PATH = ".runtime/rerun-task-lineage.json";
9
14
  export function parseDagRerunTaskArgs(args) {
10
15
  if (args.length === 0) {
11
- throw new Error("usage: dag rerun-task --run-id <id> --reason <text> --request-id <key> [--profile auto|minimal|standard|reviewed|supervised] [--task-id <id>] [--json]");
16
+ throw new Error("usage: dag rerun-task --run-id <id> --reason <text> --request-id <key> [--profile auto|minimal|standard|reviewed|supervised] [--task-id <id>] [--frontend-recovery --recovery-operation-id <id>] [--json]");
12
17
  }
13
18
  let parentRunId;
14
19
  let reason;
@@ -17,6 +22,8 @@ export function parseDagRerunTaskArgs(args) {
17
22
  let profileExplicit = false;
18
23
  let taskIdOverride;
19
24
  let execute = true;
25
+ let frontendRecovery = false;
26
+ let recoveryOperationId;
20
27
  for (let index = 0; index < args.length; index += 1) {
21
28
  const arg = args[index];
22
29
  if (arg === "--run-id")
@@ -47,6 +54,12 @@ export function parseDagRerunTaskArgs(args) {
47
54
  continue;
48
55
  else if (arg === "--no-execute")
49
56
  execute = false;
57
+ else if (arg === "--frontend-recovery")
58
+ frontendRecovery = true;
59
+ else if (arg === "--recovery-operation-id")
60
+ recoveryOperationId = args[++index];
61
+ else if (arg.startsWith("--recovery-operation-id="))
62
+ recoveryOperationId = arg.slice("--recovery-operation-id=".length);
50
63
  else if (arg.startsWith("-"))
51
64
  throw new Error(`unknown dag rerun-task flag: ${arg}`);
52
65
  else
@@ -69,6 +82,48 @@ export function parseDagRerunTaskArgs(args) {
69
82
  profileExplicit,
70
83
  ...(taskIdOverride?.trim() ? { taskIdOverride: taskIdOverride.trim() } : {}),
71
84
  execute,
85
+ frontendRecovery,
86
+ ...(recoveryOperationId ? { recoveryOperationId } : {}),
87
+ };
88
+ }
89
+ const frontendRecoveryDescriptorPath = (runDir, requestId) => path.join(runDir, ".runtime", "frontend-recovery", `${requestId}.json`);
90
+ async function readFrontendRecoveryDescriptor(runDir, parentRunId, requestId) {
91
+ const raw = JSON.parse(await readFile(frontendRecoveryDescriptorPath(runDir, requestId), "utf8"));
92
+ const plan = raw.plan;
93
+ if (raw.schemaVersion !== 1 ||
94
+ raw.parentRunId !== parentRunId ||
95
+ raw.requestId !== requestId ||
96
+ typeof raw.failureSource !== "string" ||
97
+ typeof raw.recoveryMode !== "string" ||
98
+ typeof raw.planHash !== "string" ||
99
+ !plan ||
100
+ plan.schemaVersion !== 1 ||
101
+ plan.parentRunId !== parentRunId ||
102
+ plan.requestId !== requestId ||
103
+ !Array.isArray(plan.resetNodeIds) ||
104
+ !Array.isArray(plan.importedNodeIds)) {
105
+ throw new Error("frontend recovery descriptor is invalid or unbound");
106
+ }
107
+ const expectedHash = createHash("sha256")
108
+ .update(JSON.stringify(plan))
109
+ .digest("hex");
110
+ if (raw.planHash !== expectedHash) {
111
+ throw new Error("frontend recovery descriptor plan hash mismatch");
112
+ }
113
+ return raw;
114
+ }
115
+ function buildFrontendRecoveryIntent(input) {
116
+ return {
117
+ schemaVersion: 1,
118
+ phase: "child-staging",
119
+ requestId: input.requestId,
120
+ recoveryRootRunId: input.parentRecovery?.recoveryRootRunId ?? input.parentRunId,
121
+ parentRunId: input.parentRunId,
122
+ attemptId: input.requestId,
123
+ attemptIndex: input.parentRecovery?.attemptIndex ?? 0,
124
+ continuationCount: input.parentRecovery?.continuationCount ?? 0,
125
+ failureSource: input.failureSource,
126
+ revision: 0,
72
127
  };
73
128
  }
74
129
  function parseDagRerunTaskProfile(value) {
@@ -135,6 +190,21 @@ function resolveTaskIdForRerun(spec, taskIdOverride) {
135
190
  function operatorRequestPath(cwd, requestId) {
136
191
  return path.join(cwd, DAG_RUNS_DIR, ".operator-requests", `${requestId}.json`);
137
192
  }
193
+ function computeRerunTaskRequestHash(input) {
194
+ return createHash("sha256")
195
+ .update(JSON.stringify({
196
+ parentRunId: input.parentRunId,
197
+ reason: input.reason,
198
+ requestId: input.requestId,
199
+ profile: input.profile ?? "auto",
200
+ profileExplicit: input.profileExplicit ?? false,
201
+ taskIdOverride: input.taskIdOverride ?? null,
202
+ execute: input.execute ?? true,
203
+ frontendRecovery: input.frontendRecovery ?? false,
204
+ recoveryOperationId: input.recoveryOperationId ?? null,
205
+ }))
206
+ .digest("hex");
207
+ }
138
208
  async function fileExists(filePath) {
139
209
  try {
140
210
  await access(filePath);
@@ -184,23 +254,45 @@ export async function assessGeneratedOutputConflict(spec, cwd) {
184
254
  }
185
255
  return conflictPaths;
186
256
  }
187
- async function readExistingOperatorRequest(cwd, requestId) {
257
+ async function readExistingOperatorRequest(cwd, requestId, expected) {
258
+ let raw;
188
259
  try {
189
- const raw = JSON.parse(await readFile(operatorRequestPath(cwd, requestId), "utf-8"));
190
- if (raw.result && typeof raw.result === "object") {
191
- return { ...raw.result, idempotentReplay: true };
260
+ raw = JSON.parse(await readFile(operatorRequestPath(cwd, requestId), "utf-8"));
261
+ }
262
+ catch (error) {
263
+ if (error &&
264
+ typeof error === "object" &&
265
+ "code" in error &&
266
+ error.code === "ENOENT") {
267
+ return undefined;
192
268
  }
269
+ throw new Error(`invalid persisted rerun-task operator request: ${error instanceof Error ? error.message : String(error)}`);
193
270
  }
194
- catch {
195
- // not found or unreadable
271
+ if (raw.schemaVersion !== 1 ||
272
+ raw.kind !== "standalone-task-rerun-request" ||
273
+ raw.requestId !== requestId) {
274
+ throw new Error("REQUEST_ID_REUSE_CONFLICT: operator request id belongs to a different request kind");
275
+ }
276
+ if (raw.parentRunId !== expected.parentRunId ||
277
+ raw.requestHash !== expected.requestHash) {
278
+ throw new Error("REQUEST_ID_REUSE_CONFLICT: rerun-task operator request payload drifted");
196
279
  }
197
- return undefined;
280
+ const result = raw.result;
281
+ if (!result ||
282
+ typeof result !== "object" ||
283
+ Array.isArray(result) ||
284
+ result.parentRunId !== expected.parentRunId ||
285
+ result.requestId !== requestId) {
286
+ throw new Error("invalid persisted rerun-task operator request result");
287
+ }
288
+ return { ...result, idempotentReplay: true };
198
289
  }
199
- async function persistOperatorRequest(cwd, result) {
290
+ async function persistOperatorRequest(cwd, result, requestHash) {
200
291
  await writeJsonAtomic(operatorRequestPath(cwd, result.requestId), {
201
292
  schemaVersion: 1,
202
293
  kind: "standalone-task-rerun-request",
203
294
  parentRunId: result.parentRunId,
295
+ requestHash,
204
296
  newRunId: result.newRunId,
205
297
  taskId: result.taskId,
206
298
  reason: result.reason,
@@ -263,8 +355,106 @@ function extractNewRunId(generateResult) {
263
355
  const runSummary = extractRunSummary(generateResult);
264
356
  return runSummary?.runId;
265
357
  }
358
+ async function executeFrontendRecoveryContinuation(input) {
359
+ const located = await locateDagRun(input.cwd, input.parentRunId);
360
+ if (!located || located.lifecycle !== "active") {
361
+ throw new Error(`frontend recovery parent run must remain active: ${input.parentRunId}`);
362
+ }
363
+ const lease = await acquireDagRecoveryLease({
364
+ cwd: input.cwd,
365
+ parentRunId: input.parentRunId,
366
+ requestId: input.requestId,
367
+ kind: "standalone-task-rerun",
368
+ });
369
+ if (lease.kind !== "held" ||
370
+ lease.lease.kind !== "auto-frontend-recovery" ||
371
+ lease.lease.requestId !== input.requestId ||
372
+ !input.recoveryOperationId ||
373
+ lease.lease.executionRef !== input.recoveryOperationId) {
374
+ return blockedResult({
375
+ parentRunId: input.parentRunId,
376
+ reason: input.reason,
377
+ requestId: input.requestId,
378
+ blockedReason: "recovery-lease-held",
379
+ note: "frontend recovery operation is not the owner of the parent recovery lease",
380
+ });
381
+ }
382
+ const descriptor = await readFrontendRecoveryDescriptor(located.runDir, input.parentRunId, input.requestId);
383
+ const parentState = await readDagRunState(located.runDir);
384
+ const staged = await stageRecoveryChild({
385
+ cwd: input.cwd,
386
+ parentRunId: input.parentRunId,
387
+ recoveryIntent: buildFrontendRecoveryIntent({
388
+ parentRunId: input.parentRunId,
389
+ requestId: input.requestId,
390
+ failureSource: descriptor.failureSource,
391
+ parentRecovery: parentState.frontendRecoveryState,
392
+ }),
393
+ recoveryPlan: descriptor.plan,
394
+ });
395
+ const locatedChild = await locateDagRun(input.cwd, staged.childRunId);
396
+ if (!locatedChild || locatedChild.runDir !== staged.childRunDir) {
397
+ throw new Error(`frontend recovery child lifecycle drift: ${staged.childRunId}`);
398
+ }
399
+ const childSpec = await readDagRunSpec(locatedChild.runDir);
400
+ const childState = await readDagRunState(locatedChild.runDir);
401
+ let runSummary;
402
+ if (locatedChild.lifecycle === "completed" ||
403
+ locatedChild.lifecycle === "paused") {
404
+ runSummary = {
405
+ title: childState.title,
406
+ runId: childState.runId,
407
+ status: childState.status,
408
+ ranks: childState.ranks,
409
+ nodes: childState.nodes,
410
+ runDir: locatedChild.runDir,
411
+ };
412
+ }
413
+ else {
414
+ const childRecovery = childSpec.tasks.some((task) => task.id === "frontend-implement-pi")
415
+ ? input.recovery ?? await buildDefaultFrontendRecoveryDeps(input.cwd)
416
+ : undefined;
417
+ runSummary = await runDagContinuation({
418
+ cwd: input.cwd,
419
+ spec: childSpec,
420
+ state: childState,
421
+ runDir: locatedChild.runDir,
422
+ ...(childRecovery ? { recovery: childRecovery } : {}),
423
+ });
424
+ }
425
+ const settledChild = await locateDagRun(input.cwd, staged.childRunId);
426
+ const settledChildState = settledChild
427
+ ? await readDagRunState(settledChild.runDir)
428
+ : childState;
429
+ const feedback = summarizeFeedback(childSpec.rerunFeedback, "no-unresolved-verify-review-feedback");
430
+ const result = {
431
+ ok: runSummary.status === "finished" ||
432
+ settledChildState.frontendRecoveryResult?.outcome === "recovered",
433
+ parentRunId: input.parentRunId,
434
+ newRunId: staged.childRunId,
435
+ taskId: childSpec.taskContractBinding?.taskId ??
436
+ childSpec.sourceBinding?.taskId,
437
+ reason: input.reason,
438
+ requestId: input.requestId,
439
+ mode: "generate+validate+execute",
440
+ feedback,
441
+ runSummary,
442
+ };
443
+ await writeRerunTaskLineage(input.cwd, staged.childRunId, {
444
+ parentRunId: input.parentRunId,
445
+ reason: input.reason,
446
+ requestId: input.requestId,
447
+ feedback,
448
+ });
449
+ await persistOperatorRequest(input.cwd, result, input.requestHash);
450
+ return result;
451
+ }
266
452
  export async function rerunStandaloneTask(input) {
267
- const existing = await readExistingOperatorRequest(input.cwd, input.requestId);
453
+ const requestHash = computeRerunTaskRequestHash(input);
454
+ const existing = await readExistingOperatorRequest(input.cwd, input.requestId, {
455
+ parentRunId: input.parentRunId,
456
+ requestHash,
457
+ });
268
458
  if (existing) {
269
459
  return existing;
270
460
  }
@@ -297,6 +487,17 @@ export async function rerunStandaloneTask(input) {
297
487
  note: eligibility.note,
298
488
  });
299
489
  }
490
+ if (input.frontendRecovery) {
491
+ return executeFrontendRecoveryContinuation({
492
+ cwd: input.cwd,
493
+ parentRunId: input.parentRunId,
494
+ reason: input.reason,
495
+ requestId: input.requestId,
496
+ requestHash,
497
+ recoveryOperationId: input.recoveryOperationId,
498
+ recovery: input.recovery,
499
+ });
500
+ }
300
501
  let parentFeedback;
301
502
  let missingFeedbackReason = "no-unresolved-verify-review-feedback";
302
503
  try {
@@ -308,12 +509,18 @@ export async function rerunStandaloneTask(input) {
308
509
  state,
309
510
  });
310
511
  }
311
- catch {
512
+ catch (error) {
513
+ if (state.terminalReason === "design-request-changes") {
514
+ throw new Error(`frontend design recovery feedback unavailable: ${error instanceof Error ? error.message : String(error)}`);
515
+ }
312
516
  // Feedback is additive recovery context. A corrupt/missing historical
313
517
  // artifact must be reported honestly but must not block an otherwise
314
518
  // eligible full rerun.
315
519
  missingFeedbackReason = "feedback-unavailable";
316
520
  }
521
+ if (state.terminalReason === "design-request-changes" && !parentFeedback) {
522
+ throw new Error("frontend design recovery feedback unavailable: no typed findings were found");
523
+ }
317
524
  const execute = input.execute ?? true;
318
525
  let effectiveFeedback;
319
526
  let expectedRerunFeedbackDigest;
@@ -369,6 +576,34 @@ export async function rerunStandaloneTask(input) {
369
576
  }
370
577
  }
371
578
  }
579
+ const recoveryLease = execute
580
+ ? await acquireDagRecoveryLease({
581
+ cwd: input.cwd,
582
+ parentRunId: input.parentRunId,
583
+ requestId: input.requestId,
584
+ kind: "standalone-task-rerun",
585
+ })
586
+ : undefined;
587
+ if (recoveryLease?.kind === "held") {
588
+ return blockedResult({
589
+ parentRunId: input.parentRunId,
590
+ reason: input.reason,
591
+ requestId: input.requestId,
592
+ blockedReason: "recovery-lease-held",
593
+ note: `parent run recovery is already claimed by ${recoveryLease.lease.kind} request ${recoveryLease.lease.requestId}`,
594
+ taskId: taskResolution.taskId,
595
+ });
596
+ }
597
+ if (recoveryLease?.kind === "acquired") {
598
+ // Persist a non-empty execution identity before generateTaskDagUseCase can
599
+ // execute its new DAG. The generated run id is not available yet, so the
600
+ // request id is the stable lineage handle for this entry point.
601
+ await bindDagRecoveryLeaseExecution({
602
+ leasePath: recoveryLease.leasePath,
603
+ lease: recoveryLease.lease,
604
+ executionRef: `standalone-task-rerun:${input.requestId}`,
605
+ });
606
+ }
372
607
  const generateResult = await generateTaskDagUseCase({
373
608
  repoRoot: input.cwd,
374
609
  taskId: taskResolution.taskId,
@@ -420,10 +655,13 @@ export async function rerunStandaloneTask(input) {
420
655
  ...(runSummary ? { runSummary } : {}),
421
656
  };
422
657
  if (await fileExists(operatorRequestPath(input.cwd, input.requestId))) {
423
- const replay = await readExistingOperatorRequest(input.cwd, input.requestId);
658
+ const replay = await readExistingOperatorRequest(input.cwd, input.requestId, {
659
+ parentRunId: input.parentRunId,
660
+ requestHash,
661
+ });
424
662
  if (replay)
425
663
  return replay;
426
664
  }
427
- await persistOperatorRequest(input.cwd, result);
665
+ await persistOperatorRequest(input.cwd, result, requestHash);
428
666
  return result;
429
667
  }