@tea-agent/loop-agent 0.1.0-fe-test.0

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 (1050) hide show
  1. package/AGENTS.md +114 -0
  2. package/CHANGELOG.md +2283 -0
  3. package/README.md +177 -0
  4. package/bin/agent-worker.js +22 -0
  5. package/bin/loop-agent.js +57 -0
  6. package/dist/adapters/aimax.js +91 -0
  7. package/dist/adapters/context.js +32 -0
  8. package/dist/adapters/index.js +29 -0
  9. package/dist/adapters/loop-agent.js +192 -0
  10. package/dist/adapters/types.js +1 -0
  11. package/dist/application/context-usage/skill-resolution-stats.js +263 -0
  12. package/dist/application/dag/args.js +486 -0
  13. package/dist/application/dag/generate-task-dag.js +408 -0
  14. package/dist/application/dag/report-dag.js +14 -0
  15. package/dist/application/dag/run-dag.js +109 -0
  16. package/dist/application/dag/validate-dag.js +166 -0
  17. package/dist/application/evaluation/alias.js +184 -0
  18. package/dist/application/evaluation/budget.js +192 -0
  19. package/dist/application/evaluation/campaign-hash.js +47 -0
  20. package/dist/application/evaluation/campaign-matrix.js +372 -0
  21. package/dist/application/evaluation/campaign-scorecard.js +135 -0
  22. package/dist/application/evaluation/campaign.js +370 -0
  23. package/dist/application/evaluation/candidate-hash.js +75 -0
  24. package/dist/application/evaluation/candidate.js +69 -0
  25. package/dist/application/evaluation/corpus-hash.js +38 -0
  26. package/dist/application/evaluation/corpus.js +56 -0
  27. package/dist/application/evaluation/experiment.js +294 -0
  28. package/dist/application/evaluation/ignition.js +198 -0
  29. package/dist/application/evaluation/integrity-audit.js +162 -0
  30. package/dist/application/evaluation/outer-loop.js +132 -0
  31. package/dist/application/evaluation/pi-cell-executor.js +39 -0
  32. package/dist/application/evaluation/private-verifier.js +46 -0
  33. package/dist/application/evaluation/promotion-policy.js +151 -0
  34. package/dist/application/evaluation/proposer.js +98 -0
  35. package/dist/application/evaluation/replay.js +289 -0
  36. package/dist/application/evaluation/types.js +652 -0
  37. package/dist/application/loop/run-action.js +19 -0
  38. package/dist/application/task-lifecycle/advance.js +1204 -0
  39. package/dist/application/task-lifecycle/gates.js +130 -0
  40. package/dist/application/task-lifecycle/index.js +7 -0
  41. package/dist/application/task-lifecycle/observe.js +424 -0
  42. package/dist/application/task-lifecycle/plan-transitions.js +251 -0
  43. package/dist/application/task-lifecycle/recommendations.js +180 -0
  44. package/dist/application/task-lifecycle/record.js +73 -0
  45. package/dist/application/task-lifecycle/types.js +1 -0
  46. package/dist/build-stamp.json +6 -0
  47. package/dist/cli/catalog.js +24 -0
  48. package/dist/cli/command-definitions.js +659 -0
  49. package/dist/cli/help.js +56 -0
  50. package/dist/cli/index.js +5 -0
  51. package/dist/cli/program.js +647 -0
  52. package/dist/cli/router.js +13 -0
  53. package/dist/cli/update/init-surface-notifier.js +167 -0
  54. package/dist/cli/update/notifier.js +117 -0
  55. package/dist/cli/update/npm-client.js +151 -0
  56. package/dist/cli/update/policy.js +92 -0
  57. package/dist/cli/update/runtime-activity.js +29 -0
  58. package/dist/cli/update/state.js +68 -0
  59. package/dist/cli-governance/active-residue-check.js +38 -0
  60. package/dist/cli.js +57 -0
  61. package/dist/commands/client-recovery.js +1373 -0
  62. package/dist/commands/closeout.js +13 -0
  63. package/dist/commands/coverage-audit.js +14 -0
  64. package/dist/commands/coverage-report.js +50 -0
  65. package/dist/commands/cursor-prompt.js +182 -0
  66. package/dist/commands/dag-approve.js +142 -0
  67. package/dist/commands/dag-final-verification.js +76 -0
  68. package/dist/commands/dag-init-hybrid.js +57 -0
  69. package/dist/commands/dag-reconcile-run.js +5 -0
  70. package/dist/commands/dag-reconcile-tasks.js +51 -0
  71. package/dist/commands/dag-reject.js +91 -0
  72. package/dist/commands/dag-report.js +76 -0
  73. package/dist/commands/dag-request-interrupt.js +20 -0
  74. package/dist/commands/dag-rerun-task.js +19 -0
  75. package/dist/commands/dag-rerun.js +111 -0
  76. package/dist/commands/dag-resume.js +35 -0
  77. package/dist/commands/dag-run-task.js +12 -0
  78. package/dist/commands/dag-validate.js +20 -0
  79. package/dist/commands/dag-workflow-compile.js +91 -0
  80. package/dist/commands/dag-workflow-plan.js +130 -0
  81. package/dist/commands/dag-workflow-validate.js +66 -0
  82. package/dist/commands/delegate.js +130 -0
  83. package/dist/commands/docs-archive.js +5 -0
  84. package/dist/commands/docs-audit.js +6 -0
  85. package/dist/commands/doctor.js +283 -0
  86. package/dist/commands/eval.js +1398 -0
  87. package/dist/commands/examples.js +90 -0
  88. package/dist/commands/goal.js +92 -0
  89. package/dist/commands/handoff-check.js +5 -0
  90. package/dist/commands/harvest.js +44 -0
  91. package/dist/commands/import-prd.js +81 -0
  92. package/dist/commands/init-upgrade.js +3085 -0
  93. package/dist/commands/init.js +3630 -0
  94. package/dist/commands/inspect.js +11 -0
  95. package/dist/commands/instructions.js +223 -0
  96. package/dist/commands/knowledge.js +162 -0
  97. package/dist/commands/loop-benchmark.js +72 -0
  98. package/dist/commands/loop.js +251 -0
  99. package/dist/commands/new-task.js +5 -0
  100. package/dist/commands/operator.js +44 -0
  101. package/dist/commands/pi-prompt.js +174 -0
  102. package/dist/commands/pi-reuse-benchmark.js +153 -0
  103. package/dist/commands/plan-list.js +5 -0
  104. package/dist/commands/plan.js +50 -0
  105. package/dist/commands/promote-run.js +29 -0
  106. package/dist/commands/reference-index.js +16 -0
  107. package/dist/commands/run-dag-progress.js +123 -0
  108. package/dist/commands/run-dag.js +22 -0
  109. package/dist/commands/spine.js +38 -0
  110. package/dist/commands/stats.js +113 -0
  111. package/dist/commands/status.js +57 -0
  112. package/dist/commands/study-init.js +192 -0
  113. package/dist/commands/task-advance.js +366 -0
  114. package/dist/commands/task-contract.js +269 -0
  115. package/dist/commands/task-source-prepare.js +479 -0
  116. package/dist/commands/task-status.js +133 -0
  117. package/dist/commands/workflow.js +259 -0
  118. package/dist/commands/worktree-create.js +31 -0
  119. package/dist/commands/worktree-list.js +5 -0
  120. package/dist/commands/worktree-remove.js +26 -0
  121. package/dist/executors/config-core.js +5 -0
  122. package/dist/executors/config.js +2 -0
  123. package/dist/executors/dag-pi-executor.js +1277 -0
  124. package/dist/executors/dag-static-executor.js +42 -0
  125. package/dist/executors/dag.js +3 -0
  126. package/dist/executors/index.js +6 -0
  127. package/dist/executors/model-routing.js +71 -0
  128. package/dist/executors/pi-defaults.js +9 -0
  129. package/dist/executors/pi-event-serializer.js +65 -0
  130. package/dist/executors/pi-executor.js +981 -0
  131. package/dist/executors/pi-playwright-cli-tool.js +1006 -0
  132. package/dist/executors/pi-prompt-transport.js +198 -0
  133. package/dist/executors/pi-reuse-benchmark.js +316 -0
  134. package/dist/executors/pi-runtime-reuse.js +29 -0
  135. package/dist/executors/pi-sdk-executor.js +722 -0
  136. package/dist/executors/pi-sdk.js +1 -0
  137. package/dist/executors/pi-writer-tool-policy.js +266 -0
  138. package/dist/executors/pi.js +3 -0
  139. package/dist/executors/playwright-cli-launcher.js +63 -0
  140. package/dist/executors/process-tree.js +33 -0
  141. package/dist/executors/shell-executor.js +3055 -0
  142. package/dist/executors/shell-presets.js +99 -0
  143. package/dist/executors/shell-verification.js +262 -0
  144. package/dist/executors/shell-write-guard.js +543 -0
  145. package/dist/executors/shell.js +3 -0
  146. package/dist/executors/static.js +1 -0
  147. package/dist/governance/checks.js +437 -0
  148. package/dist/governance/document-index-closure.js +164 -0
  149. package/dist/governance/exec-plans.js +549 -0
  150. package/dist/governance/harness.js +9 -0
  151. package/dist/governance/index.js +3 -0
  152. package/dist/governance/manifest-types.js +254 -0
  153. package/dist/governance/manifest.js +2 -0
  154. package/dist/governance/path-guard.js +69 -0
  155. package/dist/governance/path-guards.js +2 -0
  156. package/dist/governance/profiles.js +3 -0
  157. package/dist/governance/requirement-coverage.js +425 -0
  158. package/dist/governance/skill-safety.js +135 -0
  159. package/dist/governance/spine-audit.js +155 -0
  160. package/dist/infrastructure/evaluation/alias-store.js +199 -0
  161. package/dist/infrastructure/evaluation/campaign-store.js +154 -0
  162. package/dist/infrastructure/evaluation/candidate-store.js +439 -0
  163. package/dist/infrastructure/evaluation/corpus-store.js +181 -0
  164. package/dist/infrastructure/evaluation/experiment-store.js +124 -0
  165. package/dist/infrastructure/evaluation/ignition-store.js +82 -0
  166. package/dist/infrastructure/evaluation/private-verifier-store.js +145 -0
  167. package/dist/infrastructure/evaluation/proposer-store.js +78 -0
  168. package/dist/infrastructure/evaluation/store.js +40 -0
  169. package/dist/infrastructure/harness/active-residue-policy.js +73 -0
  170. package/dist/infrastructure/harness/artifact-store.js +72 -0
  171. package/dist/infrastructure/harness/atomic-write.js +50 -0
  172. package/dist/infrastructure/harness/completed-facts-guard.js +40 -0
  173. package/dist/infrastructure/harness/loop-action-store.js +20 -0
  174. package/dist/infrastructure/harness/loop-store.js +41 -0
  175. package/dist/infrastructure/harness/one-shot-run-store.js +94 -0
  176. package/dist/infrastructure/harness/task-store.js +108 -0
  177. package/dist/records/closeout.js +2 -0
  178. package/dist/records/harvest.js +215 -0
  179. package/dist/records/index.js +3 -0
  180. package/dist/records/one-shot-runs.js +386 -0
  181. package/dist/records/promotion.js +200 -0
  182. package/dist/shared/artifacts-core.js +107 -0
  183. package/dist/shared/artifacts.js +2 -0
  184. package/dist/shared/context-files.js +32 -0
  185. package/dist/shared/context.js +2 -0
  186. package/dist/shared/copy-dir.js +17 -0
  187. package/dist/shared/git-progress.js +172 -0
  188. package/dist/shared/index.js +5 -0
  189. package/dist/shared/logger.js +17 -0
  190. package/dist/shared/one-shot-prompt-args.js +98 -0
  191. package/dist/shared/openspec-spec.js +125 -0
  192. package/dist/shared/operator/capabilities.js +2659 -0
  193. package/dist/shared/operator/command-lifecycle.js +94 -0
  194. package/dist/shared/operator/envelope.js +59 -0
  195. package/dist/shared/operator/index.js +5 -0
  196. package/dist/shared/operator/registry.js +38 -0
  197. package/dist/shared/operator/types.js +5 -0
  198. package/dist/shared/output-truncation.js +37 -0
  199. package/dist/shared/package-metadata.js +530 -0
  200. package/dist/shared/path-refs.js +31 -0
  201. package/dist/shared/pi-retry-settings.js +23 -0
  202. package/dist/shared/playwright-cli-command-policy.js +41 -0
  203. package/dist/shared/preview.js +39 -0
  204. package/dist/shared/prompts.js +26 -0
  205. package/dist/shared/reference-context.js +264 -0
  206. package/dist/shared/resilient-git.js +133 -0
  207. package/dist/shared/runtime-activity.js +6 -0
  208. package/dist/shared/timeout-policy.js +24 -0
  209. package/dist/shared/timeout.js +1 -0
  210. package/dist/shared/types.js +5 -0
  211. package/dist/sidecars/cursor-prompt/executor.js +428 -0
  212. package/dist/sidecars/cursor-prompt/index.js +3 -0
  213. package/dist/sidecars/cursor-prompt/stream.js +121 -0
  214. package/dist/task/config-types.js +235 -0
  215. package/dist/task/config.js +2 -0
  216. package/dist/task/contract/adopt.js +166 -0
  217. package/dist/task/contract/apply.js +326 -0
  218. package/dist/task/contract/canonicalize.js +60 -0
  219. package/dist/task/contract/constants.js +30 -0
  220. package/dist/task/contract/diff.js +177 -0
  221. package/dist/task/contract/hash.js +42 -0
  222. package/dist/task/contract/import-revision.js +96 -0
  223. package/dist/task/contract/index.js +17 -0
  224. package/dist/task/contract/journal.js +155 -0
  225. package/dist/task/contract/lock.js +153 -0
  226. package/dist/task/contract/observe.js +296 -0
  227. package/dist/task/contract/paths.js +19 -0
  228. package/dist/task/contract/project.js +185 -0
  229. package/dist/task/contract/recover.js +312 -0
  230. package/dist/task/contract/request-ledger.js +37 -0
  231. package/dist/task/contract/schema.js +153 -0
  232. package/dist/task/contract/transaction.js +160 -0
  233. package/dist/task/contract/types.js +1 -0
  234. package/dist/task/contract/validate-draft.js +106 -0
  235. package/dist/task/dag-source-paths.js +50 -0
  236. package/dist/task/delegate.js +208 -0
  237. package/dist/task/frontend-preflight.js +131 -0
  238. package/dist/task/frontend-project-capability.js +570 -0
  239. package/dist/task/goal-audit.js +51 -0
  240. package/dist/task/goal-policy.js +8 -0
  241. package/dist/task/goal.js +3 -0
  242. package/dist/task/ids.js +1 -0
  243. package/dist/task/index.js +12 -0
  244. package/dist/task/lifecycle.js +1 -0
  245. package/dist/task/operator/capabilities.js +6 -0
  246. package/dist/task/operator/envelope.js +2 -0
  247. package/dist/task/operator/index.js +5 -0
  248. package/dist/task/operator/registry.js +2 -0
  249. package/dist/task/operator/types.js +1 -0
  250. package/dist/task/paths.js +1 -0
  251. package/dist/task/read-model.js +140 -0
  252. package/dist/task/runtime.js +708 -0
  253. package/dist/task/source-prepare/artifact-meta.js +137 -0
  254. package/dist/task/source-prepare/build-draft.js +232 -0
  255. package/dist/task/source-prepare/completeness.js +248 -0
  256. package/dist/task/source-prepare/index.js +10 -0
  257. package/dist/task/source-prepare/parse-intent.js +496 -0
  258. package/dist/task/source-prepare/path-policy.js +197 -0
  259. package/dist/task/source-prepare/placeholder-paths.js +77 -0
  260. package/dist/task/source-prepare/prepare.js +920 -0
  261. package/dist/task/source-prepare/reference-integrity.js +290 -0
  262. package/dist/task/source-prepare/semantic-intake.js +717 -0
  263. package/dist/task/source-prepare/types.js +7 -0
  264. package/dist/task/source-references.js +228 -0
  265. package/dist/task/source-state.js +1 -0
  266. package/dist/task/state.js +41 -0
  267. package/dist/task/subagent-guidance.js +1 -0
  268. package/dist/task/task-demand-routing.js +443 -0
  269. package/dist/task/workflow-state-types.js +92 -0
  270. package/dist/task/worktree-cleanup.js +140 -0
  271. package/dist/task/worktree.js +388 -0
  272. package/dist/verification/maven/cache.js +142 -0
  273. package/dist/verification/maven/index.js +120 -0
  274. package/dist/verification/maven/plan-commands.js +421 -0
  275. package/dist/verification/maven/pom-static.js +136 -0
  276. package/dist/verification/maven/scope-resolve.js +153 -0
  277. package/dist/verification/maven/stale.js +130 -0
  278. package/dist/verification/maven/types.js +23 -0
  279. package/dist/verification/maven/workspace-graph.js +322 -0
  280. package/dist/worker/cli.js +957 -0
  281. package/dist/worker/closeout/apply.js +73 -0
  282. package/dist/worker/closeout/preview.js +30 -0
  283. package/dist/worker/console/app-data.js +316 -0
  284. package/dist/worker/console/chat/artifact-card.js +30 -0
  285. package/dist/worker/console/chat/assistant-content.js +121 -0
  286. package/dist/worker/console/chat/chat-event-store.js +981 -0
  287. package/dist/worker/console/chat/chat-ui-policy.js +25 -0
  288. package/dist/worker/console/chat/compaction-errors.js +64 -0
  289. package/dist/worker/console/chat/composer-draft-store.js +45 -0
  290. package/dist/worker/console/chat/context-panel.js +54 -0
  291. package/dist/worker/console/chat/contract-apply-receipt-store.js +174 -0
  292. package/dist/worker/console/chat/explore-tools.js +299 -0
  293. package/dist/worker/console/chat/human-gate-card.js +45 -0
  294. package/dist/worker/console/chat/interview-adapter.js +149 -0
  295. package/dist/worker/console/chat/mcp-inventory.js +221 -0
  296. package/dist/worker/console/chat/model-resolver.js +215 -0
  297. package/dist/worker/console/chat/operation-card.js +92 -0
  298. package/dist/worker/console/chat/pi-console-config.js +376 -0
  299. package/dist/worker/console/chat/pi-runtime.js +2446 -0
  300. package/dist/worker/console/chat/repo-browser.js +140 -0
  301. package/dist/worker/console/chat/repo-walk.js +125 -0
  302. package/dist/worker/console/chat/resource-loader.js +59 -0
  303. package/dist/worker/console/chat/resource-preferences-store.js +152 -0
  304. package/dist/worker/console/chat/routes.js +3137 -0
  305. package/dist/worker/console/chat/runtime-context.js +102 -0
  306. package/dist/worker/console/chat/runtime-selection.js +96 -0
  307. package/dist/worker/console/chat/semantic-activity.js +489 -0
  308. package/dist/worker/console/chat/session-store.js +484 -0
  309. package/dist/worker/console/chat/shortcuts.js +217 -0
  310. package/dist/worker/console/chat/tool-adapter.js +131 -0
  311. package/dist/worker/console/chat/tool-preview.js +247 -0
  312. package/dist/worker/console/chat/tools.js +212 -0
  313. package/dist/worker/console/chat/turn-execution-registry.js +82 -0
  314. package/dist/worker/console/chat/turn-process.js +346 -0
  315. package/dist/worker/console/chat/usage.js +165 -0
  316. package/dist/worker/console/chat/workspace-landing.js +103 -0
  317. package/dist/worker/console/dag-confirmation.js +347 -0
  318. package/dist/worker/console/dag-execution-receipt.js +393 -0
  319. package/dist/worker/console/doctor.js +230 -0
  320. package/dist/worker/console/draft-store.js +158 -0
  321. package/dist/worker/console/harness-pi-model-matrix.js +137 -0
  322. package/dist/worker/console/human-gate-token.js +130 -0
  323. package/dist/worker/console/index.js +17 -0
  324. package/dist/worker/console/inspect-split.js +82 -0
  325. package/dist/worker/console/interview/assessment.js +67 -0
  326. package/dist/worker/console/interview/grill-me.js +277 -0
  327. package/dist/worker/console/interview/session.js +100 -0
  328. package/dist/worker/console/interview/tools.js +109 -0
  329. package/dist/worker/console/loopback.js +16 -0
  330. package/dist/worker/console/mutation-gate-receipt-store.js +184 -0
  331. package/dist/worker/console/night-aux-ticker.js +146 -0
  332. package/dist/worker/console/observe-health-match.js +179 -0
  333. package/dist/worker/console/observe-link.js +42 -0
  334. package/dist/worker/console/open-browser.js +134 -0
  335. package/dist/worker/console/operation-run-facts.js +190 -0
  336. package/dist/worker/console/operation-runner.js +353 -0
  337. package/dist/worker/console/operation-sse.js +184 -0
  338. package/dist/worker/console/operation-store.js +169 -0
  339. package/dist/worker/console/operation-wait.js +355 -0
  340. package/dist/worker/console/operator-actions.js +3768 -0
  341. package/dist/worker/console/operator-selection.js +105 -0
  342. package/dist/worker/console/operator-surface-health.js +24 -0
  343. package/dist/worker/console/operator-user-error.js +45 -0
  344. package/dist/worker/console/pi-readiness.js +288 -0
  345. package/dist/worker/console/prd-identity.js +102 -0
  346. package/dist/worker/console/prd-intake-bridge.js +441 -0
  347. package/dist/worker/console/prd-reference-discovery.js +124 -0
  348. package/dist/worker/console/recovery-cta.js +320 -0
  349. package/dist/worker/console/recovery-error-copy.js +198 -0
  350. package/dist/worker/console/recovery-selection.js +107 -0
  351. package/dist/worker/console/repo-fingerprint.js +35 -0
  352. package/dist/worker/console/resolve-dag-run-for-task.js +115 -0
  353. package/dist/worker/console/resource-loader.js +95 -0
  354. package/dist/worker/console/routes.js +475 -0
  355. package/dist/worker/console/security.js +170 -0
  356. package/dist/worker/console/server.js +357 -0
  357. package/dist/worker/console/sibling-controller.js +33 -0
  358. package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-BmOEx0gA.js +1 -0
  359. package/dist/worker/console/static/assets/arc-BGiVVe8b.js +1 -0
  360. package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-nDdu4rgJ.js +36 -0
  361. package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-Bb5u0BWf.js +132 -0
  362. package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-BlhC0j94.js +10 -0
  363. package/dist/worker/console/static/assets/channel-Tl-FkUqr.js +1 -0
  364. package/dist/worker/console/static/assets/chunk-2GRJ4B5K-Ai8_qQXI.js +1 -0
  365. package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B9gyCjqX.js +1 -0
  366. package/dist/worker/console/static/assets/chunk-5RXB4S5H-Bzbj3zGH.js +231 -0
  367. package/dist/worker/console/static/assets/chunk-5VM5RSS4-BT415PvT.js +15 -0
  368. package/dist/worker/console/static/assets/chunk-6Q2QTUOP-D_A5_5QL.js +88 -0
  369. package/dist/worker/console/static/assets/chunk-GF5L2VYU-srJkaP3f.js +206 -0
  370. package/dist/worker/console/static/assets/chunk-JWPE2WC7-UYGvEP4l.js +1 -0
  371. package/dist/worker/console/static/assets/chunk-KBJHAD2P-B8luBrrz.js +1 -0
  372. package/dist/worker/console/static/assets/chunk-RYQCIY6F-DN7wXZSq.js +1 -0
  373. package/dist/worker/console/static/assets/chunk-XXDRQBXY-DXKyMuKS.js +1 -0
  374. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BaehtRk4.js +1 -0
  375. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BaehtRk4.js +1 -0
  376. package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-dqvoynXj.js +1 -0
  377. package/dist/worker/console/static/assets/cynefin-VYW2F7L2--PcyL34A.js +166 -0
  378. package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DvWs_hTY.js +62 -0
  379. package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +321 -0
  380. package/dist/worker/console/static/assets/dagre-VZM6K2ZE-DxeIxbyO.js +4 -0
  381. package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  382. package/dist/worker/console/static/assets/diagram-7IWD3JNH-DPCG61Of.js +30 -0
  383. package/dist/worker/console/static/assets/diagram-B4RE2ZJO-CmuU50-U.js +3 -0
  384. package/dist/worker/console/static/assets/diagram-LBJQPF4R-B61XLD_v.js +24 -0
  385. package/dist/worker/console/static/assets/diagram-Q27KOJAE-BB7HTRRY.js +24 -0
  386. package/dist/worker/console/static/assets/diagram-UB23O5K3-DvkboAIp.js +41 -0
  387. package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-BdJ_y1fL.js +1 -0
  388. package/dist/worker/console/static/assets/erDiagram-JOGREHBK-9bUBZTRi.js +85 -0
  389. package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-Dw4L4RBa.js +156 -0
  390. package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-Jr82N_QP.js +292 -0
  391. package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-DAz9wFLr.js +106 -0
  392. package/dist/worker/console/static/assets/graph-DOmOIIwC.js +1 -0
  393. package/dist/worker/console/static/assets/index-B7Y-0g3J.css +1 -0
  394. package/dist/worker/console/static/assets/index-DkCrqaYg.js +325 -0
  395. package/dist/worker/console/static/assets/infoDiagram-6WML65LV-CBB5Np7y.js +2 -0
  396. package/dist/worker/console/static/assets/init-Gi6I4Gst.js +1 -0
  397. package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-D8IOp94y.js +70 -0
  398. package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-Dgd3AHGG.js +139 -0
  399. package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BmYOxLE9.js +89 -0
  400. package/dist/worker/console/static/assets/layout-D-LzfAck.js +1 -0
  401. package/dist/worker/console/static/assets/linear-DfYTEF6J.js +1 -0
  402. package/dist/worker/console/static/assets/map-DxJ2ADlA.js +1 -0
  403. package/dist/worker/console/static/assets/mermaid.core-BjuFPRqa.js +308 -0
  404. package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS-CLyUViGd.js +96 -0
  405. package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +1 -0
  406. package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-BvX2Di4-.js +1 -0
  407. package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-BFj0_OLv.js +39 -0
  408. package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-BIUrD_hx.js +7 -0
  409. package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-CHBB48fj.js +1 -0
  410. package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-sJrYw2Sf.js +84 -0
  411. package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-BzHH6fPR.js +40 -0
  412. package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-7QCnelCt.js +162 -0
  413. package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-sDYSxn72.js +1 -0
  414. package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-XpiUdwzm.js +1 -0
  415. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-eEhq2A8Y.js +1 -0
  416. package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-DvqgNPzD.js +2 -0
  417. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-la8AC7fo.js +8 -0
  418. package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-2TyOYNx0.js +120 -0
  419. package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-B-nM6wM2.js +34 -0
  420. package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-B86wPdbr.js +78 -0
  421. package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-Dr1C4AQz.js +7 -0
  422. package/dist/worker/console/static/favicon.svg +37 -0
  423. package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  424. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  425. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  426. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  427. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  428. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  429. package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  430. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  431. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  432. package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  433. package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  434. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  435. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  436. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  437. package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  438. package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  439. package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  440. package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  441. package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  442. package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  443. package/dist/worker/console/static/index.html +15 -0
  444. package/dist/worker/console/static-src/app/console-types.js +184 -0
  445. package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
  446. package/dist/worker/console/static-src/app/useOperatorActions.js +324 -0
  447. package/dist/worker/console/static-src/app/usePrdImport.js +172 -0
  448. package/dist/worker/console/static-src/app/useRecoveryActions.js +289 -0
  449. package/dist/worker/console/static-src/app/useRecoveryConsole.js +425 -0
  450. package/dist/worker/console/static-src/app/useTaskWizard.js +296 -0
  451. package/dist/worker/console/static-src/chat-markdown-security.js +38 -0
  452. package/dist/worker/console/static-src/chat-view-types.js +2 -0
  453. package/dist/worker/console/static-src/night/night-types.js +24 -0
  454. package/dist/worker/console/static-src/night/useNightBoard.js +94 -0
  455. package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
  456. package/dist/worker/console/static-src/night-prepare-result.js +118 -0
  457. package/dist/worker/console/static-src/operator-chat/activity-journey.js +125 -0
  458. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +73 -0
  459. package/dist/worker/console/static-src/operator-chat/activity-references.js +20 -0
  460. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +420 -0
  461. package/dist/worker/console/static-src/operator-chat/format.js +71 -0
  462. package/dist/worker/console/static-src/operator-chat/landing-density.js +23 -0
  463. package/dist/worker/console/static-src/operator-chat/mutation-gate.js +45 -0
  464. package/dist/worker/console/static-src/operator-chat/refs.js +86 -0
  465. package/dist/worker/console/static-src/operator-chat/resource-auto-invocation.js +91 -0
  466. package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
  467. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +257 -0
  468. package/dist/worker/console/static-src/operator-chat/session-title-watcher.js +128 -0
  469. package/dist/worker/console/static-src/operator-chat/sidebar-split.js +90 -0
  470. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +24 -0
  471. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +141 -0
  472. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +38 -0
  473. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
  474. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +690 -0
  475. package/dist/worker/console/static-src/operator-chat/turn-submission.js +158 -0
  476. package/dist/worker/console/static-src/operator-chat/types.js +1 -0
  477. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +59 -0
  478. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +495 -0
  479. package/dist/worker/console/static-src/operator-chat/useChatStream.js +780 -0
  480. package/dist/worker/console/static-src/operator-chat/useChatThread.js +280 -0
  481. package/dist/worker/console/static-src/operator-chat/useComposer.js +257 -0
  482. package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
  483. package/dist/worker/console/static-src/operator-chat/useOverlayFocus.js +84 -0
  484. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +148 -0
  485. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +358 -0
  486. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +196 -0
  487. package/dist/worker/console/static-src/operator-chat/useWorkspaceLayout.js +58 -0
  488. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +35 -0
  489. package/dist/worker/console/static-src/prd-file-import.js +218 -0
  490. package/dist/worker/console/vite.config.js +27 -0
  491. package/dist/worker/console/workflow-kinds.js +46 -0
  492. package/dist/worker/delivery/final-verification.js +302 -0
  493. package/dist/worker/delivery/git-transaction.js +497 -0
  494. package/dist/worker/delivery/package.js +537 -0
  495. package/dist/worker/delivery/verification-bundle.js +544 -0
  496. package/dist/worker/feature/acceptance-policy.js +227 -0
  497. package/dist/worker/feature/advance.js +301 -0
  498. package/dist/worker/feature/decision-loader.js +99 -0
  499. package/dist/worker/feature/discover.js +14 -0
  500. package/dist/worker/feature/doctor.js +223 -0
  501. package/dist/worker/feature/fullstack-validate.js +337 -0
  502. package/dist/worker/feature/next-action.js +140 -0
  503. package/dist/worker/feature/profile-schema.js +47 -0
  504. package/dist/worker/feature/ready-plan-projection.js +82 -0
  505. package/dist/worker/feature/reducer.js +137 -0
  506. package/dist/worker/feature/review.js +678 -0
  507. package/dist/worker/feature/run.js +390 -0
  508. package/dist/worker/feature/scaffold.js +812 -0
  509. package/dist/worker/feature/types.js +1 -0
  510. package/dist/worker/follow-up/approve.js +273 -0
  511. package/dist/worker/follow-up/factory.js +234 -0
  512. package/dist/worker/follow-up/paths.js +25 -0
  513. package/dist/worker/follow-up/policy.js +26 -0
  514. package/dist/worker/follow-up/schema.js +93 -0
  515. package/dist/worker/follow-up/store.js +96 -0
  516. package/dist/worker/loop-agent/command-result.js +1 -0
  517. package/dist/worker/loop-agent/controller-protocol.js +143 -0
  518. package/dist/worker/loop-agent/loop-agent-client.js +489 -0
  519. package/dist/worker/loop-agent/parse-json.js +14 -0
  520. package/dist/worker/materialize/harness-task-lifecycle-probe.js +126 -0
  521. package/dist/worker/materialize/harness-task-lineage.js +220 -0
  522. package/dist/worker/materialize/harness-task-materializer.js +648 -0
  523. package/dist/worker/metrics/projector.js +139 -0
  524. package/dist/worker/observability/dag-execution-trajectory.js +591 -0
  525. package/dist/worker/observability/event-history.js +216 -0
  526. package/dist/worker/observability/event-store.js +83 -0
  527. package/dist/worker/observability/events.js +79 -0
  528. package/dist/worker/observability/interrupt-eligibility.js +264 -0
  529. package/dist/worker/observability/progress-composite.js +34 -0
  530. package/dist/worker/observability/read-model.js +2499 -0
  531. package/dist/worker/observability/snapshot-store.js +43 -0
  532. package/dist/worker/observability/types.js +1 -0
  533. package/dist/worker/observe/dag-node-execution-output.js +180 -0
  534. package/dist/worker/observe/dag-run-artifacts.js +90 -0
  535. package/dist/worker/observe/health.js +58 -0
  536. package/dist/worker/observe/night-jobs.js +104 -0
  537. package/dist/worker/observe/node-input.js +441 -0
  538. package/dist/worker/observe/paths.js +174 -0
  539. package/dist/worker/observe/routes.js +1077 -0
  540. package/dist/worker/observe/server.js +117 -0
  541. package/dist/worker/observe/spec-evidence.js +398 -0
  542. package/dist/worker/observe/static/api.js +117 -0
  543. package/dist/worker/observe/static/app.js +169 -0
  544. package/dist/worker/observe/static/constants.js +182 -0
  545. package/dist/worker/observe/static/copy.js +67 -0
  546. package/dist/worker/observe/static/custom-select.js +567 -0
  547. package/dist/worker/observe/static/dag-edge-routing.js +368 -0
  548. package/dist/worker/observe/static/dag-helpers.js +161 -0
  549. package/dist/worker/observe/static/dag-history-labels.js +95 -0
  550. package/dist/worker/observe/static/dag-layout.d.ts +36 -0
  551. package/dist/worker/observe/static/dag-layout.js +163 -0
  552. package/dist/worker/observe/static/dag-model.js +145 -0
  553. package/dist/worker/observe/static/dom.js +220 -0
  554. package/dist/worker/observe/static/favicon.svg +37 -0
  555. package/dist/worker/observe/static/format-pool.d.ts +71 -0
  556. package/dist/worker/observe/static/format-pool.js +153 -0
  557. package/dist/worker/observe/static/format.js +347 -0
  558. package/dist/worker/observe/static/index.html +409 -0
  559. package/dist/worker/observe/static/kpi.js +78 -0
  560. package/dist/worker/observe/static/markdown-render.js +124 -0
  561. package/dist/worker/observe/static/operator-chrome.css +517 -0
  562. package/dist/worker/observe/static/operator-chrome.d.ts +83 -0
  563. package/dist/worker/observe/static/operator-chrome.js +623 -0
  564. package/dist/worker/observe/static/relations.js +133 -0
  565. package/dist/worker/observe/static/router.js +156 -0
  566. package/dist/worker/observe/static/run-processing.js +148 -0
  567. package/dist/worker/observe/static/shell-chrome.js +65 -0
  568. package/dist/worker/observe/static/state.js +496 -0
  569. package/dist/worker/observe/static/styles.css +3567 -0
  570. package/dist/worker/observe/static/views/batch.js +227 -0
  571. package/dist/worker/observe/static/views/dag-graph.js +508 -0
  572. package/dist/worker/observe/static/views/dag-inspector.js +1722 -0
  573. package/dist/worker/observe/static/views/dag-trajectory.js +313 -0
  574. package/dist/worker/observe/static/views/dag.js +483 -0
  575. package/dist/worker/observe/static/views/dags.js +877 -0
  576. package/dist/worker/observe/static/views/dashboard.js +747 -0
  577. package/dist/worker/observe/static/views/failures.js +143 -0
  578. package/dist/worker/observe/static/views/feature.js +492 -0
  579. package/dist/worker/observe/static/views/night.js +201 -0
  580. package/dist/worker/observe/static/views/pool.js +708 -0
  581. package/dist/worker/observe/static/views/run.js +453 -0
  582. package/dist/worker/observe/static/views/session-timeline.js +771 -0
  583. package/dist/worker/observe/static/views/shell.js +7 -0
  584. package/dist/worker/observe/static/views/task.js +315 -0
  585. package/dist/worker/observe/static/views/timeline.js +163 -0
  586. package/dist/worker/outcomes/adapters.js +153 -0
  587. package/dist/worker/outcomes/declared-artifacts.js +103 -0
  588. package/dist/worker/outcomes/evidence-tokens.js +29 -0
  589. package/dist/worker/outcomes/gate.js +40 -0
  590. package/dist/worker/outcomes/projector.js +227 -0
  591. package/dist/worker/outcomes/registry.js +1 -0
  592. package/dist/worker/outcomes/store.js +131 -0
  593. package/dist/worker/outcomes/types.js +79 -0
  594. package/dist/worker/pool/attempt-identity.js +41 -0
  595. package/dist/worker/pool/attempt-lease.js +184 -0
  596. package/dist/worker/pool/attempt-transition.js +210 -0
  597. package/dist/worker/pool/begin-attempt-with-lease.js +26 -0
  598. package/dist/worker/pool/begin-attempt.js +35 -0
  599. package/dist/worker/pool/doctor.js +165 -0
  600. package/dist/worker/pool/failure-routing.js +182 -0
  601. package/dist/worker/pool/migrate-state.js +303 -0
  602. package/dist/worker/pool/reconcile.js +285 -0
  603. package/dist/worker/pool/recovery-decision.js +163 -0
  604. package/dist/worker/pool/run-owner-store.js +126 -0
  605. package/dist/worker/pool/run-store.js +360 -0
  606. package/dist/worker/pool/runtime-reconcile-inventory.js +127 -0
  607. package/dist/worker/pool/state-projection.js +57 -0
  608. package/dist/worker/pool/types.js +17 -0
  609. package/dist/worker/pool/validation.js +144 -0
  610. package/dist/worker/preflight.js +157 -0
  611. package/dist/worker/profile-mapping.js +76 -0
  612. package/dist/worker/progress-reporter.js +63 -0
  613. package/dist/worker/report/morning-report.js +155 -0
  614. package/dist/worker/repos/repo-resolver.js +23 -0
  615. package/dist/worker/run-task/execute-prepared-task.js +178 -0
  616. package/dist/worker/run-task/run-task.js +912 -0
  617. package/dist/worker/runner/run-ready.js +561 -0
  618. package/dist/worker/runner/single-task-attempt.js +176 -0
  619. package/dist/worker/scheduler/admission.js +547 -0
  620. package/dist/worker/scheduler/auto-followup.js +99 -0
  621. package/dist/worker/scheduler/cli.js +644 -0
  622. package/dist/worker/scheduler/clock-install/darwin-launchd.js +238 -0
  623. package/dist/worker/scheduler/clock-install/linux-systemd-user.js +224 -0
  624. package/dist/worker/scheduler/clock-install/types.js +1 -0
  625. package/dist/worker/scheduler/clock-install/win32-schtasks.js +227 -0
  626. package/dist/worker/scheduler/clock-install.js +284 -0
  627. package/dist/worker/scheduler/clock.js +420 -0
  628. package/dist/worker/scheduler/dispatcher.js +503 -0
  629. package/dist/worker/scheduler/doctor.js +431 -0
  630. package/dist/worker/scheduler/evidence.js +170 -0
  631. package/dist/worker/scheduler/git-base.js +90 -0
  632. package/dist/worker/scheduler/index.js +25 -0
  633. package/dist/worker/scheduler/lease.js +114 -0
  634. package/dist/worker/scheduler/lifecycle.js +348 -0
  635. package/dist/worker/scheduler/lock.js +80 -0
  636. package/dist/worker/scheduler/morning-window.js +191 -0
  637. package/dist/worker/scheduler/night-git-finalizer.js +114 -0
  638. package/dist/worker/scheduler/night-harvest.js +412 -0
  639. package/dist/worker/scheduler/paths.js +92 -0
  640. package/dist/worker/scheduler/prepared-attempt-recovery.js +471 -0
  641. package/dist/worker/scheduler/recovery.js +277 -0
  642. package/dist/worker/scheduler/reservation.js +146 -0
  643. package/dist/worker/scheduler/retry.js +199 -0
  644. package/dist/worker/scheduler/scheduler-loop.js +272 -0
  645. package/dist/worker/scheduler/store.js +275 -0
  646. package/dist/worker/scheduler/traceability.js +54 -0
  647. package/dist/worker/scheduler/trigger.js +258 -0
  648. package/dist/worker/scheduler/types.js +369 -0
  649. package/dist/worker/scheduler/workspace-adapter.js +128 -0
  650. package/dist/worker/task-graph/acceptance-schema.js +40 -0
  651. package/dist/worker/task-graph/ready-planner.js +267 -0
  652. package/dist/worker/task-graph/ready-queue.js +23 -0
  653. package/dist/worker/task-graph/task-graph-schema.js +59 -0
  654. package/dist/worker/task-graph/types.js +1 -0
  655. package/dist/worker/task-graph/validate.js +293 -0
  656. package/dist/worker/task-spec/complexity-mapping.js +8 -0
  657. package/dist/worker/task-spec/schema.js +125 -0
  658. package/dist/worker/task-spec/types.js +1 -0
  659. package/dist/worker/task-spec/validate.js +391 -0
  660. package/dist/worker/task-spec/workflow-routing.js +149 -0
  661. package/dist/workflows/dag/authoring.js +8 -0
  662. package/dist/workflows/dag/authority-surface.js +138 -0
  663. package/dist/workflows/dag/backend-test-analysis-contract.js +507 -0
  664. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +1985 -0
  665. package/dist/workflows/dag/backend-test-case-manifest.js +638 -0
  666. package/dist/workflows/dag/backend-test-classification-contract.js +38 -0
  667. package/dist/workflows/dag/backend-test-contract-envelope.js +205 -0
  668. package/dist/workflows/dag/backend-test-coverage-contract.js +202 -0
  669. package/dist/workflows/dag/backend-test-execution-contract.js +715 -0
  670. package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
  671. package/dist/workflows/dag/backend-test-intake-context.js +178 -0
  672. package/dist/workflows/dag/backend-test-layout.js +133 -0
  673. package/dist/workflows/dag/backend-test-markdown-workflow.js +1984 -0
  674. package/dist/workflows/dag/backend-test-module-stem.js +26 -0
  675. package/dist/workflows/dag/backend-test-pytest-collection.js +859 -0
  676. package/dist/workflows/dag/backend-test-result-contract.js +1030 -0
  677. package/dist/workflows/dag/backend-test-scenario-param.js +1670 -0
  678. package/dist/workflows/dag/backend-test-scenario-partitions.js +249 -0
  679. package/dist/workflows/dag/backend-test-semantic-review-contract.js +36 -0
  680. package/dist/workflows/dag/backend-test-stability-contract.js +57 -0
  681. package/dist/workflows/dag/backend-test-writer-completeness.js +1058 -0
  682. package/dist/workflows/dag/budget-enforcement.js +67 -0
  683. package/dist/workflows/dag/canvas-observer.js +474 -0
  684. package/dist/workflows/dag/context-policy.js +137 -0
  685. package/dist/workflows/dag/contract-output-registry.js +14 -0
  686. package/dist/workflows/dag/contract-validator-registrations.js +10 -0
  687. package/dist/workflows/dag/controller-identity.js +104 -0
  688. package/dist/workflows/dag/convergence/controller.js +596 -0
  689. package/dist/workflows/dag/decision-envelope.js +557 -0
  690. package/dist/workflows/dag/decision-evidence.js +153 -0
  691. package/dist/workflows/dag/decision-gates.js +1 -0
  692. package/dist/workflows/dag/dynamic-runtime/condition.js +48 -0
  693. package/dist/workflows/dag/dynamic-runtime/loop-until.js +161 -0
  694. package/dist/workflows/dag/dynamic-runtime/map.js +435 -0
  695. package/dist/workflows/dag/dynamic-runtime/reduction.js +72 -0
  696. package/dist/workflows/dag/dynamic-runtime/shared.js +203 -0
  697. package/dist/workflows/dag/event-observer.js +132 -0
  698. package/dist/workflows/dag/executor-registry.js +23 -0
  699. package/dist/workflows/dag/facts.js +4 -0
  700. package/dist/workflows/dag/failure-category.js +118 -0
  701. package/dist/workflows/dag/failure-routing.js +105 -0
  702. package/dist/workflows/dag/final-verification.js +180 -0
  703. package/dist/workflows/dag/frontend-implementation-contract.js +1911 -0
  704. package/dist/workflows/dag/frontend-lint-baseline.js +474 -0
  705. package/dist/workflows/dag/frontend-plan-render.js +90 -0
  706. package/dist/workflows/dag/frontend-prewrite-gate.js +1011 -0
  707. package/dist/workflows/dag/frontend-project-capability.js +1 -0
  708. package/dist/workflows/dag/frontend-recovery-plan.js +73 -0
  709. package/dist/workflows/dag/frontend-recovery-root-manifest.js +123 -0
  710. package/dist/workflows/dag/frontend-recovery-run.js +539 -0
  711. package/dist/workflows/dag/frontend-repair.js +541 -0
  712. package/dist/workflows/dag/frontend-review-context.js +116 -0
  713. package/dist/workflows/dag/frontend-risk.js +161 -0
  714. package/dist/workflows/dag/frontend-test-case-checklist.js +343 -0
  715. package/dist/workflows/dag/frontend-test-case-manifest.js +104 -0
  716. package/dist/workflows/dag/frontend-test-case-quality.js +105 -0
  717. package/dist/workflows/dag/frontend-test-html-report.js +174 -0
  718. package/dist/workflows/dag/frontend-test-l5-report.js +177 -0
  719. package/dist/workflows/dag/frontend-test-result-contract.js +487 -0
  720. package/dist/workflows/dag/frontend-verification-trace.js +275 -0
  721. package/dist/workflows/dag/frontend-worktree-diff.js +201 -0
  722. package/dist/workflows/dag/frontend-writer-recovery.js +106 -0
  723. package/dist/workflows/dag/frontend-writer-rollback.js +821 -0
  724. package/dist/workflows/dag/governance-constants.js +5 -0
  725. package/dist/workflows/dag/governance-profile.js +413 -0
  726. package/dist/workflows/dag/index.js +6 -0
  727. package/dist/workflows/dag/init-hybrid.js +7345 -0
  728. package/dist/workflows/dag/interrupt-request.js +559 -0
  729. package/dist/workflows/dag/knowledge-curator.js +165 -0
  730. package/dist/workflows/dag/l5-report-metrics.js +33 -0
  731. package/dist/workflows/dag/lifecycle.js +855 -0
  732. package/dist/workflows/dag/liveness-policy.js +251 -0
  733. package/dist/workflows/dag/node-execution.js +1081 -0
  734. package/dist/workflows/dag/observer-compose.js +52 -0
  735. package/dist/workflows/dag/output-protocol.js +356 -0
  736. package/dist/workflows/dag/project-governance-context.js +514 -0
  737. package/dist/workflows/dag/prompt-source.js +88 -0
  738. package/dist/workflows/dag/prompt.js +348 -0
  739. package/dist/workflows/dag/reconcile-run.js +145 -0
  740. package/dist/workflows/dag/reconcile-tasks.js +404 -0
  741. package/dist/workflows/dag/recovery-recommendation.js +329 -0
  742. package/dist/workflows/dag/repair-artifact.js +363 -0
  743. package/dist/workflows/dag/report.js +1229 -0
  744. package/dist/workflows/dag/rerun-plan.js +614 -0
  745. package/dist/workflows/dag/rerun-run.js +556 -0
  746. package/dist/workflows/dag/rerun-task.js +370 -0
  747. package/dist/workflows/dag/retry-policy.js +263 -0
  748. package/dist/workflows/dag/run-store.js +41 -0
  749. package/dist/workflows/dag/runner.js +1069 -0
  750. package/dist/workflows/dag/runtime-contract.js +87 -0
  751. package/dist/workflows/dag/runtime.js +5 -0
  752. package/dist/workflows/dag/scheduler.js +497 -0
  753. package/dist/workflows/dag/sdd-embedded.js +128 -0
  754. package/dist/workflows/dag/skill-instructions.js +478 -0
  755. package/dist/workflows/dag/skill-snapshot.js +553 -0
  756. package/dist/workflows/dag/skills.js +41 -0
  757. package/dist/workflows/dag/spec.js +3 -0
  758. package/dist/workflows/dag/task-contract-binding.js +138 -0
  759. package/dist/workflows/dag/task-demand-routing.js +1 -0
  760. package/dist/workflows/dag/topo.js +30 -0
  761. package/dist/workflows/dag/types.js +1145 -0
  762. package/dist/workflows/dag/upstream-artifacts.js +98 -0
  763. package/dist/workflows/dag/validate.js +998 -0
  764. package/dist/workflows/dag/workspace-checkpoint.js +186 -0
  765. package/dist/workflows/dynamic/artifacts.js +65 -0
  766. package/dist/workflows/dynamic/compile.js +371 -0
  767. package/dist/workflows/dynamic/compileTypes.js +1 -0
  768. package/dist/workflows/dynamic/errors.js +5 -0
  769. package/dist/workflows/dynamic/index.js +7 -0
  770. package/dist/workflows/dynamic/profiles.js +156 -0
  771. package/dist/workflows/dynamic/spec.js +115 -0
  772. package/dist/workflows/dynamic/validate.js +275 -0
  773. package/dist/workflows/loop/actions/dag-action.js +129 -0
  774. package/dist/workflows/loop/actions/pi-review.js +267 -0
  775. package/dist/workflows/loop/actions/shared.js +157 -0
  776. package/dist/workflows/loop/actions/shell-verify.js +82 -0
  777. package/dist/workflows/loop/actions/types.js +1 -0
  778. package/dist/workflows/loop/actions/workflow-action.js +255 -0
  779. package/dist/workflows/loop/actions.js +160 -0
  780. package/dist/workflows/loop/benchmark.js +510 -0
  781. package/dist/workflows/loop/closeout.js +135 -0
  782. package/dist/workflows/loop/context.js +47 -0
  783. package/dist/workflows/loop/events.js +26 -0
  784. package/dist/workflows/loop/hash.js +32 -0
  785. package/dist/workflows/loop/index.js +8 -0
  786. package/dist/workflows/loop/paths.js +17 -0
  787. package/dist/workflows/loop/policy/auto-policy.js +112 -0
  788. package/dist/workflows/loop/policy/path-patterns.js +13 -0
  789. package/dist/workflows/loop/rounds.js +81 -0
  790. package/dist/workflows/loop/signals.js +52 -0
  791. package/dist/workflows/loop/state.js +116 -0
  792. package/dist/workflows/loop/templates.js +54 -0
  793. package/dist/workflows/loop/types.js +28 -0
  794. package/docs/README.md +82 -0
  795. package/docs/architecture/README.md +35 -0
  796. package/docs/architecture/dag-execution.md +165 -0
  797. package/docs/architecture/evolution.md +108 -0
  798. package/docs/architecture/facts-and-state.md +74 -0
  799. package/docs/architecture/runtime-boundaries.md +226 -0
  800. package/docs/architecture/system-overview.md +100 -0
  801. package/docs/architecture/worker-and-feature.md +122 -0
  802. package/docs/governance/README.md +18 -0
  803. package/docs/governance/harness-methodology-debugging.md +177 -0
  804. package/docs/governance/harness-methodology-tdd.md +130 -0
  805. package/docs/governance/harness-methodology-verification.md +27 -0
  806. package/docs/init-surface.manifest.json +358 -0
  807. package/docs/operations/README.md +15 -0
  808. package/docs/operations/local-development-environment.md +77 -0
  809. package/docs/skills/README.md +7 -0
  810. package/docs/skills/vetted-skill-registry.md +49 -0
  811. package/docs/templates/README.md +67 -0
  812. package/docs/templates/adr.md +60 -0
  813. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -0
  814. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -0
  815. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -0
  816. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -0
  817. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -0
  818. package/docs/templates/agent-dag-report.schema.json +473 -0
  819. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -0
  820. package/docs/templates/agent-dag.base.json +190 -0
  821. package/docs/templates/agent-dag.final-verification.json +185 -0
  822. package/docs/templates/agent-dag.schema.json +590 -0
  823. package/docs/templates/agent-dag.supervised-implementation.json +640 -0
  824. package/docs/templates/agent-worker-production-readiness-checklist.md +47 -0
  825. package/docs/templates/backend-test-analysis.schema.json +37 -0
  826. package/docs/templates/backend-test-case-manifest.schema.json +223 -0
  827. package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
  828. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +41 -0
  829. package/docs/templates/backend-test-dag.json +671 -0
  830. package/docs/templates/backend-test-dag.retrospect.prompt.md +165 -0
  831. package/docs/templates/backend-test-dag.review-cases.prompt.md +36 -0
  832. package/docs/templates/backend-test-execution.schema.json +138 -0
  833. package/docs/templates/backend-test-result.schema.json +99 -0
  834. package/docs/templates/branch-merge-report.md +115 -0
  835. package/docs/templates/evaluation/agents-map-slim-v1.candidate.json +9 -0
  836. package/docs/templates/evaluation/agents-map-slim-v1.md +87 -0
  837. package/docs/templates/evaluation/agents-map-verbose-v0.candidate.json +9 -0
  838. package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -0
  839. package/docs/templates/evaluation/campaign-budget-v1.json +12 -0
  840. package/docs/templates/evaluation/campaign-dogfood-v0.json +24 -0
  841. package/docs/templates/evaluation/campaign-evidence-v1.json +44 -0
  842. package/docs/templates/evaluation/context-policy-baseline-v1.json +17 -0
  843. package/docs/templates/evaluation/context-policy-role-specialized-v1.json +28 -0
  844. package/docs/templates/evaluation/corpus-dogfood-v0.manifest.json +118 -0
  845. package/docs/templates/evaluation/matrix-dag-dry-run-v1.json +21 -0
  846. package/docs/templates/evaluation/matrix-fixture-v1.json +10 -0
  847. package/docs/templates/evaluation/private-verifier-dogfood-v0.json +16 -0
  848. package/docs/templates/exec-plan.md +66 -0
  849. package/docs/templates/feature-spec.md +53 -0
  850. package/docs/templates/frontend-design-contract.md +68 -0
  851. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
  852. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
  853. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
  854. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
  855. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
  856. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
  857. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
  858. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
  859. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
  860. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
  861. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
  862. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
  863. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
  864. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
  865. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
  866. package/docs/templates/frontend-eval/metrics.md +138 -0
  867. package/docs/templates/frontend-eval/smoke-targets.md +53 -0
  868. package/docs/templates/frontend-implementation-contract.schema.json +35 -0
  869. package/docs/templates/frontend-task-constraints.md +62 -0
  870. package/docs/templates/frontend-task-requirement.md +104 -0
  871. package/docs/templates/frontend-test-case-checklist.md +44 -0
  872. package/docs/templates/frontend-test-case.example.md +143 -0
  873. package/docs/templates/frontend-test-dag.generate-cases.prompt.md +63 -0
  874. package/docs/templates/frontend-test-dag.json +409 -0
  875. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +21 -0
  876. package/docs/templates/frontend-test-dag.retrospect.prompt.md +5 -0
  877. package/docs/templates/frontend-test-dag.review-cases.prompt.md +5 -0
  878. package/docs/templates/frontend-test-dag.review-execution.prompt.md +3 -0
  879. package/docs/templates/frontend-test-standard-scenarios.v1.json +159 -0
  880. package/docs/templates/harness.schema.json +351 -0
  881. package/docs/templates/hybrid-dag.json +188 -0
  882. package/docs/templates/init-evolution-review.md +35 -0
  883. package/docs/templates/init-managed-agents.md +157 -0
  884. package/docs/templates/interactive-ui-round2-experiment.md +66 -0
  885. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -0
  886. package/docs/templates/knowledge-sync-dag.json +178 -0
  887. package/docs/templates/knowledge-sync-draft.schema.json +71 -0
  888. package/docs/templates/product-line/AGENTS.md +9 -0
  889. package/docs/templates/product-line/README.md +42 -0
  890. package/docs/templates/product-line/acceptance.yaml +23 -0
  891. package/docs/templates/product-line/closeout.yaml +9 -0
  892. package/docs/templates/product-line/design.md +13 -0
  893. package/docs/templates/product-line/feature-scaffold-batch.example.yaml +31 -0
  894. package/docs/templates/product-line/feature.yaml +11 -0
  895. package/docs/templates/product-line/links.md +10 -0
  896. package/docs/templates/product-line/requirement.md +17 -0
  897. package/docs/templates/product-line/scaffold-samples/README.md +36 -0
  898. package/docs/templates/product-line/scaffold-samples/backend-only/acceptance.yaml +16 -0
  899. package/docs/templates/product-line/scaffold-samples/backend-only/design.md +14 -0
  900. package/docs/templates/product-line/scaffold-samples/backend-only/feature.yaml +14 -0
  901. package/docs/templates/product-line/scaffold-samples/backend-only/requirement.md +6 -0
  902. package/docs/templates/product-line/scaffold-samples/backend-only/tasks/BE-IMPL-001.yaml +83 -0
  903. package/docs/templates/product-line/scaffold-samples/backend-only/tasks/task-graph.yaml +14 -0
  904. package/docs/templates/product-line/scaffold-samples/fe-with-api/acceptance.yaml +18 -0
  905. package/docs/templates/product-line/scaffold-samples/fe-with-api/design.md +18 -0
  906. package/docs/templates/product-line/scaffold-samples/fe-with-api/feature.yaml +14 -0
  907. package/docs/templates/product-line/scaffold-samples/fe-with-api/requirement.md +6 -0
  908. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/BE-IMPL-001.yaml +84 -0
  909. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/CONTRACT-001.yaml +84 -0
  910. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/FE-IMPL-001.yaml +86 -0
  911. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/task-graph.yaml +40 -0
  912. package/docs/templates/product-line/scaffold-samples/frontend-only/acceptance.yaml +16 -0
  913. package/docs/templates/product-line/scaffold-samples/frontend-only/design.md +14 -0
  914. package/docs/templates/product-line/scaffold-samples/frontend-only/feature.yaml +14 -0
  915. package/docs/templates/product-line/scaffold-samples/frontend-only/requirement.md +6 -0
  916. package/docs/templates/product-line/scaffold-samples/frontend-only/tasks/FE-IMPL-001.yaml +85 -0
  917. package/docs/templates/product-line/scaffold-samples/frontend-only/tasks/task-graph.yaml +14 -0
  918. package/docs/templates/product-line/task-graph.yaml +23 -0
  919. package/docs/templates/product-line/task.yaml +68 -0
  920. package/docs/templates/product-line/test-plan.md +7 -0
  921. package/docs/templates/production-readiness-checklist.md +57 -0
  922. package/docs/templates/progress-log.md +27 -0
  923. package/docs/templates/project-start-checklist.md +9 -0
  924. package/docs/templates/qa-report.md +48 -0
  925. package/docs/templates/sprint-contract.md +29 -0
  926. package/docs/templates/worker-dogfood-evidence.md +80 -0
  927. package/docs/templates/worker-dogfood-setup.md +68 -0
  928. package/examples/decision-gate-agent-dag.json +177 -0
  929. package/examples/example-dag.json +46 -0
  930. package/examples/hybrid-loop-agent-dag.json +189 -0
  931. package/examples/l5-report-coms-process-definition.html +322 -0
  932. package/examples/l5-report-demo.html +79 -0
  933. package/harness.json +90 -0
  934. package/package.json +98 -0
  935. package/scripts/kb-bootstrap-init-skeleton.sh +241 -0
  936. package/scripts/kb-graph-incremental-prepare.mjs +386 -0
  937. package/scripts/kb-graph-materialize.mjs +105 -0
  938. package/scripts/kb-graph-promote.mjs +164 -0
  939. package/scripts/kb-query.mjs +554 -0
  940. package/skills/agent-worker/SKILL.md +48 -0
  941. package/skills/agent-worker/references/agent-worker-operator.md +159 -0
  942. package/skills/ai-engineering-context/SKILL.md +48 -0
  943. package/skills/analyze-product-dependencies/SKILL.md +108 -0
  944. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +35 -0
  945. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +38 -0
  946. package/skills/analyze-product-dependencies/references/example.md +76 -0
  947. package/skills/analyze-product-dependencies/references/forward-test-cases.md +110 -0
  948. package/skills/analyze-product-dependencies/references/input-contract.md +34 -0
  949. package/skills/analyze-product-dependencies/references/kb-integration.md +64 -0
  950. package/skills/analyze-product-dependencies/references/scouting-rules.md +76 -0
  951. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +460 -0
  952. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +194 -0
  953. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +200 -0
  954. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +90 -0
  955. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +138 -0
  956. package/skills/analyze-product-requirements/SKILL.md +145 -0
  957. package/skills/analyze-product-requirements/references/acceptance-criteria.md +87 -0
  958. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +70 -0
  959. package/skills/analyze-product-requirements/references/example.md +104 -0
  960. package/skills/analyze-product-requirements/references/forward-test-cases.md +144 -0
  961. package/skills/analyze-product-requirements/references/kb-integration.md +56 -0
  962. package/skills/analyze-product-requirements/references/product-analysis-schema.md +41 -0
  963. package/skills/analyze-product-requirements/references/product-requirement-schema.md +42 -0
  964. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +66 -0
  965. package/skills/analyze-product-requirements/scripts/compute-source-identity.mjs +35 -0
  966. package/skills/analyze-product-requirements/scripts/test-validators.mjs +501 -0
  967. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +100 -0
  968. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +109 -0
  969. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +109 -0
  970. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +175 -0
  971. package/skills/browser-tools/SKILL.md +196 -0
  972. package/skills/browser-tools/browser-content.js +103 -0
  973. package/skills/browser-tools/browser-cookies.js +35 -0
  974. package/skills/browser-tools/browser-eval.js +53 -0
  975. package/skills/browser-tools/browser-hn-scraper.js +108 -0
  976. package/skills/browser-tools/browser-nav.js +44 -0
  977. package/skills/browser-tools/browser-pick.js +162 -0
  978. package/skills/browser-tools/browser-screenshot.js +34 -0
  979. package/skills/browser-tools/browser-start.js +86 -0
  980. package/skills/browser-tools/package-lock.json +2556 -0
  981. package/skills/browser-tools/package.json +19 -0
  982. package/skills/code-review-core/SKILL.md +20 -0
  983. package/skills/codebase-scout/SKILL.md +19 -0
  984. package/skills/frontend-bounded-implement/SKILL.md +53 -0
  985. package/skills/frontend-design-review/SKILL.md +79 -0
  986. package/skills/frontend-design-review/references/review-checklist.md +43 -0
  987. package/skills/frontend-implementation/SKILL.md +52 -0
  988. package/skills/frontend-implementation/references/code-standards.md +33 -0
  989. package/skills/frontend-implementation/references/design-spec.md +56 -0
  990. package/skills/frontend-implementation/references/node-contracts.md +31 -0
  991. package/skills/frontend-review/SKILL.md +55 -0
  992. package/skills/frontend-review/references/review-findings.md +50 -0
  993. package/skills/frontend-verification/SKILL.md +61 -0
  994. package/skills/frontend-verification/references/verification-checklist.md +48 -0
  995. package/skills/grill-me/SKILL.md +10 -0
  996. package/skills/grill-with-docs/SKILL.md +80 -0
  997. package/skills/grill-with-docs/adr-format.md +58 -0
  998. package/skills/grill-with-docs/context-format.md +52 -0
  999. package/skills/init-capability-evolution/SKILL.md +70 -0
  1000. package/skills/local-jacoco-coverage/SKILL.md +281 -0
  1001. package/skills/local-jacoco-coverage/references/requirement-to-source-mapping.md +85 -0
  1002. package/skills/local-jacoco-coverage/references/runtime-alignment.md +106 -0
  1003. package/skills/local-jacoco-coverage/scripts/run-coverage-analysis.sh +148 -0
  1004. package/skills/local-jacoco-coverage/scripts/start-jacoco-agent.sh +110 -0
  1005. package/skills/loop-agent/SKILL.md +68 -0
  1006. package/skills/loop-agent/references/README.md +67 -0
  1007. package/skills/loop-agent/references/command-reference.md +645 -0
  1008. package/skills/loop-agent/references/docs-converge.md +126 -0
  1009. package/skills/loop-agent/references/harness-policy.md +289 -0
  1010. package/skills/loop-agent/references/hybrid-dag.md +245 -0
  1011. package/skills/loop-agent/references/learned/README.md +21 -0
  1012. package/skills/loop-agent/references/long-running-loop.md +57 -0
  1013. package/skills/loop-agent/references/model-routing.md +45 -0
  1014. package/skills/loop-agent/references/multi-worktree.md +54 -0
  1015. package/skills/loop-agent/references/one-shot-runs.md +84 -0
  1016. package/skills/loop-agent/references/orchestrator-and-interventions.md +175 -0
  1017. package/skills/loop-agent/references/pi-prompt.md +23 -0
  1018. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -0
  1019. package/skills/loop-agent/references/post-implementation-and-patterns.md +54 -0
  1020. package/skills/loop-agent/references/source-and-plan-practice.md +169 -0
  1021. package/skills/loop-agent/references/task-workflow.md +104 -0
  1022. package/skills/loop-agent/references/verification-and-failure-handling.md +148 -0
  1023. package/skills/playwright-cli/SKILL.md +93 -0
  1024. package/skills/playwright-cli/references/element-attributes.md +23 -0
  1025. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  1026. package/skills/playwright-cli/references/request-mocking.md +87 -0
  1027. package/skills/playwright-cli/references/running-code.md +241 -0
  1028. package/skills/playwright-cli/references/session-management.md +225 -0
  1029. package/skills/playwright-cli/references/storage-state.md +275 -0
  1030. package/skills/playwright-cli/references/test-generation.md +433 -0
  1031. package/skills/playwright-cli/references/tracing.md +5 -0
  1032. package/skills/playwright-cli/references/video-recording.md +5 -0
  1033. package/skills/playwright-cli-case-generator/SKILL.md +108 -0
  1034. package/skills/requesting-code-review/SKILL.md +101 -0
  1035. package/skills/requesting-code-review/code-reviewer.md +168 -0
  1036. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  1037. package/skills/systematic-debugging/SKILL.md +312 -0
  1038. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  1039. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  1040. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  1041. package/skills/systematic-debugging/find-polluter.sh +63 -0
  1042. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  1043. package/skills/systematic-debugging/test-academic.md +14 -0
  1044. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  1045. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  1046. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  1047. package/skills/test-driven-development/SKILL.md +27 -0
  1048. package/skills/using-git-worktrees/SKILL.md +215 -0
  1049. package/skills/verification-before-completion/SKILL.md +154 -0
  1050. package/skills/webapp-testing/SKILL.md +19 -0
