@tea-agent/loop-agent 0.41.1-beta.0 → 0.42.0-next.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 (273) hide show
  1. package/CHANGELOG.md +61 -207
  2. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  3. package/dist/adapters/context-transfer/pi-session.js +61 -0
  4. package/dist/application/dag/generate-task-dag.js +19 -75
  5. package/dist/application/task-lifecycle/advance.js +5 -24
  6. package/dist/application/task-lifecycle/observe.js +17 -171
  7. package/dist/application/task-lifecycle/plan-transitions.js +7 -42
  8. package/dist/application/task-lifecycle/recommendations.js +2 -13
  9. package/dist/build-stamp.json +3 -3
  10. package/dist/cli/command-definitions.js +12 -7
  11. package/dist/cli/program.js +23 -6
  12. package/dist/commands/client-recovery.js +0 -3
  13. package/dist/commands/dag-artifact.js +284 -0
  14. package/dist/commands/dag-context.js +184 -0
  15. package/dist/commands/dag-rerun.js +206 -1
  16. package/dist/commands/init.js +1 -27
  17. package/dist/commands/task-advance.js +0 -19
  18. package/dist/executors/dag-pi-executor.js +266 -3792
  19. package/dist/executors/pi-executor.js +4 -15
  20. package/dist/executors/pi-sdk-executor.js +3 -0
  21. package/dist/executors/shell-executor.js +217 -945
  22. package/dist/executors/shell-write-guard.js +0 -7
  23. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  24. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  25. package/dist/infrastructure/console/context-export-store.js +160 -0
  26. package/dist/infrastructure/console/dir-lock.js +132 -0
  27. package/dist/infrastructure/console/operation-store.js +197 -0
  28. package/dist/infrastructure/harness/artifact-store.js +10 -1
  29. package/dist/infrastructure/harness/atomic-write.js +12 -2
  30. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  31. package/dist/shared/context-transfer.js +418 -0
  32. package/dist/shared/dag-failure-category.js +0 -12
  33. package/dist/shared/openspec-spec.js +4 -70
  34. package/dist/shared/operator/capabilities.js +14 -7
  35. package/dist/shared/operator/safe-run-summary.js +1 -0
  36. package/dist/shared/path-safety.js +93 -0
  37. package/dist/shared/preview.js +28 -4
  38. package/dist/task/config-types.js +9 -35
  39. package/dist/task/contract/adopt.js +0 -4
  40. package/dist/task/contract/import-revision.js +0 -4
  41. package/dist/task/contract/project.js +0 -3
  42. package/dist/task/contract/schema.js +1 -2
  43. package/dist/task/frontend-project-capability.js +20 -203
  44. package/dist/task/runtime.js +2 -5
  45. package/dist/task/source-prepare/build-draft.js +3 -3
  46. package/dist/task/source-prepare/fragment-inventory.js +9 -15
  47. package/dist/task/source-prepare/prepare.js +1 -89
  48. package/dist/task/source-prepare/semantic-intake.js +2 -6
  49. package/dist/task/source-references.js +1 -22
  50. package/dist/task/task-demand-routing.js +3 -0
  51. package/dist/worker/console/chat/chat-event-store.js +2 -2
  52. package/dist/worker/console/chat/pi-runtime.js +3 -2
  53. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  54. package/dist/worker/console/chat/routes.js +13 -4
  55. package/dist/worker/console/chat/semantic-activity.js +12 -11
  56. package/dist/worker/console/chat/session-stats.js +96 -0
  57. package/dist/worker/console/chat/session-store.js +1 -1
  58. package/dist/worker/console/chat/turn-process.js +28 -4
  59. package/dist/worker/console/chat/user-questions.js +1 -1
  60. package/dist/worker/console/console-update-runtime.js +1 -1
  61. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  62. package/dist/worker/console/dag-confirmation.js +1 -1
  63. package/dist/worker/console/dag-execution-receipt.js +1 -1
  64. package/dist/worker/console/doctor.js +1 -1
  65. package/dist/worker/console/draft-store.js +1 -1
  66. package/dist/worker/console/human-gate-token.js +1 -1
  67. package/dist/worker/console/index.js +3 -6
  68. package/dist/worker/console/interview/assessment.js +1 -1
  69. package/dist/worker/console/interview/session.js +1 -1
  70. package/dist/worker/console/operation-runner.js +45 -4
  71. package/dist/worker/console/operation-sse.js +1 -1
  72. package/dist/worker/console/operation-wait.js +1 -1
  73. package/dist/worker/console/operator-actions.js +107 -168
  74. package/dist/worker/console/operator-surface-health.js +1 -1
  75. package/dist/worker/console/operator-user-error.js +169 -0
  76. package/dist/worker/console/pi-plugins.js +60 -1
  77. package/dist/worker/console/routes.js +637 -8
  78. package/dist/worker/console/security.js +35 -0
  79. package/dist/worker/console/server.js +6 -6
  80. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-DZZ8m3PO.js} +1 -1
  81. package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-D6PvaVd-.js} +1 -1
  82. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-B_OTOiI8.js} +1 -1
  83. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-Bv6rqHBg.js} +1 -1
  84. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-B8eHr0oz.js} +1 -1
  85. package/dist/worker/console/static/assets/channel-BU5gOilw.js +1 -0
  86. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-DYwR0im2.js} +1 -1
  87. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-D2WPGqXt.js} +1 -1
  88. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CQISJ_I7.js} +1 -1
  89. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-C0o2Du1e.js} +1 -1
  90. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-4f8kr-U7.js} +1 -1
  91. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-D_OWvXzX.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-CasdPz5X.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-Cj8lRrla.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-CUvD1FSd.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CqLw_eqB.js} +1 -1
  96. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +1 -0
  97. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +1 -0
  98. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-ku-WqJPx.js} +1 -1
  99. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-Bq_PYDhl.js} +1 -1
  100. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-BiGZV641.js} +1 -1
  101. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-C4JRlglF.js} +1 -1
  102. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-B0dNeWYG.js} +1 -1
  103. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-Cj35YvYM.js} +1 -1
  104. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-uzQoJ2-8.js} +1 -1
  105. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-D1a-Buoz.js} +1 -1
  106. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-XjRrLRSs.js} +1 -1
  107. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-Da_O24cW.js} +1 -1
  108. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-BLJ8jrYU.js} +1 -1
  109. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-B9GrLjM0.js} +1 -1
  110. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-CeJ0TqiK.js} +1 -1
  111. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js} +1 -1
  112. package/dist/worker/console/static/assets/index-H9rFJiGL.css +1 -0
  113. package/dist/worker/console/static/assets/index-xwu9GxEc.js +451 -0
  114. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-DB26i3d8.js} +1 -1
  115. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-BOGRyeZT.js} +1 -1
  116. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-CZRoBO_6.js} +1 -1
  117. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-qlWhJyoB.js} +1 -1
  118. package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CfUiDDB3.js} +1 -1
  119. package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-BQe6fpqj.js} +5 -5
  120. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-zFzWHw64.js} +1 -1
  121. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-DFGUqjZO.js} +1 -1
  122. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-BB5i0l8Z.js} +1 -1
  123. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-obbVZ_X5.js} +1 -1
  124. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-BwdaNzc1.js} +1 -1
  125. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-CkTJaugh.js} +1 -1
  126. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-BwJ-hIgq.js} +1 -1
  127. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-BzSgmm7w.js} +1 -1
  128. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-Bn3oj5Q1.js} +1 -1
  129. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-dxhGtL8K.js} +1 -1
  130. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +1 -0
  131. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-BLpEInya.js} +2 -2
  132. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +8 -0
  133. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-ChJGYcXy.js} +1 -1
  134. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-DK-qjRer.js} +1 -1
  135. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-3qeaWAg-.js} +1 -1
  136. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CootlyP9.js} +1 -1
  137. package/dist/worker/console/static/index.html +7 -2
  138. package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
  139. package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
  140. package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
  141. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
  142. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
  143. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
  144. package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
  145. package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
  146. package/dist/worker/console/workspace-context.js +28 -112
  147. package/dist/worker/console/workspace-registry.js +2 -2
  148. package/dist/worker/continuation/worker-continuation.js +278 -0
  149. package/dist/worker/observability/read-model.js +4 -0
  150. package/dist/worker/observe/health.js +1 -1
  151. package/dist/worker/observe/node-transparency.js +572 -0
  152. package/dist/worker/observe/routes.js +51 -1
  153. package/dist/worker/observe/static/api.js +69 -5
  154. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  155. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  156. package/dist/worker/observe/static/dag-node-purpose.js +0 -5
  157. package/dist/worker/observe/static/state.js +3 -1
  158. package/dist/worker/observe/static/styles.css +1037 -126
  159. package/dist/worker/observe/static/views/dag-inspector.js +1217 -65
  160. package/dist/worker/observe/static/views/session-timeline.js +121 -25
  161. package/dist/worker/outcomes/adapters.js +19 -0
  162. package/dist/worker/outcomes/types.js +1 -0
  163. package/dist/worker/pool/attempt-lease.js +97 -66
  164. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  165. package/dist/worker/pool/reconcile.js +46 -1
  166. package/dist/worker/pool/run-store.js +2 -0
  167. package/dist/worker/pool/state-projection.js +2 -0
  168. package/dist/worker/task-spec/workflow-routing.js +8 -3
  169. package/dist/workflows/dag/artifact-bindings.js +149 -0
  170. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  171. package/dist/workflows/dag/backend-test-result-contract.js +1 -1
  172. package/dist/workflows/dag/budget-enforcement.js +10 -0
  173. package/dist/workflows/dag/context-receipt.js +305 -0
  174. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  175. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  176. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  177. package/dist/workflows/dag/contract-validator-registrations.js +2 -1
  178. package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
  179. package/dist/workflows/dag/frontend-plan-render.js +1 -2
  180. package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
  181. package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
  182. package/dist/workflows/dag/frontend-recovery-run.js +20 -33
  183. package/dist/workflows/dag/frontend-repair.js +432 -1
  184. package/dist/workflows/dag/frontend-review-context.js +15 -261
  185. package/dist/workflows/dag/frontend-verification-trace.js +24 -249
  186. package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
  187. package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
  188. package/dist/workflows/dag/init-hybrid.js +640 -685
  189. package/dist/workflows/dag/interrupt-request.js +0 -7
  190. package/dist/workflows/dag/node-execution.js +116 -759
  191. package/dist/workflows/dag/path-safety.js +1 -0
  192. package/dist/workflows/dag/prompt.js +30 -38
  193. package/dist/workflows/dag/report.js +1 -37
  194. package/dist/workflows/dag/rerun-feedback.js +1 -256
  195. package/dist/workflows/dag/rerun-plan.js +557 -11
  196. package/dist/workflows/dag/rerun-run.js +601 -38
  197. package/dist/workflows/dag/rerun-task.js +0 -29
  198. package/dist/workflows/dag/retry-policy.js +104 -214
  199. package/dist/workflows/dag/runner.js +142 -430
  200. package/dist/workflows/dag/scheduler.js +16 -114
  201. package/dist/workflows/dag/skill-snapshot.js +17 -0
  202. package/dist/workflows/dag/types.js +195 -246
  203. package/dist/workflows/dag/validate.js +12 -28
  204. package/docs/architecture/runtime-boundaries.md +6 -6
  205. package/docs/architecture/worker-and-feature.md +1 -1
  206. package/docs/init-surface.manifest.json +12 -30
  207. package/docs/skills/vetted-skill-registry.md +2 -4
  208. package/docs/templates/README.md +0 -2
  209. package/docs/templates/agent-dag-report.schema.json +2 -8
  210. package/docs/templates/agent-dag.schema.json +33 -1
  211. package/docs/templates/frontend-implementation-contract.schema.json +1 -4
  212. package/docs/templates/product-line/task.yaml +1 -1
  213. package/harness.json +3 -3
  214. package/package.json +3 -1
  215. package/skills/frontend-bounded-implement/SKILL.md +14 -15
  216. package/skills/frontend-design-review/SKILL.md +41 -22
  217. package/skills/frontend-implementation/SKILL.md +52 -0
  218. package/skills/frontend-implementation/references/code-standards.md +33 -0
  219. package/skills/frontend-implementation/references/design-spec.md +56 -0
  220. package/skills/frontend-implementation/references/node-contracts.md +31 -0
  221. package/skills/frontend-review/SKILL.md +15 -20
  222. package/skills/frontend-review/references/review-findings.md +7 -6
  223. package/skills/frontend-verification/SKILL.md +1 -1
  224. package/skills/loop-agent/references/command-reference.md +6 -1
  225. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  226. package/dist/commands/dag-follow-up.js +0 -138
  227. package/dist/executors/pi-read-budget-policy.js +0 -239
  228. package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
  229. package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
  230. package/dist/worker/console/operation-store.js +0 -389
  231. package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
  232. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
  233. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
  234. package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
  235. package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
  236. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
  237. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
  238. package/dist/worker/console/static-src/active-run-badge.js +0 -17
  239. package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
  240. package/dist/workflows/dag/dag-retry-schema.js +0 -138
  241. package/dist/workflows/dag/frontend-closeout.js +0 -221
  242. package/dist/workflows/dag/frontend-design-policy.js +0 -400
  243. package/dist/workflows/dag/frontend-human-decision.js +0 -182
  244. package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
  245. package/dist/workflows/dag/frontend-read-budgets.js +0 -12
  246. package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
  247. package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
  248. package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
  249. package/dist/workflows/dag/frontend-review-findings.js +0 -270
  250. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
  251. package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
  252. package/dist/workflows/dag/frontend-shape-facts.js +0 -409
  253. package/dist/workflows/dag/frontend-shape.js +0 -427
  254. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
  255. package/dist/workflows/dag/frontend-split-application-service.js +0 -203
  256. package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
  257. package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
  258. package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
  259. package/dist/workflows/dag/frontend-writer-admission.js +0 -285
  260. package/dist/workflows/dag/frontend-writer-status.js +0 -256
  261. package/dist/workflows/dag/recovery-lease.js +0 -80
  262. package/docs/templates/frontend-implementation-dag.json +0 -89
  263. package/docs/templates/spec-registry.schema.json +0 -45
  264. package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
  265. package/skills/frontend-contract/SKILL.md +0 -23
  266. package/skills/frontend-contract/references/contract-protocol.md +0 -34
  267. package/skills/frontend-plan/SKILL.md +0 -26
  268. package/skills/frontend-plan/references/decision-contract.md +0 -37
  269. package/skills/frontend-plan/references/design-decisions.md +0 -17
  270. package/skills/frontend-scout/SKILL.md +0 -25
  271. package/skills/frontend-scout/references/design-evidence.md +0 -16
  272. package/skills/frontend-scout/references/scout-evidence.md +0 -23
  273. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -4,166 +4,11 @@ import path from "node:path";
