@tea-agent/loop-agent 0.39.0-next.9 → 0.39.1

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 (288) hide show
  1. package/AGENTS.md +4 -1
  2. package/CHANGELOG.md +269 -69
  3. package/README.md +11 -5
  4. package/bin/loop-agent.js +7 -3
  5. package/dist/application/dag/generate-task-dag.js +9 -0
  6. package/dist/application/task-lifecycle/advance.js +16 -0
  7. package/dist/build-stamp.json +3 -3
  8. package/dist/cli/command-definitions.js +16 -5
  9. package/dist/cli/program.js +16 -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/dag-rerun.js +55 -1
  14. package/dist/commands/init-model-catalog.js +464 -0
  15. package/dist/commands/init-upgrade.js +265 -97
  16. package/dist/commands/init.js +428 -27
  17. package/dist/commands/inspect-next.js +8 -0
  18. package/dist/commands/task-advance.js +31 -0
  19. package/dist/executors/dag-pi-executor.js +124 -18
  20. package/dist/executors/pi-event-serializer.js +42 -1
  21. package/dist/executors/pi-executor.js +51 -0
  22. package/dist/executors/pi-extension-resolver.js +233 -0
  23. package/dist/executors/pi-playwright-cli-tool.js +74 -28
  24. package/dist/executors/pi-sdk-executor.js +187 -59
  25. package/dist/executors/shell-executor.js +555 -34
  26. package/dist/executors/shell-write-guard.js +7 -0
  27. package/dist/shared/dag-failure-category.js +138 -0
  28. package/dist/shared/dag-prompt-override.js +27 -0
  29. package/dist/shared/operator/capabilities.js +181 -27
  30. package/dist/shared/playwright-cli-command-policy.js +15 -0
  31. package/dist/shared/update/console-notifier.js +100 -0
  32. package/dist/{cli → shared}/update/npm-client.js +70 -15
  33. package/dist/{cli → shared}/update/state.js +41 -13
  34. package/dist/task/config-types.js +28 -0
  35. package/dist/task/contract/apply.js +11 -1
  36. package/dist/task/contract/constants.js +3 -0
  37. package/dist/task/contract/diff.js +26 -0
  38. package/dist/task/contract/hash.js +3 -0
  39. package/dist/task/contract/observe.js +25 -4
  40. package/dist/task/contract/paths.js +2 -1
  41. package/dist/task/contract/project.js +20 -1
  42. package/dist/task/contract/schema.js +8 -1
  43. package/dist/task/contract/transaction.js +12 -0
  44. package/dist/task/source-prepare/build-draft.js +15 -0
  45. package/dist/task/source-prepare/completeness.js +188 -0
  46. package/dist/task/source-prepare/fragment-inventory.js +474 -0
  47. package/dist/task/source-prepare/index.js +3 -0
  48. package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
  49. package/dist/task/source-prepare/ledger-review.js +214 -0
  50. package/dist/task/source-prepare/ledger.js +545 -0
  51. package/dist/task/source-prepare/parse-intent.js +24 -4
  52. package/dist/task/source-prepare/prepare.js +262 -1
  53. package/dist/task/source-prepare/semantic-intake.js +19 -3
  54. package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
  55. package/dist/task/source-prepare/types.js +22 -0
  56. package/dist/worker/cli.js +18 -2
  57. package/dist/worker/console/chat/deferred-turn.js +91 -0
  58. package/dist/worker/console/chat/mcp-inventory.js +221 -0
  59. package/dist/worker/console/chat/operation-card.js +8 -1
  60. package/dist/worker/console/chat/pi-console-config.js +81 -10
  61. package/dist/worker/console/chat/pi-runtime.js +165 -83
  62. package/dist/worker/console/chat/repo-walk.js +10 -1
  63. package/dist/worker/console/chat/routes.js +480 -55
  64. package/dist/worker/console/chat/semantic-activity.js +20 -8
  65. package/dist/worker/console/chat/session-store.js +146 -2
  66. package/dist/worker/console/chat/turn-process.js +10 -10
  67. package/dist/worker/console/console-handoff.js +82 -0
  68. package/dist/worker/console/console-update-and-init.js +115 -0
  69. package/dist/worker/console/console-update-runtime.js +84 -0
  70. package/dist/worker/console/dag-execution-receipt.js +33 -0
  71. package/dist/worker/console/draft-store.js +49 -0
  72. package/dist/worker/console/inspect-split.js +18 -0
  73. package/dist/worker/console/operation-run-facts.js +19 -3
  74. package/dist/worker/console/operation-store.js +1 -1
  75. package/dist/worker/console/operator-actions.js +320 -4
  76. package/dist/worker/console/operator-surface-health.js +1 -0
  77. package/dist/worker/console/operator-user-error.js +2 -2
  78. package/dist/worker/console/pi-readiness.js +5 -4
  79. package/dist/worker/console/prd-intake-bridge.js +13 -0
  80. package/dist/worker/console/recovery-cta.js +4 -4
  81. package/dist/worker/console/recovery-error-copy.js +4 -4
  82. package/dist/worker/console/routes.js +46 -2
  83. package/dist/worker/console/security.js +63 -9
  84. package/dist/worker/console/server.js +111 -7
  85. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-CTx1TrNa.js} +1 -1
  86. package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-B4i9cVxX.js} +1 -1
  87. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-BAXE8_Rl.js} +1 -1
  88. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-C5Xh59at.js} +1 -1
  89. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-B_asOzAu.js} +1 -1
  90. package/dist/worker/console/static/assets/channel-Dj_4Moei.js +1 -0
  91. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-wRO_iRNc.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-ByOaSh_G.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-DjLzudan.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-JCNGkXYT.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-BhrPXXMZ.js} +1 -1
  96. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-TJ5JuEBT.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-VVthn_mD.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-D60-Mny4.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-zXv7aVye.js} +1 -1
  100. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-By9VQoi1.js} +1 -1
  101. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0YYAyCa.js +1 -0
  102. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0YYAyCa.js +1 -0
  103. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-C4zQLe-M.js} +1 -1
  104. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-wh5N2ReN.js} +1 -1
  105. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-Dlu5wIqs.js} +1 -1
  106. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-CrDgthc6.js} +1 -1
  107. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-zN1clg27.js} +1 -1
  108. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BN-o7J7-.js} +1 -1
  109. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C8SuPt0d.js} +1 -1
  110. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-BoizuHbZ.js} +1 -1
  111. package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-BzUzXixG.js} +1 -1
  112. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-Ev3lA4Cf.js} +1 -1
  113. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-GALUzRbs.js} +1 -1
  114. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-CZJN65Sb.js} +1 -1
  115. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-JLB9OESl.js} +1 -1
  116. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-CcvCr4GD.js} +1 -1
  117. package/dist/worker/console/static/assets/index-CW88Fjbw.js +406 -0
  118. package/dist/worker/console/static/assets/index-DP0ZJl39.css +1 -0
  119. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-HG5z7aVg.js} +1 -1
  120. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-BHEu9chR.js} +1 -1
  121. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-Cp_Up8-a.js} +1 -1
  122. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-DvVeY-em.js} +1 -1
  123. package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-aGmcjgTn.js} +1 -1
  124. package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-Biyo-tqA.js} +5 -5
  125. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-DyxY2pIb.js} +1 -1
  126. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-CkdGuc50.js} +1 -1
  127. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-NNQI9xVR.js} +1 -1
  128. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-PAJkuz_G.js} +1 -1
  129. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-ZwrQod0O.js} +1 -1
  130. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-D3ZfrzM2.js} +1 -1
  131. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-DDGFmWk5.js} +1 -1
  132. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-DO6mmXTA.js} +1 -1
  133. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-DMbCQiJt.js} +1 -1
  134. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA-Di_YUEfC.js} +1 -1
  135. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrdDvela.js +1 -0
  136. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-Bn2PphNJ.js} +2 -2
  137. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPkEAULl.js +8 -0
  138. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-DPh8pRsp.js} +1 -1
  139. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-GMfl26pW.js} +1 -1
  140. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-DTlmVrmF.js} +1 -1
  141. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-YGBjKAS5.js} +1 -1
  142. package/dist/worker/console/static/index.html +2 -2
  143. package/dist/worker/console/static-src/app/console-types.js +40 -1
  144. package/dist/worker/console/static-src/app/useOperatorActions.js +6 -1
  145. package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
  146. package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
  147. package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
  148. package/dist/worker/console/static-src/app/useTaskWizard.js +73 -1
  149. package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
  150. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +74 -26
  151. package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
  152. package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
  153. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
  154. package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
  155. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
  156. package/dist/worker/console/static-src/operator-chat/turn-submission.js +54 -0
  157. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
  158. package/dist/worker/console/static-src/operator-chat/useChatStream.js +237 -11
  159. package/dist/worker/console/static-src/operator-chat/useChatThread.js +50 -22
  160. package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
  161. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
  162. package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
  163. package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
  164. package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
  165. package/dist/worker/loop-agent/loop-agent-client.js +7 -3
  166. package/dist/worker/materialize/harness-task-lineage.js +5 -2
  167. package/dist/worker/observability/init-runtime-activity.js +26 -0
  168. package/dist/worker/observability/read-model.js +22 -0
  169. package/dist/worker/observe/node-input.js +160 -3
  170. package/dist/worker/observe/node-process.js +377 -0
  171. package/dist/worker/observe/routes.js +224 -3
  172. package/dist/worker/observe/shell-handler-keys.js +34 -0
  173. package/dist/worker/observe/static/api.js +67 -1
  174. package/dist/worker/observe/static/constants.js +8 -1
  175. package/dist/worker/observe/static/dag-history-labels.js +4 -0
  176. package/dist/worker/observe/static/format.js +60 -0
  177. package/dist/worker/observe/static/index.html +5 -0
  178. package/dist/worker/observe/static/markdown-render.js +20 -1
  179. package/dist/worker/observe/static/operator-chrome.js +4 -0
  180. package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
  181. package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
  182. package/dist/worker/observe/static/state.js +13 -2
  183. package/dist/worker/observe/static/styles.css +85 -0
  184. package/dist/worker/observe/static/views/dag-inspector.js +912 -8
  185. package/dist/worker/observe/static/views/dag.js +8 -1
  186. package/dist/worker/observe/static/views/failures.js +5 -2
  187. package/dist/worker/observe/static/views/session-timeline.js +78 -9
  188. package/dist/worker/observe/static/views/task.js +55 -0
  189. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +856 -54
  190. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  191. package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
  192. package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
  193. package/dist/workflows/dag/backend-test-intake-context.js +32 -0
  194. package/dist/workflows/dag/backend-test-layout.js +133 -0
  195. package/dist/workflows/dag/backend-test-markdown-workflow.js +343 -33
  196. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  197. package/dist/workflows/dag/backend-test-pytest-collection.js +454 -36
  198. package/dist/workflows/dag/backend-test-result-contract.js +1 -1
  199. package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
  200. package/dist/workflows/dag/backend-test-scenario-partitions.js +321 -0
  201. package/dist/workflows/dag/backend-test-writer-completeness.js +110 -61
  202. package/dist/workflows/dag/contract-output-registry.js +15 -0
  203. package/dist/workflows/dag/contract-validator-registrations.js +3 -1
  204. package/dist/workflows/dag/convergence/controller.js +141 -0
  205. package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
  206. package/dist/workflows/dag/dynamic-runtime/map.js +25 -9
  207. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
  208. package/dist/workflows/dag/failure-category.js +7 -116
  209. package/dist/workflows/dag/frontend-implementation-contract.js +465 -6
  210. package/dist/workflows/dag/frontend-plan-render.js +24 -0
  211. package/dist/workflows/dag/frontend-prewrite-gate.js +293 -84
  212. package/dist/workflows/dag/frontend-repair.js +14 -0
  213. package/dist/workflows/dag/frontend-review-context.js +92 -10
  214. package/dist/workflows/dag/frontend-risk.js +15 -2
  215. package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
  216. package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
  217. package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
  218. package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
  219. package/dist/workflows/dag/frontend-test-html-report.js +3 -1
  220. package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
  221. package/dist/workflows/dag/frontend-test-layout.js +159 -0
  222. package/dist/workflows/dag/frontend-test-markdown.js +61 -0
  223. package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
  224. package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
  225. package/dist/workflows/dag/frontend-verification-trace.js +29 -4
  226. package/dist/workflows/dag/init-hybrid.js +1019 -209
  227. package/dist/workflows/dag/node-execution.js +228 -15
  228. package/dist/workflows/dag/prompt.js +60 -1
  229. package/dist/workflows/dag/rerun-feedback.js +212 -0
  230. package/dist/workflows/dag/rerun-plan.js +224 -27
  231. package/dist/workflows/dag/rerun-run.js +61 -6
  232. package/dist/workflows/dag/rerun-task.js +61 -2
  233. package/dist/workflows/dag/retry-policy.js +109 -0
  234. package/dist/workflows/dag/structured-output-repair.js +712 -0
  235. package/dist/workflows/dag/types.js +185 -12
  236. package/dist/workflows/dag/validate.js +64 -20
  237. package/docs/README.md +1 -0
  238. package/docs/architecture/evolution.md +45 -0
  239. package/docs/init-surface.manifest.json +6 -0
  240. package/docs/operations/README.md +1 -1
  241. package/docs/operations/local-development-environment.md +1 -5
  242. package/docs/skills/vetted-skill-registry.md +14 -0
  243. package/docs/templates/README.md +2 -2
  244. package/docs/templates/agent-dag.schema.json +44 -0
  245. package/docs/templates/backend-test-dag.json +60 -35
  246. package/docs/templates/frontend-implementation-contract.schema.json +681 -24
  247. package/docs/templates/frontend-test-case-checklist.md +2 -2
  248. package/docs/templates/frontend-test-dag.json +8 -10
  249. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
  250. package/docs/templates/init-managed-agents.md +13 -3
  251. package/harness.json +6 -4
  252. package/package.json +12 -4
  253. package/scripts/next-info.mjs +356 -0
  254. package/scripts/next-publish-gate.mjs +238 -0
  255. package/scripts/release-source-binding.mjs +251 -0
  256. package/skills/analyze-product-requirements/SKILL.md +8 -5
  257. package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
  258. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
  259. package/skills/analyze-product-requirements/references/example.md +50 -0
  260. package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
  261. package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
  262. package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
  263. package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
  264. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
  265. package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
  266. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
  267. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
  268. package/skills/codebase-scout/SKILL.md +1 -1
  269. package/skills/fe-test-ui-scout/SKILL.md +65 -0
  270. package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
  271. package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
  272. package/skills/frontend-implementation/references/node-contracts.md +2 -2
  273. package/skills/improve-codebase-architecture/SKILL.md +81 -0
  274. package/skills/improve-codebase-architecture/deepening.md +37 -0
  275. package/skills/improve-codebase-architecture/html-report.md +123 -0
  276. package/skills/improve-codebase-architecture/interface-design.md +44 -0
  277. package/skills/improve-codebase-architecture/language.md +53 -0
  278. package/skills/loop-agent/references/command-reference.md +11 -4
  279. package/skills/loop-agent/references/hybrid-dag.md +7 -0
  280. package/skills/playwright-cli/SKILL.md +23 -1
  281. package/skills/playwright-cli-case-generator/SKILL.md +29 -8
  282. package/dist/worker/console/static/assets/channel-BBOTJHvD.js +0 -1
  283. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +0 -1
  284. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +0 -1
  285. package/dist/worker/console/static/assets/index-B-qWGThd.js +0 -325
  286. package/dist/worker/console/static/assets/index-C-xRgY_0.css +0 -1
  287. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +0 -1
  288. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +0 -8