@@ -0,0 +1,1911 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { readFile, stat } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { z } from "zod";
7
+ import { writeTextArtifactFile } from "../../infrastructure/harness/artifact-store.js";
8
+ import { findPackageRoot } from "../../shared/package-metadata.js";
9
+ import { pathMatchesPattern } from "../../shared/git-progress.js";
10
+ import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
11
+ export const frontendNormalizationActionSchema = z.enum([
12
+ "remove-trailing-commas",
13
+ "strip-comments",
14
+ "canonicalize-requirement-ids",
15
+ "canonicalize-verification-alias",
16
+ "drop-unresolved-verification-symbols",
17
+ "inject-source-binding",
18
+ ]);
19
+ export function sha256Hex(input) {
20
+ return createHash("sha256").update(input, "utf8").digest("hex");
21
+ }
22
+ function sortKeysDeep(value) {
23
+ if (Array.isArray(value))
24
+ return value.map(sortKeysDeep);
25
+ if (value && typeof value === "object") {
26
+ const record = value;
27
+ const sorted = {};
28
+ for (const key of Object.keys(record).sort()) {
29
+ sorted[key] = sortKeysDeep(record[key]);
30
+ }
31
+ return sorted;
32
+ }
33
+ return value;
34
+ }
35
+ /** Deterministic JSON serialization: fixed key order, 2-space indent, no
36
+ * trailing newline. Shared by candidate/canonical/digest hashes so the same
37
+ * input always reproduces the same digest. */
38
+ export function serializeDeterministicJson(value) {
39
+ return JSON.stringify(sortKeysDeep(value), null, 2);
40
+ }
41
+ export function deterministicSha256(value) {
42
+ return sha256Hex(serializeDeterministicJson(value));
43
+ }
44
+ /** A contract materialization failure carries its classification plus the
45
+ * audit context collected up to the failure point, so the prewrite gate can
46
+ * persist a complete frontend-prewrite-result-v1 even when the canonical
47
+ * contract is never materialized. */
48
+ export class FrontendContractFailure extends Error {
49
+ kind;
50
+ candidateRawSha256;
51
+ candidateJsonSha256;
52
+ normalizationActions;
53
+ constructor(options) {
54
+ super(options.message);
55
+ this.name = "FrontendContractFailure";
56
+ this.kind = options.kind;
57
+ this.candidateRawSha256 = options.candidateRawSha256;
58
+ this.candidateJsonSha256 = options.candidateJsonSha256 ?? null;
59
+ this.normalizationActions = options.normalizationActions ?? [];
60
+ }
61
+ }
62
+ /** Write a JSON artifact with deterministic key order and return its sha256
63
+ * over the exact file bytes, so the canonical contract identity matches what
64
+ * downstream verify/repair/review re-read from disk. */
65
+ export async function writeDeterministicJsonArtifact(runDir, relativePath, value) {
66
+ const json = `${serializeDeterministicJson(value)}\n`;
67
+ const targetPath = path.join(runDir, relativePath);
68
+ await writeTextArtifactFile(targetPath, json);
69
+ return { path: targetPath, sha256: sha256Hex(json) };
70
+ }
71
+ /**
72
+ * Extract frozen command labels from the DAG run spec (run.json).
73
+ * The run spec is written before any node executes, so it is always available
74
+ * when the prewrite gate materializes the contract.
75
+ */
76
+ async function deriveFrozenCommandLabelsFromRun(runDir) {
77
+ const specPath = path.join(runDir, "run.json");
78
+ let raw;
79
+ try {
80
+ raw = await readFile(specPath, "utf8");
81
+ }
82
+ catch {
83
+ // run.json is guaranteed to exist in production (DAG runner writes it
84
+ // before any node executes). When absent (e.g. test fixtures), there is
85
+ // no frozen set to validate against and the check is skipped downstream.
86
+ return [];
87
+ }
88
+ let spec;
89
+ try {
90
+ spec = JSON.parse(raw);
91
+ }
92
+ catch (error) {
93
+ throw new Error(`cannot derive frozen command labels: run.json is not valid JSON at ${specPath}: ${error.message}`);
94
+ }
95
+ const labels = new Set();
96
+ for (const task of spec.tasks ?? []) {
97
+ const cmdLabels = task.shell?.verifyEvidence?.commandLabels;
98
+ if (cmdLabels) {
99
+ for (const label of cmdLabels)
100
+ labels.add(label);
101
+ }
102
+ }
103
+ return [...labels];
104
+ }
105
+ export const FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID = "frontend-implementation-contract-v1";
106
+ export const FRONTEND_IMPLEMENTATION_CONTRACT_PLAN_PATCH_SCHEMA_ID = "frontend-implementation-contract-plan-patch-v1";
107
+ /**
108
+ * Build the deterministic, run-owned portion of the frontend contract. The
109
+ * planner is allowed to fill semantic fields with an RFC 7386 merge patch but
110
+ * cannot redefine task identity, risk, or the implementation write boundary.
111
+ */
112
+ export function buildFrontendImplementationContractSkeleton(input) {
113
+ return {
114
+ schemaVersion: 1,
115
+ sourceBinding: canonicalFrontendContractSourceBinding(input.sourceBinding),
116
+ riskLevel: input.riskLevel,
117
+ targets: { files: [...input.targetFiles] },
118
+ mockApi: { productionDefaultOff: true },
119
+ };
120
+ }
121
+ /**
122
+ * Load the canonical frontend-implementation-contract-v1 JSON Schema from the
123
+ * installed loop-agent package docs/templates/ path. Package-root discovery
124
+ * works from both the source module and the compiled dist module without
125
+ * relying on CommonJS globals in the ESM runtime.
126
+ *
127
+ * Validation is fail-closed: missing file, malformed JSON, mismatched $id,
128
+ * missing additionalProperties: false, or incomplete top-level required keys
129
+ * all throw before any DAG prompt is assembled.
130
+ */
131
+ export function loadFrontendImplementationContractJsonSchema(startDir = path.dirname(fileURLToPath(import.meta.url))) {
132
+ const packageRoot = findPackageRoot(startDir);
133
+ if (!packageRoot) {
134
+ throw new Error(`cannot locate loop-agent package root from ${path.resolve(startDir)}`);
135
+ }
136
+ const schemaPath = path.join(packageRoot, "docs", "templates", "frontend-implementation-contract.schema.json");
137
+ let content;
138
+ try {
139
+ content = readFileSync(schemaPath, "utf-8");
140
+ }
141
+ catch (error) {
142
+ throw new Error(`cannot load frontend-implementation-contract.schema.json from current loop-agent package at ${schemaPath}: ${error.code ?? String(error)}`);
143
+ }
144
+ let parsed;
145
+ try {
146
+ parsed = JSON.parse(content);
147
+ }
148
+ catch (error) {
149
+ throw new Error(`frontend-implementation-contract.schema.json is not valid JSON: ${error.message}`);
150
+ }
151
+ if (parsed === null || typeof parsed !== "object") {
152
+ throw new Error("frontend-implementation-contract.schema.json root is not a JSON object");
153
+ }
154
+ const schema = parsed;
155
+ if (schema.$id !== FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID) {
156
+ throw new Error(`schema $id mismatch: expected ${FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID}, got ${String(schema.$id)}`);
157
+ }
158
+ if (schema.additionalProperties !== false) {
159
+ throw new Error("schema must have additionalProperties: false at top level");
160
+ }
161
+ const expectedRequired = [
162
+ "schemaVersion",
163
+ "sourceBinding",
164
+ "riskLevel",
165
+ "targets",
166
+ "requirements",
167
+ "uiStates",
168
+ "interactions",
169
+ "mockApi",
170
+ "designEvidence",
171
+ "verificationTargets",
172
+ "evidenceGaps",
173
+ ];
174
+ const actualRequired = Array.isArray(schema.required) ? schema.required : [];
175
+ const missing = expectedRequired.filter((key) => !actualRequired.includes(key));
176
+ if (missing.length > 0) {
177
+ throw new Error(`schema required fields missing: ${missing.join(", ")}`);
178
+ }
179
+ const properties = schema.properties && typeof schema.properties === "object"
180
+ ? schema.properties
181
+ : {};
182
+ const missingProperties = expectedRequired.filter((key) => !Object.hasOwn(properties, key));
183
+ if (missingProperties.length > 0) {
184
+ throw new Error(`schema properties missing: ${missingProperties.join(", ")}`);
185
+ }
186
+ const schemaVersion = properties.schemaVersion;
187
+ const mockApi = properties.mockApi;
188
+ const mockApiProperties = mockApi?.properties;
189
+ const productionDefaultOff = mockApiProperties?.productionDefaultOff;
190
+ if (schemaVersion?.const !== 1 || productionDefaultOff?.const !== true) {
191
+ throw new Error("schema fixed values are incomplete: schemaVersion.const must be 1 and mockApi.productionDefaultOff.const must be true");
192
+ }
193
+ return JSON.stringify(parsed);
194
+ }
195
+ const id = z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/);
196
+ const REQUIREMENT_ID_PATTERN = /^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/;
197
+ const safePath = z
198
+ .string()
199
+ .min(1)
200
+ .refine((value) => !value.startsWith("/") &&
201
+ !value.includes("\\") &&
202
+ !value.split("/").includes(".."), "must be a relative POSIX path without traversal");
203
+ const gap = z
204
+ .object({
205
+ requirementId: id.optional(),
206
+ description: z.string().min(1),
207
+ blocking: z.boolean(),
208
+ })
209
+ .strict();
210
+ /**
211
+ * Structured component/token selection contract (ADR 0016). Every user-visible
212
+ * UI purpose the plan touches must be declared here so the prewrite gate can
213
+ * deterministically cross-check that the selection comes from the frontend
214
+ * specification (openspec/ai_workspace component + theme specs) instead of
215
+ * relying on design-review's soft model judgment.
216
+ *
217
+ * - `specified`: the frontend spec mandates this component for the purpose →
218
+ * `specReference` is required (path/section/line hit).
219
+ * - `reuse-existing`: reuse an existing repo component/convention not named by
220
+ * the spec → `specReference` may be null, rationale explains the basis.
221
+ * - `new`: neither spec nor existing code fits → `specReference` must be null,
222
+ * rationale must declare the deviation (design-review approves it).
223
+ *
224
+ * `specReference` is nullable + optional because `stripNullValuesDeep` strips
225
+ * null object values before validation, so a `new` choice's `specReference:
226
+ * null` becomes an absent field; the superRefine below treats absent and null
227
+ * identically.
228
+ */
229
+ const uiComponentSpecReferenceSchema = z
230
+ .object({
231
+ path: safePath,
232
+ section: z.string(),
233
+ line: z.number().int().nullable().optional(),
234
+ })
235
+ .strict();
236
+ const uiComponentChoiceSchema = z
237
+ .object({
238
+ purpose: z.string().min(1),
239
+ component: z.string().min(1),
240
+ decision: z.enum(["specified", "reuse-existing", "new"]),
241
+ specReference: uiComponentSpecReferenceSchema.nullable().optional(),
242
+ rationale: z.string().min(1),
243
+ })
244
+ .strict();
245
+ const uiComponentChoicesSchema = z.array(uiComponentChoiceSchema);
246
+ /**
247
+ * Normalize unstable model emissions before the strict schema sees them:
248
+ * recursively drop null values (object entries and array elements). A null for
249
+ * an optional field becomes "absent" (accepted); a null for a required field
250
+ * still fails, but with a clean Required issue instead of a confusing type
251
+ * mismatch. Unknown keys and misspellings remain strict rejections.
252
+ */
253
+ function stripNullValuesDeep(value) {
254
+ if (Array.isArray(value)) {
255
+ return value
256
+ .filter((item) => item !== null)
257
+ .map((item) => stripNullValuesDeep(item));
258
+ }
259
+ if (value && typeof value === "object") {
260
+ const record = value;
261
+ const stripped = {};
262
+ for (const [key, item] of Object.entries(record)) {
263
+ if (item !== null)
264
+ stripped[key] = stripNullValuesDeep(item);
265
+ }
266
+ return stripped;
267
+ }
268
+ return value;
269
+ }
270
+ export const frontendImplementationContractSchema = z
271
+ .preprocess(stripNullValuesDeep, z
272
+ .object({
273
+ schemaVersion: z.literal(1),
274
+ sourceBinding: z
275
+ .object({
276
+ taskId: z.string().min(1),
277
+ requirementPath: safePath,
278
+ requirementSha256: z.string().regex(/^[a-f0-9]{64}$/),
279
+ referencePaths: z.array(safePath),
280
+ requirementIds: z.array(id),
281
+ })
282
+ .strict(),
283
+ riskLevel: z.enum(["small", "standard", "high-risk"]),
284
+ targets: z
285
+ .object({
286
+ files: z.array(safePath).min(1),
287
+ routes: z.array(z.string().startsWith("/")).optional(),
288
+ publicApiChanges: z.array(z.string().min(1)).optional(),
289
+ })
290
+ .strict(),
291
+ requirements: z.array(z
292
+ .object({
293
+ id,
294
+ expectedOutcome: z.string().min(1),
295
+ implementationTargets: z.array(safePath),
296
+ verificationTargetIds: z.array(z.string().min(1)),
297
+ evidenceGap: gap.optional(),
298
+ })
299
+ .strict()).min(1),
300
+ uiStates: z.array(z
301
+ .object({
302
+ name: z.string().min(1),
303
+ applicable: z.boolean(),
304
+ expectedBehavior: z.string().min(1).optional(),
305
+ implementationTargets: z.array(safePath).optional(),
306
+ verificationTargetIds: z.array(z.string().min(1)).optional(),
307
+ notApplicableReason: z.string().min(1).optional(),
308
+ })
309
+ .strict()),
310
+ interactions: z.array(z
311
+ .object({
312
+ name: z.string().min(1),
313
+ trigger: z.string().min(1),
314
+ expectedBehavior: z.string().min(1),
315
+ implementationTargets: z.array(safePath),
316
+ verificationTargetIds: z.array(z.string().min(1)),
317
+ })
318
+ .strict()),
319
+ mockApi: z
320
+ .object({
321
+ strategy: z.enum([
322
+ "native",
323
+ "browser-intercept",
324
+ "request-adapter",
325
+ "not-needed",
326
+ ]),
327
+ productionDefaultOff: z.literal(true),
328
+ activation: z.preprocess((value) => value === "" || value === null || value === undefined
329
+ ? "explicit activation boundary"
330
+ : value, z.string().min(1)),
331
+ endpoints: z.array(z
332
+ .object({
333
+ method: z.enum([
334
+ "GET",
335
+ "POST",
336
+ "PUT",
337
+ "PATCH",
338
+ "DELETE",
339
+ "HEAD",
340
+ "OPTIONS",
341
+ ]),
342
+ path: z.string().startsWith("/"),
343
+ // Models occasionally emit an empty fixture when Mock is
344
+ // intentionally not needed. Treat it like an omitted optional
345
+ // field; active Mock strategies still fail the refinement below.
346
+ fixture: z.preprocess((value) => (value === "" || value === null ? undefined : value), safePath.optional()),
347
+ consumer: safePath.optional(),
348
+ })
349
+ .strict()),
350
+ })
351
+ .strict(),
352
+ designEvidence: z
353
+ .object({
354
+ source: z.string().min(1),
355
+ paths: z.array(safePath),
356
+ conflicts: z.array(z.string().min(1)),
357
+ })
358
+ .strict(),
359
+ verificationTargets: z.array(z
360
+ .object({
361
+ id: z.string().min(1),
362
+ type: z.enum(["static", "unit", "component", "integration", "mock"]),
363
+ commandLabel: z.string().min(1),
364
+ file: safePath,
365
+ symbol: z.preprocess((value) => (value === "" || value === null ? undefined : value), z.string().min(1).optional()),
366
+ requirementIds: z.array(id),
367
+ uiStates: z.array(z.string().min(1)),
368
+ })
369
+ .strict()).min(1),
370
+ evidenceGaps: z.array(gap),
371
+ implementationSteps: z.array(z.string().min(1)).optional(),
372
+ stylingStrategy: z.string().min(1).optional(),
373
+ uiComponentChoices: uiComponentChoicesSchema.optional(),
374
+ dependencyPolicy: z.string().min(1).optional(),
375
+ residualRisks: z.array(z.string().min(1)).optional(),
376
+ realIntegrationGap: z.string().min(1).optional(),
377
+ })
378
+ .strict()
379
+ .superRefine((value, ctx) => {
380
+ const verificationIds = value.verificationTargets.map((target) => target.id);
381
+ if (new Set(verificationIds).size !== verificationIds.length)
382
+ ctx.addIssue({
383
+ code: "custom",
384
+ message: "duplicate verification target id",
385
+ path: ["verificationTargets"],
386
+ });
387
+ const known = new Set(value.sourceBinding.requirementIds);
388
+ const stateNames = new Set(value.uiStates.map((state) => state.name));
389
+ for (const target of value.verificationTargets) {
390
+ // Verification evidence may point to read-only project files such as
391
+ // tsconfig.json or a test entrypoint; implementation targets remain
392
+ // governed by targets.files and the writer writeSet.
393
+ for (const requirementId of target.requirementIds) {
394
+ if (!known.has(requirementId)) {
395
+ ctx.addIssue({
396
+ code: "custom",
397
+ message: `verification target references unknown requirement ${requirementId}`,
398
+ path: ["verificationTargets"],
399
+ });
400
+ }
401
+ }
402
+ for (const stateName of target.uiStates) {
403
+ if (!stateNames.has(stateName)) {
404
+ ctx.addIssue({
405
+ code: "custom",
406
+ message: `verification target references unknown UI state ${stateName}`,
407
+ path: ["verificationTargets"],
408
+ });
409
+ }
410
+ }
411
+ }
412
+ for (const requirement of value.requirements) {
413
+ if (!known.has(requirement.id))
414
+ ctx.addIssue({
415
+ code: "custom",
416
+ message: "unknown requirement id",
417
+ path: ["requirements"],
418
+ });
419
+ if ((!requirement.implementationTargets.length ||
420
+ !requirement.verificationTargetIds.length) &&
421
+ !requirement.evidenceGap)
422
+ ctx.addIssue({
423
+ code: "custom",
424
+ message: "requirement needs targets or evidenceGap",
425
+ path: ["requirements"],
426
+ });
427
+ for (const targetId of requirement.verificationTargetIds)
428
+ if (!verificationIds.includes(targetId))
429
+ ctx.addIssue({
430
+ code: "custom",
431
+ message: `unknown verification target ${targetId}`,
432
+ path: ["requirements"],
433
+ });
434
+ }
435
+ for (const state of value.uiStates) {
436
+ if (state.applicable &&
437
+ (!state.expectedBehavior ||
438
+ !state.implementationTargets?.length ||
439
+ !state.verificationTargetIds?.length))
440
+ ctx.addIssue({
441
+ code: "custom",
442
+ message: "applicable UI state requires behavior, implementation, and verification",
443
+ path: ["uiStates"],
444
+ });
445
+ if (!state.applicable && !state.notApplicableReason)
446
+ ctx.addIssue({
447
+ code: "custom",
448
+ message: "non-applicable UI state requires reason",
449
+ path: ["uiStates"],
450
+ });
451
+ }
452
+ for (const [index, choice] of (value.uiComponentChoices ?? []).entries()) {
453
+ if (choice.decision === "specified") {
454
+ if (!choice.specReference || !choice.specReference.path) {
455
+ ctx.addIssue({
456
+ code: "custom",
457
+ message: "specified component choice requires a non-empty specReference.path",
458
+ path: ["uiComponentChoices", index, "specReference"],
459
+ });
460
+ }
461
+ }
462
+ else if (choice.decision === "new") {
463
+ if (choice.specReference) {
464
+ ctx.addIssue({
465
+ code: "custom",
466
+ message: "new component choice must not carry a specReference",
467
+ path: ["uiComponentChoices", index, "specReference"],
468
+ });
469
+ }
470
+ }
471
+ }
472
+ if (value.mockApi.strategy !== "not-needed") {
473
+ if (value.mockApi.endpoints.length === 0) {
474
+ ctx.addIssue({
475
+ code: "custom",
476
+ message: "Mock strategy requires at least one endpoint",
477
+ path: ["mockApi", "endpoints"],
478
+ });
479
+ }
480
+ value.mockApi.endpoints.forEach((endpoint, index) => {
481
+ if (!endpoint.fixture) {
482
+ ctx.addIssue({
483
+ code: "custom",
484
+ message: "Mock endpoint requires a fixture path",
485
+ path: ["mockApi", "endpoints", index, "fixture"],
486
+ });
487
+ }
488
+ if (!endpoint.consumer) {
489
+ ctx.addIssue({
490
+ code: "custom",
491
+ message: "Mock endpoint requires a consumer path",
492
+ path: ["mockApi", "endpoints", index, "consumer"],
493
+ });
494
+ }
495
+ for (const [field, file] of [["fixture", endpoint.fixture], ["consumer", endpoint.consumer]]) {
496
+ if (file && !value.targets.files.some((pattern) => pathMatchesPattern(file, pattern))) {
497
+ ctx.addIssue({
498
+ code: "custom",
499
+ message: `Mock ${field} is outside contract targets: ${file}`,
500
+ path: ["mockApi", "endpoints", index, field],
501
+ });
502
+ }
503
+ }
504
+ });
505
+ }
506
+ }));
507
+ function isPlainObject(value) {
508
+ return value !== null && typeof value === "object" && !Array.isArray(value);
509
+ }
510
+ /**
511
+ * Apply an RFC 7386 merge-patch on a target contract without mutating it.
512
+ * `null` removes the key; plain objects merge recursively when the target value
513
+ * is also a plain object; arrays and scalars replace the whole value. The
514
+ * result is re-validated by analyzeFrontendImplementationContract before it is
515
+ * ever materialized, so a patch that removes a required field still fails
516
+ * closed at the gate.
517
+ */
518
+ export function applyFrontendContractMergePatch(target, patch) {
519
+ if (!isPlainObject(patch)) {
520
+ throw new Error("frontend contract patch must be a JSON object");
521
+ }
522
+ const merge = (base, delta) => {
523
+ if (delta === null)
524
+ return undefined;
525
+ if (!isPlainObject(delta))
526
+ return delta;
527
+ const result = isPlainObject(base)
528
+ ? { ...base }
529
+ : {};
530
+ for (const [key, value] of Object.entries(delta)) {
531
+ if (value === null) {
532
+ delete result[key];
533
+ }
534
+ else if (isPlainObject(value) && isPlainObject(result[key])) {
535
+ result[key] = merge(result[key], value);
536
+ }
537
+ else {
538
+ result[key] = value;
539
+ }
540
+ }
541
+ return result;
542
+ };
543
+ return merge(target, patch);
544
+ }
545
+ export function applyFrontendImplementationContractPatch(target, patch) {
546
+ return applyFrontendContractMergePatch(target, patch);
547
+ }
548
+ export async function assertFrontendSourceBindingFresh(input) {
549
+ for (const source of input.binding.sources) {
550
+ const absolute = resolveDagTaskSourcePath({
551
+ workspaceRoot: input.workspaceRoot,
552
+ taskId: input.binding.taskId,
553
+ sourcePath: source.path,
554
+ });
555
+ let content;
556
+ try {
557
+ content = await readFile(absolute);
558
+ const info = await stat(absolute);
559
+ if (!info.isFile())
560
+ throw new Error("not a regular file");
561
+ }
562
+ catch (error) {
563
+ throw new Error(`frontend source binding file unavailable: ${source.path}: ${error instanceof Error ? error.message : String(error)}`);
564
+ }
565
+ const actual = createHash("sha256").update(content).digest("hex");
566
+ if (actual !== source.sha256) {
567
+ throw new Error(`frontend source binding is stale: ${source.path}`);
568
+ }
569
+ }
570
+ }
571
+ const SECRET_KEY = /(?:password|passwd|secret|token|api[_-]?key|private[_-]?key|credential|authorization)/i;
572
+ const SECRET_VALUE = /(?:-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:sk|ghp|github_pat|xox[baprs]|AKIA)[-_A-Za-z0-9]{12,}\b)/;
573
+ function secretIssues(value, at = "$", issues = []) {
574
+ if (typeof value === "string" && SECRET_VALUE.test(value))
575
+ issues.push(`${at}: secret-like value is forbidden`);
576
+ if (Array.isArray(value))
577
+ value.forEach((child, index) => secretIssues(child, `${at}[${index}]`, issues));
578
+ else if (value && typeof value === "object")
579
+ for (const [key, child] of Object.entries(value)) {
580
+ if (SECRET_KEY.test(key))
581
+ issues.push(`${at}.${key}: secret-shaped field name is forbidden`);
582
+ secretIssues(child, `${at}.${key}`, issues);
583
+ }
584
+ return issues;
585
+ }
586
+ function extractFrontendImplementationJsonWithAudit(text) {
587
+ const trimmed = text.trim();
588
+ const actions = [];
589
+ const recordAction = (action) => {
590
+ if (!actions.includes(action))
591
+ actions.push(action);
592
+ };
593
+ const parse = (source) => {
594
+ try {
595
+ return JSON.parse(source);
596
+ }
597
+ catch (error) {
598
+ // Apply only deterministic, structure-preserving repairs before the
599
+ // quote repair below. These are common JSONC/model formatting defects,
600
+ // not a general parser relaxation.
601
+ const withoutComments = source
602
+ .replace(/\/\*[\s\S]*?\*\//g, "")
603
+ .replace(/^\s*\/\/.*$/gm, "");
604
+ const withoutTrailingCommas = withoutComments.replace(/,\s*([}\]])/g, "$1");
605
+ if (withoutComments !== source)
606
+ recordAction("strip-comments");
607
+ if (withoutTrailingCommas !== withoutComments)
608
+ recordAction("remove-trailing-commas");
609
+ try {
610
+ return JSON.parse(withoutTrailingCommas);
611
+ }
612
+ catch {
613
+ // Continue with the narrower embedded-quote repair.
614
+ }
615
+ // Models sometimes put ordinary ASCII quotes inside a JSON string
616
+ // (for example: `reason: "支持..."`). Repair only quotes that are
617
+ // clearly not structural: a closing quote is followed by JSON
618
+ // punctuation, while an embedded quote is followed by content.
619
+ let repaired = "";
620
+ let inString = false;
621
+ let escaped = false;
622
+ for (let index = 0; index < source.length; index += 1) {
623
+ const character = source[index];
624
+ if (character !== '"') {
625
+ repaired += character;
626
+ if (inString && character === "\\" && !escaped)
627
+ escaped = true;
628
+ else
629
+ escaped = false;
630
+ continue;
631
+ }
632
+ if (escaped) {
633
+ repaired += character;
634
+ escaped = false;
635
+ continue;
636
+ }
637
+ if (!inString) {
638
+ inString = true;
639
+ repaired += character;
640
+ continue;
641
+ }
642
+ const next = source.slice(index + 1).trimStart()[0];
643
+ if ([",", "}", "]", ":"].includes(next ?? "") || source.slice(index + 1).trim() === "") {
644
+ inString = false;
645
+ repaired += character;
646
+ }
647
+ else {
648
+ repaired += "\\\"";
649
+ }
650
+ }
651
+ try {
652
+ return JSON.parse(repaired);
653
+ }
654
+ catch {
655
+ throw error;
656
+ }
657
+ }
658
+ };
659
+ const isContract = (candidate) => {
660
+ const record = asRecord(candidate);
661
+ return (record?.schemaVersion === 1 &&
662
+ asRecord(record.targets) !== null &&
663
+ Array.isArray(record.requirements) &&
664
+ Array.isArray(record.verificationTargets));
665
+ };
666
+ if (trimmed.startsWith("{") && trimmed.endsWith("}"))
667
+ return { value: parse(trimmed), actions };
668
+ const balancedObjects = [];
669
+ for (let start = 0; start < trimmed.length; start += 1) {
670
+ if (trimmed[start] !== "{")
671
+ continue;
672
+ let depth = 0;
673
+ let inString = false;
674
+ let escaped = false;
675
+ for (let index = start; index < trimmed.length; index += 1) {
676
+ const character = trimmed[index];
677
+ if (inString) {
678
+ if (escaped)
679
+ escaped = false;
680
+ else if (character === "\\")
681
+ escaped = true;
682
+ else if (character === '"')
683
+ inString = false;
684
+ continue;
685
+ }
686
+ if (character === '"')
687
+ inString = true;
688
+ else if (character === "{")
689
+ depth += 1;
690
+ else if (character === "}" && --depth === 0) {
691
+ try {
692
+ const candidate = parse(trimmed.slice(start, index + 1));
693
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate))
694
+ balancedObjects.push(candidate);
695
+ }
696
+ catch {
697
+ // Continue scanning for a later complete JSON object.
698
+ }
699
+ break;
700
+ }
701
+ }
702
+ }
703
+ const balancedContracts = balancedObjects.filter(isContract);
704
+ if (balancedContracts.length === 1)
705
+ return { value: balancedContracts[0], actions };
706
+ if (balancedObjects.length === 1)
707
+ return { value: balancedObjects[0], actions };
708
+ const blocks = [...trimmed.matchAll(/```json\s*\n([\s\S]*?)\n```/gi)];
709
+ if (blocks.length === 0)
710
+ throw new Error("output must contain exactly one fenced json object");
711
+ const candidates = [];
712
+ for (const block of blocks) {
713
+ try {
714
+ const candidate = parse(block[1]);
715
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate))
716
+ candidates.push(candidate);
717
+ }
718
+ catch {
719
+ // Ignore incomplete model scratch blocks. A later complete contract
720
+ // block may still be deterministically recoverable.
721
+ }
722
+ }
723
+ const contractCandidates = candidates.filter(isContract);
724
+ if (contractCandidates.length === 1)
725
+ return { value: contractCandidates[0], actions };
726
+ if (candidates.length === 1)
727
+ return { value: candidates[0], actions };
728
+ if (candidates.length === 0)
729
+ throw new Error("output must contain exactly one valid fenced json object (found 0)");
730
+ throw new Error(`output must contain exactly one valid frontend contract json object (found ${contractCandidates.length || candidates.length})`);
731
+ }
732
+ export function extractFrontendImplementationJson(text) {
733
+ return extractFrontendImplementationJsonWithAudit(text).value;
734
+ }
735
+ /**
736
+ * Build the authoritative frontend-implementation-contract sourceBinding from
737
+ * the DAG-owned binding. Model JSON must not invent taskId/path/sha256; the
738
+ * gate overwrites whatever the model emitted so identity cannot drift.
739
+ */
740
+ export function canonicalFrontendContractSourceBinding(binding) {
741
+ const requirement = binding.sources.find((source) => source.kind === "requirement");
742
+ if (!requirement)
743
+ throw new Error("frontend implementation contract gate requires a requirement source in DAG sourceBinding");
744
+ return {
745
+ taskId: binding.taskId,
746
+ requirementPath: requirement.path,
747
+ requirementSha256: requirement.sha256,
748
+ referencePaths: binding.sources
749
+ .filter((source) => source.kind === "reference")
750
+ .map((source) => source.path)
751
+ .sort(),
752
+ requirementIds: binding.requirementIds.map(canonicalizeRequirementId),
753
+ };
754
+ }
755
+ function asRecord(value) {
756
+ return value && typeof value === "object" && !Array.isArray(value)
757
+ ? value
758
+ : null;
759
+ }
760
+ function asString(value) {
761
+ return typeof value === "string" ? value.trim() : "";
762
+ }
763
+ function asStringArray(value) {
764
+ if (!Array.isArray(value))
765
+ return [];
766
+ return value
767
+ .map((item) => asString(item))
768
+ .filter((item) => item.length > 0);
769
+ }
770
+ /**
771
+ * Pass structured component choices through the free-form compatibility
772
+ * branch. The strict-shape branch already spreads unknown fields, so only this
773
+ * explicit constructor needs to re-emit `uiComponentChoices`; a missing/empty
774
+ * array normalizes to undefined (absent) so the optional schema field stays
775
+ * compatible with legacy free-form contracts.
776
+ */
777
+ function normalizeUiComponentChoices(value) {
778
+ if (!Array.isArray(value) || value.length === 0)
779
+ return undefined;
780
+ return value;
781
+ }
782
+ function normalizeFrontendRoute(value) {
783
+ const route = asString(value).replaceAll("\\", "/").replace(/^\/+/, "");
784
+ if (!route || route === "." || route.split("/").includes(".."))
785
+ return null;
786
+ return `/${route}`;
787
+ }
788
+ function normalizeFrontendContractOptionalFields(value) {
789
+ const record = asRecord(value);
790
+ if (!record)
791
+ return value;
792
+ const targets = asRecord(record.targets);
793
+ const mockApi = asRecord(record.mockApi);
794
+ return {
795
+ ...record,
796
+ ...(targets
797
+ ? {
798
+ targets: {
799
+ ...targets,
800
+ routes: Array.isArray(targets.routes)
801
+ ? targets.routes.map(normalizeFrontendRoute).filter((route) => Boolean(route))
802
+ : targets.routes,
803
+ },
804
+ }
805
+ : {}),
806
+ ...(mockApi
807
+ ? {
808
+ mockApi: {
809
+ ...mockApi,
810
+ endpoints: Array.isArray(mockApi.endpoints)
811
+ ? mockApi.endpoints.map((item) => {
812
+ const endpoint = asRecord(item);
813
+ if (!endpoint)
814
+ return item;
815
+ return {
816
+ ...endpoint,
817
+ fixture: asString(endpoint.fixture) || undefined,
818
+ consumer: asString(endpoint.consumer) || undefined,
819
+ };
820
+ })
821
+ : mockApi.endpoints,
822
+ },
823
+ }
824
+ : {}),
825
+ };
826
+ }
827
+ function isRequirementId(value) {
828
+ return REQUIREMENT_ID_PATTERN.test(value);
829
+ }
830
+ /** Normalize the compact IDs models commonly emit (for example AC1) before
831
+ * strict schema validation. This keeps governance strict while making the
832
+ * boundary tolerant of presentation-only formatting differences. */
833
+ function canonicalizeRequirementId(value) {
834
+ const trimmed = value.trim().toUpperCase();
835
+ if (REQUIREMENT_ID_PATTERN.test(trimmed))
836
+ return trimmed;
837
+ const compact = /^(REQ|BR|AC)(\d+)$/.exec(trimmed);
838
+ if (compact)
839
+ return `${compact[1]}-${compact[2]}`;
840
+ return value;
841
+ }
842
+ function canonicalizeRequirementIdsInPayload(value) {
843
+ if (Array.isArray(value))
844
+ return value.map(canonicalizeRequirementIdsInPayload);
845
+ if (!value || typeof value !== "object")
846
+ return value;
847
+ const record = value;
848
+ const out = {};
849
+ for (const [key, child] of Object.entries(record)) {
850
+ if (key === "id" || key === "requirementId") {
851
+ out[key] = typeof child === "string" ? canonicalizeRequirementId(child) : child;
852
+ }
853
+ else if (key === "requirementIds" && Array.isArray(child)) {
854
+ out[key] = child.map((item) => typeof item === "string" ? canonicalizeRequirementId(item) : item);
855
+ }
856
+ else {
857
+ out[key] = canonicalizeRequirementIdsInPayload(child);
858
+ }
859
+ }
860
+ return out;
861
+ }
862
+ function canonicalizeVerificationTargetAliases(value) {
863
+ const record = asRecord(value);
864
+ if (!record || !Array.isArray(record.verificationTargets))
865
+ return value;
866
+ const targets = record.verificationTargets
867
+ .map((item) => asRecord(item))
868
+ .filter((item) => Boolean(item));
869
+ const aliases = new Map();
870
+ for (const target of targets) {
871
+ const id = asString(target.id);
872
+ const label = asString(target.commandLabel).toLowerCase();
873
+ if (!id)
874
+ continue;
875
+ aliases.set(id.toLowerCase(), id);
876
+ if (label.includes("typecheck"))
877
+ aliases.set("vt-typecheck", id);
878
+ if (label.includes("unified surface"))
879
+ aliases.set("vt-unified-surface", id);
880
+ }
881
+ const requirements = Array.isArray(record.requirements)
882
+ ? record.requirements.map((item) => {
883
+ const requirement = asRecord(item);
884
+ if (!requirement || !Array.isArray(requirement.verificationTargetIds))
885
+ return item;
886
+ return {
887
+ ...requirement,
888
+ verificationTargetIds: requirement.verificationTargetIds.map((id) => typeof id === "string" ? aliases.get(id.toLowerCase()) ?? id : id),
889
+ };
890
+ })
891
+ : record.requirements;
892
+ return { ...record, requirements };
893
+ }
894
+ /**
895
+ * Model-written evidence gaps are hypotheses, not authoritative coverage
896
+ * facts. If the plan contains a real verification target that explicitly
897
+ * names a requirement, the executor can prove that the requirement has a
898
+ * verification path even when the model forgot to wire the target into the
899
+ * requirement entry or conservatively marked its gap as blocking.
900
+ *
901
+ * Keep gaps blocking when that proof cannot be derived. This is deliberately
902
+ * narrow: it uses only sourceBinding requirement IDs and structurally present
903
+ * verification targets, and never invents a command, file, or target.
904
+ */
905
+ function deriveFrontendVerificationCoverage(value, canonicalBinding) {
906
+ const record = asRecord(value);
907
+ if (!record)
908
+ return value;
909
+ const rawTargets = Array.isArray(record.verificationTargets)
910
+ ? record.verificationTargets
911
+ : [];
912
+ const targetIds = new Set(rawTargets
913
+ .map((item) => asRecord(item))
914
+ .filter((item) => Boolean(item))
915
+ .map((item) => asString(item.id))
916
+ .filter(Boolean));
917
+ const targetIdsByRequirement = new Map();
918
+ for (const item of rawTargets) {
919
+ const target = asRecord(item);
920
+ if (!target)
921
+ continue;
922
+ const targetId = asString(target.id);
923
+ const commandLabel = asString(target.commandLabel) || asString(target.command);
924
+ const file = asString(target.file);
925
+ // A target is usable evidence only when it has an identity, command and
926
+ // file. The strict schema will validate the final shape afterwards.
927
+ if (!targetId || !commandLabel || !file)
928
+ continue;
929
+ const targetRequirementIds = asStringArray(target.requirementIds);
930
+ const inferredRequirementIds = targetRequirementIds.length > 0
931
+ ? targetRequirementIds
932
+ : rawTargets.length === 1
933
+ ? canonicalBinding.requirementIds
934
+ : [];
935
+ for (const requirementId of inferredRequirementIds) {
936
+ const canonicalId = canonicalizeRequirementId(requirementId);
937
+ if (!canonicalBinding.requirementIds.includes(canonicalId))
938
+ continue;
939
+ const ids = targetIdsByRequirement.get(canonicalId) ?? [];
940
+ if (!ids.includes(targetId))
941
+ ids.push(targetId);
942
+ targetIdsByRequirement.set(canonicalId, ids);
943
+ }
944
+ }
945
+ const provenRequirementIds = new Set(targetIdsByRequirement.keys());
946
+ const requirements = Array.isArray(record.requirements)
947
+ ? record.requirements.map((item) => {
948
+ const requirement = asRecord(item);
949
+ if (!requirement)
950
+ return item;
951
+ const requirementId = canonicalizeRequirementId(asString(requirement.id));
952
+ const inferredTargetIds = targetIdsByRequirement.get(requirementId) ?? [];
953
+ const existingTargetIds = asStringArray(requirement.verificationTargetIds)
954
+ .filter((id) => targetIds.has(id));
955
+ const verificationTargetIds = [...new Set([
956
+ ...existingTargetIds,
957
+ ...inferredTargetIds,
958
+ ])];
959
+ const evidenceGap = asRecord(requirement.evidenceGap);
960
+ const hasProof = provenRequirementIds.has(requirementId);
961
+ return {
962
+ ...requirement,
963
+ ...(verificationTargetIds.length > 0 ? { verificationTargetIds } : {}),
964
+ ...(hasProof
965
+ ? (evidenceGap ? { evidenceGap: { ...evidenceGap, blocking: false } } : {})
966
+ : {
967
+ evidenceGap: {
968
+ requirementId,
969
+ description: `No executable verification target can be derived for ${requirementId}`,
970
+ blocking: true,
971
+ },
972
+ }),
973
+ };
974
+ })
975
+ : record.requirements;
976
+ const modelEvidenceGaps = Array.isArray(record.evidenceGaps)
977
+ ? record.evidenceGaps.map((item) => {
978
+ const gap = asRecord(item);
979
+ if (!gap)
980
+ return item;
981
+ const requirementId = canonicalizeRequirementId(asString(gap.requirementId));
982
+ // Model gaps are advisory. Blocking status is reconstructed below
983
+ // from the source binding and executable verification targets.
984
+ // Drop a null/empty optional requirementId so the strict schema
985
+ // accepts model emissions (null is not a valid string).
986
+ const { requirementId: _rawRequirementId, ...rest } = gap;
987
+ return {
988
+ ...rest,
989
+ ...(requirementId ? { requirementId } : {}),
990
+ blocking: false,
991
+ };
992
+ })
993
+ : [];
994
+ const derivedBlockingGaps = canonicalBinding.requirementIds
995
+ .filter((requirementId) => !provenRequirementIds.has(requirementId))
996
+ .map((requirementId) => ({
997
+ requirementId,
998
+ description: `No executable verification target can be derived for ${requirementId}`,
999
+ blocking: true,
1000
+ }));
1001
+ return {
1002
+ ...record,
1003
+ requirements,
1004
+ evidenceGaps: [...modelEvidenceGaps, ...derivedBlockingGaps],
1005
+ };
1006
+ }
1007
+ function assertFrontendContractPathsSafe(value) {
1008
+ const record = asRecord(value);
1009
+ if (!record)
1010
+ return;
1011
+ const pathFields = ["files", "file", "implementationTargets", "fixture", "consumer"];
1012
+ for (const [key, child] of Object.entries(record)) {
1013
+ if (pathFields.includes(key) && Array.isArray(child)) {
1014
+ for (const item of child) {
1015
+ if (typeof item === "string" && (item.startsWith("/") || item.includes("\\") || item.split("/").includes("..")))
1016
+ throw new Error(`invalid-output: unsafe frontend contract path ${item}`);
1017
+ }
1018
+ }
1019
+ else if (pathFields.includes(key) && typeof child === "string" && (child.startsWith("/") || child.includes("\\") || child.split("/").includes(".."))) {
1020
+ throw new Error(`invalid-output: unsafe frontend contract path ${child}`);
1021
+ }
1022
+ if (Array.isArray(child))
1023
+ child.forEach(assertFrontendContractPathsSafe);
1024
+ else if (child && typeof child === "object")
1025
+ assertFrontendContractPathsSafe(child);
1026
+ }
1027
+ }
1028
+ function deriveContractRequirementIds(value) {
1029
+ const record = asRecord(value);
1030
+ if (!record)
1031
+ return [];
1032
+ const ids = [];
1033
+ const push = (candidate) => {
1034
+ const value = asString(candidate);
1035
+ const canonical = canonicalizeRequirementId(value);
1036
+ if (!canonical || !isRequirementId(canonical) || ids.includes(canonical))
1037
+ return;
1038
+ ids.push(canonical);
1039
+ };
1040
+ if (Array.isArray(record.requirements)) {
1041
+ for (const item of record.requirements)
1042
+ push(asRecord(item)?.id);
1043
+ }
1044
+ if (Array.isArray(record.requirementCoverage)) {
1045
+ for (const item of record.requirementCoverage)
1046
+ push(asRecord(item)?.id);
1047
+ }
1048
+ const rawVerification = Array.isArray(record.verificationTargets)
1049
+ ? record.verificationTargets
1050
+ : asRecord(record.verificationTargets)
1051
+ ? Object.values(asRecord(record.verificationTargets))
1052
+ : [];
1053
+ for (const item of rawVerification) {
1054
+ for (const requirementId of asStringArray(asRecord(item)?.requirementIds)) {
1055
+ push(requirementId);
1056
+ }
1057
+ }
1058
+ for (const item of Array.isArray(record.evidenceGaps) ? record.evidenceGaps : []) {
1059
+ push(asRecord(item)?.requirementId);
1060
+ }
1061
+ return ids;
1062
+ }
1063
+ function withDerivedRequirementIdsWhenUnscoped(binding, parsed) {
1064
+ if (binding.requirementIds.length > 0)
1065
+ return binding;
1066
+ const derivedIds = deriveContractRequirementIds(parsed);
1067
+ return derivedIds.length > 0
1068
+ ? { ...binding, requirementIds: derivedIds }
1069
+ : binding;
1070
+ }
1071
+ function looksLikeStrictFrontendContract(value) {
1072
+ const record = asRecord(value);
1073
+ if (!record)
1074
+ return false;
1075
+ return (record.schemaVersion === 1 &&
1076
+ asRecord(record.targets) !== null &&
1077
+ Array.isArray(record.requirements) &&
1078
+ Array.isArray(record.uiStates) &&
1079
+ asRecord(record.mockApi) !== null &&
1080
+ asRecord(record.designEvidence) !== null);
1081
+ }
1082
+ /**
1083
+ * Coerce common free-form plan JSON into frontend-implementation-contract-v1.
1084
+ * Near-schema payloads are left untouched so unknown-key fail-closed still holds.
1085
+ */
1086
+ export function coerceFrontendImplementationContractInput(value, canonicalBinding) {
1087
+ const rawRecord = asRecord(value);
1088
+ const rawMockApi = asRecord(rawRecord?.mockApi);
1089
+ if (rawMockApi &&
1090
+ typeof rawMockApi.strategy === "string" &&
1091
+ !["native", "browser-intercept", "request-adapter", "not-needed"].includes(rawMockApi.strategy))
1092
+ return value;
1093
+ const verificationTargetIds = rawRecord && Array.isArray(rawRecord.verificationTargets)
1094
+ ? new Set(rawRecord.verificationTargets
1095
+ .map((item) => asString(asRecord(item)?.id))
1096
+ .filter(Boolean))
1097
+ : undefined;
1098
+ const normalizedValue = rawRecord
1099
+ ? {
1100
+ ...rawRecord,
1101
+ requirements: verificationTargetIds && Array.isArray(rawRecord.requirements)
1102
+ ? rawRecord.requirements.map((item) => {
1103
+ const requirement = asRecord(item);
1104
+ if (!requirement || !Array.isArray(requirement.verificationTargetIds))
1105
+ return item;
1106
+ const filtered = requirement.verificationTargetIds.filter((id) => typeof id === "string" && verificationTargetIds.has(id));
1107
+ return {
1108
+ ...requirement,
1109
+ verificationTargetIds: filtered.length > 0 ? filtered : requirement.verificationTargetIds,
1110
+ };
1111
+ })
1112
+ : rawRecord.requirements,
1113
+ uiStates: Array.isArray(rawRecord.uiStates)
1114
+ ? rawRecord.uiStates.map((item) => {
1115
+ const state = asRecord(item);
1116
+ if (!state)
1117
+ return item;
1118
+ if (state.applicable === true &&
1119
+ (state.notApplicableReason === null ||
1120
+ (typeof state.notApplicableReason === "string" &&
1121
+ state.notApplicableReason.trim() === ""))) {
1122
+ const { notApplicableReason: _emptyReason, ...withoutEmptyReason } = state;
1123
+ return withoutEmptyReason;
1124
+ }
1125
+ if (state.applicable === false &&
1126
+ (state.expectedBehavior === null ||
1127
+ (typeof state.expectedBehavior === "string" &&
1128
+ state.expectedBehavior.trim() === ""))) {
1129
+ const { expectedBehavior: _emptyBehavior, ...withoutEmptyBehavior } = state;
1130
+ return withoutEmptyBehavior;
1131
+ }
1132
+ return item;
1133
+ })
1134
+ : rawRecord.uiStates,
1135
+ }
1136
+ : value;
1137
+ const normalizedOptionalFields = normalizeFrontendContractOptionalFields(normalizedValue);
1138
+ // A payload can have the strict top-level shape while still containing
1139
+ // empty requirement coverage arrays. Do not trust shape alone: route such
1140
+ // payloads through the compatibility normalizer so targets and verification
1141
+ // references are deterministically filled from the contract context.
1142
+ if (looksLikeStrictFrontendContract(normalizedOptionalFields)) {
1143
+ const strictRecord = asRecord(normalizedOptionalFields);
1144
+ const strictMockApi = asRecord(strictRecord?.mockApi);
1145
+ if (strictMockApi &&
1146
+ typeof strictMockApi.strategy === "string" &&
1147
+ !["native", "browser-intercept", "request-adapter", "not-needed"].includes(strictMockApi.strategy))
1148
+ return normalizedValue;
1149
+ const targetFiles = asStringArray(asRecord(strictRecord?.targets)?.files);
1150
+ const verificationIds = Array.isArray(strictRecord?.verificationTargets)
1151
+ ? strictRecord.verificationTargets.map((item) => asString(asRecord(item)?.id)).filter(Boolean)
1152
+ : [];
1153
+ if (Array.isArray(strictRecord?.requirements)) {
1154
+ const requirements = strictRecord.requirements.map((item) => {
1155
+ const requirement = asRecord(item);
1156
+ if (!requirement)
1157
+ return item;
1158
+ return {
1159
+ ...requirement,
1160
+ implementationTargets: asStringArray(requirement.implementationTargets).length > 0
1161
+ ? requirement.implementationTargets
1162
+ : targetFiles,
1163
+ verificationTargetIds: asStringArray(requirement.verificationTargetIds).length > 0
1164
+ ? requirement.verificationTargetIds
1165
+ : verificationIds,
1166
+ };
1167
+ });
1168
+ const strictMockApi = asRecord(strictRecord.mockApi);
1169
+ const strictTargetFiles = asStringArray(asRecord(strictRecord.targets)?.files);
1170
+ const mockEndpoints = Array.isArray(strictMockApi?.endpoints)
1171
+ ? strictMockApi.endpoints.map((item) => {
1172
+ const endpoint = asRecord(item);
1173
+ if (!endpoint)
1174
+ return item;
1175
+ return {
1176
+ ...endpoint,
1177
+ consumer: asString(endpoint.consumer) ||
1178
+ strictTargetFiles[0],
1179
+ };
1180
+ })
1181
+ : strictMockApi?.endpoints;
1182
+ return {
1183
+ ...strictRecord,
1184
+ requirements,
1185
+ ...(strictMockApi ? { mockApi: { ...strictMockApi, endpoints: mockEndpoints } } : {}),
1186
+ };
1187
+ }
1188
+ }
1189
+ const record = asRecord(normalizedOptionalFields);
1190
+ if (!record)
1191
+ return value;
1192
+ const implementation = asRecord(record.implementation);
1193
+ const mockApiIn = asRecord(record.mockApi) ?? {};
1194
+ const api = asRecord(record.api) ?? {};
1195
+ const component = asRecord(record.component);
1196
+ const targetFiles = [
1197
+ ...asStringArray(asRecord(record.targets)?.files),
1198
+ ...asStringArray(implementation?.targetFiles),
1199
+ ...asStringArray(record.writeSet),
1200
+ ...asStringArray(implementation?.writeSet),
1201
+ ...asStringArray(asRecord(component?.paths)?.implementation ? [asRecord(component?.paths)?.implementation] : []),
1202
+ ...asStringArray(asRecord(component?.paths)?.unitTests ? [asRecord(component?.paths)?.unitTests] : []),
1203
+ ...asStringArray(asRecord(component?.paths)?.domHelper ? [asRecord(component?.paths)?.domHelper] : []),
1204
+ ].filter((item, index, arr) => arr.indexOf(item) === index);
1205
+ if (targetFiles.length === 0) {
1206
+ throw new Error("frontend contract compatibility input must declare target files");
1207
+ }
1208
+ const riskRaw = asString(record.riskLevel) ||
1209
+ asString(record.risk) ||
1210
+ "standard";
1211
+ const riskLevel = riskRaw === "small" || riskRaw === "standard" || riskRaw === "high-risk"
1212
+ ? riskRaw
1213
+ : riskRaw.includes("high")
1214
+ ? "high-risk"
1215
+ : "standard";
1216
+ const verificationTargets = [];
1217
+ const rawVerification = Array.isArray(record.verificationTargets)
1218
+ ? record.verificationTargets
1219
+ : asRecord(record.verificationTargets)
1220
+ ? Object.entries(asRecord(record.verificationTargets)).map(([key, val]) => ({
1221
+ ...(asRecord(val) ?? {}),
1222
+ id: key,
1223
+ }))
1224
+ : [];
1225
+ for (const [index, item] of rawVerification.entries()) {
1226
+ const vt = asRecord(item) ?? {};
1227
+ const id = asString(vt.id) || `VT-${String(index + 1).padStart(3, "0")}`;
1228
+ const typeRaw = asString(vt.type) || asString(vt.phase) || "unit";
1229
+ const type = ["static", "unit", "component", "integration", "mock"].includes(typeRaw)
1230
+ ? typeRaw
1231
+ : typeRaw.includes("type")
1232
+ ? "static"
1233
+ : "unit";
1234
+ const commandLabel = asString(vt.commandLabel) ||
1235
+ asString(vt.command) ||
1236
+ (type === "static" ? "npm run typecheck" : "npm run test:unit:fe");
1237
+ const file = asString(vt.file) ||
1238
+ (Array.isArray(vt.symbols) ? targetFiles.find((p) => p.includes("__tests__")) : "") ||
1239
+ targetFiles.find((p) => p.includes("__tests__")) ||
1240
+ targetFiles[0];
1241
+ const requirementIds = asStringArray(vt.requirementIds);
1242
+ const uiStateNames = asStringArray(vt.uiStates);
1243
+ verificationTargets.push({
1244
+ id,
1245
+ type,
1246
+ commandLabel,
1247
+ file,
1248
+ symbol: asString(vt.symbol) || undefined,
1249
+ requirementIds: requirementIds.length > 0
1250
+ ? requirementIds
1251
+ : [...canonicalBinding.requirementIds],
1252
+ uiStates: uiStateNames.length > 0 ? uiStateNames : ["success", "error"],
1253
+ });
1254
+ }
1255
+ if (verificationTargets.length === 0) {
1256
+ throw new Error("frontend contract compatibility input must declare verification targets");
1257
+ }
1258
+ const defaultVerificationIds = verificationTargets.map((item) => String(item.id));
1259
+ const requirements = [];
1260
+ const reqSource = Array.isArray(record.requirements)
1261
+ ? record.requirements
1262
+ : Array.isArray(record.requirementCoverage)
1263
+ ? record.requirementCoverage
1264
+ : [];
1265
+ for (const item of reqSource) {
1266
+ const req = asRecord(item) ?? {};
1267
+ const id = asString(req.id);
1268
+ if (!id)
1269
+ continue;
1270
+ const implementationTargetsRaw = asStringArray(req.implementationTargets);
1271
+ const implementationTargets = implementationTargetsRaw.length > 0
1272
+ ? implementationTargetsRaw
1273
+ : targetFiles;
1274
+ const verificationTargetIdsRaw = asStringArray(req.verificationTargetIds);
1275
+ const verificationTargetIds = verificationTargetIdsRaw.length > 0
1276
+ ? verificationTargetIdsRaw
1277
+ : defaultVerificationIds;
1278
+ const gapText = asString(asRecord(req.evidenceGap)?.description) ||
1279
+ asString(req.realIntegrationGap);
1280
+ const out = {
1281
+ id,
1282
+ expectedOutcome: asString(req.expectedOutcome),
1283
+ implementationTargets: implementationTargets.length > 0 ? implementationTargets : targetFiles,
1284
+ verificationTargetIds: verificationTargetIds.length > 0
1285
+ ? verificationTargetIds
1286
+ : defaultVerificationIds,
1287
+ };
1288
+ // Do not mark free-form realIntegrationGap as blocking; defer to FE-TEST/FINAL-VERIFY.
1289
+ if (gapText) {
1290
+ out.evidenceGap = {
1291
+ description: gapText,
1292
+ blocking: false,
1293
+ requirementId: id,
1294
+ };
1295
+ }
1296
+ requirements.push(out);
1297
+ }
1298
+ for (const id of canonicalBinding.requirementIds) {
1299
+ if (!requirements.some((item) => item.id === id)) {
1300
+ throw new Error(`frontend contract compatibility input does not cover ${id}`);
1301
+ }
1302
+ }
1303
+ const uiStates = [];
1304
+ if (Array.isArray(record.uiStates)) {
1305
+ for (const item of record.uiStates) {
1306
+ const state = asRecord(item);
1307
+ if (!state)
1308
+ continue;
1309
+ uiStates.push(state);
1310
+ }
1311
+ }
1312
+ else if (asRecord(record.uiStates)) {
1313
+ for (const [name, raw] of Object.entries(asRecord(record.uiStates))) {
1314
+ const state = asRecord(raw);
1315
+ const text = asString(raw);
1316
+ const na = text.toUpperCase().includes("N/A") ||
1317
+ text.toLowerCase().includes("not applicable") ||
1318
+ text.toLowerCase() === "n/a-minimal";
1319
+ if (na || (state && asString(state.notApplicableReason))) {
1320
+ uiStates.push({
1321
+ name,
1322
+ applicable: false,
1323
+ notApplicableReason: asString(state?.notApplicableReason) || text || "not applicable",
1324
+ });
1325
+ continue;
1326
+ }
1327
+ const expectedBehavior = (state &&
1328
+ [
1329
+ asString(state.expectedBehavior),
1330
+ asString(state.role) ? `role=${asString(state.role)}` : "",
1331
+ asString(state.ariaLive) ? `ariaLive=${asString(state.ariaLive)}` : "",
1332
+ asString(state.source) || asString(state.contentFrom),
1333
+ asString(state.textMatchHint),
1334
+ typeof state.silentFailure === "boolean"
1335
+ ? `silentFailure=${state.silentFailure}`
1336
+ : "",
1337
+ ]
1338
+ .filter(Boolean)
1339
+ .join("; ")) ||
1340
+ text ||
1341
+ `${name} state`;
1342
+ uiStates.push({
1343
+ name,
1344
+ applicable: true,
1345
+ expectedBehavior,
1346
+ implementationTargets: targetFiles,
1347
+ verificationTargetIds: defaultVerificationIds,
1348
+ });
1349
+ }
1350
+ }
1351
+ if (uiStates.length === 0) {
1352
+ uiStates.push({
1353
+ name: "success",
1354
+ applicable: true,
1355
+ expectedBehavior: "visible success state",
1356
+ implementationTargets: targetFiles,
1357
+ verificationTargetIds: defaultVerificationIds,
1358
+ }, {
1359
+ name: "error",
1360
+ applicable: true,
1361
+ expectedBehavior: "visible error state",
1362
+ implementationTargets: targetFiles,
1363
+ verificationTargetIds: defaultVerificationIds,
1364
+ });
1365
+ }
1366
+ const strategyRaw = asString(mockApiIn.strategy) || "not-needed";
1367
+ const allowedStrategies = [
1368
+ "native",
1369
+ "browser-intercept",
1370
+ "request-adapter",
1371
+ "not-needed",
1372
+ ];
1373
+ if (!allowedStrategies.includes(strategyRaw)) {
1374
+ throw new Error(`frontend contract compatibility input has unsupported Mock strategy: ${strategyRaw}`);
1375
+ }
1376
+ const strategy = strategyRaw;
1377
+ const productionDefaultOff = mockApiIn.productionDefaultOff === true || mockApiIn.productionMockOff === true;
1378
+ if (!productionDefaultOff) {
1379
+ throw new Error("frontend contract compatibility input must prove production Mock is off");
1380
+ }
1381
+ const activation = asString(mockApiIn.activation) ||
1382
+ (strategy === "not-needed"
1383
+ ? "production remains real fetch; unit tests may inject fetchImpl only"
1384
+ : "documented mock activation");
1385
+ const endpoints = [];
1386
+ for (const item of Array.isArray(mockApiIn.endpoints) ? mockApiIn.endpoints : []) {
1387
+ const ep = asRecord(item);
1388
+ if (!ep)
1389
+ continue;
1390
+ const method = asString(ep.method).toUpperCase() || "GET";
1391
+ const pathValue = asString(ep.path) || asString(api.path);
1392
+ if (!pathValue.startsWith("/"))
1393
+ continue;
1394
+ endpoints.push({
1395
+ method,
1396
+ path: pathValue,
1397
+ fixture: asString(ep.fixture) || undefined,
1398
+ consumer: asString(ep.consumer) || undefined,
1399
+ });
1400
+ }
1401
+ if (endpoints.length === 0 && asString(api.path).startsWith("/")) {
1402
+ endpoints.push({
1403
+ method: asString(api.method).toUpperCase() || "GET",
1404
+ path: asString(api.path),
1405
+ consumer: targetFiles[0],
1406
+ });
1407
+ }
1408
+ const evidenceGaps = [];
1409
+ for (const item of Array.isArray(record.evidenceGaps) ? record.evidenceGaps : []) {
1410
+ const gapItem = asRecord(item);
1411
+ if (!gapItem)
1412
+ continue;
1413
+ const description = asString(gapItem.description);
1414
+ if (!description)
1415
+ continue;
1416
+ evidenceGaps.push({
1417
+ description,
1418
+ blocking: gapItem.blocking === true,
1419
+ requirementId: asString(gapItem.requirementId) || undefined,
1420
+ });
1421
+ }
1422
+ const realGap = asString(record.realIntegrationGap);
1423
+ if (realGap) {
1424
+ evidenceGaps.push({
1425
+ description: realGap,
1426
+ blocking: false,
1427
+ });
1428
+ }
1429
+ for (const item of Array.isArray(record.residualRisks) ? record.residualRisks : []) {
1430
+ const text = asString(item) || asString(asRecord(item)?.description);
1431
+ if (!text)
1432
+ continue;
1433
+ evidenceGaps.push({ description: text, blocking: false });
1434
+ }
1435
+ const designPaths = [
1436
+ ...canonicalBinding.referencePaths,
1437
+ ...asStringArray(asRecord(record.designEvidence)?.paths),
1438
+ ].filter((item, index, arr) => arr.indexOf(item) === index);
1439
+ return {
1440
+ schemaVersion: 1,
1441
+ sourceBinding: canonicalBinding,
1442
+ riskLevel,
1443
+ targets: {
1444
+ files: targetFiles,
1445
+ routes: asStringArray(asRecord(record.targets)?.routes),
1446
+ publicApiChanges: asStringArray(asRecord(record.targets)?.publicApiChanges),
1447
+ },
1448
+ requirements,
1449
+ uiStates,
1450
+ interactions: Array.isArray(record.interactions) ? record.interactions : [],
1451
+ mockApi: {
1452
+ strategy,
1453
+ productionDefaultOff: true,
1454
+ activation,
1455
+ endpoints,
1456
+ },
1457
+ designEvidence: {
1458
+ source: asString(asRecord(record.designEvidence)?.source) ||
1459
+ "repository-fallback+task-source",
1460
+ paths: designPaths.length > 0 ? designPaths : [canonicalBinding.requirementPath],
1461
+ conflicts: asStringArray(asRecord(record.designEvidence)?.conflicts),
1462
+ },
1463
+ verificationTargets,
1464
+ evidenceGaps,
1465
+ implementationSteps: asStringArray(record.implementationSteps),
1466
+ stylingStrategy: asString(record.stylingStrategy) || undefined,
1467
+ dependencyPolicy: asString(record.dependencyPolicy) || undefined,
1468
+ residualRisks: asStringArray(record.residualRisks),
1469
+ realIntegrationGap: asString(record.realIntegrationGap) || undefined,
1470
+ uiComponentChoices: normalizeUiComponentChoices(record.uiComponentChoices),
1471
+ };
1472
+ }
1473
+ /**
1474
+ * Deterministic contract validation shared by the prewrite gate and the plan
1475
+ * node output self-check. Extracts, security-checks, coerces, and schema-parses
1476
+ * a model-emitted frontend contract, so schema/typo/null violations surface as
1477
+ * invalid-output at the producing node (where they can retry) instead of only
1478
+ * failing later at the prewrite gate.
1479
+ */
1480
+ export async function analyzeFrontendImplementationContract(input) {
1481
+ if (!input.sourceBinding)
1482
+ throw new Error("frontend implementation contract gate requires DAG sourceBinding");
1483
+ let rawOutput;
1484
+ if (input.rawContractText !== undefined) {
1485
+ rawOutput = input.rawContractText;
1486
+ }
1487
+ else if (input.fromNodeId !== undefined) {
1488
+ const record = JSON.parse(await readFile(path.join(input.runDir, `${input.fromNodeId}.json`), "utf8"));
1489
+ rawOutput = record.assistantText ?? record.stdout ?? "";
1490
+ }
1491
+ else {
1492
+ throw new Error("analyzeFrontendImplementationContract requires fromNodeId or rawContractText");
1493
+ }
1494
+ const rawContractText = rawOutput.trim();
1495
+ const candidateRawSha256 = sha256Hex(rawOutput);
1496
+ const normalizationActions = [];
1497
+ const pushAction = (action) => {
1498
+ if (!normalizationActions.includes(action))
1499
+ normalizationActions.push(action);
1500
+ };
1501
+ function fail(kind, message, candidateJsonSha256) {
1502
+ throw new FrontendContractFailure({
1503
+ kind,
1504
+ message,
1505
+ candidateRawSha256,
1506
+ candidateJsonSha256,
1507
+ normalizationActions,
1508
+ });
1509
+ }
1510
+ if (rawContractText.includes('"files":["/'))
1511
+ fail("blocked", "invalid-output: absolute frontend contract path is forbidden", null);
1512
+ if (/(?:"(?:files|file|implementationTargets|fixture|consumer)"\s*:\s*(?:\[\s*)?)"\//.test(rawContractText))
1513
+ fail("blocked", "invalid-output: absolute frontend contract path is forbidden", null);
1514
+ if (/(?:"strategy"\s*:\s*")(?!native\b|browser-intercept\b|request-adapter\b|not-needed\b)[^"]+"/.test(rawContractText))
1515
+ fail("blocked", "invalid-output: unsupported mock strategy", null);
1516
+ let parsed;
1517
+ try {
1518
+ const extracted = extractFrontendImplementationJsonWithAudit(rawContractText);
1519
+ parsed = extracted.value;
1520
+ for (const action of extracted.actions)
1521
+ pushAction(action);
1522
+ }
1523
+ catch (error) {
1524
+ fail("retryable-invalid", `invalid-output: ${error instanceof Error ? error.message : String(error)}`, null);
1525
+ }
1526
+ const candidateJsonSha256 = deterministicSha256(parsed);
1527
+ const secrets = secretIssues(parsed);
1528
+ if (secrets.length)
1529
+ fail("blocked", `invalid-output: ${secrets.join("; ")}`, candidateJsonSha256);
1530
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1531
+ fail("retryable-invalid", "invalid-output: frontend contract must be a JSON object", candidateJsonSha256);
1532
+ const parsedTargetObject = parsed.targets;
1533
+ const parsedFiles = parsedTargetObject && typeof parsedTargetObject === "object"
1534
+ ? parsedTargetObject.files
1535
+ : undefined;
1536
+ if (Array.isArray(parsedFiles) && parsedFiles.some((file) => typeof file === "string" && file.startsWith("/")))
1537
+ fail("blocked", "invalid-output: absolute frontend contract path is forbidden", candidateJsonSha256);
1538
+ // Normalize model-emitted compact requirement IDs before deriving the
1539
+ // canonical binding or invoking the strict zod schema.
1540
+ const beforeIds = JSON.stringify(parsed);
1541
+ parsed = canonicalizeRequirementIdsInPayload(parsed);
1542
+ if (JSON.stringify(parsed) !== beforeIds)
1543
+ pushAction("canonicalize-requirement-ids");
1544
+ const beforeAliases = JSON.stringify(parsed);
1545
+ parsed = canonicalizeVerificationTargetAliases(parsed);
1546
+ if (JSON.stringify(parsed) !== beforeAliases)
1547
+ pushAction("canonicalize-verification-alias");
1548
+ try {
1549
+ assertFrontendContractPathsSafe(parsed);
1550
+ }
1551
+ catch (error) {
1552
+ fail("blocked", error instanceof Error ? error.message : String(error), candidateJsonSha256);
1553
+ }
1554
+ // Validate verificationTarget commandLabels against frozen command set.
1555
+ // The frozen set is derived from DAG verification shell task verifyEvidence.
1556
+ const frozenLabels = await deriveFrozenCommandLabelsFromRun(input.runDir);
1557
+ if (frozenLabels.length > 0) {
1558
+ const frozen = new Set(frozenLabels);
1559
+ const parsedVt = asRecord(parsed)?.verificationTargets;
1560
+ if (Array.isArray(parsedVt)) {
1561
+ for (const vt of parsedVt) {
1562
+ const label = asString(asRecord(vt)?.commandLabel);
1563
+ if (label && !frozen.has(label)) {
1564
+ fail("blocked", `invalid-output: verificationTarget commandLabel "${label}" is not in the frozen command set [${[...frozen].join(", ")}]`, candidateJsonSha256);
1565
+ }
1566
+ }
1567
+ }
1568
+ }
1569
+ const parsedTargets = asRecord(parsed)?.targets;
1570
+ const parsedTargetFiles = asStringArray(asRecord(parsedTargets)?.files);
1571
+ if (parsedTargetFiles.some((file) => file.startsWith("/") || file.includes("\\")))
1572
+ fail("blocked", "invalid-output: frontend contract target paths must be relative POSIX paths", candidateJsonSha256);
1573
+ const parsedStates = asRecord(parsed)?.uiStates;
1574
+ if (Array.isArray(parsedStates) && parsedStates.some((item) => {
1575
+ const state = asRecord(item);
1576
+ return state?.applicable === true &&
1577
+ (!asString(state.expectedBehavior) || asStringArray(state.implementationTargets).length === 0 || asStringArray(state.verificationTargetIds).length === 0);
1578
+ }))
1579
+ fail("retryable-invalid", "invalid-output: applicable UI state requires behavior, implementation, and verification", candidateJsonSha256);
1580
+ const parsedMockApi = asRecord(parsed)?.mockApi;
1581
+ if (asRecord(parsedMockApi) &&
1582
+ typeof asRecord(parsedMockApi)?.strategy === "string" &&
1583
+ !["native", "browser-intercept", "request-adapter", "not-needed"].includes(String(asRecord(parsedMockApi)?.strategy)))
1584
+ fail("blocked", `invalid-output: unsupported mock strategy ${String(asRecord(parsedMockApi)?.strategy)}`, candidateJsonSha256);
1585
+ const baseCanonicalBinding = canonicalFrontendContractSourceBinding(input.sourceBinding);
1586
+ const canonicalBinding = withDerivedRequirementIdsWhenUnscoped(baseCanonicalBinding, parsed);
1587
+ // Always inject DAG-owned identity. Model-provided sourceBinding is advisory
1588
+ // only and must not fail a otherwise-valid contract. Record the action only
1589
+ // when the model's emitted binding actually differs from canonical identity.
1590
+ const modelBinding = asRecord(asRecord(parsed)?.sourceBinding);
1591
+ if (serializeDeterministicJson(modelBinding ?? null) !== serializeDeterministicJson(canonicalBinding))
1592
+ pushAction("inject-source-binding");
1593
+ // There is exactly one post-security candidate. A fallback candidate would
1594
+ // allow malformed raw fields to bypass the boundary checks above.
1595
+ const normalizedContract = coerceFrontendImplementationContractInput(parsed, canonicalBinding);
1596
+ const candidate = deriveFrontendVerificationCoverage({
1597
+ ...(asRecord(normalizedContract) ?? parsed),
1598
+ sourceBinding: canonicalBinding,
1599
+ }, canonicalBinding);
1600
+ const result = frontendImplementationContractSchema.safeParse(candidate);
1601
+ if (!result.success)
1602
+ fail("retryable-invalid", `invalid-output: ${result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`, candidateJsonSha256);
1603
+ const blockingGaps = [
1604
+ ...result.data.evidenceGaps,
1605
+ ...result.data.requirements.flatMap((item) => item.evidenceGap ? [item.evidenceGap] : []),
1606
+ ].filter((item) => item.blocking);
1607
+ if (blockingGaps.length > 0)
1608
+ fail("blocked", `frontend contract has blocking evidence gap: ${blockingGaps
1609
+ .map((item) => item.requirementId ?? item.description)
1610
+ .join(", ")}`, candidateJsonSha256);
1611
+ for (const requirementId of canonicalBinding.requirementIds)
1612
+ if (!result.data.requirements.some((item) => item.id === requirementId) &&
1613
+ !result.data.evidenceGaps.some((item) => item.requirementId === requirementId))
1614
+ fail("blocked", `frontend contract does not cover ${requirementId}`, candidateJsonSha256);
1615
+ return {
1616
+ canonical: result.data,
1617
+ candidateRawSha256,
1618
+ candidateJsonSha256,
1619
+ normalizationActions,
1620
+ };
1621
+ }
1622
+ export async function writeFrontendImplementationContractArtifact(input) {
1623
+ const written = await writeDeterministicJsonArtifact(input.runDir, path.posix.join(input.outputDir, input.artifactName), input.canonical);
1624
+ return {
1625
+ path: written.path,
1626
+ sha256: written.sha256,
1627
+ schemaId: FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID,
1628
+ };
1629
+ }
1630
+ const FRONTEND_PLAN_PATCH_PROTECTED_PATHS = [
1631
+ "/schemaVersion",
1632
+ "/sourceBinding",
1633
+ "/riskLevel",
1634
+ "/targets/files",
1635
+ "/mockApi/productionDefaultOff",
1636
+ ];
1637
+ function frontendPlanPatchProtectedPathViolations(patch) {
1638
+ const record = asRecord(patch);
1639
+ if (!record)
1640
+ return ["/"];
1641
+ const violations = [];
1642
+ for (const key of ["schemaVersion", "sourceBinding", "riskLevel"]) {
1643
+ if (Object.hasOwn(record, key))
1644
+ violations.push(`/${key}`);
1645
+ }
1646
+ const targets = asRecord(record.targets);
1647
+ if (targets && Object.hasOwn(targets, "files"))
1648
+ violations.push("/targets/files");
1649
+ const mockApi = asRecord(record.mockApi);
1650
+ if (mockApi && Object.hasOwn(mockApi, "productionDefaultOff"))
1651
+ violations.push("/mockApi/productionDefaultOff");
1652
+ return violations;
1653
+ }
1654
+ function workspaceRootFromDagRunDir(runDir) {
1655
+ const absolute = path.resolve(runDir);
1656
+ const segments = absolute.split(path.sep);
1657
+ const harnessIndex = segments.lastIndexOf(".harness");
1658
+ if (harnessIndex < 0 ||
1659
+ segments[harnessIndex + 1] !== "dag-runs" ||
1660
+ harnessIndex + 3 >= segments.length) {
1661
+ return null;
1662
+ }
1663
+ const joined = segments.slice(0, harnessIndex).join(path.sep);
1664
+ return joined || path.parse(absolute).root;
1665
+ }
1666
+ function verificationSymbolMatchesFile(symbol, content) {
1667
+ if (symbol.trim().toLowerCase() === "all describe blocks")
1668
+ return content.includes("describe(");
1669
+ const symbols = symbol.includes(" / ")
1670
+ ? symbol
1671
+ .split(/[((]/, 1)[0]
1672
+ .split("/")
1673
+ .map((item) => item.trim())
1674
+ .filter(Boolean)
1675
+ : [symbol];
1676
+ return symbols.every((item) => {
1677
+ const describeTitle = item.match(/^describe\((?:['"])(.+?)(?:['"])/i)?.[1];
1678
+ return [item, describeTitle, describeTitle ? "describe(" : undefined]
1679
+ .filter((candidate) => Boolean(candidate))
1680
+ .some((candidate) => content.includes(candidate));
1681
+ });
1682
+ }
1683
+ async function dropUnresolvedVerificationSymbols(input) {
1684
+ const workspaceRoot = workspaceRootFromDagRunDir(input.runDir);
1685
+ const targets = asRecord(input.contract)?.verificationTargets;
1686
+ if (!workspaceRoot || !Array.isArray(targets))
1687
+ return false;
1688
+ let changed = false;
1689
+ for (const targetValue of targets) {
1690
+ const target = asRecord(targetValue);
1691
+ const file = asString(target?.file);
1692
+ const symbol = asString(target?.symbol);
1693
+ if (!target || !file || !symbol)
1694
+ continue;
1695
+ const absolute = path.resolve(workspaceRoot, file);
1696
+ const relative = path.relative(workspaceRoot, absolute);
1697
+ if (relative.startsWith("..") ||
1698
+ path.isAbsolute(relative) ||
1699
+ relative.includes("..")) {
1700
+ continue;
1701
+ }
1702
+ let content;
1703
+ try {
1704
+ content = await readFile(absolute, "utf8");
1705
+ }
1706
+ catch {
1707
+ // Missing files remain a trace-time hard failure; do not hide them by
1708
+ // changing the optional symbol during candidate normalization.
1709
+ continue;
1710
+ }
1711
+ if (verificationSymbolMatchesFile(symbol, content))
1712
+ continue;
1713
+ delete target.symbol;
1714
+ changed = true;
1715
+ }
1716
+ return changed;
1717
+ }
1718
+ function extractSingleOpenspecCitationsBlock(text) {
1719
+ const blocks = text.match(/```openspec-citations[ \t]*\r?\n[\s\S]*?\r?\n```/g) ?? [];
1720
+ if (blocks.length !== 1) {
1721
+ throw new Error(`frontend plan patch output must include exactly one fenced openspec-citations block (found ${blocks.length})`);
1722
+ }
1723
+ return blocks[0];
1724
+ }
1725
+ async function writeFrontendPlanCandidateRaw(input) {
1726
+ const relativePath = path.posix.join("contracts", "candidates", input.nodeId, `attempt-${input.attempt}.raw.md`);
1727
+ await writeTextArtifactFile(path.join(input.runDir, relativePath), input.text.endsWith("\n") ? input.text : `${input.text}\n`);
1728
+ return relativePath;
1729
+ }
1730
+ /**
1731
+ * Compile the planner's editable RFC 7386 patch against the deterministic
1732
+ * runtime skeleton. Every attempt is preserved under contracts/candidates so
1733
+ * format/schema failures can be inspected and replayed without trusting model
1734
+ * prose. A successful result returns canonical full-contract text for all
1735
+ * downstream review and prewrite nodes.
1736
+ */
1737
+ export async function validateFrontendPlanPatchNodeOutput(input) {
1738
+ const nodeId = input.nodeId ?? "frontend-plan-pi";
1739
+ const attempt = input.attempt ?? 1;
1740
+ const candidateDir = path.posix.join("contracts", "candidates", nodeId);
1741
+ const rawPath = await writeFrontendPlanCandidateRaw({
1742
+ runDir: input.runDir,
1743
+ nodeId,
1744
+ attempt,
1745
+ text: input.text,
1746
+ });
1747
+ const reportBase = {
1748
+ schemaVersion: 1,
1749
+ schemaId: FRONTEND_IMPLEMENTATION_CONTRACT_PLAN_PATCH_SCHEMA_ID,
1750
+ nodeId,
1751
+ attempt,
1752
+ rawPath,
1753
+ protectedPaths: [...FRONTEND_PLAN_PATCH_PROTECTED_PATHS],
1754
+ candidateRawSha256: sha256Hex(input.text),
1755
+ };
1756
+ let extractedPatchPath;
1757
+ let extractedPatchSha256;
1758
+ const writeReport = async (value) => {
1759
+ await writeDeterministicJsonArtifact(input.runDir, path.posix.join(candidateDir, `attempt-${attempt}.validation.json`), {
1760
+ ...reportBase,
1761
+ ...(extractedPatchPath
1762
+ ? { extractedPatchPath, extractedPatchSha256 }
1763
+ : {}),
1764
+ ...value,
1765
+ });
1766
+ };
1767
+ try {
1768
+ if (!input.sourceBinding)
1769
+ throw new Error("frontend plan patch validator requires DAG sourceBinding");
1770
+ const skeleton = input.structuredContractOutput?.skeleton;
1771
+ if (!skeleton)
1772
+ throw new Error("frontend plan patch validator requires a deterministic runtime skeleton");
1773
+ const patch = extractFrontendImplementationJson(input.text);
1774
+ if (!isPlainObject(patch))
1775
+ throw new Error("frontend plan patch must extract to one JSON object");
1776
+ extractedPatchPath = path.posix.join(candidateDir, `attempt-${attempt}.extracted.json`);
1777
+ const extractedArtifact = await writeDeterministicJsonArtifact(input.runDir, extractedPatchPath, patch);
1778
+ extractedPatchSha256 = extractedArtifact.sha256;
1779
+ const citationsBlock = extractSingleOpenspecCitationsBlock(input.text);
1780
+ const protectedViolations = frontendPlanPatchProtectedPathViolations(patch);
1781
+ if (protectedViolations.length > 0)
1782
+ throw new Error(`frontend plan patch must not modify runtime-protected paths: ${protectedViolations.join(", ")}`);
1783
+ const merged = applyFrontendContractMergePatch(skeleton, patch);
1784
+ const droppedUnresolvedSymbols = await dropUnresolvedVerificationSymbols({
1785
+ runDir: input.runDir,
1786
+ contract: merged,
1787
+ });
1788
+ const analysis = await analyzeFrontendImplementationContract({
1789
+ runDir: input.runDir,
1790
+ rawContractText: serializeDeterministicJson(merged),
1791
+ sourceBinding: input.sourceBinding,
1792
+ });
1793
+ const normalizedArtifact = await writeDeterministicJsonArtifact(input.runDir, path.posix.join(candidateDir, `attempt-${attempt}.normalized.json`), analysis.canonical);
1794
+ await writeReport({
1795
+ classification: "accepted-normalized",
1796
+ normalizedContractPath: path.posix.relative(input.runDir.replaceAll(path.sep, "/"), normalizedArtifact.path.replaceAll(path.sep, "/")),
1797
+ normalizedContractSha256: normalizedArtifact.sha256,
1798
+ normalizationActions: [
1799
+ "apply-runtime-skeleton",
1800
+ ...(droppedUnresolvedSymbols
1801
+ ? ["drop-unresolved-verification-symbols"]
1802
+ : []),
1803
+ ...analysis.normalizationActions,
1804
+ ],
1805
+ errors: [],
1806
+ });
1807
+ return {
1808
+ ok: true,
1809
+ contract: analysis.canonical,
1810
+ normalizedText: [
1811
+ "Frontend implementation contract compiled from the runtime skeleton.",
1812
+ "",
1813
+ "```json",
1814
+ serializeDeterministicJson(analysis.canonical),
1815
+ "```",
1816
+ citationsBlock,
1817
+ ].join("\n"),
1818
+ };
1819
+ }
1820
+ catch (error) {
1821
+ const reason = error instanceof Error ? error.message : String(error);
1822
+ await writeReport({
1823
+ classification: "retryable-invalid",
1824
+ normalizationActions: [],
1825
+ errors: [{ path: "$", code: "invalid-output", message: reason }],
1826
+ });
1827
+ return { ok: false, reason };
1828
+ }
1829
+ }
1830
+ /**
1831
+ * Node-output self-check for plan nodes that produce the implementation
1832
+ * contract. Mirrors the prewrite gate validation so schema/typo/null violations
1833
+ * are caught at the producing node and retried instead of failing the run later.
1834
+ * Skipped when no sourceBinding is available (the gate remains the authority).
1835
+ */
1836
+ export async function validateFrontendContractNodeOutput(input) {
1837
+ if (!input.sourceBinding)
1838
+ return { ok: true };
1839
+ try {
1840
+ const analysis = await analyzeFrontendImplementationContract({
1841
+ runDir: input.runDir,
1842
+ rawContractText: input.text,
1843
+ sourceBinding: input.sourceBinding,
1844
+ });
1845
+ return { ok: true, contract: analysis.canonical };
1846
+ }
1847
+ catch (error) {
1848
+ return {
1849
+ ok: false,
1850
+ reason: error instanceof Error ? error.message : String(error),
1851
+ };
1852
+ }
1853
+ }
1854
+ /**
1855
+ * Node-output self-check for the plan revision node, which emits an RFC 7386
1856
+ * merge-patch delta against the original contract instead of a full contract.
1857
+ * Deliberately loose and format-level: the output must contain exactly one
1858
+ * ```openspec-citations fenced block and must extract (after the citations
1859
+ * blocks are stripped) to exactly one JSON object. The delta carries no
1860
+ * schemaVersion/targets, so no full-contract schema/semantic checks run here;
1861
+ * content parsing, patch application, and merged-contract validation stay with
1862
+ * the prewrite gate, which remains the only authority.
1863
+ */
1864
+ export async function validateFrontendRevisionPatchNodeOutput(input) {
1865
+ let citationsBlock;
1866
+ try {
1867
+ citationsBlock = extractSingleOpenspecCitationsBlock(input.text);
1868
+ }
1869
+ catch (error) {
1870
+ return {
1871
+ ok: false,
1872
+ reason: error instanceof Error ? error.message : String(error),
1873
+ };
1874
+ }
1875
+ // Strip the citation blocks before extraction: their per-row JSON objects
1876
+ // would otherwise count as competing balanced objects in the extractor.
1877
+ const textWithoutCitations = input.text.replace(citationsBlock, "");
1878
+ let patch;
1879
+ try {
1880
+ patch = extractFrontendImplementationJson(textWithoutCitations);
1881
+ }
1882
+ catch (error) {
1883
+ return {
1884
+ ok: false,
1885
+ reason: `revision patch output must contain exactly one valid JSON object: ${error instanceof Error ? error.message : String(error)}`,
1886
+ };
1887
+ }
1888
+ if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
1889
+ return {
1890
+ ok: false,
1891
+ reason: "revision patch output must extract to exactly one JSON object (RFC 7386 merge-patch delta)",
1892
+ };
1893
+ }
1894
+ const protectedViolations = frontendPlanPatchProtectedPathViolations(patch);
1895
+ if (protectedViolations.length > 0) {
1896
+ return {
1897
+ ok: false,
1898
+ reason: `frontend revision patch must not modify runtime-protected paths: ${protectedViolations.join(", ")}`,
1899
+ };
1900
+ }
1901
+ return { ok: true, contract: patch };
1902
+ }
1903
+ export async function materializeFrontendImplementationContract(input) {
1904
+ const analysis = await analyzeFrontendImplementationContract(input);
1905
+ return writeFrontendImplementationContractArtifact({
1906
+ runDir: input.runDir,
1907
+ outputDir: input.outputDir,
1908
+ artifactName: input.artifactName,
1909
+ canonical: analysis.canonical,
1910
+ });
1911
+ }