@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
@@ -17,21 +17,33 @@ const ACTION_PROGRESS_COPY = {
17
17
  spineCheck: "正在运行 Spine 审计…",
18
18
  dagGenerate: "正在生成 DAG…",
19
19
  standaloneTaskRerun: "正在带反馈重新执行任务:服务端校验资格后生成并启动新 DAG…",
20
+ consoleUpdateAndInit: "正在检查活动运行、所选 npm 渠道与安全初始化;完成后 Console 将自动刷新…",
20
21
  workerTaskRetry: "正在重新排队 Worker Task…",
21
22
  };
22
23
  /** Central operator-action dispatcher: POST /api/operator/v1/operations with
23
24
  * polling, long-running background waits, interview payload handoff and the
24
25
  * per-operation SSE follow log. */
25
26
  export function useOperatorActions(params) {
26
- const { shell, applyInterviewPayload } = params;
27
+ const { shell, applyInterviewPayload, workspaceId } = params;
27
28
  const { setBusy, setOpError, setOpErrorCode, setOpMessage, setReadiness, setStep, setNav, setLastOpState, setLayers, step, nav, } = shell;
28
29
  const [operationId, setOperationId] = useState(null);
30
+ const [operationWorkspaceId, setOperationWorkspaceId] = useState(null);
29
31
  const [sseLog, setSseLog] = useState([]);
30
32
  const [followSse, setFollowSse] = useState(true);
31
33
  const sseAbort = useRef(null);
32
34
  const [confirmationId, setConfirmationId] = useState(null);
33
35
  const [confirmationDetails, setConfirmationDetails] = useState(null);
34
36
  const runAction = useCallback(async (action, actionParams = {}, opts) => {
37
+ const requestWorkspaceId = workspaceId?.trim() || null;
38
+ if (!requestWorkspaceId) {
39
+ const message = "当前 Workspace 不可用;请先选择有效 Workspace 后再执行操作";
40
+ setOpError(message);
41
+ setOpErrorCode("WORKSPACE_REQUIRED");
42
+ return {
43
+ ok: false,
44
+ error: { code: "WORKSPACE_REQUIRED", message },
45
+ };
46
+ }
35
47
  setBusy(true);
36
48
  setOpError(null);
37
49
  setOpErrorCode(null);
@@ -40,7 +52,7 @@ export function useOperatorActions(params) {
40
52
  // other UI feedback, which reads as a frozen page.
41
53
  setOpMessage(ACTION_PROGRESS_COPY[action] ?? null);
42
54
  try {
43
- const { status, body } = await postOperator(action, actionParams, opts?.clientRequestId);
55
+ const { status, body } = await postOperator(action, actionParams, opts?.clientRequestId, requestWorkspaceId);
44
56
  if (status === 412 || body.error?.code === "PI_SETUP_REQUIRED") {
45
57
  setOpError(formatOperatorUserError(body.error?.message ?? "Pi 未就绪或已降级:该操作暂不可用"));
46
58
  setOpErrorCode(body.error?.code ?? "PI_SETUP_REQUIRED");
@@ -62,6 +74,7 @@ export function useOperatorActions(params) {
62
74
  return body;
63
75
  }
64
76
  if (body.operationId) {
77
+ setOperationWorkspaceId(requestWorkspaceId ?? null);
65
78
  const activeChatSessionId = window.localStorage
66
79
  .getItem(ACTIVE_CHAT_SESSION_STORAGE_KEY)
67
80
  ?.trim();
@@ -94,7 +107,7 @@ export function useOperatorActions(params) {
94
107
  setOpMessage(`操作已接受,后台运行中:${body.operationId}(请看事件流;可同时打开 Observe)`);
95
108
  // Recovery / long actions still need terminal feedback on this page.
96
109
  const acceptedOpId = body.operationId;
97
- void waitForOperation(acceptedOpId, 30 * 60_000).then((finalOp) => {
110
+ void waitForOperation(acceptedOpId, 30 * 60_000, requestWorkspaceId).then((finalOp) => {
98
111
  if (!finalOp)
99
112
  return;
100
113
  setLastOpState(finalOp.state ?? null);
@@ -118,11 +131,15 @@ export function useOperatorActions(params) {
118
131
  command: "succeeded",
119
132
  }));
120
133
  }
134
+ if (action === "consoleUpdateAndInit" && finalOp.state === "restarting") {
135
+ setOpMessage("新 Console 正在接棒,页面即将刷新…");
136
+ setTimeout(() => window.location.reload(), 800);
137
+ }
121
138
  });
122
139
  }
123
140
  else {
124
141
  setOpMessage(`操作已接受:${body.operationId}`);
125
- const finalOp = await waitForOperation(body.operationId);
142
+ const finalOp = await waitForOperation(body.operationId, 300_000, requestWorkspaceId);
126
143
  if (finalOp) {
127
144
  setLastOpState(finalOp.state ?? null);
128
145
  if (finalOp.state === "failed" ||
@@ -222,6 +239,7 @@ export function useOperatorActions(params) {
222
239
  setNav,
223
240
  setLastOpState,
224
241
  setLayers,
242
+ workspaceId,
225
243
  ]);
226
244
  const summarizeEnvelope = useCallback((body) => {
227
245
  if (!body)
@@ -260,7 +278,10 @@ export function useOperatorActions(params) {
260
278
  sseAbort.current = ac;
261
279
  setSseLog((prev) => [...prev, `开始跟随事件流 ${opId}`]);
262
280
  try {
263
- const res = await fetch(`/api/operator/v1/operations/${encodeURIComponent(opId)}/events`, { credentials: "same-origin", signal: ac.signal });
281
+ const query = operationWorkspaceId
282
+ ? `?workspaceId=${encodeURIComponent(operationWorkspaceId)}`
283
+ : "";
284
+ const res = await fetch(`/api/operator/v1/operations/${encodeURIComponent(opId)}/events${query}`, { credentials: "same-origin", signal: ac.signal });
264
285
  if (!res.ok || !res.body) {
265
286
  setSseLog((prev) => [...prev, `事件流 HTTP ${res.status}`]);
266
287
  return;
@@ -294,7 +315,7 @@ export function useOperatorActions(params) {
294
315
  ]);
295
316
  }
296
317
  }
297
- }, [followSse]);
318
+ }, [followSse, operationWorkspaceId]);
298
319
  useEffect(() => {
299
320
  if (operationId && followSse && (step === "run" || nav === "recovery")) {
300
321
  void followOperationEvents(operationId);
@@ -306,6 +327,19 @@ export function useOperatorActions(params) {
306
327
  sseAbort.current?.abort();
307
328
  setOpMessage("已停止跟随日志(未取消子进程)");
308
329
  }, [setOpMessage]);
330
+ /** AC-004 (2026-08-26): Operate workspace-switch reset. Aborts the old
331
+ * workspace's operation SSE follow and clears operationId + human-gate
332
+ * confirmation state. The wizard/recovery ids are cleared by App; this
333
+ * hook owns only its own operation/confirmation projection. */
334
+ const resetForWorkspaceScope = useCallback(() => {
335
+ sseAbort.current?.abort();
336
+ setOperationId(null);
337
+ setOperationWorkspaceId(null);
338
+ setSseLog([]);
339
+ setFollowSse(true);
340
+ setConfirmationId(null);
341
+ setConfirmationDetails(null);
342
+ }, []);
309
343
  return {
310
344
  runAction,
311
345
  summarizeEnvelope,
@@ -316,6 +350,7 @@ export function useOperatorActions(params) {
316
350
  followSse,
317
351
  setFollowSse,
318
352
  stopFollowSse,
353
+ resetForWorkspaceScope,
319
354
  confirmationId,
320
355
  setConfirmationId,
321
356
  confirmationDetails,
@@ -1,8 +1,9 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { classifyRecoveryFact, consoleRecoveryCtas, partitionRecoveryCtas, recommendedRecoveryCtaId, } from "../../recovery-cta.js";
3
3
  import { buildRecoveryReason, isAutoRecoveryReason, pickDefaultRecoveryNodeId, pickDefaultRecoveryRunId, sortRecoveryNodeCandidates, } from "../../recovery-selection.js";
4
- import { postOperator, RECOVERY_FACT_LABEL, } from "./console-types.js";
4
+ import { postOperator, scopedReadUrl, RECOVERY_FACT_LABEL, } from "./console-types.js";
5
5
  import { useRecoveryActions } from "./useRecoveryActions.js";
6
+ import { applyCanonicalHref, buildInspectHref, } from "../shell/workspace-route.js";
6
7
  /** Recovery workspace: DAG-run / node candidate selection, fact-classified
7
8
  * CTA lanes, structured reason prefill, plan preflight and gated reruns. */
8
9
  export function useRecoveryConsole(params) {
@@ -36,12 +37,12 @@ export function useRecoveryConsole(params) {
36
37
  const inspectPath = useCallback(() => {
37
38
  if (dagRunId.trim()) {
38
39
  // from=recovery 激活统一壳的对象上下文 chip 与显式返回路径
39
- return `/inspect/#/dag/${encodeURIComponent(dagRunId.trim())}?from=recovery`;
40
+ return buildInspectHref(`/dag/${encodeURIComponent(dagRunId.trim())}`, "from=recovery");
40
41
  }
41
- return "/inspect/#/";
42
+ return "/#/inspect";
42
43
  }, [dagRunId]);
43
44
  const openFullInspect = useCallback(() => {
44
- window.location.assign(inspectPath());
45
+ applyCanonicalHref(inspectPath());
45
46
  }, [inspectPath]);
46
47
  /** Resolve DAG run id for inspect (side panel or full page). */
47
48
  const resolveDagRunForInspect = useCallback(async () => {
@@ -70,7 +71,7 @@ export function useRecoveryConsole(params) {
70
71
  setDagRunId(resolvedDag);
71
72
  setObserveLinkStatus("match");
72
73
  setObserveLinkMessage(null);
73
- setObserveUrl(`/inspect/#/dag/${encodeURIComponent(resolvedDag)}`);
74
+ setObserveUrl(buildInspectHref(`/dag/${encodeURIComponent(resolvedDag)}`));
74
75
  return resolvedDag;
75
76
  }
76
77
  setOpError("尚未找到该任务的 DAG 运行,请稍后再打开检视");
@@ -97,7 +98,7 @@ export function useRecoveryConsole(params) {
97
98
  const resolved = await resolveDagRunForInspect();
98
99
  if (!resolved)
99
100
  return;
100
- window.location.assign(`/inspect/#/dag/${encodeURIComponent(resolved)}?from=recovery`);
101
+ applyCanonicalHref(buildInspectHref(`/dag/${encodeURIComponent(resolved)}`, "from=recovery"));
101
102
  }, [resolveDagRunForInspect]);
102
103
  const loadRecoveryRuns = useCallback(async () => {
103
104
  setRecoveryRunsLoading(true);
@@ -107,7 +108,7 @@ export function useRecoveryConsole(params) {
107
108
  if (id)
108
109
  qs.set("taskId", id);
109
110
  qs.set("limit", "40");
110
- const res = await fetch(`/api/operator/v1/recovery/candidates?${qs.toString()}`, { credentials: "same-origin" });
111
+ const res = await fetch(scopedReadUrl(`/api/operator/v1/recovery/candidates?${qs.toString()}`), { credentials: "same-origin" });
111
112
  if (!res.ok) {
112
113
  setRecoveryRuns([]);
113
114
  return;
@@ -133,7 +134,7 @@ export function useRecoveryConsole(params) {
133
134
  }
134
135
  setRecoveryNodesLoading(true);
135
136
  try {
136
- const res = await fetch(`/api/dag-runs/${encodeURIComponent(id)}`, {
137
+ const res = await fetch(scopedReadUrl(`/api/dag-runs/${encodeURIComponent(id)}`), {
137
138
  credentials: "same-origin",
138
139
  });
139
140
  if (!res.ok) {
@@ -219,6 +220,22 @@ export function useRecoveryConsole(params) {
219
220
  setInterruptEligibility(null);
220
221
  return;
221
222
  }
223
+ // Interrupt eligibility only changes while a run is live. Polling a
224
+ // terminal run every 4s is pure noise — back off to a slow keepalive
225
+ // (the status itself can still flip if reconciliation rewrites it).
226
+ const idleStatuses = new Set([
227
+ "finished",
228
+ "failed",
229
+ "partial_failed",
230
+ "superseded",
231
+ "abandoned",
232
+ "interrupted",
233
+ ]);
234
+ const intervalMs = recoveryRunStatus && idleStatuses.has(recoveryRunStatus) ? 30_000 : 4_000;
235
+ // While live (4s cadence) also refresh the run status itself: the
236
+ // status is only read once per selection, so a run that settles while
237
+ // selected used to keep the fast eligibility poll forever (2026-08-23).
238
+ const refreshRunStatus = intervalMs === 4_000;
222
239
  let cancelled = false;
223
240
  const load = async () => {
224
241
  const { body } = await postOperator("dagInterruptEligibility", { runId });
@@ -234,14 +251,32 @@ export function useRecoveryConsole(params) {
234
251
  targetOperationId: result.targetOperationId,
235
252
  targetOperationState: result.targetOperationState,
236
253
  });
254
+ if (!refreshRunStatus)
255
+ return;
256
+ try {
257
+ const res = await fetch(scopedReadUrl(`/api/dag-runs/${encodeURIComponent(runId)}`), { credentials: "same-origin" });
258
+ if (!res.ok || cancelled)
259
+ return;
260
+ const runBody = (await res.json());
261
+ if (cancelled)
262
+ return;
263
+ const next = (typeof runBody.effectiveStatus === "string" &&
264
+ runBody.effectiveStatus) ||
265
+ (typeof runBody.status === "string" && runBody.status) ||
266
+ null;
267
+ setRecoveryRunStatus(next);
268
+ }
269
+ catch {
270
+ // Keep the last known status; the next round retries.
271
+ }
237
272
  };
238
273
  void load();
239
- const timer = window.setInterval(() => void load(), 4000);
274
+ const timer = window.setInterval(() => void load(), intervalMs);
240
275
  return () => {
241
276
  cancelled = true;
242
277
  window.clearInterval(timer);
243
278
  };
244
- }, [nav, dagRunId]);
279
+ }, [nav, dagRunId, recoveryRunStatus]);
245
280
  useEffect(() => {
246
281
  if (nav !== "recovery")
247
282
  return;
@@ -0,0 +1,47 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import { isTerminalDagRun } from "../../inspect-split.js";
3
+ import { scopedReadUrl } from "./console-types.js";
4
+ import { summarizeRunProgress, } from "../pages/tasks/run-panel-progress.js";
5
+ const RUN_PROGRESS_POLL_INTERVAL_MS = 5000;
6
+ /**
7
+ * Live run progress for the wizard RunPanel. Polls the DAG run detail
8
+ * endpoint every 5s while the run is live and stops once terminal; returns
9
+ * null until the first payload arrives. Errors keep the last known state.
10
+ */
11
+ export function useRunProgress(dagRunId) {
12
+ const [dag, setDag] = useState(null);
13
+ useEffect(() => {
14
+ const runId = dagRunId.trim();
15
+ setDag(null);
16
+ if (!runId)
17
+ return;
18
+ const controller = new AbortController();
19
+ let timer;
20
+ const load = async () => {
21
+ try {
22
+ const response = await fetch(scopedReadUrl(`/api/dag-runs/${encodeURIComponent(runId)}`), { credentials: "same-origin", signal: controller.signal });
23
+ if (!response.ok)
24
+ return;
25
+ const body = (await response.json());
26
+ if (controller.signal.aborted)
27
+ return;
28
+ setDag(body);
29
+ if (isTerminalDagRun(body) && timer !== undefined) {
30
+ window.clearInterval(timer);
31
+ timer = undefined;
32
+ }
33
+ }
34
+ catch {
35
+ // Keep the last known state; transient failures surface via staleness.
36
+ }
37
+ };
38
+ void load();
39
+ timer = window.setInterval(() => void load(), RUN_PROGRESS_POLL_INTERVAL_MS);
40
+ return () => {
41
+ controller.abort();
42
+ if (timer !== undefined)
43
+ window.clearInterval(timer);
44
+ };
45
+ }, [dagRunId]);
46
+ return useMemo(() => summarizeRunProgress(dag), [dag]);
47
+ }
@@ -1,6 +1,14 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { ensureUniqueDocumentNames, operatorRequestFits, operatorRequestTooLargeMessage, } from "../prd-file-import.js";
3
- import { requestId, WORKFLOW_OPTIONS, } from "./console-types.js";
3
+ import { requestId, scopedReadUrl, WORKFLOW_OPTIONS, } from "./console-types.js";
4
+ export async function fetchLedgerSummary(taskId, fetchImpl) {
5
+ const impl = fetchImpl ?? fetch;
6
+ const response = await impl(scopedReadUrl(`/api/tasks/${encodeURIComponent(taskId)}/ledger`), { credentials: "same-origin" });
7
+ if (!response.ok)
8
+ return { ok: false };
9
+ const summary = (await response.json());
10
+ return { ok: true, summary };
11
+ }
4
12
  /** Happy Path task wizard: import-prd → interview → contract → dag-confirm →
5
13
  * run. Owns task identity, interview projection and contract preflight/apply. */
6
14
  export function useTaskWizard(params) {
@@ -24,16 +32,27 @@ export function useTaskWizard(params) {
24
32
  const [contractDiffSummary, setContractDiffSummary] = useState(null);
25
33
  const [contractApplied, setContractApplied] = useState(false);
26
34
  const [spineOk, setSpineOk] = useState(null);
35
+ const [ledgerLoading, setLedgerLoading] = useState(false);
36
+ const [ledgerError, setLedgerError] = useState(null);
37
+ const [ledgerSummary, setLedgerSummary] = useState(null);
27
38
  const [contractPreflightKey, setContractPreflightKey] = useState(null);
28
39
  const contractPreflightRunning = useRef(false);
40
+ /** Epoch guard for ledger fetches: only the newest request may write wizard
41
+ * state, so a concurrent in-flight preflight response can never overwrite
42
+ * a fresh post-apply result (AC-001). */
43
+ const ledgerFetchEpoch = useRef(0);
29
44
  const resetContractLanding = useCallback(() => {
30
45
  setContractValidateOk(null);
31
46
  setContractValidateSummary(null);
32
47
  setContractDiffSummary(null);
33
48
  setContractApplied(false);
34
49
  setSpineOk(null);
50
+ setLedgerLoading(false);
51
+ setLedgerError(null);
52
+ setLedgerSummary(null);
35
53
  setContractPreflightKey(null);
36
54
  contractPreflightRunning.current = false;
55
+ ledgerFetchEpoch.current = 0;
37
56
  }, []);
38
57
  const enterContractStep = useCallback(() => {
39
58
  resetContractLanding();
@@ -56,6 +75,39 @@ export function useTaskWizard(params) {
56
75
  : "访谈已完成:草稿已绑定完整评估结果");
57
76
  }
58
77
  }, [enterContractStep, setOpMessage]);
78
+ /** Epoch-guarded shared ledger refresh used by both the contract-step
79
+ * preflight and the post-apply re-GET. `failureSemantics` keeps the
80
+ * preflight five-state behavior (non-ok → empty state, no error) while the
81
+ * post-apply refresh treats a non-ok/network failure as a safe-degrade
82
+ * error per AC-001. */
83
+ const refreshLedgerState = useCallback(async (id, failureSemantics) => {
84
+ const epoch = ++ledgerFetchEpoch.current;
85
+ setLedgerLoading(true);
86
+ setLedgerError(null);
87
+ try {
88
+ const outcome = await fetchLedgerSummary(id);
89
+ if (ledgerFetchEpoch.current !== epoch)
90
+ return;
91
+ if (!outcome.ok) {
92
+ setLedgerSummary(null);
93
+ if (failureSemantics) {
94
+ setLedgerError("ledger 接口不可达(安全降级)");
95
+ }
96
+ return;
97
+ }
98
+ setLedgerSummary(outcome.summary);
99
+ }
100
+ catch {
101
+ if (ledgerFetchEpoch.current !== epoch)
102
+ return;
103
+ setLedgerError("ledger 接口不可达(安全降级)");
104
+ setLedgerSummary(null);
105
+ }
106
+ finally {
107
+ if (ledgerFetchEpoch.current === epoch)
108
+ setLedgerLoading(false);
109
+ }
110
+ }, []);
59
111
  const runContractPreflight = useCallback(async (force = false) => {
60
112
  const id = taskId.trim();
61
113
  if (!id)
@@ -74,6 +126,9 @@ export function useTaskWizard(params) {
74
126
  setContractDiffSummary(null);
75
127
  setSpineOk(null);
76
128
  setContractApplied(false);
129
+ // Shared epoch-guarded refresh preserves the five-state semantics:
130
+ // non-ok → empty state, network failure → safe-degrade error.
131
+ await refreshLedgerState(id, false);
77
132
  try {
78
133
  const validateBody = await runAction("contractValidate", {
79
134
  taskId: id,
@@ -101,6 +156,7 @@ export function useTaskWizard(params) {
101
156
  contractPreflightKey,
102
157
  contractValidateOk,
103
158
  isActionSuccess,
159
+ refreshLedgerState,
104
160
  runAction,
105
161
  setOpMessage,
106
162
  summarizeEnvelope,
@@ -116,6 +172,11 @@ export function useTaskWizard(params) {
116
172
  return;
117
173
  }
118
174
  setContractApplied(true);
175
+ // AC-001: re-GET the read-only ledger right after apply succeeds and
176
+ // write the latest summary BEFORE spineCheck/setStep, so the first
177
+ // dag-confirm frame shows post-apply statistics (never a loading/empty
178
+ // flash); refresh failure safe-degrades via ledgerError.
179
+ await refreshLedgerState(id, true);
119
180
  const spineBody = await runAction("spineCheck", { taskId: id });
120
181
  if (isActionSuccess(spineBody)) {
121
182
  setSpineOk(true);
@@ -125,7 +186,15 @@ export function useTaskWizard(params) {
125
186
  }
126
187
  setSpineOk(false);
127
188
  setOpMessage("契约已应用,但 Spine 审计未通过:可重试 Spine");
128
- }, [assessmentId, isActionSuccess, runAction, setOpMessage, setStep, taskId]);
189
+ }, [
190
+ assessmentId,
191
+ isActionSuccess,
192
+ refreshLedgerState,
193
+ runAction,
194
+ setOpMessage,
195
+ setStep,
196
+ taskId,
197
+ ]);
129
198
  const retrySpineOnly = useCallback(async () => {
130
199
  const id = taskId.trim();
131
200
  if (!id)
@@ -282,6 +351,9 @@ export function useTaskWizard(params) {
282
351
  contractValidateOk,
283
352
  contractValidateSummary,
284
353
  contractDiffSummary,
354
+ ledgerLoading,
355
+ ledgerError,
356
+ ledgerSummary,
285
357
  contractApplied,
286
358
  spineOk,
287
359
  applyInterviewPayload,
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useState } from "react";
2
+ import { scopedReadUrl } from "../app/console-types.js";
2
3
  /** Night board data plane: schedules list + doctor report + task pool +
3
4
  * foreground aux-ticker projection, loaded together on mount/refresh. */
4
5
  export function useNightBoard(params) {
@@ -16,10 +17,12 @@ export function useNightBoard(params) {
16
17
  setLoadError(null);
17
18
  try {
18
19
  const [jobsRes, doctorRes, tasksRes, tickerRes] = await Promise.all([
19
- fetch("/api/night-jobs", { credentials: "same-origin" }),
20
- fetch("/api/night-jobs/doctor", { credentials: "same-origin" }),
21
- fetch("/api/tasks", { credentials: "same-origin" }),
22
- fetch("/api/operator/v1/night-aux-ticker", {
20
+ fetch(scopedReadUrl("/api/night-jobs"), { credentials: "same-origin" }),
21
+ fetch(scopedReadUrl("/api/night-jobs/doctor"), {
22
+ credentials: "same-origin",
23
+ }),
24
+ fetch(scopedReadUrl("/api/tasks"), { credentials: "same-origin" }),
25
+ fetch(scopedReadUrl("/api/operator/v1/night-aux-ticker"), {
23
26
  credentials: "same-origin",
24
27
  }),
25
28
  ]);
@@ -59,7 +62,7 @@ export function useNightBoard(params) {
59
62
  }, [refreshBoard]);
60
63
  const onMorningReport = useCallback(async () => {
61
64
  try {
62
- const res = await fetch("/api/night-jobs/morning", {
65
+ const res = await fetch(scopedReadUrl("/api/night-jobs/morning"), {
63
66
  credentials: "same-origin",
64
67
  });
65
68
  if (!res.ok) {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Operator-facing recovery advice per normalized DAG failure category
3
+ * (mirrors DAG_NORMALIZED_FAILURE_CATEGORIES). The task journey card used to
4
+ * stop at "原因分类为 X" without telling the operator what to do next; this
5
+ * maps each class to the concrete next move, aligned with the Recovery
6
+ * workspace CTAs (node rerun / reconcile / models page).
7
+ */
8
+ const FAILURE_CATEGORY_ADVICE = {
9
+ validation: "节点输出未通过契约或门禁校验。可在恢复页从失败节点重跑;若错误指向任务契约(如验证命令、Mock 策略),先修正任务配置并重新生成 DAG。",
10
+ executor: "执行器或模型提供方调用失败(网络、配额或运行时错误)。稍后可在恢复页从失败节点重跑;反复出现请到「模型与认证」页检查配置与凭据。",
11
+ "write-guard": "写入越过了授权边界(writeSet / allowedPaths)。检查计划中的写入路径是否越界,修正后从失败节点重跑。",
12
+ timeout: "节点执行超时。可在恢复页从失败节点重跑;反复超时建议缩小任务范围或排查卡住的命令。",
13
+ auth: "模型或服务认证失败。请先到「模型与认证」页检查 API Key 与模型配置,再从失败节点重跑。",
14
+ "human-required": "节点正在等待人工审批。完成审批后该节点可继续执行;也可在恢复页查看对账状态。",
15
+ "human-rejected": "人工审批被拒绝。请调整任务描述或契约后重新发起任务。",
16
+ "shell-command": "验证/构建命令以非零退出。进入 DAG 查看命令输出定位具体失败,修复后从失败节点重跑。",
17
+ "static-error": "静态检查(typecheck / lint 等)失败。修复对应代码错误后从失败节点重跑。",
18
+ "decision-envelope": "决策与证据不匹配(决策包络校验失败)。进入 DAG 查看节点决策依据,修正后重跑。",
19
+ unknown: "失败原因未归类。进入 DAG 查看节点详情,或在恢复页使用对账 / 诊断入口。",
20
+ };
21
+ export function failureCategoryAdvice(category) {
22
+ if (!category)
23
+ return undefined;
24
+ return FAILURE_CATEGORY_ADVICE[category];
25
+ }