@@ -0,0 +1,377 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { redactSecrets, truncateUtf8Preview } from "../../shared/preview.js";
3
+ import { isSafeObservabilityIdentifier } from "../observability/event-store.js";
4
+ import { parseExecutionOutputKey, } from "./dag-node-execution-output.js";
5
+ import { resolveDagRunArtifact, resolveDagRunJson, } from "./dag-run-artifacts.js";
6
+ import { namedShellHandlers } from "./shell-handler-keys.js";
7
+ export const NODE_PROCESS_SCHEMA_VERSION = 1;
8
+ export const NODE_PROCESS_FIELD_MAX_BYTES = 16 * 1024;
9
+ function boundField(value) {
10
+ if (value == null || value === "")
11
+ return { text: null, truncated: false };
12
+ const redacted = redactSecrets(value);
13
+ const truncated = truncateUtf8Preview(redacted, NODE_PROCESS_FIELD_MAX_BYTES);
14
+ return {
15
+ text: truncated,
16
+ truncated: truncated !== redacted,
17
+ };
18
+ }
19
+ function asRecord(value) {
20
+ if (!value || typeof value !== "object" || Array.isArray(value))
21
+ return null;
22
+ return value;
23
+ }
24
+ function asString(value) {
25
+ return typeof value === "string" && value.trim() ? value : null;
26
+ }
27
+ function asNumber(value) {
28
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
29
+ }
30
+ function asBoolean(value) {
31
+ return value === true;
32
+ }
33
+ async function readJsonArtifact(repoRoot, dagRunId, segments) {
34
+ const resolved = resolveDagRunArtifact(repoRoot, dagRunId, segments);
35
+ if (!resolved.ok)
36
+ return null;
37
+ try {
38
+ const raw = await readFile(resolved.result.absolutePath, "utf8");
39
+ const parsed = JSON.parse(raw);
40
+ return asRecord(parsed);
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ async function readTextArtifact(repoRoot, dagRunId, segments) {
47
+ const resolved = resolveDagRunArtifact(repoRoot, dagRunId, segments);
48
+ if (!resolved.ok)
49
+ return { text: null, truncated: false };
50
+ try {
51
+ const raw = await readFile(resolved.result.absolutePath, "utf8");
52
+ return boundField(raw);
53
+ }
54
+ catch {
55
+ return { text: null, truncated: false };
56
+ }
57
+ }
58
+ function emptyBody(dagRunId, nodeId, kind, emptyReason, extras = {}) {
59
+ return {
60
+ schemaVersion: NODE_PROCESS_SCHEMA_VERSION,
61
+ dagRunId,
62
+ nodeId,
63
+ kind,
64
+ executor: extras.executor ?? null,
65
+ handler: extras.handler ?? null,
66
+ emptyReason,
67
+ key: extras.key ?? null,
68
+ commands: extras.commands ?? [],
69
+ steps: extras.steps ?? [],
70
+ };
71
+ }
72
+ function classifyKind(task) {
73
+ const executor = asString(task.executor);
74
+ const shell = asRecord(task.shell);
75
+ const handlers = namedShellHandlers(shell);
76
+ const handler = handlers[0] ?? null;
77
+ const commands = shell?.commands;
78
+ const commandsDeclared = Array.isArray(commands) &&
79
+ commands.some((item) => typeof item === "string" && item.trim());
80
+ if (executor === "static" || task.static) {
81
+ return { kind: "static", executor: executor ?? "static", handler, commandsDeclared };
82
+ }
83
+ if (executor === "shell" || shell) {
84
+ if (commandsDeclared) {
85
+ return { kind: "shell-commands", executor: executor ?? "shell", handler, commandsDeclared };
86
+ }
87
+ if (handler) {
88
+ return { kind: "shell-preset", executor: executor ?? "shell", handler, commandsDeclared };
89
+ }
90
+ return { kind: "shell-commands", executor: executor ?? "shell", handler, commandsDeclared };
91
+ }
92
+ return {
93
+ kind: "pi-session",
94
+ executor: executor ?? "pi",
95
+ handler,
96
+ commandsDeclared,
97
+ };
98
+ }
99
+ function receiptAttemptSegments(nodeId, parsed) {
100
+ if (parsed?.kind === "attempt") {
101
+ return [nodeId, `attempt-${parsed.index}`, "shell-command-receipt.json"];
102
+ }
103
+ return [nodeId, "shell-command-receipt.json"];
104
+ }
105
+ async function loadShellCommands(repoRoot, dagRunId, nodeId, parsed) {
106
+ const preferred = await readJsonArtifact(repoRoot, dagRunId, receiptAttemptSegments(nodeId, parsed));
107
+ const receipt = preferred ??
108
+ (parsed
109
+ ? await readJsonArtifact(repoRoot, dagRunId, [
110
+ nodeId,
111
+ "shell-command-receipt.json",
112
+ ])
113
+ : null);
114
+ if (!receipt)
115
+ return null;
116
+ const rawCommands = Array.isArray(receipt.commands) ? receipt.commands : [];
117
+ const commands = [];
118
+ for (let i = 0; i < rawCommands.length; i += 1) {
119
+ const entry = asRecord(rawCommands[i]) ?? {};
120
+ const stdout = await readTextArtifact(repoRoot, dagRunId, [
121
+ nodeId,
122
+ "commands",
123
+ `${i + 1}.stdout.txt`,
124
+ ]);
125
+ const stderr = await readTextArtifact(repoRoot, dagRunId, [
126
+ nodeId,
127
+ "commands",
128
+ `${i + 1}.stderr.txt`,
129
+ ]);
130
+ const commandBound = boundField(asString(entry.command) ?? "");
131
+ commands.push({
132
+ index: i + 1,
133
+ command: commandBound.text ?? "(command)",
134
+ ok: asBoolean(entry.ok),
135
+ exitCode: asNumber(entry.exitCode),
136
+ failureCategory: asString(entry.failureCategory),
137
+ startedAt: asString(entry.startedAt),
138
+ finishedAt: asString(entry.finishedAt),
139
+ durationMs: asNumber(entry.wallDurationMs) ??
140
+ asNumber(entry.executionDurationMs) ??
141
+ null,
142
+ reused: asBoolean(entry.reused),
143
+ stdout: stdout.text,
144
+ stderr: stderr.text,
145
+ truncated: commandBound.truncated || stdout.truncated || stderr.truncated,
146
+ });
147
+ }
148
+ return commands;
149
+ }
150
+ function handlerLabel(handler) {
151
+ if (handler === "frontendBrowserToolPreflight")
152
+ return "浏览器工具能力探测";
153
+ if (handler === "frontendTestStandardScenarios")
154
+ return "标准场景物化";
155
+ if (handler === "frontendTestEnvironmentProbe")
156
+ return "前端环境探测";
157
+ if (handler === "frontendTestCaseChecklist")
158
+ return "用例清单校验";
159
+ if (handler === "frontendTestCaseManifest")
160
+ return "用例清单物化";
161
+ if (handler === "frontendTestEvidenceValidation")
162
+ return "证据校验";
163
+ if (handler === "frontendTestResultFinalize")
164
+ return "结果定稿";
165
+ if (handler === "frontendTestReports" || handler === "frontendTestL5Report" || handler === "frontendTestHtmlReport") {
166
+ return "测试报告";
167
+ }
168
+ if (handler === "frontendPrewriteGate")
169
+ return "前端预写门禁";
170
+ if (handler === "finalWriteSetApprovalGate")
171
+ return "writeSet 终态批准";
172
+ return handler;
173
+ }
174
+ async function loadPresetSteps(repoRoot, dagRunId, nodeId, handler, parsed) {
175
+ const written = await readJsonArtifact(repoRoot, dagRunId, [
176
+ nodeId,
177
+ "process-receipt.json",
178
+ ]);
179
+ if (written && Array.isArray(written.steps)) {
180
+ return written.steps.flatMap((item, index) => {
181
+ const step = asRecord(item);
182
+ if (!step)
183
+ return [];
184
+ const detail = boundField(asString(step.detail));
185
+ return [
186
+ {
187
+ id: asString(step.id) ?? `step-${index + 1}`,
188
+ label: asString(step.label) ?? handlerLabel(handler ?? "preset"),
189
+ ok: asBoolean(step.ok),
190
+ detail: detail.text,
191
+ durationMs: asNumber(step.durationMs),
192
+ truncated: detail.truncated,
193
+ },
194
+ ];
195
+ });
196
+ }
197
+ const capability = await readJsonArtifact(repoRoot, dagRunId, [
198
+ nodeId,
199
+ "frontend-browser-capability.json",
200
+ ]);
201
+ if (capability && handler === "frontendBrowserToolPreflight") {
202
+ const detail = boundField(JSON.stringify(capability, null, 2));
203
+ return [
204
+ {
205
+ id: "capability",
206
+ label: handlerLabel(handler),
207
+ ok: asString(capability.status) !== "failed",
208
+ detail: detail.text,
209
+ durationMs: null,
210
+ truncated: detail.truncated,
211
+ },
212
+ ];
213
+ }
214
+ const attemptIndex = parsed?.kind === "attempt" ? parsed.index : 1;
215
+ const attempt = await readJsonArtifact(repoRoot, dagRunId, [
216
+ nodeId,
217
+ `attempt-${attemptIndex}.json`,
218
+ ]);
219
+ if (attempt) {
220
+ const ok = asBoolean(attempt.ok);
221
+ const detail = boundField(ok
222
+ ? (asString(attempt.stdout) ?? asString(attempt.assistantText))
223
+ : (asString(attempt.stderr) ?? asString(attempt.stdout)));
224
+ return [
225
+ {
226
+ id: "run",
227
+ label: handlerLabel(handler ?? "preset"),
228
+ ok,
229
+ detail: detail.text,
230
+ durationMs: asNumber(attempt.durationMs),
231
+ truncated: detail.truncated,
232
+ },
233
+ ];
234
+ }
235
+ return [];
236
+ }
237
+ export async function loadDagNodeProcess(repoRoot, dagRunId, nodeId, rawKey) {
238
+ if (!isSafeObservabilityIdentifier(dagRunId) ||
239
+ !isSafeObservabilityIdentifier(nodeId)) {
240
+ return {
241
+ status: 400,
242
+ body: { error: "Invalid dag run or node identifier", reason: "invalid-id" },
243
+ };
244
+ }
245
+ let parsedKey = null;
246
+ if (rawKey != null && String(rawKey).trim() !== "") {
247
+ const parsed = parseExecutionOutputKey(rawKey);
248
+ if (!parsed.ok) {
249
+ return {
250
+ status: 400,
251
+ body: {
252
+ error: "Invalid execution key (expected attempt-N or pass-N)",
253
+ reason: "invalid-key",
254
+ },
255
+ };
256
+ }
257
+ parsedKey = parsed;
258
+ }
259
+ const runResolved = resolveDagRunJson(repoRoot, dagRunId);
260
+ if (!runResolved.ok) {
261
+ if (runResolved.reason === "invalid-id") {
262
+ return {
263
+ status: 400,
264
+ body: { error: "Invalid dag run or node identifier", reason: "invalid-id" },
265
+ };
266
+ }
267
+ if (runResolved.reason === "ambiguous") {
268
+ return {
269
+ status: 409,
270
+ body: { error: "Ambiguous dag run lifecycle", reason: "ambiguous" },
271
+ };
272
+ }
273
+ return {
274
+ status: 404,
275
+ body: { error: "DAG run not found", reason: "not-found" },
276
+ };
277
+ }
278
+ let spec = {};
279
+ try {
280
+ const raw = JSON.parse(await readFile(runResolved.result.absolutePath, "utf8"));
281
+ spec = asRecord(raw) ?? {};
282
+ }
283
+ catch {
284
+ return {
285
+ status: 404,
286
+ body: { error: "run.json is unreadable", reason: "not-found" },
287
+ };
288
+ }
289
+ const tasks = Array.isArray(spec.tasks) ? spec.tasks : [];
290
+ const task = tasks
291
+ .map((item) => asRecord(item))
292
+ .find((item) => item && asString(item.id) === nodeId);
293
+ if (!task) {
294
+ return {
295
+ status: 200,
296
+ body: emptyBody(dagRunId, nodeId, "none", "not-executed"),
297
+ };
298
+ }
299
+ const classified = classifyKind(task);
300
+ const key = parsedKey?.key ?? null;
301
+ if (classified.kind === "pi-session") {
302
+ return {
303
+ status: 200,
304
+ body: emptyBody(dagRunId, nodeId, "pi-session", "pi-session", {
305
+ executor: classified.executor,
306
+ key,
307
+ }),
308
+ };
309
+ }
310
+ if (classified.kind === "static") {
311
+ return {
312
+ status: 200,
313
+ body: emptyBody(dagRunId, nodeId, "static", "static-no-process", {
314
+ executor: classified.executor,
315
+ key,
316
+ }),
317
+ };
318
+ }
319
+ if (classified.kind === "shell-commands" || classified.kind === "shell-preset") {
320
+ const commands = await loadShellCommands(repoRoot, dagRunId, nodeId, parsedKey);
321
+ if (commands && commands.length > 0) {
322
+ return {
323
+ status: 200,
324
+ body: {
325
+ schemaVersion: NODE_PROCESS_SCHEMA_VERSION,
326
+ dagRunId,
327
+ nodeId,
328
+ kind: "shell-commands",
329
+ executor: classified.executor,
330
+ handler: classified.handler,
331
+ emptyReason: null,
332
+ key,
333
+ commands,
334
+ steps: [],
335
+ },
336
+ };
337
+ }
338
+ if (classified.handler || classified.kind === "shell-preset") {
339
+ const steps = await loadPresetSteps(repoRoot, dagRunId, nodeId, classified.handler, parsedKey);
340
+ if (steps.length > 0) {
341
+ return {
342
+ status: 200,
343
+ body: {
344
+ schemaVersion: NODE_PROCESS_SCHEMA_VERSION,
345
+ dagRunId,
346
+ nodeId,
347
+ kind: "shell-preset",
348
+ executor: classified.executor,
349
+ handler: classified.handler,
350
+ emptyReason: null,
351
+ key,
352
+ commands: [],
353
+ steps,
354
+ },
355
+ };
356
+ }
357
+ }
358
+ return {
359
+ status: 200,
360
+ body: emptyBody(dagRunId, nodeId, classified.kind, classified.kind === "shell-preset"
361
+ ? "shell-preset-pending"
362
+ : "shell-receipt-missing", {
363
+ executor: classified.executor,
364
+ handler: classified.handler,
365
+ key,
366
+ }),
367
+ };
368
+ }
369
+ return {
370
+ status: 200,
371
+ body: emptyBody(dagRunId, nodeId, "none", "not-executed", {
372
+ executor: classified.executor,
373
+ handler: classified.handler,
374
+ key,
375
+ }),
376
+ };
377
+ }
@@ -11,11 +11,16 @@ import { buildGlobalSnapshot, clampDagRunHistoryPageSize, clampTaskRunHistoryLim
11
11
  import { loadDagRunExecutionTrajectory } from "../observability/dag-execution-trajectory.js";
12
12
  import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
13
13
  import { dagSourceBindingSchema } from "../../workflows/dag/types.js";
14
+ import { getTaskPaths } from "../../task/runtime.js";
15
+ import { classifyLedgerGate, computeLedgerInputDigest, LedgerParseError, parseLedgerJson, recoverLedgerInputContract, summarizeLedger, } from "../../task/source-prepare/ledger.js";
14
16
  import { getTaskPoolRoot } from "../pool/run-store.js";
17
+ import { resolveDagRunArtifact, } from "./dag-run-artifacts.js";
18
+ import { frontendPrewriteResultV1Schema } from "../../workflows/dag/frontend-prewrite-gate.js";
15
19
  import { isAllowedArtifactTextPath, resolveArtifactPath, resolveRepoFilePreview, toRepoRelativeArtifactPath, } from "./paths.js";
16
20
  import { extractSpecEvidence, extractSpecReadContent, } from "./spec-evidence.js";
17
21
  import { buildDagNodeInput } from "./node-input.js";
18
22
  import { loadDagNodeExecutionOutput } from "./dag-node-execution-output.js";
23
+ import { loadDagNodeProcess } from "./node-process.js";
19
24
  import { buildObserveHealthV1 } from "./health.js";
20
25
  import { buildNightJobDetail, buildNightJobsDoctor, buildNightJobsList, buildNightJobsMorning, } from "./night-jobs.js";
21
26
  const ARTIFACT_PREVIEW_MAX_BYTES = 64 * 1024;
@@ -69,6 +74,15 @@ export const ROUTES = [
69
74
  pattern: /^\/api\/tasks\/([^/]+)\/runs$/,
70
75
  handler: handleTaskRuns,
71
76
  },
77
+ /**
78
+ * Read-only source fidelity ledger safe summary (AC-009 / AC-FIDELITY-009):
79
+ * 只返回计数/状态/failure code,不泄漏敏感原文。
80
+ */
81
+ {
82
+ method: "GET",
83
+ pattern: /^\/api\/tasks\/([^/]+)\/ledger$/,
84
+ handler: handleTaskLedger,
85
+ },
72
86
  {
73
87
  method: "GET",
74
88
  pattern: /^\/api\/tasks\/([^/]+)$/,
@@ -100,6 +114,11 @@ export const ROUTES = [
100
114
  pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/execution-output$/,
101
115
  handler: handleDagNodeExecutionOutput,
102
116
  },
117
+ {
118
+ method: "GET",
119
+ pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/process$/,
120
+ handler: handleDagNodeProcess,
121
+ },
103
122
  {
104
123
  method: "GET",
105
124
  pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/spec-evidence$/,
@@ -110,6 +129,16 @@ export const ROUTES = [
110
129
  pattern: /^\/api\/dag-runs\/([^/]+)\/nodes\/([^/]+)\/spec-evidence\/file$/,
111
130
  handler: handleDagNodeSpecEvidenceFile,
112
131
  },
132
+ // DAG run 专属只读 prewrite 结果(AC-006 / AC-BLOCK-006):唯一读路径为
133
+ // run-owned contracts/frontend-prewrite-result.json(resolveDagRunArtifact 四类
134
+ // fail-closed),不扩大 isAllowedArtifactTextPath 通用 allowlist;响应以
135
+ // frontendPrewriteResultV1Schema(.strict())解析,Inspector 不再做
136
+ // /api/artifacts 预览 → JSON.stringify → JSON.parse 双编码。
137
+ {
138
+ method: "GET",
139
+ pattern: /^\/api\/dag-runs\/([^/]+)\/prewrite-result$/,
140
+ handler: handleDagRunPrewriteResult,
141
+ },
113
142
  {
114
143
  method: "GET",
115
144
  pattern: /^\/api\/dag-runs\/([^/]+)$/,
@@ -221,14 +250,23 @@ export async function serveObserveStatic(req, res, staticDir, options = {}) {
221
250
  sendJson(res, 404, { error: "Not found" });
222
251
  return;
223
252
  }
224
- const content = await readFile(resolvedFile);
253
+ let content = await readFile(resolvedFile);
225
254
  const contentType = contentTypeFor(pathname);
226
- res.writeHead(200, {
255
+ const headers = {
227
256
  "Content-Type": contentType,
228
257
  "Content-Length": content.byteLength,
229
258
  "X-Content-Type-Options": "nosniff",
230
259
  "X-Frame-Options": "SAMEORIGIN",
231
- });
260
+ };
261
+ if (contentType.startsWith("text/html") && options.htmlEnhancer) {
262
+ const enhanced = options.htmlEnhancer(Buffer.from(content));
263
+ content = enhanced.content;
264
+ headers["Content-Length"] = content.byteLength;
265
+ if (enhanced.setCookie) {
266
+ headers["Set-Cookie"] = enhanced.setCookie;
267
+ }
268
+ }
269
+ res.writeHead(200, headers);
232
270
  res.end(content);
233
271
  }
234
272
  async function handleHealth(_req, res, _match, ctx) {
@@ -433,6 +471,117 @@ async function handleTaskRuns(_req, res, match, ctx) {
433
471
  });
434
472
  sendJson(res, 200, page);
435
473
  }
474
+ /**
475
+ * GET /api/tasks/:id/ledger — 只读 ledger 安全摘要。任务存在但无 ledger 时返回
476
+ * `{ present: false }`(空态,不伪造计数);任务不存在返回 404;ledger 解析失败
477
+ * 返回带稳定 SOURCE_FIDELITY_* 错误码的 `{ present: false }`(AC-HARD-003:与
478
+ * parseLedgerJson 同一稳定码,不泄漏原文)。binding v2 标记 + ledgerSha256 +
479
+ * inputDigest 仅在真实 v2 绑定(非空 canonical requirement 集)时展示(AC-HARD-004:
480
+ * v1+optional 冒充 v2 不标记);inputDigest 与当前权威源输入不一致时置 stale。
481
+ */
482
+ async function handleTaskLedger(_req, res, match, ctx) {
483
+ const taskId = match.params.id;
484
+ if (!isSafeObservabilityIdentifier(taskId)) {
485
+ sendJson(res, 400, { error: "Invalid task identifier" });
486
+ return;
487
+ }
488
+ const taskPaths = getTaskPaths(ctx.repoRoot, taskId);
489
+ try {
490
+ await stat(taskPaths.taskDir);
491
+ }
492
+ catch {
493
+ sendJson(res, 404, { error: "Task not found" });
494
+ return;
495
+ }
496
+ const ledgerPath = path.join(taskPaths.sourceDir, "requirement-ledger.json");
497
+ let raw;
498
+ try {
499
+ raw = await readFile(ledgerPath, "utf8");
500
+ }
501
+ catch (error) {
502
+ if (error.code === "ENOENT") {
503
+ sendJson(res, 200, { present: false, taskId });
504
+ return;
505
+ }
506
+ sendJson(res, 200, {
507
+ present: false,
508
+ taskId,
509
+ error: "ledger-unreadable",
510
+ });
511
+ return;
512
+ }
513
+ let ledger;
514
+ try {
515
+ ledger = parseLedgerJson(raw);
516
+ }
517
+ catch (error) {
518
+ // AC-HARD-003:解析失败分支使用与 parseLedgerJson 相同的稳定 SOURCE_FIDELITY_*
519
+ // 错误码(结构化可消费),而非裸字符串/裸 Error。
520
+ const code = error instanceof LedgerParseError
521
+ ? error.code
522
+ : "SOURCE_FIDELITY_LEDGER_STALE";
523
+ sendJson(res, 200, {
524
+ present: false,
525
+ taskId,
526
+ error: code,
527
+ });
528
+ return;
529
+ }
530
+ const classification = classifyLedgerGate(ledger);
531
+ const summary = summarizeLedger(ledger, {
532
+ gateStatus: classification.ok ? "pass" : "fail",
533
+ failureCode: classification.ok ? null : classification.code,
534
+ });
535
+ // v2 binding 语义(AC-HARD-004):仅在真实 v2 绑定(canonical requirement 非空,
536
+ // 具备 requirement→fragment 映射证据)时标记 bindingSchemaVersion=2 并透传
537
+ // ledgerSha256/inputDigest;空 canonical 的 ledger 是退化/伪造 v2,不标记。
538
+ const contract = recoverLedgerInputContract(ledger);
539
+ const realV2Binding = contract.canonicalRequirements.length > 0;
540
+ let stale = false;
541
+ if (realV2Binding) {
542
+ // 当前权威源输入:ledger.sourcePaths(repo-root relative POSIX)→ 当前字节;
543
+ // 缺失/清空的权威源视为 stale(fail-closed,与 init-hybrid 语义一致)。
544
+ const currentByPath = new Map();
545
+ for (const sourcePath of contract.sourcePaths) {
546
+ try {
547
+ currentByPath.set(sourcePath, await readFile(path.resolve(ctx.repoRoot, sourcePath), "utf8"));
548
+ }
549
+ catch {
550
+ // 权威源缺失:保持缺失,expectedInputDigest 不一致 → stale。
551
+ }
552
+ }
553
+ const expectedDigest = computeLedgerInputDigest({
554
+ sourceDocuments: contract.sourcePaths.map((sourcePath) => ({
555
+ path: sourcePath,
556
+ content: currentByPath.get(sourcePath) ?? "",
557
+ })),
558
+ canonicalRequirements: contract.canonicalRequirements,
559
+ });
560
+ stale = ledger.inputDigest !== expectedDigest;
561
+ }
562
+ const ledgerSha256 = createHash("sha256").update(raw).digest("hex");
563
+ sendJson(res, 200, {
564
+ present: true,
565
+ taskId,
566
+ ...(realV2Binding
567
+ ? {
568
+ bindingSchemaVersion: 2,
569
+ ledgerSha256,
570
+ inputDigest: ledger.inputDigest,
571
+ stale,
572
+ }
573
+ : {}),
574
+ fragmentCount: summary.fragmentCount,
575
+ canonicalRequirementCount: summary.canonicalRequirementCount,
576
+ contextOnlyCount: summary.contextOnlyCount,
577
+ duplicateCount: summary.duplicateCount,
578
+ unresolvedCount: summary.unresolvedCount,
579
+ conflictCount: summary.conflictCount,
580
+ gateStatus: summary.gateStatus,
581
+ failureCode: summary.failureCode,
582
+ exclusions: summary.exclusions,
583
+ });
584
+ }
436
585
  function ambiguousTaskBody(taskId, candidates) {
437
586
  return {
438
587
  error: "Task identity ambiguous",
@@ -520,6 +669,60 @@ async function handleDagRuns(_req, res, match, ctx) {
520
669
  const body = listDagRunHistoryFromSnapshot(snapshot, { page, pageSize });
521
670
  sendJson(res, 200, body);
522
671
  }
672
+ /**
673
+ * GET /api/dag-runs/:dagRunId/prewrite-result(AC-006 / AC-BLOCK-006)
674
+ * 只读 run-owned contracts/frontend-prewrite-result.json:resolveDagRunArtifact 是
675
+ * 唯一读路径(active/paused/completed 生命周期;invalid-id / unsafe-path /
676
+ * not-found / ambiguous 四类 fail-closed)。响应以 frontendPrewriteResultV1Schema
677
+ * (.strict())解析,拒绝 envelope 之外的字段;不扩大通用 artifact allowlist。
678
+ */
679
+ async function handleDagRunPrewriteResult(_req, res, match, ctx) {
680
+ const dagRunId = match.params.id;
681
+ const resolved = resolveDagRunArtifact(ctx.repoRoot, dagRunId, [
682
+ "contracts",
683
+ "frontend-prewrite-result.json",
684
+ ]);
685
+ if (!resolved.ok) {
686
+ if (resolved.reason === "invalid-id") {
687
+ sendJson(res, 400, { error: "Invalid dag run identifier" });
688
+ return;
689
+ }
690
+ if (resolved.reason === "ambiguous") {
691
+ sendJson(res, 409, { error: "DAG run identity ambiguous" });
692
+ return;
693
+ }
694
+ sendJson(res, 404, { error: "Prewrite result not found" });
695
+ return;
696
+ }
697
+ let raw;
698
+ try {
699
+ raw = await readFile(resolved.result.absolutePath, "utf8");
700
+ }
701
+ catch {
702
+ sendJson(res, 404, { error: "Prewrite result not found" });
703
+ return;
704
+ }
705
+ let parsed;
706
+ try {
707
+ parsed = JSON.parse(raw);
708
+ }
709
+ catch {
710
+ sendJson(res, 500, {
711
+ error: "Prewrite result is not valid JSON",
712
+ reason: "prewrite-result-unparseable",
713
+ });
714
+ return;
715
+ }
716
+ const envelope = frontendPrewriteResultV1Schema.safeParse(parsed);
717
+ if (!envelope.success) {
718
+ sendJson(res, 500, {
719
+ error: "Prewrite result is not a valid frontend-prewrite-result-v1 envelope",
720
+ reason: "prewrite-result-schema-invalid",
721
+ });
722
+ return;
723
+ }
724
+ sendJson(res, 200, envelope.data);
725
+ }
523
726
  export async function handleDagRunById(_req, res, match, ctx) {
524
727
  const snapshot = await getSnapshot(ctx);
525
728
  const dagRun = snapshot.dagRuns.find((d) => d.dagRunId === match.params.id);
@@ -570,6 +773,24 @@ async function handleDagNodeExecutionOutput(_req, res, match, ctx) {
570
773
  const result = await loadDagNodeExecutionOutput(ctx.repoRoot, dagRunId, nodeId, key);
571
774
  sendJson(res, result.status, result.body);
572
775
  }
776
+ async function handleDagNodeProcess(_req, res, match, ctx) {
777
+ const dagRunId = match.params.id;
778
+ const nodeId = match.params.sub;
779
+ if (!isSafeObservabilityIdentifier(dagRunId) ||
780
+ !isSafeObservabilityIdentifier(nodeId)) {
781
+ sendJson(res, 400, { error: "Invalid dag run or node identifier" });
782
+ return;
783
+ }
784
+ if (match.query.has("path") || match.query.has("artifactPath")) {
785
+ sendJson(res, 400, {
786
+ error: "Artifact path is not accepted; use optional key=attempt-N|pass-N",
787
+ reason: "path-not-allowed",
788
+ });
789
+ return;
790
+ }
791
+ const result = await loadDagNodeProcess(ctx.repoRoot, dagRunId, nodeId, match.query.get("key"));
792
+ sendJson(res, result.status, result.body);
793
+ }
573
794
  async function handleDagNodeSessionEvents(_req, res, match, ctx) {
574
795
  const dagRunId = match.params.id;
575
796
  const nodeId = match.params.sub;