@tea-agent/loop-agent 0.39.0-beta.0 → 0.39.0-beta.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 (331) hide show
  1. package/AGENTS.md +4 -1
  2. package/CHANGELOG.md +248 -95
  3. package/README.md +9 -5
  4. package/bin/loop-agent.js +7 -3
  5. package/dist/application/task-lifecycle/advance.js +12 -0
  6. package/dist/application/task-lifecycle/recommendations.js +13 -2
  7. package/dist/build-stamp.json +3 -3
  8. package/dist/cli/command-definitions.js +23 -5
  9. package/dist/cli/program.js +21 -2
  10. package/dist/cli/update/notifier.js +2 -2
  11. package/dist/cli/update/runtime-activity.js +1 -29
  12. package/dist/cli.js +2 -1
  13. package/dist/commands/client-recovery.js +3 -0
  14. package/dist/commands/dag-follow-up.js +138 -0
  15. package/dist/commands/dag-rerun.js +55 -1
  16. package/dist/commands/init-model-catalog.js +464 -0
  17. package/dist/commands/init-upgrade.js +265 -97
  18. package/dist/commands/init.js +455 -28
  19. package/dist/commands/inspect-next.js +8 -0
  20. package/dist/commands/task-advance.js +19 -0
  21. package/dist/executors/dag-pi-executor.js +2306 -61
  22. package/dist/executors/pi-executor.js +7 -0
  23. package/dist/executors/pi-extension-resolver.js +233 -0
  24. package/dist/executors/pi-playwright-cli-tool.js +74 -28
  25. package/dist/executors/pi-read-budget-policy.js +239 -0
  26. package/dist/executors/pi-sdk-executor.js +180 -33
  27. package/dist/executors/shell-executor.js +1249 -224
  28. package/dist/executors/shell-write-guard.js +14 -0
  29. package/dist/executors/workspace-write-snapshot.js +68 -0
  30. package/dist/infrastructure/harness/atomic-write.js +23 -0
  31. package/dist/shared/dag-failure-category.js +150 -0
  32. package/dist/shared/dag-prompt-override.js +27 -0
  33. package/dist/shared/openspec-spec.js +70 -4
  34. package/dist/shared/operator/capabilities.js +39 -0
  35. package/dist/shared/playwright-cli-command-policy.js +15 -0
  36. package/dist/shared/update/console-notifier.js +100 -0
  37. package/dist/{cli → shared}/update/npm-client.js +70 -15
  38. package/dist/{cli → shared}/update/state.js +41 -13
  39. package/dist/task/config-types.js +50 -5
  40. package/dist/task/contract/apply.js +11 -1
  41. package/dist/task/contract/constants.js +2 -0
  42. package/dist/task/contract/hash.js +3 -0
  43. package/dist/task/contract/observe.js +25 -4
  44. package/dist/task/contract/paths.js +2 -1
  45. package/dist/task/contract/project.js +18 -1
  46. package/dist/task/contract/schema.js +4 -1
  47. package/dist/task/contract/transaction.js +12 -0
  48. package/dist/task/frontend-project-capability.js +199 -18
  49. package/dist/task/source-prepare/completeness.js +188 -0
  50. package/dist/task/source-prepare/fragment-inventory.js +477 -0
  51. package/dist/task/source-prepare/index.js +3 -0
  52. package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
  53. package/dist/task/source-prepare/ledger-review.js +214 -0
  54. package/dist/task/source-prepare/ledger.js +545 -0
  55. package/dist/task/source-prepare/parse-intent.js +24 -4
  56. package/dist/task/source-prepare/prepare.js +298 -1
  57. package/dist/task/source-prepare/semantic-intake.js +25 -5
  58. package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
  59. package/dist/task/source-prepare/types.js +22 -0
  60. package/dist/task/source-references.js +22 -1
  61. package/dist/worker/cli.js +18 -2
  62. package/dist/worker/console/chat/chat-event-store.js +62 -4
  63. package/dist/worker/console/chat/context-panel.js +7 -0
  64. package/dist/worker/console/chat/deferred-turn.js +12 -0
  65. package/dist/worker/console/chat/operation-card.js +8 -1
  66. package/dist/worker/console/chat/pi-console-config.js +81 -10
  67. package/dist/worker/console/chat/pi-runtime.js +264 -16
  68. package/dist/worker/console/chat/provider-error.js +98 -0
  69. package/dist/worker/console/chat/routes.js +298 -32
  70. package/dist/worker/console/chat/session-stats.js +179 -0
  71. package/dist/worker/console/chat/session-store.js +93 -63
  72. package/dist/worker/console/chat/todos.js +115 -0
  73. package/dist/worker/console/chat/tool-preview.js +42 -0
  74. package/dist/worker/console/chat/turn-process.js +20 -55
  75. package/dist/worker/console/chat/user-questions.js +266 -0
  76. package/dist/worker/console/chat/workspace-landing.js +14 -0
  77. package/dist/worker/console/console-handoff.js +82 -0
  78. package/dist/worker/console/console-update-and-init.js +115 -0
  79. package/dist/worker/console/console-update-runtime.js +84 -0
  80. package/dist/worker/console/dag-execution-receipt.js +33 -0
  81. package/dist/worker/console/draft-store.js +49 -0
  82. package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
  83. package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
  84. package/dist/worker/console/index.js +3 -0
  85. package/dist/worker/console/inspect-split.js +18 -0
  86. package/dist/worker/console/native-directory-picker.js +13 -0
  87. package/dist/worker/console/operation-run-facts.js +19 -3
  88. package/dist/worker/console/operation-store.js +227 -7
  89. package/dist/worker/console/operator-actions.js +388 -0
  90. package/dist/worker/console/operator-surface-health.js +1 -0
  91. package/dist/worker/console/operator-user-error.js +2 -2
  92. package/dist/worker/console/prd-intake-bridge.js +13 -0
  93. package/dist/worker/console/routes.js +355 -3
  94. package/dist/worker/console/security.js +67 -13
  95. package/dist/worker/console/server.js +609 -164
  96. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-BAsjg7ew.js} +1 -1
  97. package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-CyIlrwZw.js} +1 -1
  98. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-Bo4tobhH.js} +1 -1
  99. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dfpow9gu.js} +1 -1
  100. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-DPmzG_B4.js} +1 -1
  101. package/dist/worker/console/static/assets/channel-DSrJAn0-.js +1 -0
  102. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-pKQpB1UW.js} +1 -1
  103. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-5n9YYquZ.js} +1 -1
  104. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-DP3CtA2a.js} +1 -1
  105. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-DYxMJPUy.js} +1 -1
  106. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-CS3D50Io.js} +1 -1
  107. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-gSdDfo5_.js} +1 -1
  108. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-A48q0ZNt.js} +1 -1
  109. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-ow8Mm3DY.js} +1 -1
  110. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-CMKfLWYE.js} +1 -1
  111. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BiTBdQPX.js} +1 -1
  112. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DeMmPl0_.js +1 -0
  113. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DeMmPl0_.js +1 -0
  114. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-Cm7exRz_.js} +1 -1
  115. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-D9O5qGOu.js} +1 -1
  116. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-CkvQnako.js} +1 -1
  117. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-B1rzp7CB.js} +1 -1
  118. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BP07Ipcc.js} +1 -1
  119. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BlPzweQb.js} +1 -1
  120. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-CxPa7n0S.js} +1 -1
  121. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-CMTZs53A.js} +1 -1
  122. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-CxG_X52r.js} +1 -1
  123. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D8axtJM0.js} +1 -1
  124. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-DIsGzn7b.js} +1 -1
  125. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CU9zkDG0.js} +1 -1
  126. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-DCjTmz5P.js} +1 -1
  127. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-BksdmFzg.js} +1 -1
  128. package/dist/worker/console/static/assets/index-B-O5ChYq.css +1 -0
  129. package/dist/worker/console/static/assets/index-CfnY8AQx.js +407 -0
  130. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-CGzzzJtL.js} +1 -1
  131. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BdseB80N.js} +1 -1
  132. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-DM9gsgha.js} +1 -1
  133. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-3q-_9R2b.js} +1 -1
  134. package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-B8N-Mxsd.js} +1 -1
  135. package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-EPIHUUsu.js} +5 -5
  136. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-CxwO12aa.js} +1 -1
  137. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-bPqbVX6Y.js} +1 -1
  138. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-KzqIvzeX.js} +1 -1
  139. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-CyofpWiV.js} +1 -1
  140. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-cTWJvLTP.js} +1 -1
  141. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-BK--q27B.js} +1 -1
  142. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-C0SDOHXK.js} +1 -1
  143. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-Cong6YIo.js} +1 -1
  144. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DUVkgZx8.js} +1 -1
  145. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C7XgxuZg.js} +1 -1
  146. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dzbnd5oX.js +1 -0
  147. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-D9Ayvwcy.js} +2 -2
  148. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BY0hHk6G.js +8 -0
  149. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-Byb000cH.js} +1 -1
  150. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DMLegPt4.js} +1 -1
  151. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-ChMMeOsP.js} +1 -1
  152. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-bEMEPisW.js} +1 -1
  153. package/dist/worker/console/static/index.html +2 -2
  154. package/dist/worker/console/static-src/active-run-badge.js +17 -0
  155. package/dist/worker/console/static-src/app/console-types.js +68 -4
  156. package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
  157. package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
  158. package/dist/worker/console/static-src/app/useRecoveryConsole.js +45 -10
  159. package/dist/worker/console/static-src/app/useRunProgress.js +47 -0
  160. package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
  161. package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
  162. package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
  163. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +140 -8
  164. package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
  165. package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
  166. package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
  167. package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
  168. package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
  169. package/dist/worker/console/static-src/operator-chat/turn-submission.js +16 -3
  170. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +168 -72
  171. package/dist/worker/console/static-src/operator-chat/useChatStream.js +102 -21
  172. package/dist/worker/console/static-src/operator-chat/useChatThread.js +184 -4
  173. package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
  174. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
  175. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  176. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
  177. package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
  178. package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
  179. package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
  180. package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
  181. package/dist/worker/console/static-src/shell/workspace-route.js +337 -0
  182. package/dist/worker/console/workspace-context.js +344 -0
  183. package/dist/worker/console/workspace-registry.js +214 -0
  184. package/dist/worker/loop-agent/loop-agent-client.js +7 -3
  185. package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
  186. package/dist/worker/materialize/harness-task-lineage.js +5 -2
  187. package/dist/worker/observability/init-runtime-activity.js +26 -0
  188. package/dist/worker/observability/read-model.js +22 -0
  189. package/dist/worker/observe/node-input.js +160 -3
  190. package/dist/worker/observe/node-process.js +377 -0
  191. package/dist/worker/observe/routes.js +224 -3
  192. package/dist/worker/observe/server.js +4 -0
  193. package/dist/worker/observe/shell-handler-keys.js +34 -0
  194. package/dist/worker/observe/static/api.js +90 -4
  195. package/dist/worker/observe/static/app.js +18 -70
  196. package/dist/worker/observe/static/constants.js +8 -1
  197. package/dist/worker/observe/static/dag-history-labels.js +4 -0
  198. package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
  199. package/dist/worker/observe/static/dag-node-purpose.js +208 -0
  200. package/dist/worker/observe/static/format.js +60 -0
  201. package/dist/worker/observe/static/index.html +6 -1
  202. package/dist/worker/observe/static/inspect-workspace.js +307 -0
  203. package/dist/worker/observe/static/markdown-render.js +20 -1
  204. package/dist/worker/observe/static/operator-chrome.css +145 -20
  205. package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
  206. package/dist/worker/observe/static/operator-chrome.js +330 -96
  207. package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
  208. package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
  209. package/dist/worker/observe/static/router.d.ts +46 -0
  210. package/dist/worker/observe/static/router.js +25 -1
  211. package/dist/worker/observe/static/state.d.ts +50 -0
  212. package/dist/worker/observe/static/state.js +184 -1
  213. package/dist/worker/observe/static/styles.css +395 -0
  214. package/dist/worker/observe/static/views/dag-graph.js +4 -2
  215. package/dist/worker/observe/static/views/dag-inspector.js +1067 -9
  216. package/dist/worker/observe/static/views/dag.d.ts +13 -0
  217. package/dist/worker/observe/static/views/dag.js +14 -5
  218. package/dist/worker/observe/static/views/dashboard.js +6 -4
  219. package/dist/worker/observe/static/views/failures.js +5 -2
  220. package/dist/worker/observe/static/views/night.js +2 -2
  221. package/dist/worker/observe/static/views/pool.js +1 -0
  222. package/dist/worker/observe/static/views/run.js +3 -1
  223. package/dist/worker/observe/static/views/session-timeline.js +78 -9
  224. package/dist/worker/observe/static/views/task.js +56 -1
  225. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +49 -3
  226. package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
  227. package/dist/workflows/dag/backend-test-pytest-collection.js +98 -14
  228. package/dist/workflows/dag/backend-test-writer-completeness.js +10 -5
  229. package/dist/workflows/dag/contract-output-registry.js +15 -0
  230. package/dist/workflows/dag/contract-validator-registrations.js +1 -2
  231. package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
  232. package/dist/workflows/dag/dynamic-runtime/map.js +1 -1
  233. package/dist/workflows/dag/failure-category.js +7 -116
  234. package/dist/workflows/dag/frontend-closeout.js +221 -0
  235. package/dist/workflows/dag/frontend-design-policy.js +400 -0
  236. package/dist/workflows/dag/frontend-human-decision.js +182 -0
  237. package/dist/workflows/dag/frontend-implementation-contract.js +534 -178
  238. package/dist/workflows/dag/frontend-plan-render.js +24 -0
  239. package/dist/workflows/dag/frontend-prewrite-gate.js +371 -393
  240. package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
  241. package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
  242. package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
  243. package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
  244. package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
  245. package/dist/workflows/dag/frontend-recovery-run.js +33 -20
  246. package/dist/workflows/dag/frontend-repair.js +7 -424
  247. package/dist/workflows/dag/frontend-review-context.js +288 -14
  248. package/dist/workflows/dag/frontend-review-findings.js +270 -0
  249. package/dist/workflows/dag/frontend-risk.js +15 -2
  250. package/dist/workflows/dag/frontend-shadow-dual-write.js +814 -0
  251. package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
  252. package/dist/workflows/dag/frontend-shape-facts.js +409 -0
  253. package/dist/workflows/dag/frontend-shape.js +427 -0
  254. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
  255. package/dist/workflows/dag/frontend-split-application-service.js +203 -0
  256. package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
  257. package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
  258. package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
  259. package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
  260. package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
  261. package/dist/workflows/dag/frontend-test-html-report.js +3 -1
  262. package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
  263. package/dist/workflows/dag/frontend-test-layout.js +159 -0
  264. package/dist/workflows/dag/frontend-test-markdown.js +61 -0
  265. package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
  266. package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
  267. package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
  268. package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
  269. package/dist/workflows/dag/frontend-verification-trace.js +52 -17
  270. package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
  271. package/dist/workflows/dag/frontend-writer-admission.js +272 -0
  272. package/dist/workflows/dag/frontend-writer-status.js +244 -0
  273. package/dist/workflows/dag/init-hybrid.js +984 -618
  274. package/dist/workflows/dag/interrupt-request.js +10 -3
  275. package/dist/workflows/dag/lifecycle.js +3 -3
  276. package/dist/workflows/dag/node-execution.js +468 -34
  277. package/dist/workflows/dag/prompt.js +44 -19
  278. package/dist/workflows/dag/recovery-lease.js +80 -0
  279. package/dist/workflows/dag/report.js +37 -1
  280. package/dist/workflows/dag/rerun-feedback.js +124 -1
  281. package/dist/workflows/dag/rerun-plan.js +144 -5
  282. package/dist/workflows/dag/rerun-run.js +81 -6
  283. package/dist/workflows/dag/rerun-task.js +29 -0
  284. package/dist/workflows/dag/retry-policy.js +318 -8
  285. package/dist/workflows/dag/runner.js +429 -122
  286. package/dist/workflows/dag/scheduler.js +114 -16
  287. package/dist/workflows/dag/structured-output-repair.js +712 -0
  288. package/dist/workflows/dag/types.js +321 -31
  289. package/dist/workflows/dag/upstream-artifacts.js +0 -4
  290. package/dist/workflows/dag/validate.js +92 -32
  291. package/docs/README.md +2 -0
  292. package/docs/architecture/evolution.md +45 -0
  293. package/docs/init-surface.manifest.json +9 -0
  294. package/docs/operations/README.md +1 -1
  295. package/docs/operations/local-development-environment.md +21 -0
  296. package/docs/templates/README.md +2 -1
  297. package/docs/templates/agent-dag-report.schema.json +8 -2
  298. package/docs/templates/agent-dag.schema.json +45 -1
  299. package/docs/templates/frontend-implementation-contract.schema.json +684 -24
  300. package/docs/templates/frontend-test-case-checklist.md +2 -2
  301. package/docs/templates/frontend-test-dag.json +8 -10
  302. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
  303. package/docs/templates/init-managed-agents.md +13 -3
  304. package/docs/templates/spec-registry.schema.json +45 -0
  305. package/harness.json +3 -3
  306. package/package.json +7 -1
  307. package/scripts/next-info.mjs +356 -0
  308. package/scripts/next-publish-gate.mjs +238 -0
  309. package/scripts/release-source-binding.mjs +251 -0
  310. package/skills/codebase-scout/SKILL.md +1 -1
  311. package/skills/fe-test-ui-scout/SKILL.md +65 -0
  312. package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
  313. package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
  314. package/skills/frontend-bounded-implement/SKILL.md +8 -14
  315. package/skills/frontend-design-review/SKILL.md +20 -41
  316. package/skills/frontend-implementation/SKILL.md +3 -4
  317. package/skills/frontend-implementation/references/design-spec.md +15 -10
  318. package/skills/frontend-implementation/references/node-contracts.md +23 -13
  319. package/skills/frontend-review/SKILL.md +22 -14
  320. package/skills/frontend-review/references/review-findings.md +6 -7
  321. package/skills/loop-agent/references/command-reference.md +12 -4
  322. package/skills/loop-agent/references/hybrid-dag.md +8 -1
  323. package/skills/playwright-cli/SKILL.md +23 -1
  324. package/skills/playwright-cli-case-generator/SKILL.md +29 -8
  325. package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
  326. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
  327. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
  328. package/dist/worker/console/static/assets/index-DTZOKgAn.js +0 -404
  329. package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
  330. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
  331. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { access, copyFile, mkdir, readdir, readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
5
+ import { composeDagPromptOverridePrompt, isDagRerunPromptOverrideEnabled, } from "../../shared/dag-prompt-override.js";
5
6
  import { resolveRunningControllerIdentity } from "../../shared/package-metadata.js";
6
7
  import { readControllerIdentityArtifact, captureControllerIdentity } from "./controller-identity.js";
7
8
  import { DAG_RUNS_DIR, getDagRunDir, isDagNonExecutionLifecycle, locateDagRun, readDagRunSpec, readDagRunState, } from "./lifecycle.js";
@@ -11,6 +12,7 @@ import { prepareActiveRunDir, writeNodeRecord, writeRunSpec, writeRunState, } fr
11
12
  import { cloneParentSkillSnapshotForContinuation, readSkillSnapshot, } from "./skill-snapshot.js";
12
13
  import { topoSortToRanks } from "./topo.js";
13
14
  import { captureWorkspaceCheckpoint, readWorkspaceCheckpoint, WORKSPACE_CHECKPOINT_START_REL, WORKSPACE_CHECKPOINT_TERMINAL_REL, writeWorkspaceCheckpoint, } from "./workspace-checkpoint.js";
15
+ import { acquireDagRecoveryLease, bindDagRecoveryLeaseExecution, } from "./recovery-lease.js";
14
16
  export const RERUN_PLAN_REL_PATH = ".runtime/rerun-plan.json";
15
17
  export const IMPORT_MANIFEST_REL_PATH = ".runtime/import-manifest.json";
16
18
  export const WORKER_ASSOCIATION_REL_PATH = ".runtime/worker-association.json";
@@ -190,6 +192,8 @@ export async function planDagRerun(input) {
190
192
  parentSpec: nonCompletedSpec,
191
193
  parentState: nonCompletedState,
192
194
  parentLifecycle,
195
+ promptOverrides: input.promptOverrides,
196
+ promptOverrideEnabled: isDagRerunPromptOverrideEnabled(),
193
197
  workerManaged: nonCompletedWorkerHints.workerManaged,
194
198
  workerAssociation: nonCompletedWorkerHints.workerManaged
195
199
  ? {
@@ -229,6 +233,8 @@ export async function planDagRerun(input) {
229
233
  parentTerminalWorkspace,
230
234
  currentWorkspace,
231
235
  skillSnapshotOk,
236
+ promptOverrides: input.promptOverrides,
237
+ promptOverrideEnabled: isDagRerunPromptOverrideEnabled(),
232
238
  workerManaged: workerHints.workerManaged,
233
239
  workerAssociation: workerHints.workerManaged
234
240
  ? {
@@ -384,15 +390,57 @@ function buildPendingNodeRecord(task) {
384
390
  ...(task.outputMode ? { outputMode: task.outputMode } : {}),
385
391
  };
386
392
  }
393
+ /**
394
+ * Stage the continuation spec with operator prompt overrides applied. The
395
+ * parent spec stays frozen; every override must match the plan entry (sha256
396
+ * of the composed final prompt and of the parent base prompt) so the
397
+ * two-phase planHash remains the single source of truth for what changed.
398
+ * "append" entries keep the frozen base and append the operator addendum in a
399
+ * marked block; "replace" entries swap the whole prompt.
400
+ */
401
+ function applyPromptOverridesToSpec(input) {
402
+ const planned = new Map((input.plan.promptOverrides ?? []).map((entry) => [entry.nodeId, entry]));
403
+ const spec = structuredClone(input.spec);
404
+ for (const override of input.overrides) {
405
+ const entry = planned.get(override.nodeId);
406
+ if (!entry) {
407
+ throw new Error(`prompt override is not present in the rerun plan: ${override.nodeId}`);
408
+ }
409
+ const task = spec.tasks.find((candidate) => candidate.id === override.nodeId);
410
+ if (!task) {
411
+ throw new Error(`prompt override node missing from spec: ${override.nodeId}`);
412
+ }
413
+ if (sha256Hex(task.subtask_prompt) !== entry.basePromptSha256) {
414
+ throw new Error(`prompt override base prompt drifted from plan: ${override.nodeId}`);
415
+ }
416
+ const finalPrompt = composeDagPromptOverridePrompt({
417
+ basePrompt: task.subtask_prompt,
418
+ text: override.prompt,
419
+ mode: override.mode === "append" ? "append" : "replace",
420
+ });
421
+ if (sha256Hex(finalPrompt) !== entry.promptSha256) {
422
+ throw new Error(`prompt override text does not match the planned sha256: ${override.nodeId}`);
423
+ }
424
+ task.subtask_prompt = finalPrompt;
425
+ }
426
+ return spec;
427
+ }
387
428
  async function stageContinuationRun(input) {
388
- const { ranks } = topoSortToRanks(input.parentSpec);
389
- const runId = await buildDagRunId(input.parentSpec, { cwd: input.cwd });
429
+ const specForRun = input.promptOverrides !== undefined && input.promptOverrides.length > 0
430
+ ? applyPromptOverridesToSpec({
431
+ spec: input.parentSpec,
432
+ plan: input.plan,
433
+ overrides: input.promptOverrides,
434
+ })
435
+ : input.parentSpec;
436
+ const { ranks } = topoSortToRanks(specForRun);
437
+ const runId = await buildDagRunId(specForRun, { cwd: input.cwd });
390
438
  const runDir = getDagRunDir(input.cwd, "active", runId);
391
439
  await prepareActiveRunDir(runDir);
392
- await writeRunSpec(runDir, input.parentSpec);
440
+ await writeRunSpec(runDir, specForRun);
393
441
  const resetSet = new Set(input.plan.resetNodeIds);
394
442
  const importedSet = new Set(input.plan.importedNodeIds);
395
- const tasksById = new Map(input.parentSpec.tasks.map((task) => [task.id, task]));
443
+ const tasksById = new Map(specForRun.tasks.map((task) => [task.id, task]));
396
444
  const importManifestNodes = [];
397
445
  const nodes = {};
398
446
  for (const nodeId of input.plan.importedNodeIds) {
@@ -433,6 +481,9 @@ async function stageContinuationRun(input) {
433
481
  reason: input.reason,
434
482
  requestId: input.requestId,
435
483
  planHash: input.plan.planHash,
484
+ ...(input.plan.promptOverrides
485
+ ? { promptOverrides: input.plan.promptOverrides }
486
+ : {}),
436
487
  createdAt,
437
488
  };
438
489
  const state = {
@@ -482,7 +533,7 @@ async function stageContinuationRun(input) {
482
533
  await writeJsonAtomic(path.join(runDir, IMPORT_MANIFEST_REL_PATH), importManifest);
483
534
  await writeJsonAtomic(path.join(runDir, RERUN_PLAN_REL_PATH), input.plan);
484
535
  await writeRunState(runDir, state);
485
- return { runId, runDir, state };
536
+ return { runId, runDir, state, spec: specForRun };
486
537
  }
487
538
  export async function executeDagRerun(input) {
488
539
  const existing = await readExistingOperatorRequest(input.cwd, input.requestId);
@@ -493,6 +544,7 @@ export async function executeDagRerun(input) {
493
544
  cwd: input.cwd,
494
545
  parentRunId: input.parentRunId,
495
546
  selectedNodeId: input.selectedNodeId,
547
+ promptOverrides: input.promptOverrides,
496
548
  });
497
549
  if (plan.planHash !== input.planHash) {
498
550
  return {
@@ -521,6 +573,20 @@ export async function executeDagRerun(input) {
521
573
  }
522
574
  const parentSpec = await readDagRunSpec(located.runDir);
523
575
  const parentState = await readDagRunState(located.runDir);
576
+ const recoveryLease = await acquireDagRecoveryLease({
577
+ cwd: input.cwd,
578
+ parentRunId: input.parentRunId,
579
+ requestId: input.requestId,
580
+ kind: "rerun-from-node",
581
+ });
582
+ if (recoveryLease.kind === "held") {
583
+ return {
584
+ ok: false,
585
+ plan,
586
+ errorCode: "RECOVERY_LEASE_HELD",
587
+ message: `parent run recovery is already claimed by ${recoveryLease.lease.kind} request ${recoveryLease.lease.requestId}`,
588
+ };
589
+ }
524
590
  const staged = await stageContinuationRun({
525
591
  cwd: input.cwd,
526
592
  parentRunDir: located.runDir,
@@ -530,10 +596,19 @@ export async function executeDagRerun(input) {
530
596
  plan,
531
597
  reason: input.reason,
532
598
  requestId: input.requestId,
599
+ promptOverrides: input.promptOverrides,
600
+ });
601
+ // Stage is non-executing; bind the child identity immediately before handing
602
+ // it to the runner so no recovery DAG can start without durable parent
603
+ // lineage ownership.
604
+ await bindDagRecoveryLeaseExecution({
605
+ leasePath: recoveryLease.leasePath,
606
+ lease: recoveryLease.lease,
607
+ executionRef: staged.state.runId,
533
608
  });
534
609
  const continuationOptions = {
535
610
  cwd: input.cwd,
536
- spec: parentSpec,
611
+ spec: staged.spec,
537
612
  state: staged.state,
538
613
  runDir: staged.runDir,
539
614
  ...(input.maxConcurrent !== undefined
@@ -4,6 +4,7 @@ import { generateTaskDagUseCase } from "../../application/dag/generate-task-dag.
4
4
  import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
5
5
  import { DAG_RUNS_DIR, isTerminalDagRunStatus, locateDagRun, readDagRunSpec, readDagRunState, } from "./lifecycle.js";
6
6
  import { deriveDagRerunFeedback } from "./rerun-feedback.js";
7
+ import { acquireDagRecoveryLease, bindDagRecoveryLeaseExecution, } from "./recovery-lease.js";
7
8
  import { parseDagSpec, } from "./types.js";
8
9
  export const RERUN_TASK_LINEAGE_REL_PATH = ".runtime/rerun-task-lineage.json";
9
10
  export function parseDagRerunTaskArgs(args) {
@@ -369,6 +370,34 @@ export async function rerunStandaloneTask(input) {
369
370
  }
370
371
  }
371
372
  }
373
+ const recoveryLease = execute
374
+ ? await acquireDagRecoveryLease({
375
+ cwd: input.cwd,
376
+ parentRunId: input.parentRunId,
377
+ requestId: input.requestId,
378
+ kind: "standalone-task-rerun",
379
+ })
380
+ : undefined;
381
+ if (recoveryLease?.kind === "held") {
382
+ return blockedResult({
383
+ parentRunId: input.parentRunId,
384
+ reason: input.reason,
385
+ requestId: input.requestId,
386
+ blockedReason: "recovery-lease-held",
387
+ note: `parent run recovery is already claimed by ${recoveryLease.lease.kind} request ${recoveryLease.lease.requestId}`,
388
+ taskId: taskResolution.taskId,
389
+ });
390
+ }
391
+ if (recoveryLease?.kind === "acquired") {
392
+ // Persist a non-empty execution identity before generateTaskDagUseCase can
393
+ // execute its new DAG. The generated run id is not available yet, so the
394
+ // request id is the stable lineage handle for this entry point.
395
+ await bindDagRecoveryLeaseExecution({
396
+ leasePath: recoveryLease.leasePath,
397
+ lease: recoveryLease.lease,
398
+ executionRef: `standalone-task-rerun:${input.requestId}`,
399
+ });
400
+ }
372
401
  const generateResult = await generateTaskDagUseCase({
373
402
  repoRoot: input.cwd,
374
403
  taskId: taskResolution.taskId,
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ import { projectProtocolFailureReason, } from "./frontend-recovery-capsule.js";
3
+ import { TYPED_EVENT_FACT_KINDS } from "./frontend-typed-event-store.js";
2
4
  /**
3
5
  * Failure categories that are safe to auto-retry for read-only Pi nodes.
4
6
  *
@@ -23,6 +25,29 @@ export const PROTOCOL_INVALID_RETRY_CATEGORY = "protocol-invalid";
23
25
  export const STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY = "invalid-output";
24
26
  /** Provider stopReason=length truncated the response before the JSON contract completed. */
25
27
  export const STRUCTURED_OUTPUT_TRUNCATED_RETRY_CATEGORY = "structured-output-truncated";
28
+ /**
29
+ * The review node emitted a verdict in response text (e.g. "VERDICT: pass")
30
+ * but never committed the authoritative typed terminal tool call
31
+ * (approve_review / request_review_changes). Read-only and safe to retry with
32
+ * a corrected instruction; the typed terminal is the only authority.
33
+ */
34
+ export const REVIEW_TERMINAL_MISSING_RETRY_CATEGORY = "review-terminal-missing";
35
+ /**
36
+ * The plan node issued too many read-only tool calls (read/grep/ls/find),
37
+ * blowing up the context window and eventually a 400 request-too-large. The
38
+ * plan should trust upstream typed facts instead of re-reading contract/scout
39
+ * outputs and source files repeatedly. Read-only and safe to retry with a
40
+ * reduced-reading instruction.
41
+ */
42
+ export const READ_BURST_RETRY_CATEGORY = "read-burst";
43
+ /**
44
+ * Provider returned a context-overflow error (400 request-too-large / context
45
+ * window exceeded). The SDK compacts and auto-retries once internally; when it
46
+ * still surfaces, the executor classifies this so retry policies and reports
47
+ * treat it as overflow (not empty-output) and can retry with a compacted
48
+ * session / fresh context.
49
+ */
50
+ export const CONTEXT_OVERFLOW_RETRY_CATEGORY = "context-overflow";
26
51
  /** Retry only a proven no-op from an explicitly opt-in bounded Pi writer. */
27
52
  export const WRITER_EMPTY_DIFF_RETRY_CATEGORY = "writer-empty-diff";
28
53
  /** Retry when a backend-test writer finished but Completeness Gate found missing/broken targets. */
@@ -55,6 +80,9 @@ export const ALL_DAG_RETRY_CATEGORIES = [
55
80
  WRITER_EMPTY_DIFF_RETRY_CATEGORY,
56
81
  INCOMPLETE_WRITE_SET_RETRY_CATEGORY,
57
82
  WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
83
+ REVIEW_TERMINAL_MISSING_RETRY_CATEGORY,
84
+ READ_BURST_RETRY_CATEGORY,
85
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
58
86
  ];
59
87
  const RETRY_SAFE_PI_ROLES = new Set([
60
88
  "planner",
@@ -104,13 +132,35 @@ export const dagRetryPolicySchema = z
104
132
  /**
105
133
  * The default retry policy applied to safe generated read-only Pi nodes.
106
134
  * Total attempts: 3, exponential backoff with cap.
135
+ *
136
+ * Includes `context-overflow`: a read-only node that blew the context window
137
+ * (400 request too large, e.g. a review node accumulating too many reads) is a
138
+ * transient, safe-to-retry failure — the retry re-runs the node with a
139
+ * compact-context instruction instead of failing the whole run. Previously
140
+ * only writer transport policies had context-overflow; contract/scout/
141
+ * design-review/review fell straight to ERROR without a retry attempt.
107
142
  */
108
143
  export const DEFAULT_READ_ONLY_PI_RETRY_POLICY = {
109
144
  maxAttempts: 3,
110
145
  backoff: "exponential",
111
146
  initialDelayMs: 2000,
112
147
  maxDelayMs: 30000,
113
- retryCategories: [...DEFAULT_DAG_RETRY_CATEGORIES],
148
+ retryCategories: [
149
+ ...DEFAULT_DAG_RETRY_CATEGORIES,
150
+ READ_BURST_RETRY_CATEGORY,
151
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
152
+ ],
153
+ };
154
+ /** Scout is the discovery authority. A missing/incomplete typed target surface
155
+ * is safe to retry in-node, rather than making Plan rediscover the repository. */
156
+ export const FRONTEND_SCOUT_COMPLETENESS_RETRY_POLICY = {
157
+ ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
158
+ retryCategories: [
159
+ ...DEFAULT_DAG_RETRY_CATEGORIES,
160
+ STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
161
+ READ_BURST_RETRY_CATEGORY,
162
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
163
+ ],
114
164
  };
115
165
  /**
116
166
  * Structured planner nodes may retry with a compact-output instruction
@@ -120,7 +170,11 @@ export const DEFAULT_READ_ONLY_PI_RETRY_POLICY = {
120
170
  */
121
171
  export const STRUCTURED_REQUIRED_PI_RETRY_POLICY = {
122
172
  ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
123
- retryCategories: [...STRUCTURED_REQUIRED_DAG_RETRY_CATEGORIES],
173
+ retryCategories: [
174
+ ...STRUCTURED_REQUIRED_DAG_RETRY_CATEGORIES,
175
+ READ_BURST_RETRY_CATEGORY,
176
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
177
+ ],
124
178
  };
125
179
  /**
126
180
  * Planner read-only nodes (standard DAG contract-pi / plan-pi) may retry with
@@ -131,7 +185,12 @@ export const STRUCTURED_REQUIRED_PI_RETRY_POLICY = {
131
185
  */
132
186
  export const PLANNER_OUTPUT_LIMIT_RETRY_POLICY = {
133
187
  ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
134
- retryCategories: [...DEFAULT_DAG_RETRY_CATEGORIES, STRUCTURED_OUTPUT_RETRY_CATEGORY],
188
+ retryCategories: [
189
+ ...DEFAULT_DAG_RETRY_CATEGORIES,
190
+ STRUCTURED_OUTPUT_RETRY_CATEGORY,
191
+ READ_BURST_RETRY_CATEGORY,
192
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
193
+ ],
135
194
  };
136
195
  /**
137
196
  * Default retry for reviewer / recovery nodes that declare outputProtocol.
@@ -141,6 +200,22 @@ export const PROTOCOL_AWARE_PI_RETRY_POLICY = {
141
200
  ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
142
201
  retryCategories: [...PROTOCOL_AWARE_DAG_RETRY_CATEGORIES],
143
202
  };
203
+ /**
204
+ * frontend-review-pi retry policy. The review verdict must be committed
205
+ * through the typed terminal tools (approve_review / request_review_changes);
206
+ * a model that only echoes "VERDICT: pass" text fails as
207
+ * review-terminal-missing. That is read-only and safe to retry with a
208
+ * corrected instruction so a single model slip does not burn the whole run.
209
+ */
210
+ export const FRONTEND_REVIEW_TERMINAL_RETRY_POLICY = {
211
+ ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
212
+ retryCategories: [
213
+ ...DEFAULT_DAG_RETRY_CATEGORIES,
214
+ READ_BURST_RETRY_CATEGORY,
215
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
216
+ REVIEW_TERMINAL_MISSING_RETRY_CATEGORY,
217
+ ],
218
+ };
144
219
  /**
145
220
  * The sole writer retry policy for requireChangedFiles writers. It is
146
221
  * intentionally not included in any read-only default: a writer may retry only
@@ -163,7 +238,46 @@ export const WRITER_TRANSPORT_RETRY_POLICY = {
163
238
  backoff: "exponential",
164
239
  initialDelayMs: 5000,
165
240
  maxDelayMs: 30000,
166
- retryCategories: [WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY],
241
+ retryCategories: [
242
+ WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
243
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
244
+ ],
245
+ };
246
+ /**
247
+ * Implicit runtime retry for canonical final verify shells. Not a DagRetryPolicy:
248
+ * `spawn-error` / `test-timeout` are runtime command categories and must not enter
249
+ * `dagRetryPolicySchema` / `ALL_DAG_RETRY_CATEGORIES`.
250
+ */
251
+ export const VERIFY_SHELL_RETRY_CATEGORIES = [
252
+ "timeout",
253
+ "spawn-error",
254
+ "test-timeout",
255
+ ];
256
+ export const VERIFY_SHELL_RETRY_POLICY = {
257
+ maxAttempts: 2,
258
+ backoff: "exponential",
259
+ initialDelayMs: 2000,
260
+ maxDelayMs: 30000,
261
+ retryCategories: VERIFY_SHELL_RETRY_CATEGORIES,
262
+ };
263
+ export const TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE = "standard-reviewed-supervised-v1";
264
+ /**
265
+ * Target-template exclusive implement-pi transport retry. Same schema category
266
+ * as frontend (`writer-clean-timeout`) so DagSpec validation stays unchanged;
267
+ * only the delay cap is wider. Context overflow is added so a writer whose
268
+ * session hit the context window (compact+retry inside the SDK) can retry once
269
+ * more at the DAG level with a fresh attempt. Do not mutate
270
+ * {@link WRITER_TRANSPORT_RETRY_POLICY}.
271
+ */
272
+ export const TARGET_TEMPLATE_WRITER_TRANSPORT_RETRY_POLICY = {
273
+ maxAttempts: 2,
274
+ backoff: "exponential",
275
+ initialDelayMs: 5000,
276
+ maxDelayMs: 60000,
277
+ retryCategories: [
278
+ WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
279
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
280
+ ],
167
281
  };
168
282
  /**
169
283
  * Backend-test generation writers: empty-diff once, plus bounded incomplete-write-set
@@ -229,15 +343,77 @@ export function isWriterEmptyDiffRetryCandidate(task) {
229
343
  task.writerOutcomePolicy?.requireChangedFiles === true);
230
344
  }
231
345
  /**
232
- * Standard exclusive implementers (implementation-outcome writers) may take a
233
- * single clean-timeout transport retry. Backend completeness writers already
234
- * have their own policy and are excluded here.
346
+ * Standard exclusive implementers (implementation-outcome and frontend-facts
347
+ * writers) may take a single clean-timeout transport retry. Backend
348
+ * completeness writers already have their own policy and are excluded here.
235
349
  */
236
350
  export function isWriterTransportRetryCandidate(task) {
237
351
  return (isExclusivePiImplementer(task) &&
238
- task.writerOutcomePolicy?.type === "implementation-outcome-v1" &&
352
+ (task.writerOutcomePolicy?.type === "implementation-outcome-v1" ||
353
+ task.writerOutcomePolicy?.type === "frontend-facts-v1") &&
239
354
  task.writerOutcomePolicy.requireChangedFiles !== true);
240
355
  }
356
+ /**
357
+ * Canonical standard exclusive `implement-pi` only. Frontend designers and
358
+ * completeness writers stay on the existing timeout-only transport candidate.
359
+ */
360
+ export function isTargetTemplateImplementPi(task) {
361
+ return isWriterTransportRetryCandidate(task) && task.id === "implement-pi";
362
+ }
363
+ export function hasTargetTemplateTransientRetryProfile(task) {
364
+ return (task?.transientRetryProfile === TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE);
365
+ }
366
+ /**
367
+ * Dual-condition eligibility: canonical shape AND generated-path stamp.
368
+ * Custom / other-template nodes that only match id or shape stay ineligible.
369
+ */
370
+ export function isTargetTemplateTransientRetryNode(task) {
371
+ if (!hasTargetTemplateTransientRetryProfile(task))
372
+ return false;
373
+ return (isCanonicalFinalVerifyShellRetryCandidate(task) ||
374
+ isTargetTemplateImplementPi(task));
375
+ }
376
+ /**
377
+ * Shape-based eligibility for implicit canonical final verify-shell retry.
378
+ * Does not consult node id or a template field.
379
+ */
380
+ export function isCanonicalFinalVerifyShellRetryCandidate(task) {
381
+ if (task.executor !== "shell")
382
+ return false;
383
+ if (task.role !== "verifier")
384
+ return false;
385
+ if (task.writePolicy !== undefined &&
386
+ task.writePolicy !== "read-only" &&
387
+ task.writePolicy !== "none") {
388
+ return false;
389
+ }
390
+ if ((task.writeSet?.length ?? 0) > 0)
391
+ return false;
392
+ const shell = task.shell;
393
+ if (!shell || (shell.commands?.length ?? 0) === 0)
394
+ return false;
395
+ const evidence = shell.verifyEvidence;
396
+ if (!evidence)
397
+ return false;
398
+ if (evidence.phase !== "final")
399
+ return false;
400
+ if (evidence.quota !== "full")
401
+ return false;
402
+ if (evidence.commandSource !== "adapter")
403
+ return false;
404
+ if (evidence.finalFullRequired !== true)
405
+ return false;
406
+ if (shell.frontendPrewriteGate ||
407
+ shell.frontendVerificationBundle ||
408
+ shell.frontendBrowserToolPreflight ||
409
+ shell.backendTestPipeline ||
410
+ shell.jacocoCoverage ||
411
+ shell.repairArtifactGate ||
412
+ shell.verdictGate) {
413
+ return false;
414
+ }
415
+ return true;
416
+ }
241
417
  export function isSafeReadOnlyPiRetryCandidate(task) {
242
418
  if (task.executor !== "pi")
243
419
  return false;
@@ -279,3 +455,137 @@ export function computeBackoffDelayMs(attemptNumber, policy) {
279
455
  const raw = policy.initialDelayMs * 2 ** exponent;
280
456
  return Math.min(raw, policy.maxDelayMs);
281
457
  }
458
+ /**
459
+ * A+B (AC-001/AC-005): project raw executor signals to the canonical 7-value
460
+ * protocol failure reason. Specific signals win first, then the existing
461
+ * alias/enum passthrough; unknown signals fail closed to `undefined`.
462
+ */
463
+ export function projectFrontendNodeProtocolFailureReason(input) {
464
+ // 1. length stop reason without a terminal → length-before-terminal.
465
+ if (input.stopReason === "length" && input.hasTerminalFact === false) {
466
+ return "length-before-terminal";
467
+ }
468
+ // 2. timeout with reasoning-side evidence → reasoning-timeout.
469
+ if (input.failureCategory === "timeout" &&
470
+ /(?:reasoning|thinking|thought)/i.test(input.assistantText ?? "")) {
471
+ return "reasoning-timeout";
472
+ }
473
+ // 3. empty output without a terminal → provider-ended-without-terminal.
474
+ if (input.failureCategory === "empty-output" &&
475
+ input.hasTerminalFact === false) {
476
+ return "provider-ended-without-terminal";
477
+ }
478
+ // 4. Alias/enum passthrough; unknown fails closed to undefined.
479
+ return projectProtocolFailureReason(input.failureCategory ?? "");
480
+ }
481
+ export const FRONTEND_PLAN_LADDER = [
482
+ "normal",
483
+ "compact-terminal-first",
484
+ "bounded-tool-only",
485
+ "backup-model",
486
+ ];
487
+ /**
488
+ * Map a 7-value protocol failure reason to the closest provider capability
489
+ * fact kind for backup-model routing. `unsupported-tool-capability` is a
490
+ * protocol reason, not a provider capability kind, so it is deliberately NOT
491
+ * a valid route (fail-closed to `unsupported-provider-capability`).
492
+ */
493
+ export function frontendPlanProtocolReasonToCapabilityKind(reason) {
494
+ switch (reason) {
495
+ case "length-before-terminal":
496
+ return "length-before-terminal";
497
+ case "reasoning-timeout":
498
+ return "reasoning-timeout";
499
+ case "provider-ended-without-terminal":
500
+ return "provider-ended-without-terminal";
501
+ case "terminal-event-missing":
502
+ case "terminal-conflict":
503
+ return "terminal-commit";
504
+ case "typed-fact-invalid":
505
+ return "read-only-fact-channel";
506
+ case "unsupported-tool-capability":
507
+ return "unsupported-tool-capability";
508
+ default:
509
+ return undefined;
510
+ }
511
+ }
512
+ /**
513
+ * A+B (AC-003): resolve a backup-model route against the frozen seven-kind
514
+ * provider capability catalog (the exact set `routeFrontendProviderCapability`
515
+ * consults). `unsupported-tool-capability` is a protocol reason, not a
516
+ * provider capability kind, so it (and any unmapped reason) fails closed to
517
+ * `unsupported-provider-capability` — never swapped for the 7-value reason.
518
+ */
519
+ export function resolveFrontendPlanBackupRoute(reason) {
520
+ const factKind = frontendPlanProtocolReasonToCapabilityKind(reason);
521
+ if (!factKind) {
522
+ return { ok: false, reason: "unsupported-provider-capability" };
523
+ }
524
+ return TYPED_EVENT_FACT_KINDS.includes(factKind)
525
+ ? { ok: true, factKind }
526
+ : { ok: false, reason: "unsupported-provider-capability" };
527
+ }
528
+ /**
529
+ * A+B (AC-002): deterministic ladder resolution. Reason-mandated upgrades
530
+ * (length → compact-terminal-first; terminal/timeout/empty → bounded-tool-only)
531
+ * never regress below the current rung. A repeated failure fingerprint with no
532
+ * new committed fact escalates one rung instead of re-running the same step;
533
+ * at `backup-model` that becomes `non-converging`, and an unavailable backup
534
+ * provider route fails closed to `unsupported-provider-capability`.
535
+ */
536
+ export function resolveFrontendPlanRetryStep(input) {
537
+ if (input.currentStep === "non-converging" ||
538
+ input.currentStep === "unsupported-provider-capability") {
539
+ return input.currentStep;
540
+ }
541
+ const currentIndex = FRONTEND_PLAN_LADDER.indexOf(input.currentStep);
542
+ const reasonStep = reasonMandatedFrontendPlanRetryStep(input.protocolFailureReason);
543
+ const reasonIndex = reasonStep
544
+ ? FRONTEND_PLAN_LADDER.indexOf(reasonStep)
545
+ : -1;
546
+ let nextIndex = Math.max(currentIndex, reasonIndex);
547
+ const repeated = input.priorFingerprints.includes(input.fingerprint);
548
+ if (repeated && !input.hasNewCommittedFact) {
549
+ nextIndex = Math.max(nextIndex, currentIndex + 1);
550
+ }
551
+ const nextStep = nextIndex >= FRONTEND_PLAN_LADDER.length
552
+ ? "non-converging"
553
+ : (FRONTEND_PLAN_LADDER[nextIndex] ?? "non-converging");
554
+ if (nextStep === "backup-model" && input.backupRouteAvailable === false) {
555
+ return "unsupported-provider-capability";
556
+ }
557
+ return nextStep;
558
+ }
559
+ function reasonMandatedFrontendPlanRetryStep(reason) {
560
+ switch (reason) {
561
+ case "length-before-terminal":
562
+ return "compact-terminal-first";
563
+ case "terminal-event-missing":
564
+ case "reasoning-timeout":
565
+ case "provider-ended-without-terminal":
566
+ return "bounded-tool-only";
567
+ default:
568
+ return undefined;
569
+ }
570
+ }
571
+ /**
572
+ * Opt-in retry policy mounted on the frontend-plan-pi node. maxAttempts=3
573
+ * bounds plan retry cost: the four-rung ladder is only partially traversable
574
+ * before the run fails non-converging (rungs: normal → bounded-tool-only →
575
+ * compact-terminal-first → backup-model). `invalid-output` (typed-fact schema
576
+ * violations, e.g. verification targets referencing undeclared UI states)
577
+ * retries at the `normal` rung with the contractCheck reason injected via
578
+ * `<retry_instruction>` (§5.1 ladder).
579
+ */
580
+ export const FRONTEND_PLAN_LADDER_RETRY_POLICY = {
581
+ maxAttempts: 3,
582
+ backoff: "exponential",
583
+ initialDelayMs: 2000,
584
+ maxDelayMs: 30000,
585
+ retryCategories: [
586
+ ...DEFAULT_DAG_RETRY_CATEGORIES,
587
+ STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
588
+ READ_BURST_RETRY_CATEGORY,
589
+ CONTEXT_OVERFLOW_RETRY_CATEGORY,
590
+ ],
591
+ };