4
4
  import { writeDagRunJsonArtifact } from "../../infrastructure/harness/artifact-store.js";
5
5
  import { formatFrontendWorktreeDiffStdout, runFrontendWorktreeDiffGate, } from "./frontend-worktree-diff.js";
6
6
  import { frontendLintAssessmentArtifactSchema } from "./frontend-lint-baseline.js";
7
- import { deterministicSha256, FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID, frontendImplementationContractSchema, writeFrontendContractReviewArtifacts, } from "./frontend-implementation-contract.js";
7
+ import { frontendImplementationContractSchema, } from "./frontend-implementation-contract.js";
8
+ import { frontendRepairAssessmentSchema } from "./frontend-repair.js";
8
9
  import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
9
- import { ledgerStatsLine, parseLedgerJson } from "../../task/source-prepare/ledger.js";
10
- export const FRONTEND_REVIEW_CONTEXT_SCHEMA_ID = "frontend-review-context-v2";
11
- /**
12
- * Deterministically assemble the cumulative diff from a task baseline and the
13
- * current candidate snapshot. Same input always yields the same output:
14
- * paths are sorted, statuses are derived from a fixed decision table, and both
15
- * digests use canonical JSON hashing.
16
- */
17
- export function assembleCumulativeDiffContext(input) {
18
- const baselineByPath = new Map(input.baselineEntries.map((entry) => [entry.path, entry.sha256]));
19
- const currentByPath = new Map(input.currentEntries.map((entry) => [entry.path, entry.sha256]));
20
- const allPaths = new Set([
21
- ...baselineByPath.keys(),
22
- ...currentByPath.keys(),
23
- ]);
24
- const sortedPaths = [...allPaths].sort();
25
- const perPath = sortedPaths
26
- .map((p) => {
27
- const baselineSha256 = baselineByPath.get(p) ?? null;
28
- const hasCurrent = currentByPath.has(p);
29
- const currentSha256 = hasCurrent
30
- ? (currentByPath.get(p) ?? null)
31
- : null;
32
- let status;
33
- if (baselineSha256 === null) {
34
- status = currentSha256 === null ? "deleted" : "added";
35
- }
36
- else if (currentSha256 === null) {
37
- status = hasCurrent ? "deleted" : "unchanged";
38
- }
39
- else {
40
- status =
41
- currentSha256 === baselineSha256 ? "unchanged" : "modified";
42
- }
43
- return { path: p, status, baselineSha256, currentSha256 };
44
- })
45
- // Cumulative diff carries only actual changes. The worktree baseline is
46
- // the full repository inventory (git ls-files -co), so an unfiltered
47
- // perPath enumerates thousands of unchanged hashes (e.g. 3000+ entries
48
- // ≈ 700KB), ballooning the review context and blowing the review node
49
- // read budget on a single read. Unchanged entries add no diff evidence.
50
- .filter((entry) => entry.status !== "unchanged");
51
- const baselineDigest = deterministicSha256([...input.baselineEntries].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
52
- const cumulativeDigest = deterministicSha256(perPath);
53
- return {
54
- schemaId: "frontend-cumulative-diff-context-v1",
55
- baselineDigest,
56
- perPath,
57
- cumulativeDigest,
58
- empty: perPath.length === 0,
59
- };
60
- }
61
- async function sha256File(filePath) {
62
- try {
63
- const bytes = await readFile(filePath);
64
- return createHash("sha256").update(bytes).digest("hex");
65
- }
66
- catch {
67
- return null;
68
- }
69
- }
70
- /**
71
- * Capture the cumulative diff from the frozen worktree baseline and the
72
- * run-owned diff gate output. Pure status/digest logic lives in
73
- * `assembleCumulativeDiffContext`; this wrapper only does IO.
74
- */
75
- export async function captureCumulativeDiffContext(input) {
76
- const root = path.resolve(input.workspaceRoot);
77
- let baselineEntries = [];
78
- try {
79
- const raw = JSON.parse(await readFile(path.join(input.runDir, "contracts/frontend-worktree-baseline.json"), "utf8"));
80
- baselineEntries = (raw.files ?? [])
81
- .map((file) => ({ path: file, sha256: raw.hashes?.[file] ?? null }))
82
- .filter((entry) => entry.sha256 !== null);
83
- }
84
- catch {
85
- // Missing baseline → empty baseline entries (still deterministic).
86
- }
87
- const candidatePaths = [
88
- ...new Set([...input.diff.changedFiles, ...input.diff.untrackedFiles]),
89
- ];
90
- const currentEntries = [];
91
- for (const file of candidatePaths) {
92
- currentEntries.push({
93
- path: file,
94
- sha256: await sha256File(path.join(root, file)),
95
- });
96
- }
97
- return assembleCumulativeDiffContext({ baselineEntries, currentEntries });
98
- }
99
- const TYPED_TO_LEGACY = {
100
- approve_review: "pass",
101
- request_review_changes: "request-revision",
102
- };
103
- function isTypedReviewTerminalVerdict(value) {
104
- return value === "approve_review" || value === "request_review_changes";
105
- }
106
- function isLegacyReviewVerdict(value) {
107
- return value === "pass" || value === "request-revision";
108
- }
109
- /**
110
- * Deterministic, fail-closed equivalence between the typed review terminal
111
- * verdict(s) observed in the attempt and the legacy JSON verdict.
112
- *
113
- * Fail-closed cases (all yield `match: false` with an explicit reason):
114
- * - no typed terminal fact;
115
- * - more than one typed terminal fact (conflicting);
116
- * - an unknown typed terminal kind;
117
- * - a missing/illegal legacy JSON verdict.
118
- */
119
- export function compareTypedReviewToLegacyJsonVerdict(input) {
120
- const kinds = [...input.typedKinds];
121
- const legacyVerdict = isLegacyReviewVerdict(input.legacyVerdict ?? "")
122
- ? input.legacyVerdict
123
- : undefined;
124
- if (kinds.length === 0) {
125
- return {
126
- typedVerdict: undefined,
127
- legacyVerdict,
128
- match: false,
129
- reason: "typed review terminal fact missing",
130
- };
131
- }
132
- if (kinds.length > 1) {
133
- return {
134
- typedVerdict: undefined,
135
- legacyVerdict,
136
- match: false,
137
- reason: `conflicting typed review terminal facts: ${[...new Set(kinds)].join(",")}`,
138
- };
139
- }
140
- const typedVerdict = kinds[0];
141
- if (!isTypedReviewTerminalVerdict(typedVerdict)) {
142
- return {
143
- typedVerdict: undefined,
144
- legacyVerdict,
145
- match: false,
146
- reason: `unknown typed review terminal fact kind: ${typedVerdict}`,
147
- };
148
- }
149
- if (!legacyVerdict) {
150
- return {
151
- typedVerdict,
152
- legacyVerdict: undefined,
153
- match: false,
154
- reason: "legacy JSON verdict missing or not pass|request-revision",
155
- };
156
- }
157
- const expected = TYPED_TO_LEGACY[typedVerdict];
158
- return {
159
- typedVerdict,
160
- legacyVerdict,
161
- match: expected === legacyVerdict,
162
- reason: expected === legacyVerdict
163
- ? "typed review verdict matches legacy JSON verdict"
164
- : `typed review verdict ${typedVerdict} maps to ${expected} but legacy JSON verdict is ${legacyVerdict}`,
165
- };
166
- }
10
+ import { ledgerStatsLine, parseLedgerJson, } from "../../task/source-prepare/ledger.js";
11
+ export const FRONTEND_REVIEW_CONTEXT_SCHEMA_ID = "frontend-review-context-v1";
167
12
  /**
168
13
  * review-context 稳定失败枚举(AC-011 / AC-HARD-005):v2 drift / manifest 失败返回
169
14
  * 结构化、可消费的 failure code(稳定枚举 + reason 的 envelope),既有调用方无需字符串
@@ -207,37 +52,6 @@ async function readOptionalLintAssessment(runDir) {
207
52
  throw new Error(`frontend review context invalid ${relativePath}`);
208
53
  }
209
54
  }
210
- async function readOptionalRepairAssessment(runDir) {
211
- const relativePath = "contracts/frontend-repair-assessment.json";
212
- try {
213
- return JSON.parse(await readFile(path.join(runDir, relativePath), "utf8"));
214
- }
215
- catch (error) {
216
- if (error &&
217
- typeof error === "object" &&
218
- "code" in error &&
219
- error.code === "ENOENT") {
220
- return undefined;
221
- }
222
- throw new Error(`frontend review context invalid ${relativePath}`);
223
- }
224
- }
225
- /** M8: verify failure ownership facts written by frontend-verify-shell. */
226
- async function readOptionalVerifyFailure(runDir) {
227
- const relativePath = "contracts/frontend-verify-failure.json";
228
- try {
229
- return JSON.parse(await readFile(path.join(runDir, relativePath), "utf8"));
230
- }
231
- catch (error) {
232
- if (error &&
233
- typeof error === "object" &&
234
- "code" in error &&
235
- error.code === "ENOENT") {
236
- return undefined;
237
- }
238
- throw new Error(`frontend review context invalid ${relativePath}`);
239
- }
240
- }
241
55
  function assertReviewEvidence(input) {
242
56
  const contract = frontendImplementationContractSchema.safeParse(input.contract);
243
57
  if (!contract.success) {
@@ -280,105 +94,47 @@ function assertReviewEvidence(input) {
280
94
  trace.mockStrategy !== contract.data.mockApi.strategy) {
281
95
  throw new FrontendReviewContextFailure("review-context-verification-unbound", "frontend review context verification trace is not bound to a passing contract");
282
96
  }
97
+ const assessment = frontendRepairAssessmentSchema.safeParse(input.repairAssessment);
98
+ if (!assessment.success) {
99
+ throw new FrontendReviewContextFailure("review-context-invalid-contract", "frontend review context invalid repair assessment");
100
+ }
283
101
  }
284
102
  export async function runFrontendReviewContextGate(input) {
285
- const contractRelativePath = "contracts/frontend-implementation-contract.json";
286
- let contractRaw;
287
- try {
288
- contractRaw = await readFile(path.join(input.runDir, contractRelativePath), "utf8");
289
- }
290
- catch (error) {
291
- throw new FrontendReviewContextFailure("review-context-manifest-missing", `frontend review context missing or invalid ${contractRelativePath}: ${error instanceof Error ? error.message : String(error)}`);
292
- }
293
- let contract;
294
- try {
295
- contract = JSON.parse(contractRaw);
296
- }
297
- catch (error) {
298
- throw new FrontendReviewContextFailure("review-context-manifest-missing", `frontend review context missing or invalid ${contractRelativePath}: ${error instanceof Error ? error.message : String(error)}`);
299
- }
103
+ const contract = await readRequiredJson(input.runDir, "contracts/frontend-implementation-contract.json");
300
104
  const parsedContract = frontendImplementationContractSchema.safeParse(contract);
301
105
  if (!parsedContract.success) {
302
106
  throw new FrontendReviewContextFailure("review-context-invalid-contract", "frontend review context invalid implementation contract");
303
107
  }
304
- // Authorized diff surface = deliverable files + every verification-target
305
- // file the contract references. The writer legitimately writes VT test
306
- // artifacts (behavior verification), and the reviewer must audit their
307
- // diffs; scoping this to targets.files alone failed the baseline-overlap
308
- // check on app.test.js (r19 extreme smoke).
309
- const authorizedChangedPaths = [
310
- ...new Set([
311
- ...parsedContract.data.targets.files,
312
- ...parsedContract.data.verificationTargets.map((target) => target.file),
313
- ]),
314
- ];
315
108
  const diff = await runFrontendWorktreeDiffGate({
316
109
  ...input,
317
- authorizedChangedPaths,
110
+ authorizedChangedPaths: parsedContract.data.targets.files,
318
111
  });
319
112
  const verificationTrace = await readRequiredJson(input.runDir, "contracts/frontend-verification-trace.json");
320
- const repairAssessment = await readOptionalRepairAssessment(input.runDir);
321
- const verifyFailure = await readOptionalVerifyFailure(input.runDir);
113
+ const repairAssessment = await readRequiredJson(input.runDir, "contracts/frontend-repair-assessment.json");
322
114
  const lintAssessment = await readOptionalLintAssessment(input.runDir);
323
- const cumulativeDiff = await captureCumulativeDiffContext({
324
- runDir: input.runDir,
325
- workspaceRoot: input.workspaceRoot,
326
- diff,
327
- });
328
115
  assertReviewEvidence({ contract, verificationTrace, repairAssessment });
329
116
  // Source fidelity ledger 核对(AC-008 / AC-FIDELITY-008):ledger 缺失/漂移即阻断;
330
117
  // 同时把核对清单注入 review-context 供最终 reviewer 机械化核对。
331
118
  const sourceFidelity = await buildReviewSourceFidelityChecklist(input, parsedContract.data);
332
- const contractReviewArtifacts = await writeFrontendContractReviewArtifacts({
333
- runDir: input.runDir,
334
- outputDir: "contracts",
335
- artifactName: "frontend-implementation-contract.json",
336
- canonical: parsedContract.data,
337
- canonicalArtifact: {
338
- path: path.join(input.runDir, contractRelativePath),
339
- sha256: createHash("sha256").update(contractRaw).digest("hex"),
340
- bytes: Buffer.byteLength(contractRaw, "utf8"),
341
- },
342
- });
343
119
  const payload = {
344
- schemaVersion: 2,
120
+ schemaVersion: 1,
345
121
  schemaId: FRONTEND_REVIEW_CONTEXT_SCHEMA_ID,
346
- contractRef: {
347
- path: contractRelativePath,
348
- schemaId: FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID,
349
- sha256: contractReviewArtifacts.index.contract.sha256,
350
- bytes: contractReviewArtifacts.index.contract.bytes,
351
- indexPath: contractReviewArtifacts.index.relativePath,
352
- indexSha256: contractReviewArtifacts.index.artifactSha256,
353
- capacityDiagnosticPath: contractReviewArtifacts.capacity.relativePath,
354
- capacityDiagnosticSha256: contractReviewArtifacts.capacity.artifactSha256,
355
- sections: contractReviewArtifacts.index.sections,
356
- },
122
+ contract,
357
123
  verificationTrace,
358
- ...(repairAssessment ? { repairAssessment } : {}),
359
- ...(verifyFailure ? { verifyFailure } : {}),
124
+ repairAssessment,
360
125
  ...(lintAssessment ? { lintAssessment } : {}),
361
126
  sourceFidelity,
362
127
  diff: {
363
128
  schemaId: diff.schemaId,
364
129
  patchPath: diff.patchPath,
365
130
  patchSha256: diff.patchSha256,
366
- reviewSummaryPath: diff.reviewSummaryPath,
367
- reviewIndexPath: diff.reviewIndexPath,
368
131
  changedFiles: diff.changedFiles,
369
132
  untrackedFiles: diff.untrackedFiles,
370
133
  empty: diff.empty,
371
134
  },
372
- cumulativeDiff,
373
135
  };
374
136
  const artifactPath = await writeDagRunJsonArtifact(input.runDir, "contracts/frontend-review-context.json", payload);
375
- return {
376
- ok: true,
377
- artifactPath,
378
- diff,
379
- contractRef: payload.contractRef,
380
- capacity: contractReviewArtifacts.capacity,
381
- };
137
+ return { ok: true, artifactPath, diff };
382
138
  }
383
139
  /**
384
140
  * 最终 review 核对清单(机械化):ledger 缺失/漂移、canonical requirement 漏项、
@@ -437,8 +193,6 @@ export function formatFrontendReviewContextStdout(result) {
437
193
  return [
438
194
  "Frontend review context: pass",
439
195
  `Artifact: ${result.artifactPath}`,
440
- `Contract index: ${result.contractRef.indexPath}`,
441
- `Capacity: ${result.capacity.overallLevel} (${result.capacity.relativePath}, non-blocking)`,
442
196
  formatFrontendWorktreeDiffStdout(result.diff),
443
197
  ].join("\n");
444
198
  }
@@ -49,236 +49,15 @@ function symbolEvidenceCandidates(symbol) {
49
49
  return [symbol, describeTitle, "describe("];
50
50
  return [symbol];
51
51
  }
52
- export function isConfigurationVerificationFile(file) {
52
+ function isConfigurationVerificationFile(file) {
53
53
  return /(?:^|\/)(?:package\.json|tsconfig(?:\.[^/]+)?\.json|(?:vite|webpack|rollup|docusaurus)\.config\.[cm]?[jt]s)$/.test(file.replace(/\\/g, "/"));
54
54
  }
55
- function isIdentifierStart(char) {
56
- return char !== undefined && /[A-Za-z_$]/.test(char);
57
- }
58
- function isIdentifierPart(char) {
59
- return char !== undefined && /[A-Za-z0-9_$]/.test(char);
60
- }
61
- function readQuotedString(content, start) {
62
- const quote = content[start];
63
- if (quote !== "'" && quote !== '"' && quote !== "`")
64
- return null;
65
- let value = "";
66
- for (let index = start + 1; index < content.length; index += 1) {
67
- const char = content[index];
68
- if (char === "\\") {
69
- const escaped = content[index + 1];
70
- if (escaped === undefined)
71
- return null;
72
- value += escaped;
73
- index += 1;
74
- continue;
75
- }
76
- if (quote === "`" && char === "$" && content[index + 1] === "{") {
77
- // Interpolated templates are not literal titles: their runtime value is
78
- // not deterministic from source text alone.
79
- return null;
80
- }
81
- if (char === quote)
82
- return { end: index + 1, value };
83
- value += char;
84
- }
85
- return null;
86
- }
87
- function skipQuotedSource(content, start) {
88
- const quote = content[start];
89
- for (let index = start + 1; index < content.length; index += 1) {
90
- if (content[index] === "\\") {
91
- index += 1;
92
- continue;
93
- }
94
- if (content[index] === quote)
95
- return index + 1;
96
- }
97
- return content.length;
98
- }
99
- /**
100
- * Small JS/TS lexer for test-title extraction. It intentionally emits only
101
- * identifiers, string literals, and punctuation while skipping comments. This
102
- * prevents prose in comments or ordinary strings from becoming trace evidence
103
- * without adding a parser as a production dependency.
104
- */
105
- function lexFrontendTestSource(content) {
106
- const tokens = [];
107
- let index = 0;
108
- while (index < content.length) {
109
- const char = content[index];
110
- if (/\s/.test(char ?? "")) {
111
- index += 1;
112
- continue;
113
- }
114
- if (char === "/" && content[index + 1] === "/") {
115
- index += 2;
116
- while (index < content.length && content[index] !== "\n")
117
- index += 1;
118
- continue;
119
- }
120
- if (char === "/" && content[index + 1] === "*") {
121
- const end = content.indexOf("*/", index + 2);
122
- index = end < 0 ? content.length : end + 2;
123
- continue;
124
- }
125
- const quoted = readQuotedString(content, index);
126
- if (quoted) {
127
- tokens.push({ type: "string", value: quoted.value });
128
- index = quoted.end;
129
- continue;
130
- }
131
- if (char === "'" || char === '"' || char === "`") {
132
- // Unterminated or interpolated strings are skipped instead of being
133
- // tokenized as executable source.
134
- index = skipQuotedSource(content, index);
135
- continue;
136
- }
137
- if (isIdentifierStart(char)) {
138
- let end = index + 1;
139
- while (isIdentifierPart(content[end]))
140
- end += 1;
141
- tokens.push({ type: "identifier", value: content.slice(index, end) });
142
- index = end;
143
- continue;
144
- }
145
- tokens.push({ type: "punctuation", value: char ?? "" });
146
- index += 1;
147
- }
148
- return tokens;
149
- }
150
- /** Extract literal titles from real describe/it/test calls in JS/TS source. */
151
- export function extractFrontendTestTitles(content) {
152
- const tokens = lexFrontendTestSource(content);
153
- const titles = [];
154
- const configurationModifiers = new Set(["each", "runIf", "skipIf"]);
155
- function closingParenIndex(openIndex) {
156
- let depth = 0;
157
- for (let cursor = openIndex; cursor < tokens.length; cursor += 1) {
158
- if (tokens[cursor]?.type !== "punctuation")
159
- continue;
160
- if (tokens[cursor]?.value === "(")
161
- depth += 1;
162
- if (tokens[cursor]?.value === ")") {
163
- depth -= 1;
164
- if (depth === 0)
165
- return cursor;
166
- }
167
- }
168
- return null;
169
- }
170
- for (let index = 0; index < tokens.length; index += 1) {
171
- const token = tokens[index];
172
- if (token?.type !== "identifier" ||
173
- !["describe", "it", "test"].includes(token.value) ||
174
- (tokens[index - 1]?.type === "punctuation" &&
175
- tokens[index - 1]?.value === ".")) {
176
- continue;
177
- }
178
- let cursor = index + 1;
179
- let hasConfigurationModifier = false;
180
- while (tokens[cursor]?.type === "punctuation" &&
181
- tokens[cursor]?.value === "." &&
182
- tokens[cursor + 1]?.type === "identifier") {
183
- if (tokens[cursor + 1]?.type === "identifier" &&
184
- configurationModifiers.has(tokens[cursor + 1].value)) {
185
- hasConfigurationModifier = true;
186
- }
187
- cursor += 2;
188
- }
189
- if (tokens[cursor]?.type !== "punctuation" ||
190
- tokens[cursor]?.value !== "(") {
191
- continue;
192
- }
193
- if (hasConfigurationModifier) {
194
- const close = closingParenIndex(cursor);
195
- if (close === null ||
196
- tokens[close + 1]?.type !== "punctuation" ||
197
- tokens[close + 1]?.value !== "(") {
198
- continue;
199
- }
200
- cursor = close + 1;
201
- }
202
- if (tokens[cursor + 1]?.type !== "string")
203
- continue;
204
- titles.push(tokens[cursor + 1].value);
205
- }
206
- return titles;
207
- }
208
- function traceKeyMatchesTitle(traceKey, title) {
209
- const escaped = traceKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
210
- return new RegExp(`(?:^|[^A-Za-z0-9_-])${escaped}(?:$|[^A-Za-z0-9_-])`).test(title);
211
- }
212
- function formatObservedTestTitles(titles) {
213
- if (titles.length === 0)
214
- return "(none)";
215
- const limit = 8;
216
- const preview = titles
217
- .slice(0, limit)
218
- .map((title) => title.length > 120 ? `${title.slice(0, 117)}...` : title)
219
- .join(" | ");
220
- return titles.length > limit
221
- ? `${preview} | ... (${titles.length - limit} more)`
222
- : preview;
223
- }
224
- async function evaluateTargetFileTrace(input) {
225
- const fileIssues = await assertFileAndSymbol({
226
- workspaceRoot: input.workspaceRoot,
227
- file: input.target.file,
228
- });
229
- if (fileIssues.length || input.target.type === "static") {
230
- return { issues: fileIssues };
231
- }
232
- const absolute = path.resolve(input.workspaceRoot, input.target.file);
233
- const content = await readFile(absolute, "utf8");
234
- const observedTestTitles = extractFrontendTestTitles(content);
235
- if (observedTestTitles.some((title) => traceKeyMatchesTitle(input.target.id, title))) {
236
- return { issues: [], observedTestTitles };
237
- }
238
- const observed = formatObservedTestTitles(observedTestTitles);
239
- return {
240
- issues: [
241
- `trace key not found in test title: ${input.target.id} in ${input.target.file}; observed test titles: ${observed}`,
242
- ],
243
- observedTestTitles,
244
- };
245
- }
246
- /**
247
- * Lightweight writer-boundary check. It validates file existence and stable
248
- * behavior trace tokens only; frozen commands remain the final trace gate's
249
- * responsibility after command execution.
250
- */
251
- export async function evaluateFrontendWriterTracePreflight(input) {
252
- const targets = [];
253
- const failures = [];
254
- for (const target of input.contract.verificationTargets) {
255
- const fileTrace = await evaluateTargetFileTrace({
256
- workspaceRoot: input.workspaceRoot,
257
- target,
258
- });
259
- for (const detail of fileTrace.issues) {
260
- failures.push({ category: "coverage-gap", targetId: target.id, detail });
261
- }
262
- targets.push({
263
- id: target.id,
264
- commandLabel: target.commandLabel,
265
- file: target.file,
266
- ...(target.symbol ? { symbol: target.symbol } : {}),
267
- ...(target.type !== "static" ? { traceKey: target.id } : {}),
268
- status: fileTrace.issues.length ? "failed" : "ok",
269
- matchedNodeIds: [],
270
- issues: fileTrace.issues,
271
- });
272
- }
273
- return { ok: failures.length === 0, targets, failures };
274
- }
275
55
  /**
276
56
  * Pure verification-target evaluation shared by the trace gate and the repair
277
57
  * assess gate. It derives, for each contract verification target, whether the
278
58
  * target bound to frozen evidence (commandLabel owners), and whether the file
279
- * and stable behavior-target id exist in a real test title. It performs no
280
- * writes and throws nothing: failures are returned as `hardIssues` plus
281
- * per-target `status`.
59
+ * and optional symbol exist in the workspace. It performs no writes and throws
60
+ * nothing: failures are returned as `hardIssues` plus per-target `status`.
282
61
  */
283
62
  export async function evaluateVerificationTargets(input) {
284
63
  const targets = [];
@@ -296,11 +75,17 @@ export async function evaluateVerificationTargets(input) {
296
75
  if (!owners?.length) {
297
76
  issues.push(`commandLabel not executed in current-run frozen evidence: ${target.commandLabel}`);
298
77
  }
299
- const fileTrace = await evaluateTargetFileTrace({
78
+ const fileIssues = await assertFileAndSymbol({
300
79
  workspaceRoot: input.workspaceRoot,
301
- target,
80
+ file: target.file,
81
+ // Configuration files prove that the command's entrypoint exists;
82
+ // they do not expose source symbols. LLMs sometimes derive a
83
+ // filename fragment such as "build" from tsconfig.build.json.
84
+ symbol: isConfigurationVerificationFile(target.file)
85
+ ? undefined
86
+ : target.symbol,
302
87
  });
303
- issues.push(...fileTrace.issues);
88
+ issues.push(...fileIssues);
304
89
  const status = issues.length ? "failed" : "ok";
305
90
  if (issues.length)
306
91
  hardIssues.push(...issues.map((i) => `${target.id}: ${i}`));
@@ -309,7 +94,6 @@ export async function evaluateVerificationTargets(input) {
309
94
  commandLabel: target.commandLabel,
310
95
  file: target.file,
311
96
  symbol: target.symbol,
312
- ...(target.type !== "static" ? { traceKey: target.id } : {}),
313
97
  status,
314
98
  matchedNodeIds: owners ?? [],
315
99
  issues,
@@ -317,25 +101,6 @@ export async function evaluateVerificationTargets(input) {
317
101
  }
318
102
  return { targets, hardIssues };
319
103
  }
320
- /**
321
- * Legacy deterministic symbol resolvability check retained by canonical
322
- * normalization (`dropUnresolvedVerificationSymbols`). New plan tools do not
323
- * expose symbol and runtime trace binding uses the stable target id. This
324
- * helper still handles old `all describe blocks`, `describe('title')`, and
325
- * slash-delimited labels so existing run artifacts normalize consistently.
326
- */
327
- export function verificationSymbolMatchesContent(symbol, content) {
328
- if (symbol.trim().toLowerCase() === "all describe blocks")
329
- return content.includes("describe(");
330
- const symbols = symbol.includes(" / ")
331
- ? symbol
332
- .split(/[((]/, 1)[0]
333
- .split("/")
334
- .map((item) => item.trim())
335
- .filter(Boolean)
336
- : [symbol];
337
- return symbols.every((item) => symbolEvidenceCandidates(item).some((candidate) => content.includes(candidate)));
338
- }
339
104
  async function assertFileAndSymbol(input) {
340
105
  const issues = [];
341
106
  const absolute = path.resolve(input.workspaceRoot, input.file);
@@ -359,8 +124,18 @@ async function assertFileAndSymbol(input) {
359
124
  }
360
125
  if (input.symbol) {
361
126
  const content = await readFile(absolute, "utf8");
362
- if (!verificationSymbolMatchesContent(input.symbol, content)) {
363
- issues.push(`symbol not found: ${input.symbol} in ${input.file}`);
127
+ const symbols = input.symbol.includes(" / ")
128
+ ? input.symbol
129
+ .split(/[((]/, 1)[0]
130
+ .split("/")
131
+ .map((symbol) => symbol.trim())
132
+ .filter(Boolean)
133
+ : [input.symbol];
134
+ for (const symbol of symbols) {
135
+ const matched = symbolEvidenceCandidates(symbol).some((candidate) => content.includes(candidate));
136
+ if (!matched) {
137
+ issues.push(`symbol not found: ${symbol} in ${input.file}`);
138
+ }
364
139
  }
365
140
  }
366
141
  return issues;