mandrel 1.57.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 (843) hide show
  1. package/.agents/README.md +954 -0
  2. package/.agents/docs/SDLC.md +1420 -0
  3. package/.agents/docs/agentrc-reference.json +278 -0
  4. package/.agents/docs/configuration.md +1040 -0
  5. package/.agents/docs/workflows.md +59 -0
  6. package/.agents/instructions.md +384 -0
  7. package/.agents/personas/architect.md +107 -0
  8. package/.agents/personas/devops-engineer.md +36 -0
  9. package/.agents/personas/engineer-mobile.md +119 -0
  10. package/.agents/personas/engineer-web.md +110 -0
  11. package/.agents/personas/engineer.md +90 -0
  12. package/.agents/personas/product.md +88 -0
  13. package/.agents/personas/project-manager.md +110 -0
  14. package/.agents/personas/qa-engineer.md +91 -0
  15. package/.agents/personas/refactorer.md +110 -0
  16. package/.agents/personas/security-engineer.md +112 -0
  17. package/.agents/personas/sre.md +86 -0
  18. package/.agents/personas/technical-writer.md +100 -0
  19. package/.agents/personas/ux-designer.md +95 -0
  20. package/.agents/rules/api-conventions.md +75 -0
  21. package/.agents/rules/changelog-style.md +238 -0
  22. package/.agents/rules/gherkin-standards.md +146 -0
  23. package/.agents/rules/git-conventions.md +146 -0
  24. package/.agents/rules/orchestration-error-handling.md +35 -0
  25. package/.agents/rules/security-baseline.md +92 -0
  26. package/.agents/rules/shell-conventions.md +70 -0
  27. package/.agents/rules/test-seams.md +59 -0
  28. package/.agents/rules/testing-standards.md +177 -0
  29. package/.agents/runtime-deps.json +18 -0
  30. package/.agents/schemas/acceptance-eval-verdict.schema.json +93 -0
  31. package/.agents/schemas/agentrc.schema.json +1583 -0
  32. package/.agents/schemas/audit-results.schema.json +69 -0
  33. package/.agents/schemas/audit-rules.json +134 -0
  34. package/.agents/schemas/audit-rules.schema.json +69 -0
  35. package/.agents/schemas/baselines/baseline-envelope.schema.json +44 -0
  36. package/.agents/schemas/baselines/bundle-size.schema.json +47 -0
  37. package/.agents/schemas/baselines/coverage.schema.json +50 -0
  38. package/.agents/schemas/baselines/crap.schema.json +52 -0
  39. package/.agents/schemas/baselines/duplication.schema.json +62 -0
  40. package/.agents/schemas/baselines/lighthouse.schema.json +59 -0
  41. package/.agents/schemas/baselines/lint.schema.json +47 -0
  42. package/.agents/schemas/baselines/maintainability.schema.json +71 -0
  43. package/.agents/schemas/baselines/mutation.schema.json +52 -0
  44. package/.agents/schemas/crap-baseline.schema.json +57 -0
  45. package/.agents/schemas/crap-report.schema.json +102 -0
  46. package/.agents/schemas/dispatch-manifest.json +232 -0
  47. package/.agents/schemas/epic-perf-report.schema.json +89 -0
  48. package/.agents/schemas/epic-spec.schema.json +183 -0
  49. package/.agents/schemas/friction-event.schema.json +56 -0
  50. package/.agents/schemas/lifecycle/README.md +18 -0
  51. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +13 -0
  52. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +13 -0
  53. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +13 -0
  54. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +12 -0
  55. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +13 -0
  56. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +13 -0
  57. package/.agents/schemas/lifecycle/close-validate.end.schema.json +18 -0
  58. package/.agents/schemas/lifecycle/close-validate.start.schema.json +13 -0
  59. package/.agents/schemas/lifecycle/code-review.end.schema.json +30 -0
  60. package/.agents/schemas/lifecycle/code-review.start.schema.json +12 -0
  61. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +14 -0
  62. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +13 -0
  63. package/.agents/schemas/lifecycle/epic.blocked.schema.json +13 -0
  64. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +12 -0
  65. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +12 -0
  66. package/.agents/schemas/lifecycle/epic.close.end.schema.json +12 -0
  67. package/.agents/schemas/lifecycle/epic.complete.schema.json +13 -0
  68. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +13 -0
  69. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +12 -0
  70. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +13 -0
  71. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +14 -0
  72. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +17 -0
  73. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +15 -0
  74. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +18 -0
  75. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +12 -0
  76. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +16 -0
  77. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +12 -0
  78. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +28 -0
  79. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +16 -0
  80. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +15 -0
  81. package/.agents/schemas/lifecycle/ledger-record.schema.json +59 -0
  82. package/.agents/schemas/lifecycle/notification.emitted.schema.json +18 -0
  83. package/.agents/schemas/lifecycle/pr.created.schema.json +14 -0
  84. package/.agents/schemas/lifecycle/retro.end.schema.json +16 -0
  85. package/.agents/schemas/lifecycle/retro.start.schema.json +12 -0
  86. package/.agents/schemas/lifecycle/story.blocked.schema.json +13 -0
  87. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +17 -0
  88. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +15 -0
  89. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +20 -0
  90. package/.agents/schemas/lifecycle/story.merged.schema.json +13 -0
  91. package/.agents/schemas/mi-report.schema.json +58 -0
  92. package/.agents/schemas/model-attribution.schema.json +49 -0
  93. package/.agents/schemas/qa-finding.schema.json +133 -0
  94. package/.agents/schemas/qa-ledger.schema.json +89 -0
  95. package/.agents/schemas/risk-verdict.schema.json +53 -0
  96. package/.agents/schemas/signal-event.schema.json +58 -0
  97. package/.agents/schemas/skill.schema.json +31 -0
  98. package/.agents/schemas/skills-index.schema.json +81 -0
  99. package/.agents/schemas/story-perf-summary.schema.json +73 -0
  100. package/.agents/schemas/validation-evidence.schema.json +78 -0
  101. package/.agents/scripts/README.md +93 -0
  102. package/.agents/scripts/acceptance-eval.js +284 -0
  103. package/.agents/scripts/acceptance-spec-reconciler.js +556 -0
  104. package/.agents/scripts/agents-bootstrap-github.js +634 -0
  105. package/.agents/scripts/analyze-execution.js +369 -0
  106. package/.agents/scripts/assert-branch.js +83 -0
  107. package/.agents/scripts/audit-labels-bootstrap.js +253 -0
  108. package/.agents/scripts/audit-to-stories.js +257 -0
  109. package/.agents/scripts/bootstrap.js +1378 -0
  110. package/.agents/scripts/check-baselines.js +81 -0
  111. package/.agents/scripts/check-dead-exports.js +311 -0
  112. package/.agents/scripts/check-doc-links.js +401 -0
  113. package/.agents/scripts/check-gherkin-placeholders.js +663 -0
  114. package/.agents/scripts/check-lifecycle-doc-drift.js +402 -0
  115. package/.agents/scripts/check-lifecycle-lint.js +379 -0
  116. package/.agents/scripts/check-prepush-recovery.js +90 -0
  117. package/.agents/scripts/check-windows-git-perf.js +138 -0
  118. package/.agents/scripts/cleanup-repo-test-temp.js +67 -0
  119. package/.agents/scripts/coverage-capture.js +112 -0
  120. package/.agents/scripts/detect-merges.js +111 -0
  121. package/.agents/scripts/diagnose-friction.js +257 -0
  122. package/.agents/scripts/diagnose.js +240 -0
  123. package/.agents/scripts/dispatcher.js +295 -0
  124. package/.agents/scripts/drain-pending-cleanup.js +147 -0
  125. package/.agents/scripts/epic-audit-prepare.js +419 -0
  126. package/.agents/scripts/epic-audit-recheck.js +241 -0
  127. package/.agents/scripts/epic-deliver-note-intervention.js +192 -0
  128. package/.agents/scripts/epic-deliver-preflight.js +407 -0
  129. package/.agents/scripts/epic-deliver-prepare.js +383 -0
  130. package/.agents/scripts/epic-execute-record-wave.js +463 -0
  131. package/.agents/scripts/epic-plan-clarity.js +201 -0
  132. package/.agents/scripts/epic-plan-decompose.js +79 -0
  133. package/.agents/scripts/epic-plan-healthcheck.js +363 -0
  134. package/.agents/scripts/epic-plan-spec-validate.js +111 -0
  135. package/.agents/scripts/epic-plan-spec.js +198 -0
  136. package/.agents/scripts/epic-reconcile.js +637 -0
  137. package/.agents/scripts/evidence-gate.js +235 -0
  138. package/.agents/scripts/generate-config-docs.js +516 -0
  139. package/.agents/scripts/generate-lifecycle-docs.js +224 -0
  140. package/.agents/scripts/generate-skills-index.js +252 -0
  141. package/.agents/scripts/generate-workflows-doc.js +168 -0
  142. package/.agents/scripts/git-cleanup.js +124 -0
  143. package/.agents/scripts/git-pr-quality-gate.js +203 -0
  144. package/.agents/scripts/git-rebase-and-resolve.js +234 -0
  145. package/.agents/scripts/hierarchy-gate.js +176 -0
  146. package/.agents/scripts/hydrate-context.js +179 -0
  147. package/.agents/scripts/install-matrix-assert.js +282 -0
  148. package/.agents/scripts/lib/Graph.js +326 -0
  149. package/.agents/scripts/lib/ITicketingProvider.js +349 -0
  150. package/.agents/scripts/lib/Logger.js +194 -0
  151. package/.agents/scripts/lib/audit-suite/cli.js +64 -0
  152. package/.agents/scripts/lib/audit-suite/findings.js +164 -0
  153. package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +32 -0
  154. package/.agents/scripts/lib/audit-suite/frontmatter.js +110 -0
  155. package/.agents/scripts/lib/audit-suite/index.js +22 -0
  156. package/.agents/scripts/lib/audit-suite/runner.js +233 -0
  157. package/.agents/scripts/lib/audit-suite/selector.js +235 -0
  158. package/.agents/scripts/lib/audit-suite/substitutions.js +124 -0
  159. package/.agents/scripts/lib/audit-suite/workflow-loader.js +49 -0
  160. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +130 -0
  161. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +114 -0
  162. package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +93 -0
  163. package/.agents/scripts/lib/audit-to-stories/group-findings.js +265 -0
  164. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +246 -0
  165. package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +160 -0
  166. package/.agents/scripts/lib/auto-refresh-baselines.js +308 -0
  167. package/.agents/scripts/lib/baseline-loader.js +0 -0
  168. package/.agents/scripts/lib/baseline-schema-registry.js +69 -0
  169. package/.agents/scripts/lib/baseline-snapshot.js +716 -0
  170. package/.agents/scripts/lib/baselines/component-matcher.js +21 -0
  171. package/.agents/scripts/lib/baselines/components.js +126 -0
  172. package/.agents/scripts/lib/baselines/diff-scope-cli.js +203 -0
  173. package/.agents/scripts/lib/baselines/duplication-scanner.js +220 -0
  174. package/.agents/scripts/lib/baselines/env-overrides.js +129 -0
  175. package/.agents/scripts/lib/baselines/envelope.js +368 -0
  176. package/.agents/scripts/lib/baselines/exit-codes.js +89 -0
  177. package/.agents/scripts/lib/baselines/git-base.js +0 -0
  178. package/.agents/scripts/lib/baselines/kernel.js +111 -0
  179. package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +220 -0
  180. package/.agents/scripts/lib/baselines/kinds/bundle-size.js +157 -0
  181. package/.agents/scripts/lib/baselines/kinds/coverage.js +194 -0
  182. package/.agents/scripts/lib/baselines/kinds/crap.js +555 -0
  183. package/.agents/scripts/lib/baselines/kinds/duplication.js +197 -0
  184. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +185 -0
  185. package/.agents/scripts/lib/baselines/kinds/lint.js +172 -0
  186. package/.agents/scripts/lib/baselines/kinds/maintainability.js +340 -0
  187. package/.agents/scripts/lib/baselines/kinds/mutation.js +153 -0
  188. package/.agents/scripts/lib/baselines/path-canon.js +279 -0
  189. package/.agents/scripts/lib/baselines/preview-gates.js +298 -0
  190. package/.agents/scripts/lib/baselines/reader.js +321 -0
  191. package/.agents/scripts/lib/baselines/refresh-service.js +733 -0
  192. package/.agents/scripts/lib/baselines/scope.js +291 -0
  193. package/.agents/scripts/lib/baselines/writer.js +312 -0
  194. package/.agents/scripts/lib/bdd-runner-detect.js +417 -0
  195. package/.agents/scripts/lib/bdd-scenario-scanner.js +310 -0
  196. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +202 -0
  197. package/.agents/scripts/lib/bootstrap/branch-protection.js +222 -0
  198. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +171 -0
  199. package/.agents/scripts/lib/bootstrap/commit-push.js +146 -0
  200. package/.agents/scripts/lib/bootstrap/gh-list.js +153 -0
  201. package/.agents/scripts/lib/bootstrap/gh-preflight.js +306 -0
  202. package/.agents/scripts/lib/bootstrap/hitl-confirm.js +89 -0
  203. package/.agents/scripts/lib/bootstrap/install-ledger.js +174 -0
  204. package/.agents/scripts/lib/bootstrap/manifest.js +272 -0
  205. package/.agents/scripts/lib/bootstrap/merge-methods.js +108 -0
  206. package/.agents/scripts/lib/bootstrap/preflight.js +195 -0
  207. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +801 -0
  208. package/.agents/scripts/lib/bootstrap/prompt.js +480 -0
  209. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +370 -0
  210. package/.agents/scripts/lib/bootstrap/summary.js +75 -0
  211. package/.agents/scripts/lib/bootstrap/workflow-audit.js +256 -0
  212. package/.agents/scripts/lib/branch-name-guard.js +98 -0
  213. package/.agents/scripts/lib/c8-cli-path.js +21 -0
  214. package/.agents/scripts/lib/changed-files.js +184 -0
  215. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +104 -0
  216. package/.agents/scripts/lib/checks/core-bare-clean.js +48 -0
  217. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +54 -0
  218. package/.agents/scripts/lib/checks/index.js +288 -0
  219. package/.agents/scripts/lib/checks/push-hook-parity.js +106 -0
  220. package/.agents/scripts/lib/checks/stale-origin-epic.js +49 -0
  221. package/.agents/scripts/lib/checks/state.js +558 -0
  222. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +186 -0
  223. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +182 -0
  224. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +92 -0
  225. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +81 -0
  226. package/.agents/scripts/lib/checks/worktree-residue-biome.js +55 -0
  227. package/.agents/scripts/lib/cli/parse-numeric.js +60 -0
  228. package/.agents/scripts/lib/cli/standard-args.js +351 -0
  229. package/.agents/scripts/lib/cli-args.js +286 -0
  230. package/.agents/scripts/lib/cli-utils.js +69 -0
  231. package/.agents/scripts/lib/close-validation/projections/head-sha.js +44 -0
  232. package/.agents/scripts/lib/close-validation/projections/inputs.js +86 -0
  233. package/.agents/scripts/lib/close-validation/projections/maintainability.js +286 -0
  234. package/.agents/scripts/lib/close-validation.js +897 -0
  235. package/.agents/scripts/lib/codebase-snapshot.js +513 -0
  236. package/.agents/scripts/lib/command-header.js +33 -0
  237. package/.agents/scripts/lib/config/acceptance-eval.js +95 -0
  238. package/.agents/scripts/lib/config/baselines.js +60 -0
  239. package/.agents/scripts/lib/config/ci.js +30 -0
  240. package/.agents/scripts/lib/config/commands.js +36 -0
  241. package/.agents/scripts/lib/config/defaults.js +119 -0
  242. package/.agents/scripts/lib/config/explain.js +348 -0
  243. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +23 -0
  244. package/.agents/scripts/lib/config/gates/coverage.schema.js +18 -0
  245. package/.agents/scripts/lib/config/gates/crap.schema.js +33 -0
  246. package/.agents/scripts/lib/config/gates/duplication.schema.js +26 -0
  247. package/.agents/scripts/lib/config/gates/index.js +36 -0
  248. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +23 -0
  249. package/.agents/scripts/lib/config/gates/lint.schema.js +9 -0
  250. package/.agents/scripts/lib/config/gates/maintainability.schema.js +20 -0
  251. package/.agents/scripts/lib/config/gates/mutation.schema.js +12 -0
  252. package/.agents/scripts/lib/config/gates/shared.js +117 -0
  253. package/.agents/scripts/lib/config/github.js +122 -0
  254. package/.agents/scripts/lib/config/lifecycle.js +40 -0
  255. package/.agents/scripts/lib/config/limits.js +211 -0
  256. package/.agents/scripts/lib/config/paths.js +73 -0
  257. package/.agents/scripts/lib/config/preflight.js +58 -0
  258. package/.agents/scripts/lib/config/quality.js +665 -0
  259. package/.agents/scripts/lib/config/retro.js +77 -0
  260. package/.agents/scripts/lib/config/runners.js +105 -0
  261. package/.agents/scripts/lib/config/runtime.js +167 -0
  262. package/.agents/scripts/lib/config/shared.js +46 -0
  263. package/.agents/scripts/lib/config/sync-agentrc.js +243 -0
  264. package/.agents/scripts/lib/config/temp-paths.js +373 -0
  265. package/.agents/scripts/lib/config/validate-orchestration.js +81 -0
  266. package/.agents/scripts/lib/config/worktree-isolation.js +80 -0
  267. package/.agents/scripts/lib/config-resolver.js +298 -0
  268. package/.agents/scripts/lib/config-schema-shared.js +32 -0
  269. package/.agents/scripts/lib/config-schema.js +20 -0
  270. package/.agents/scripts/lib/config-settings-schema-delivery.js +332 -0
  271. package/.agents/scripts/lib/config-settings-schema-quality.js +165 -0
  272. package/.agents/scripts/lib/config-settings-schema.js +420 -0
  273. package/.agents/scripts/lib/coverage-baseline.js +352 -0
  274. package/.agents/scripts/lib/coverage-capture.js +195 -0
  275. package/.agents/scripts/lib/coverage-utils.js +239 -0
  276. package/.agents/scripts/lib/cpu-pool.js +223 -0
  277. package/.agents/scripts/lib/crap-engine.js +119 -0
  278. package/.agents/scripts/lib/crap-utils.js +479 -0
  279. package/.agents/scripts/lib/degraded-mode.js +69 -0
  280. package/.agents/scripts/lib/dependency-parser.js +129 -0
  281. package/.agents/scripts/lib/duplicate-search.js +189 -0
  282. package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +70 -0
  283. package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +197 -0
  284. package/.agents/scripts/lib/dynamic-workflow/capability.js +396 -0
  285. package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +80 -0
  286. package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +72 -0
  287. package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +90 -0
  288. package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +43 -0
  289. package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +83 -0
  290. package/.agents/scripts/lib/env-loader.js +52 -0
  291. package/.agents/scripts/lib/epic-merge-lock.js +239 -0
  292. package/.agents/scripts/lib/epic-plan-clarity.js +142 -0
  293. package/.agents/scripts/lib/epic-plan-ideation.js +228 -0
  294. package/.agents/scripts/lib/error-redactor.js +125 -0
  295. package/.agents/scripts/lib/errors/index.js +67 -0
  296. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +230 -0
  297. package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +207 -0
  298. package/.agents/scripts/lib/feedback-loop/graduator-core.js +421 -0
  299. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +480 -0
  300. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +229 -0
  301. package/.agents/scripts/lib/findings/classify-finding.js +195 -0
  302. package/.agents/scripts/lib/findings/promote-finding.js +353 -0
  303. package/.agents/scripts/lib/findings/route-finding.js +283 -0
  304. package/.agents/scripts/lib/findings/semantic-issue-search.js +179 -0
  305. package/.agents/scripts/lib/findings/severity.js +102 -0
  306. package/.agents/scripts/lib/gates/baseline-store.js +106 -0
  307. package/.agents/scripts/lib/gates/friction.js +43 -0
  308. package/.agents/scripts/lib/gh-exec.js +553 -0
  309. package/.agents/scripts/lib/git/cached-fetch.js +0 -0
  310. package/.agents/scripts/lib/git/sync-from-base.js +162 -0
  311. package/.agents/scripts/lib/git-branch-cleanup.js +213 -0
  312. package/.agents/scripts/lib/git-branch-lifecycle.js +353 -0
  313. package/.agents/scripts/lib/git-merge-orchestrator.js +261 -0
  314. package/.agents/scripts/lib/git-utils.js +363 -0
  315. package/.agents/scripts/lib/github-url.js +29 -0
  316. package/.agents/scripts/lib/install-cmd-parser.js +51 -0
  317. package/.agents/scripts/lib/issue-link-parser.js +74 -0
  318. package/.agents/scripts/lib/json-utils.js +60 -0
  319. package/.agents/scripts/lib/label-constants.js +169 -0
  320. package/.agents/scripts/lib/label-taxonomy.js +200 -0
  321. package/.agents/scripts/lib/maintainability-engine.js +164 -0
  322. package/.agents/scripts/lib/maintainability-utils.js +343 -0
  323. package/.agents/scripts/lib/mandrel-catalog.js +170 -0
  324. package/.agents/scripts/lib/mutation/baseline-snapshot.js +238 -0
  325. package/.agents/scripts/lib/mutation/config-detector.js +119 -0
  326. package/.agents/scripts/lib/mutation/stryker-runner.js +306 -0
  327. package/.agents/scripts/lib/mutation/survivor-report.js +160 -0
  328. package/.agents/scripts/lib/notifications/notifier.js +75 -0
  329. package/.agents/scripts/lib/observability/active-story-env.js +182 -0
  330. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +221 -0
  331. package/.agents/scripts/lib/observability/perf-aggregator.js +887 -0
  332. package/.agents/scripts/lib/observability/perf-report-readers.js +319 -0
  333. package/.agents/scripts/lib/observability/perf-report-render.js +182 -0
  334. package/.agents/scripts/lib/observability/signals-writer.js +296 -0
  335. package/.agents/scripts/lib/observability/source-classifier.js +103 -0
  336. package/.agents/scripts/lib/observability/tool-trace-hook.js +417 -0
  337. package/.agents/scripts/lib/onboard/detect-stack.js +300 -0
  338. package/.agents/scripts/lib/onboard/scaffold-docs.js +128 -0
  339. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +173 -0
  340. package/.agents/scripts/lib/orchestration/cascade-grouping.js +275 -0
  341. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +131 -0
  342. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +80 -0
  343. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +132 -0
  344. package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +142 -0
  345. package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +149 -0
  346. package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +158 -0
  347. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +56 -0
  348. package/.agents/scripts/lib/orchestration/code-review.js +652 -0
  349. package/.agents/scripts/lib/orchestration/column-sync.js +286 -0
  350. package/.agents/scripts/lib/orchestration/context-envelope.js +280 -0
  351. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +581 -0
  352. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +88 -0
  353. package/.agents/scripts/lib/orchestration/detectors-phase.js +188 -0
  354. package/.agents/scripts/lib/orchestration/dispatch-engine.js +144 -0
  355. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +206 -0
  356. package/.agents/scripts/lib/orchestration/doc-reader.js +94 -0
  357. package/.agents/scripts/lib/orchestration/epic-cleanup.js +473 -0
  358. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +310 -0
  359. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +167 -0
  360. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +151 -0
  361. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +74 -0
  362. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +78 -0
  363. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +72 -0
  364. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +155 -0
  365. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +321 -0
  366. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +75 -0
  367. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +86 -0
  368. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +235 -0
  369. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +197 -0
  370. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +48 -0
  371. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +94 -0
  372. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +414 -0
  373. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +55 -0
  374. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +105 -0
  375. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +235 -0
  376. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +120 -0
  377. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +118 -0
  378. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +295 -0
  379. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +186 -0
  380. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +50 -0
  381. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +146 -0
  382. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +110 -0
  383. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +392 -0
  384. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +217 -0
  385. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +235 -0
  386. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +69 -0
  387. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +32 -0
  388. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +291 -0
  389. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +175 -0
  390. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +37 -0
  391. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +127 -0
  392. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +400 -0
  393. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +285 -0
  394. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +66 -0
  395. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +797 -0
  396. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +619 -0
  397. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +335 -0
  398. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +230 -0
  399. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +363 -0
  400. package/.agents/scripts/lib/orchestration/error-journal.js +139 -0
  401. package/.agents/scripts/lib/orchestration/file-assumption-enum.js +31 -0
  402. package/.agents/scripts/lib/orchestration/file-assumptions.js +506 -0
  403. package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +116 -0
  404. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +241 -0
  405. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +489 -0
  406. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +88 -0
  407. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +219 -0
  408. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +309 -0
  409. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +99 -0
  410. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +123 -0
  411. package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +57 -0
  412. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +114 -0
  413. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +426 -0
  414. package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +84 -0
  415. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +365 -0
  416. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +72 -0
  417. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +69 -0
  418. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +214 -0
  419. package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +137 -0
  420. package/.agents/scripts/lib/orchestration/label-transitions.js +43 -0
  421. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +309 -0
  422. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +147 -0
  423. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +155 -0
  424. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +226 -0
  425. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +69 -0
  426. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +378 -0
  427. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +248 -0
  428. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +527 -0
  429. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +259 -0
  430. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +278 -0
  431. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +355 -0
  432. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +647 -0
  433. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +331 -0
  434. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +140 -0
  435. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +421 -0
  436. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +168 -0
  437. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +668 -0
  438. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +322 -0
  439. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +114 -0
  440. package/.agents/scripts/lib/orchestration/manifest-builder.js +216 -0
  441. package/.agents/scripts/lib/orchestration/model-attribution.js +390 -0
  442. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +147 -0
  443. package/.agents/scripts/lib/orchestration/phase-runner.js +87 -0
  444. package/.agents/scripts/lib/orchestration/plan-review-routing.js +63 -0
  445. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +86 -0
  446. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +212 -0
  447. package/.agents/scripts/lib/orchestration/planning-context-budget.js +213 -0
  448. package/.agents/scripts/lib/orchestration/planning-risk.js +155 -0
  449. package/.agents/scripts/lib/orchestration/planning-state-manager.js +318 -0
  450. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +56 -0
  451. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +33 -0
  452. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +78 -0
  453. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +68 -0
  454. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +118 -0
  455. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +396 -0
  456. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +205 -0
  457. package/.agents/scripts/lib/orchestration/pr-base-guard.js +47 -0
  458. package/.agents/scripts/lib/orchestration/preflight-cache.js +164 -0
  459. package/.agents/scripts/lib/orchestration/reassert-status-column.js +202 -0
  460. package/.agents/scripts/lib/orchestration/reconciler.js +137 -0
  461. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +152 -0
  462. package/.agents/scripts/lib/orchestration/recut.js +56 -0
  463. package/.agents/scripts/lib/orchestration/resolves-token.js +127 -0
  464. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +94 -0
  465. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +448 -0
  466. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +335 -0
  467. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +133 -0
  468. package/.agents/scripts/lib/orchestration/retro-heuristics.js +57 -0
  469. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +275 -0
  470. package/.agents/scripts/lib/orchestration/retro-proposals.js +395 -0
  471. package/.agents/scripts/lib/orchestration/retro-runner.js +171 -0
  472. package/.agents/scripts/lib/orchestration/review-depth.js +93 -0
  473. package/.agents/scripts/lib/orchestration/review-providers/codex.js +363 -0
  474. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +205 -0
  475. package/.agents/scripts/lib/orchestration/review-providers/native.js +805 -0
  476. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +73 -0
  477. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +396 -0
  478. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +373 -0
  479. package/.agents/scripts/lib/orchestration/review-providers/types.js +89 -0
  480. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +107 -0
  481. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +159 -0
  482. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +194 -0
  483. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +81 -0
  484. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +190 -0
  485. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +70 -0
  486. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +106 -0
  487. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +42 -0
  488. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +73 -0
  489. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +225 -0
  490. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +315 -0
  491. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +149 -0
  492. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +110 -0
  493. package/.agents/scripts/lib/orchestration/spec-freshness.js +320 -0
  494. package/.agents/scripts/lib/orchestration/spec-renderer.js +456 -0
  495. package/.agents/scripts/lib/orchestration/spec-section-validator.js +80 -0
  496. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +797 -0
  497. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +163 -0
  498. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +152 -0
  499. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +387 -0
  500. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +266 -0
  501. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +48 -0
  502. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +67 -0
  503. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +161 -0
  504. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +117 -0
  505. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +86 -0
  506. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +147 -0
  507. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +142 -0
  508. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +62 -0
  509. package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +221 -0
  510. package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +123 -0
  511. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +216 -0
  512. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +636 -0
  513. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +198 -0
  514. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +105 -0
  515. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +222 -0
  516. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +220 -0
  517. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +291 -0
  518. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +234 -0
  519. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +110 -0
  520. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +86 -0
  521. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +112 -0
  522. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +157 -0
  523. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +434 -0
  524. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +290 -0
  525. package/.agents/scripts/lib/orchestration/story-close-recovery.js +643 -0
  526. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +67 -0
  527. package/.agents/scripts/lib/orchestration/task-body-validator.js +391 -0
  528. package/.agents/scripts/lib/orchestration/ticket-lease.js +358 -0
  529. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +783 -0
  530. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +367 -0
  531. package/.agents/scripts/lib/orchestration/ticket-validator.js +691 -0
  532. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +723 -0
  533. package/.agents/scripts/lib/orchestration/ticketing/reads.js +474 -0
  534. package/.agents/scripts/lib/orchestration/ticketing/state.js +559 -0
  535. package/.agents/scripts/lib/orchestration/ticketing.js +55 -0
  536. package/.agents/scripts/lib/orchestration/wave-marker.js +28 -0
  537. package/.agents/scripts/lib/orchestration/wave-record-io.js +277 -0
  538. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +189 -0
  539. package/.agents/scripts/lib/orchestration/wave-record-projection.js +423 -0
  540. package/.agents/scripts/lib/path-security.js +25 -0
  541. package/.agents/scripts/lib/plan-phase-cleanup.js +125 -0
  542. package/.agents/scripts/lib/preflight-runner.js +196 -0
  543. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +95 -0
  544. package/.agents/scripts/lib/presentation/manifest-builder.js +245 -0
  545. package/.agents/scripts/lib/presentation/manifest-formatter.js +243 -0
  546. package/.agents/scripts/lib/presentation/manifest-helpers.js +213 -0
  547. package/.agents/scripts/lib/presentation/manifest-persistence.js +262 -0
  548. package/.agents/scripts/lib/presentation/manifest-procedures.js +55 -0
  549. package/.agents/scripts/lib/presentation/manifest-render-waves.js +252 -0
  550. package/.agents/scripts/lib/presentation/manifest-renderer.js +188 -0
  551. package/.agents/scripts/lib/presentation/manifest-story-views.js +119 -0
  552. package/.agents/scripts/lib/provider-factory.js +80 -0
  553. package/.agents/scripts/lib/push-epic-retry.js +209 -0
  554. package/.agents/scripts/lib/qa/console-allowlist.js +151 -0
  555. package/.agents/scripts/lib/qa/coverage-report.js +181 -0
  556. package/.agents/scripts/lib/qa/coverage-verdict.js +296 -0
  557. package/.agents/scripts/lib/qa/propose-missing-test.js +95 -0
  558. package/.agents/scripts/lib/qa/qa-context-hydrator.js +296 -0
  559. package/.agents/scripts/lib/qa/qa-session.js +197 -0
  560. package/.agents/scripts/lib/qa/redact-evidence.js +245 -0
  561. package/.agents/scripts/lib/qa/resolve-qa-contract.js +190 -0
  562. package/.agents/scripts/lib/qa/resolve-selection.js +373 -0
  563. package/.agents/scripts/lib/runtime-deps/ensure-installed.js +100 -0
  564. package/.agents/scripts/lib/runtime-deps/manifest.js +96 -0
  565. package/.agents/scripts/lib/runtime-deps/preflight.js +78 -0
  566. package/.agents/scripts/lib/runtime-deps/scan-imports.js +202 -0
  567. package/.agents/scripts/lib/signals/detectors/common.js +36 -0
  568. package/.agents/scripts/lib/signals/detectors/hotspot.js +298 -0
  569. package/.agents/scripts/lib/signals/detectors/index.js +14 -0
  570. package/.agents/scripts/lib/signals/detectors/retry.js +289 -0
  571. package/.agents/scripts/lib/signals/detectors/rework.js +204 -0
  572. package/.agents/scripts/lib/signals/index.js +39 -0
  573. package/.agents/scripts/lib/signals/read.js +268 -0
  574. package/.agents/scripts/lib/signals/schema.js +225 -0
  575. package/.agents/scripts/lib/signals/span-tree.js +290 -0
  576. package/.agents/scripts/lib/signals/write.js +19 -0
  577. package/.agents/scripts/lib/single-story/confirm-merge.js +201 -0
  578. package/.agents/scripts/lib/single-story/story-merged-notify.js +126 -0
  579. package/.agents/scripts/lib/single-story-sweep/protection.js +274 -0
  580. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +169 -0
  581. package/.agents/scripts/lib/single-story-sweep.js +329 -0
  582. package/.agents/scripts/lib/skills/parse-skill.js +202 -0
  583. package/.agents/scripts/lib/skills/walk-skill-files.js +56 -0
  584. package/.agents/scripts/lib/spec/index.js +36 -0
  585. package/.agents/scripts/lib/spec/loader.js +425 -0
  586. package/.agents/scripts/lib/spec/state.js +217 -0
  587. package/.agents/scripts/lib/story-body/story-body.js +743 -0
  588. package/.agents/scripts/lib/story-init/blocker-validator.js +68 -0
  589. package/.agents/scripts/lib/story-init/branch-initializer.js +422 -0
  590. package/.agents/scripts/lib/story-init/context-resolver.js +92 -0
  591. package/.agents/scripts/lib/story-init/donor-precheck.js +207 -0
  592. package/.agents/scripts/lib/story-init/hierarchy-tracer.js +36 -0
  593. package/.agents/scripts/lib/story-init/state-transitioner.js +80 -0
  594. package/.agents/scripts/lib/story-init/task-graph-builder.js +114 -0
  595. package/.agents/scripts/lib/story-init/transition-summary.js +34 -0
  596. package/.agents/scripts/lib/story-lifecycle.js +186 -0
  597. package/.agents/scripts/lib/story-plan.js +246 -0
  598. package/.agents/scripts/lib/task-utils.js +26 -0
  599. package/.agents/scripts/lib/templates/decomposer-prompts.js +168 -0
  600. package/.agents/scripts/lib/test-env.js +30 -0
  601. package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +52 -0
  602. package/.agents/scripts/lib/test-isolate/list-files.js +90 -0
  603. package/.agents/scripts/lib/test-isolate/parse-tap.js +75 -0
  604. package/.agents/scripts/lib/test-isolate/runner.js +483 -0
  605. package/.agents/scripts/lib/test-profile/parse-tap.js +136 -0
  606. package/.agents/scripts/lib/test-profile/render-report.js +45 -0
  607. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +35 -0
  608. package/.agents/scripts/lib/test-tiers.js +94 -0
  609. package/.agents/scripts/lib/util/concurrent-map.js +59 -0
  610. package/.agents/scripts/lib/util/phase-timer-state.js +72 -0
  611. package/.agents/scripts/lib/util/phase-timer.js +163 -0
  612. package/.agents/scripts/lib/util/poll-loop.js +86 -0
  613. package/.agents/scripts/lib/util/with-timeout.js +32 -0
  614. package/.agents/scripts/lib/validation-evidence.js +323 -0
  615. package/.agents/scripts/lib/wave-runner/tick.js +665 -0
  616. package/.agents/scripts/lib/wave-runner/wave-checkpoint.js +91 -0
  617. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +19 -0
  618. package/.agents/scripts/lib/workers/crap-worker.js +197 -0
  619. package/.agents/scripts/lib/workers/maintainability-report-worker.js +137 -0
  620. package/.agents/scripts/lib/workers/maintainability-worker.js +79 -0
  621. package/.agents/scripts/lib/workspace-provisioner.js +189 -0
  622. package/.agents/scripts/lib/worktree/bootstrapper.js +48 -0
  623. package/.agents/scripts/lib/worktree/inspector.js +140 -0
  624. package/.agents/scripts/lib/worktree/lifecycle/creation.js +118 -0
  625. package/.agents/scripts/lib/worktree/lifecycle/drift-detection.js +62 -0
  626. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +276 -0
  627. package/.agents/scripts/lib/worktree/lifecycle/gc.js +49 -0
  628. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +178 -0
  629. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +264 -0
  630. package/.agents/scripts/lib/worktree/lifecycle/precheck.js +100 -0
  631. package/.agents/scripts/lib/worktree/lifecycle/reap.js +588 -0
  632. package/.agents/scripts/lib/worktree/lifecycle/registry-sync.js +124 -0
  633. package/.agents/scripts/lib/worktree/lifecycle/shared.js +26 -0
  634. package/.agents/scripts/lib/worktree/lifecycle-manager.js +40 -0
  635. package/.agents/scripts/lib/worktree/node-modules-strategy.js +349 -0
  636. package/.agents/scripts/lib/worktree-manager.js +243 -0
  637. package/.agents/scripts/lifecycle-diff.js +206 -0
  638. package/.agents/scripts/lifecycle-emit-story-dispatch.js +194 -0
  639. package/.agents/scripts/lifecycle-emit.js +479 -0
  640. package/.agents/scripts/lint-baseline.js +507 -0
  641. package/.agents/scripts/lint-label-vocabulary.js +237 -0
  642. package/.agents/scripts/loc-delta.js +205 -0
  643. package/.agents/scripts/notify.js +307 -0
  644. package/.agents/scripts/package.json +3 -0
  645. package/.agents/scripts/post-structured-comment.js +127 -0
  646. package/.agents/scripts/pr-watch-with-update.js +152 -0
  647. package/.agents/scripts/providers/github/auth.js +65 -0
  648. package/.agents/scripts/providers/github/board-add.js +63 -0
  649. package/.agents/scripts/providers/github/branch-protection.js +186 -0
  650. package/.agents/scripts/providers/github/cache.js +72 -0
  651. package/.agents/scripts/providers/github/comments.js +131 -0
  652. package/.agents/scripts/providers/github/compose.js +111 -0
  653. package/.agents/scripts/providers/github/errors.js +242 -0
  654. package/.agents/scripts/providers/github/issues.js +242 -0
  655. package/.agents/scripts/providers/github/labels.js +179 -0
  656. package/.agents/scripts/providers/github/mappers.js +126 -0
  657. package/.agents/scripts/providers/github/merge-methods.js +82 -0
  658. package/.agents/scripts/providers/github/project-board.js +47 -0
  659. package/.agents/scripts/providers/github/projects-v2-graphql.js +472 -0
  660. package/.agents/scripts/providers/github/prs.js +103 -0
  661. package/.agents/scripts/providers/github/request-helpers.js +110 -0
  662. package/.agents/scripts/providers/github/sub-issues.js +369 -0
  663. package/.agents/scripts/providers/github/tickets.js +381 -0
  664. package/.agents/scripts/providers/github/transient-retry.js +62 -0
  665. package/.agents/scripts/providers/github.js +157 -0
  666. package/.agents/scripts/quality-preview.js +327 -0
  667. package/.agents/scripts/quality-watch.js +223 -0
  668. package/.agents/scripts/render-manifest.js +143 -0
  669. package/.agents/scripts/resync-status-column.js +176 -0
  670. package/.agents/scripts/retro-run.js +167 -0
  671. package/.agents/scripts/run-audit-suite.js +97 -0
  672. package/.agents/scripts/run-coverage.js +103 -0
  673. package/.agents/scripts/run-lint.js +94 -0
  674. package/.agents/scripts/run-test-profile.js +126 -0
  675. package/.agents/scripts/run-tests.js +185 -0
  676. package/.agents/scripts/run-verify.js +56 -0
  677. package/.agents/scripts/select-audits.js +155 -0
  678. package/.agents/scripts/signals-view.js +294 -0
  679. package/.agents/scripts/single-story-close.js +83 -0
  680. package/.agents/scripts/single-story-confirm-merge.js +183 -0
  681. package/.agents/scripts/single-story-init.js +692 -0
  682. package/.agents/scripts/stories-wave-tick.js +415 -0
  683. package/.agents/scripts/story-close.js +246 -0
  684. package/.agents/scripts/story-deliver-prepare.js +267 -0
  685. package/.agents/scripts/story-init.js +516 -0
  686. package/.agents/scripts/story-phase.js +327 -0
  687. package/.agents/scripts/story-plan.js +284 -0
  688. package/.agents/scripts/sync-agentrc.js +71 -0
  689. package/.agents/scripts/sync-branch-from-base.js +138 -0
  690. package/.agents/scripts/sync-claude-commands.js +151 -0
  691. package/.agents/scripts/test-isolate.js +222 -0
  692. package/.agents/scripts/test-wrapper.js +108 -0
  693. package/.agents/scripts/update-coverage-baseline.js +129 -0
  694. package/.agents/scripts/update-crap-baseline.js +177 -0
  695. package/.agents/scripts/update-duplication-baseline.js +134 -0
  696. package/.agents/scripts/update-maintainability-baseline.js +183 -0
  697. package/.agents/scripts/update-mutation-baseline.js +189 -0
  698. package/.agents/scripts/update-ticket-state.js +107 -0
  699. package/.agents/scripts/validate-docs-freshness.js +259 -0
  700. package/.agents/scripts/validate-skills.js +278 -0
  701. package/.agents/scripts/wave-tick.js +335 -0
  702. package/.agents/skills/core/analyze-execution/SKILL.md +98 -0
  703. package/.agents/skills/core/api-and-interface-design/SKILL.md +327 -0
  704. package/.agents/skills/core/baseline-refresh/SKILL.md +181 -0
  705. package/.agents/skills/core/browser-testing-with-devtools/SKILL.md +352 -0
  706. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +274 -0
  707. package/.agents/skills/core/ci-cd-and-automation/examples.md +211 -0
  708. package/.agents/skills/core/code-review-and-quality/SKILL.md +421 -0
  709. package/.agents/skills/core/code-simplification/SKILL.md +389 -0
  710. package/.agents/skills/core/context-engineering/SKILL.md +309 -0
  711. package/.agents/skills/core/context-engineering/examples.md +58 -0
  712. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +338 -0
  713. package/.agents/skills/core/deprecation-and-migration/SKILL.md +250 -0
  714. package/.agents/skills/core/diagnose-friction/SKILL.md +79 -0
  715. package/.agents/skills/core/documentation-and-adrs/SKILL.md +323 -0
  716. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +145 -0
  717. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +425 -0
  718. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +393 -0
  719. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +357 -0
  720. package/.agents/skills/core/git-workflow-and-versioning/SKILL.md +352 -0
  721. package/.agents/skills/core/hydrate-context/SKILL.md +118 -0
  722. package/.agents/skills/core/idea-refinement/SKILL.md +317 -0
  723. package/.agents/skills/core/idea-refinement/examples.md +437 -0
  724. package/.agents/skills/core/idea-refinement/frameworks.md +135 -0
  725. package/.agents/skills/core/idea-refinement/refinement-criteria.md +155 -0
  726. package/.agents/skills/core/idea-refinement/scripts/idea-refine.sh +15 -0
  727. package/.agents/skills/core/incremental-implementation/SKILL.md +271 -0
  728. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +213 -0
  729. package/.agents/skills/core/knowledge-transfer/SKILL.md +175 -0
  730. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +117 -0
  731. package/.agents/skills/core/performance-optimization/SKILL.md +314 -0
  732. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +277 -0
  733. package/.agents/skills/core/property-based-testing/SKILL.md +148 -0
  734. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +105 -0
  735. package/.agents/skills/core/refactoring-discipline/SKILL.md +111 -0
  736. package/.agents/skills/core/scope-triage/SKILL.md +127 -0
  737. package/.agents/skills/core/security-and-hardening/SKILL.md +400 -0
  738. package/.agents/skills/core/shipping-and-launch/SKILL.md +328 -0
  739. package/.agents/skills/core/spec-driven-development/SKILL.md +252 -0
  740. package/.agents/skills/core/test-driven-development/SKILL.md +475 -0
  741. package/.agents/skills/core/using-agent-skills/SKILL.md +232 -0
  742. package/.agents/skills/skills.index.json +596 -0
  743. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +31 -0
  744. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +51 -0
  745. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +48 -0
  746. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +31 -0
  747. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +33 -0
  748. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +31 -0
  749. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +51 -0
  750. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +54 -0
  751. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +29 -0
  752. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +30 -0
  753. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +57 -0
  754. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +9 -0
  755. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +48 -0
  756. package/.agents/skills/stack/frontend/astro/SKILL.md +62 -0
  757. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +30 -0
  758. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +29 -0
  759. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +50 -0
  760. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +58 -0
  761. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +34 -0
  762. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +51 -0
  763. package/.agents/skills/stack/qa/gherkin-authoring/SKILL.md +257 -0
  764. package/.agents/skills/stack/qa/gherkin-authoring/examples/invoice-issue.feature +41 -0
  765. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +199 -0
  766. package/.agents/skills/stack/qa/playwright/SKILL.md +50 -0
  767. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +188 -0
  768. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +142 -0
  769. package/.agents/skills/stack/qa/qa-harness/SKILL.md +220 -0
  770. package/.agents/skills/stack/qa/vitest/SKILL.md +51 -0
  771. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +68 -0
  772. package/.agents/starter-agentrc.json +22 -0
  773. package/.agents/templates/agent-protocol.md +72 -0
  774. package/.agents/templates/docs/architecture.md +30 -0
  775. package/.agents/templates/docs/decisions.md +24 -0
  776. package/.agents/templates/epic-from-idea.md +21 -0
  777. package/.agents/templates/single-story-body.md +17 -0
  778. package/.agents/workflows/agents-update.md +415 -0
  779. package/.agents/workflows/audit-architecture.md +312 -0
  780. package/.agents/workflows/audit-clean-code.md +179 -0
  781. package/.agents/workflows/audit-dependencies.md +91 -0
  782. package/.agents/workflows/audit-devops.md +110 -0
  783. package/.agents/workflows/audit-lighthouse.md +260 -0
  784. package/.agents/workflows/audit-performance.md +161 -0
  785. package/.agents/workflows/audit-privacy.md +104 -0
  786. package/.agents/workflows/audit-quality.md +191 -0
  787. package/.agents/workflows/audit-security.md +156 -0
  788. package/.agents/workflows/audit-seo.md +118 -0
  789. package/.agents/workflows/audit-sre.md +139 -0
  790. package/.agents/workflows/audit-to-stories.md +257 -0
  791. package/.agents/workflows/audit-ux-ui.md +102 -0
  792. package/.agents/workflows/epic-deliver.md +864 -0
  793. package/.agents/workflows/epic-plan.md +998 -0
  794. package/.agents/workflows/explain.md +118 -0
  795. package/.agents/workflows/git-cleanup.md +250 -0
  796. package/.agents/workflows/git-commit-all.md +15 -0
  797. package/.agents/workflows/git-merge-pr.md +377 -0
  798. package/.agents/workflows/git-pr-all.md +278 -0
  799. package/.agents/workflows/git-push.md +60 -0
  800. package/.agents/workflows/helpers/_merge-conflict-template.md +54 -0
  801. package/.agents/workflows/helpers/acceptance-self-eval.md +74 -0
  802. package/.agents/workflows/helpers/agents-sync-config.md +129 -0
  803. package/.agents/workflows/helpers/code-quality-guardrails.md +101 -0
  804. package/.agents/workflows/helpers/code-review.md +370 -0
  805. package/.agents/workflows/helpers/diagnose.md +117 -0
  806. package/.agents/workflows/helpers/epic-audit.md +295 -0
  807. package/.agents/workflows/helpers/epic-deliver-story.md +370 -0
  808. package/.agents/workflows/helpers/epic-plan-decompose.md +199 -0
  809. package/.agents/workflows/helpers/epic-plan-spec.md +184 -0
  810. package/.agents/workflows/helpers/epic-testing.md +125 -0
  811. package/.agents/workflows/helpers/parallel-tooling.md +88 -0
  812. package/.agents/workflows/helpers/signals.md +112 -0
  813. package/.agents/workflows/helpers/single-story-deliver.md +636 -0
  814. package/.agents/workflows/helpers/worktree-lifecycle.md +317 -0
  815. package/.agents/workflows/onboard.md +207 -0
  816. package/.agents/workflows/qa-assist.md +293 -0
  817. package/.agents/workflows/qa-explore.md +350 -0
  818. package/.agents/workflows/qa-run-harness.md +288 -0
  819. package/.agents/workflows/story-deliver.md +327 -0
  820. package/.agents/workflows/story-plan.md +233 -0
  821. package/LICENSE +21 -0
  822. package/README.md +193 -0
  823. package/bin/mandrel.js +56 -0
  824. package/bin/postinstall.js +195 -0
  825. package/lib/cli/__tests__/migrate.test.js +268 -0
  826. package/lib/cli/__tests__/sync-local-zone.test.js +247 -0
  827. package/lib/cli/__tests__/sync.test.js +372 -0
  828. package/lib/cli/__tests__/update-major.test.js +217 -0
  829. package/lib/cli/__tests__/update.test.js +696 -0
  830. package/lib/cli/__tests__/version-check.test.js +398 -0
  831. package/lib/cli/doctor.js +124 -0
  832. package/lib/cli/explain.js +107 -0
  833. package/lib/cli/migrate.js +260 -0
  834. package/lib/cli/registry.js +830 -0
  835. package/lib/cli/sync-commands.js +50 -0
  836. package/lib/cli/sync.js +200 -0
  837. package/lib/cli/uninstall.js +795 -0
  838. package/lib/cli/update.js +854 -0
  839. package/lib/cli/version-check.js +206 -0
  840. package/lib/migrations/README.md +69 -0
  841. package/lib/migrations/__tests__/index.test.js +216 -0
  842. package/lib/migrations/index.js +164 -0
  843. package/package.json +105 -0
@@ -0,0 +1,897 @@
1
+ /**
2
+ * close-validation.js — Shift-left validation gates for story-close.
3
+ *
4
+ * Runs typecheck, lint, test, format check, and maintainability/coverage/
5
+ * CRAP regression checks before the story merge so drift is caught in the
6
+ * worktree rather than at pre-push time on the Epic branch. Format command
7
+ * is configurable via `project.commands.formatCheck`; default is
8
+ * `npx biome format .`. All gates inherit stdio so the operator sees the
9
+ * raw output; the returned summary surfaces actionable hints on failure.
10
+ *
11
+ * Pre-merge MI projection (Story #781) is re-exported from
12
+ * `close-validation/projections/maintainability.js` — the engine that
13
+ * surfaces, by name, the files that would breach their per-file MI
14
+ * baseline post-merge so the operator can ship a `baseline-refresh:`
15
+ * commit atomically with the Story PR.
16
+ */
17
+
18
+ import { execFileSync, spawn } from 'node:child_process';
19
+ import { writeFile as defaultWriteFile } from 'node:fs/promises';
20
+ import { diffNameOnly } from './changed-files.js';
21
+ import { defaultGetHeadSha } from './close-validation/projections/head-sha.js';
22
+ import { getCommands } from './config/commands.js';
23
+ import { storyArtifactPath } from './config/temp-paths.js';
24
+ import { getSpawnCount as defaultGetSpawnCount } from './gh-exec.js';
25
+ import {
26
+ recordPass as defaultRecordPass,
27
+ shouldSkip as defaultShouldSkip,
28
+ hashCommandConfig,
29
+ } from './validation-evidence.js';
30
+
31
+ /**
32
+ * @typedef {Object} Gate
33
+ * @property {string} name - Short label used in progress logs.
34
+ * @property {string} cmd - Executable to run.
35
+ * @property {string[]} args - Arguments passed to `cmd`.
36
+ * @property {string} [hint] - Remediation hint shown on failure.
37
+ * @property {{ baseRef: string }} [changedFileScope] - Optional Story-diff scope.
38
+ * @property {Record<string, string>} [env] - Optional per-gate environment
39
+ * overlay. Merged over `process.env` for this gate's spawned child only.
40
+ * Used to thread the epic baseRef into the `check-baselines` gate via
41
+ * `BASELINE_REF` (Story #3890) so baseline regressions compare against the
42
+ * epic integration branch rather than `origin/main`.
43
+ * @property {(cmd: string, args: string[], opts: { cwd: string, gateName?: string, log?: (m: string) => void, signal?: AbortSignal, env?: Record<string, string> }) => Promise<{ status: number }> | { status: number }} [run]
44
+ * - Optional in-process runner. Story #1973: when present, the gate
45
+ * executes via this callable instead of spawning `cmd`/`args` through
46
+ * the default runner — used for per-kind baseline gates that import
47
+ * `compare(head, base)` directly.
48
+ */
49
+
50
+ /**
51
+ * Fallback typecheck command — the gate is mandatory by design (Epic-branch
52
+ * type regressions surface in the next Story's pre-push otherwise).
53
+ */
54
+ const TYPECHECK_FALLBACK = 'npm run typecheck';
55
+
56
+ const TYPECHECK_HINT =
57
+ 'TypeScript regression — fix type errors on the Story branch before retrying close. If the failure is a stale generated type (e.g. wrangler types), regenerate locally and commit before the close.';
58
+
59
+ /** Default formatter command when `project.commands.formatCheck` is unset. */
60
+ const FORMAT_CHECK_FALLBACK = 'npx biome format .';
61
+
62
+ /** Default formatter command in write mode. */
63
+ const FORMAT_WRITE_FALLBACK = 'npx biome format --write .';
64
+
65
+ /**
66
+ * Build the format-gate hint dynamically from the resolved write command so
67
+ * a Prettier-only repo gets `prettier --write` in its hint, not biome.
68
+ */
69
+ function buildFormatHint(writeCmd) {
70
+ const cmd =
71
+ writeCmd && writeCmd.trim().length > 0 ? writeCmd : FORMAT_WRITE_FALLBACK;
72
+ return `Run \`${cmd}\` to auto-fix formatting drift.`;
73
+ }
74
+
75
+ /**
76
+ * Resolve a string `project.commands.<key>` with a fallback when the
77
+ * value is missing, empty, or the resolver throws on malformed config.
78
+ * Shared engine behind the three resolveX command helpers.
79
+ *
80
+ * @param {{ project?: { commands?: object } } | null | undefined} config
81
+ * Canonical resolved config (or a bare `{ project: { commands } }` bag).
82
+ * @param {string} key
83
+ * @param {string} fallback
84
+ * @returns {string}
85
+ */
86
+ function resolveCommandWithFallback(config, key, fallback) {
87
+ try {
88
+ // `getCommands` reads `config.project.commands` from the canonical
89
+ // resolved config.
90
+ const cmds = getCommands(config);
91
+ const value = cmds[key];
92
+ if (typeof value === 'string' && value.trim().length > 0) {
93
+ return value.trim();
94
+ }
95
+ } catch {
96
+ // Malformed config — fall through to the framework default.
97
+ }
98
+ return fallback;
99
+ }
100
+
101
+ /**
102
+ * Resolve the typecheck command. Reads `project.commands.typecheck`;
103
+ * falls back to `npm run typecheck`. The framework-wide
104
+ * `COMMANDS_DEFAULTS.typecheck` is `null` but this gate is mandatory, so
105
+ * we apply the fallback here. Exported for testing.
106
+ *
107
+ * @param {{ project?: { commands?: object } } | null | undefined} config
108
+ * @returns {string}
109
+ */
110
+ export function resolveTypecheckCommand(config) {
111
+ return resolveCommandWithFallback(config, 'typecheck', TYPECHECK_FALLBACK);
112
+ }
113
+
114
+ /**
115
+ * Resolve the format-check command. Reads `project.commands.formatCheck`;
116
+ * falls back to `npx biome format .` so existing repos keep working byte-
117
+ * for-byte. Exported for testing.
118
+ *
119
+ * @param {{ project?: { commands?: object } } | null | undefined} config
120
+ * @returns {string}
121
+ */
122
+ export function resolveFormatCheckCommand(config) {
123
+ return resolveCommandWithFallback(
124
+ config,
125
+ 'formatCheck',
126
+ FORMAT_CHECK_FALLBACK,
127
+ );
128
+ }
129
+
130
+ /**
131
+ * Resolve the format-write command used by story-close format-autofix (and
132
+ * surfaced in the format-gate hint). Reads `project.commands.formatWrite`;
133
+ * falls back to `npx biome format --write .`. Exported for testing.
134
+ *
135
+ * @param {{ project?: { commands?: object } } | null | undefined} config
136
+ * @returns {string}
137
+ */
138
+ export function resolveFormatWriteCommand(config) {
139
+ return resolveCommandWithFallback(
140
+ config,
141
+ 'formatWrite',
142
+ FORMAT_WRITE_FALLBACK,
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Compute the Story-diff file scope for formatter gates. The default Biome
148
+ * formatter used to run against `.` from inside `.worktrees/story-*`, which
149
+ * lets consumer ignore globs that exclude `.worktrees` self-exclude the whole
150
+ * run. Scoping to changed paths keeps verification real without depending on
151
+ * how consumers spell root ignore patterns.
152
+ *
153
+ * @param {{ cwd: string, baseRef: string }} opts
154
+ * @returns {string[]}
155
+ */
156
+ export function listChangedFilesForFormatGate({ cwd, baseRef }) {
157
+ if (!cwd) throw new Error('listChangedFilesForFormatGate: cwd is required');
158
+ if (!baseRef)
159
+ throw new Error('listChangedFilesForFormatGate: baseRef is required');
160
+ // Bridge execFileSync into the gitSpawn(cwd, ...args) contract so
161
+ // diffNameOnly owns the stdout → path-list conversion.
162
+ const gitSpawn = (_cwd, ...args) => {
163
+ try {
164
+ const stdout = execFileSync('git', args, {
165
+ cwd: _cwd,
166
+ encoding: 'utf8',
167
+ stdio: ['ignore', 'pipe', 'pipe'],
168
+ });
169
+ return { status: 0, stdout, stderr: '' };
170
+ } catch (err) {
171
+ return {
172
+ status: err.status ?? 1,
173
+ stdout: err.stdout ?? '',
174
+ stderr: err.stderr ?? err.message,
175
+ };
176
+ }
177
+ };
178
+ return diffNameOnly({ baseRef, cwd, gitSpawn });
179
+ }
180
+
181
+ function buildChangedFileScope(baseRef) {
182
+ if (!baseRef) return null;
183
+ return { baseRef };
184
+ }
185
+
186
+ /**
187
+ * Derive the per-gate `env` overlay that pins the `check-baselines`
188
+ * regression-compare base to the close run's integration branch
189
+ * (Story #3890).
190
+ *
191
+ * The baselines gate resolves its compare ref through `resolveScope`,
192
+ * whose environment layer reads `BASELINE_REF`. Threading
193
+ * `origin/<epicBranch>` here makes the gate diff head against the epic
194
+ * integration branch instead of the framework-default `origin/main`, so
195
+ * drift that already landed on `main` but is outside the Story's own diff
196
+ * does not surface as a phantom regression. The same convention
197
+ * (`origin/<epicBranch>`) is used by the baseline-attribution and
198
+ * auto-refresh paths, keeping read/compare bases aligned.
199
+ *
200
+ * Returns `null` when no integration branch is supplied (the gate then
201
+ * keeps its existing default-ref / consumer-config behaviour untouched).
202
+ *
203
+ * @param {string|undefined|null} epicBranch
204
+ * @returns {{ BASELINE_REF: string } | null}
205
+ */
206
+ function buildBaselinesGateEnv(epicBranch) {
207
+ if (typeof epicBranch !== 'string' || epicBranch.length === 0) return null;
208
+ return { BASELINE_REF: `origin/${epicBranch}` };
209
+ }
210
+
211
+ /**
212
+ * File extensions Biome's formatter can process. Used to filter the
213
+ * changed-file scope down to the formatter-eligible subset (Story #3410):
214
+ * passing only ineligible paths (e.g. a docs-only Story whose diff is all
215
+ * markdown) makes `biome format <files>` report "No files were processed"
216
+ * and exit 1, failing the gate for a Story that has nothing to format.
217
+ *
218
+ * The set mirrors Biome's handled languages (JS/TS family + JSON + CSS).
219
+ * Markdown, YAML, and other unhandled types are intentionally absent — the
220
+ * default formatter is biome, so the scope is keyed to what biome formats.
221
+ * Consumers who swap the formatter via `project.commands.formatCheck`
222
+ * do not get `changedFileScope` at all (see `buildDefaultGates`), so this
223
+ * filter only ever runs against the default biome command.
224
+ */
225
+ const FORMATTER_ELIGIBLE_EXTENSIONS = new Set([
226
+ 'ts',
227
+ 'tsx',
228
+ 'js',
229
+ 'jsx',
230
+ 'mjs',
231
+ 'cjs',
232
+ 'json',
233
+ 'jsonc',
234
+ 'css',
235
+ ]);
236
+
237
+ /**
238
+ * Whether a changed path is eligible for the default (biome) formatter,
239
+ * decided purely by file extension. Pure function — no I/O. Exported for
240
+ * unit coverage (Story #3410).
241
+ *
242
+ * @param {string} filePath - A repo-relative path (forward-slash normalized).
243
+ * @returns {boolean}
244
+ */
245
+ export function isFormatterEligible(filePath) {
246
+ if (typeof filePath !== 'string') return false;
247
+ const lastSlash = Math.max(
248
+ filePath.lastIndexOf('/'),
249
+ filePath.lastIndexOf('\\'),
250
+ );
251
+ const base = filePath.slice(lastSlash + 1);
252
+ const dot = base.lastIndexOf('.');
253
+ // No extension (dotfile-only or extensionless) → not formatter-eligible.
254
+ if (dot <= 0) return false;
255
+ const ext = base.slice(dot + 1).toLowerCase();
256
+ return FORMATTER_ELIGIBLE_EXTENSIONS.has(ext);
257
+ }
258
+
259
+ function applyChangedFileScope({ gate, spawnCwd, log }) {
260
+ if (!gate.changedFileScope) {
261
+ return { gate, cmd: gate.cmd, args: gate.args, skip: false };
262
+ }
263
+ const changedFiles = listChangedFilesForFormatGate({
264
+ cwd: spawnCwd,
265
+ baseRef: gate.changedFileScope.baseRef,
266
+ });
267
+ // Filter to the formatter-eligible subset before deciding to skip. A
268
+ // non-empty diff that contains zero formatter-eligible files (e.g. a
269
+ // docs-only Story) must take the skip path, not invoke biome with only
270
+ // ineligible paths — biome reports "No files were processed" and exits 1
271
+ // in that case (Story #3410).
272
+ const eligibleFiles = changedFiles.filter(isFormatterEligible);
273
+ if (eligibleFiles.length === 0) {
274
+ log(
275
+ `[close-validation] ⏭ ${gate.name} skipped (no formatter-eligible changed files)`,
276
+ );
277
+ return { gate, cmd: gate.cmd, args: gate.args, skip: true };
278
+ }
279
+ const args =
280
+ gate.args[gate.args.length - 1] === '.'
281
+ ? gate.args.slice(0, -1)
282
+ : gate.args;
283
+ log(
284
+ `[close-validation] ↳ ${gate.name} scoped to ${eligibleFiles.length} formatter-eligible changed file(s) from ${gate.changedFileScope.baseRef}...HEAD`,
285
+ );
286
+ return {
287
+ gate,
288
+ cmd: gate.cmd,
289
+ args: [...args, ...eligibleFiles],
290
+ skip: false,
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Resolve whether the CRAP gate is enabled. When enabled, the close-
296
+ * validation graph drops the standalone `test` gate because coverage-
297
+ * capture already runs the suite under c8 instrumentation (Story #1798).
298
+ *
299
+ * Reads the single canonical shape `delivery.quality.gates.crap.enabled`
300
+ * from the resolved config. Defaults to `true` so an omitted setting
301
+ * matches `CRAP_GATE_DEFAULTS.enabled`. We deliberately do NOT round-trip
302
+ * through `getQuality()` here because that resolver expects the unresolved
303
+ * `gates.crap.*` shape.
304
+ *
305
+ * @param {object|undefined|null} config - Canonical resolved config.
306
+ * @returns {boolean}
307
+ */
308
+ function isCrapGateEnabled(config) {
309
+ if (!config || typeof config !== 'object') return true;
310
+ const enabled = config?.delivery?.quality?.gates?.crap?.enabled;
311
+ return typeof enabled === 'boolean' ? enabled : true;
312
+ }
313
+
314
+ /**
315
+ * Conditionally produce the standalone `test` gate entry. Returns an empty
316
+ * array when the CRAP gate is enabled (Story #1798: coverage-capture is the
317
+ * canonical test runner in that mode); returns the legacy single-entry
318
+ * gate otherwise. Splitting this out keeps `buildDefaultGates` flat for
319
+ * the CRAP-cyclomatic gate.
320
+ *
321
+ * @param {object|undefined|null} config - Canonical resolved config.
322
+ * @returns {Gate[]}
323
+ */
324
+ function buildTestGateEntry(config) {
325
+ if (isCrapGateEnabled(config)) return [];
326
+ return [{ name: 'test', cmd: 'npm', args: ['test'] }];
327
+ }
328
+
329
+ /**
330
+ * Build the canonical close-validation gate list.
331
+ *
332
+ * Ordering (cheapest fast-fail first): typecheck → lint → [test] →
333
+ * format → coverage-capture → check-baselines. The standalone `test`
334
+ * gate is dropped when `crap.enabled === true` (Story #1798) because
335
+ * coverage-capture carries test-failure signalling under c8 in that
336
+ * mode.
337
+ *
338
+ * `typecheck` is mandatory; consumers may customise the command via
339
+ * `project.commands.typecheck` (default `npm run typecheck`).
340
+ *
341
+ * Story #2210 retired the legacy per-kind in-process regression gates
342
+ * (`check-maintainability`, `check-crap`, `check-mutation`) and their
343
+ * shared `buildInProcessBaselineGate` runner. The unified
344
+ * `check-baselines` gate is now the single source of truth for per-kind
345
+ * regression enforcement (attribution-wired floor + tolerance + schema).
346
+ * The `epicBranch` parameter threads the close run's integration branch
347
+ * into two gates: the `format` gate's `changedFileScope` (existing) and —
348
+ * since Story #3890 — the `check-baselines` gate's `BASELINE_REF` env, so
349
+ * the baselines regression compare diffs head against the epic integration
350
+ * branch (`origin/<epicBranch>`) rather than the framework-default
351
+ * `origin/main`. Without this, every child Story on an `epic/<id>` branch
352
+ * re-discovered inherited main-vs-epic drift in untouched files as phantom
353
+ * regressions and worked around it by hand-setting `BASELINE_REF`.
354
+ *
355
+ * @param {{ config?: object, epicBranch?: string }} [opts] - `config` is the
356
+ * canonical resolved config (`{ project, delivery, ... }`); gate commands
357
+ * resolve from `project.commands` and the CRAP toggle from
358
+ * `delivery.quality.gates.crap.enabled`. `epicBranch` is the close run's
359
+ * integration branch (`epic/<id>` for Epic-attached Stories, the base
360
+ * branch for standalone Stories).
361
+ * @returns {Gate[]}
362
+ */
363
+ export function buildDefaultGates({ config, epicBranch } = {}) {
364
+ const typecheckCmdString = resolveTypecheckCommand(config);
365
+ const [typecheckCmd, ...typecheckArgs] = typecheckCmdString
366
+ .split(/\s+/)
367
+ .filter(Boolean);
368
+ const formatCheckString = resolveFormatCheckCommand(config);
369
+ const [formatCmd, ...formatArgs] = formatCheckString
370
+ .split(/\s+/)
371
+ .filter(Boolean);
372
+ const formatWriteString = resolveFormatWriteCommand(config);
373
+ const formatChangedFileScope =
374
+ formatCheckString === FORMAT_CHECK_FALLBACK
375
+ ? buildChangedFileScope(epicBranch)
376
+ : null;
377
+ const baselinesGateEnv = buildBaselinesGateEnv(epicBranch);
378
+ return [
379
+ {
380
+ name: 'typecheck',
381
+ cmd: typecheckCmd,
382
+ args: typecheckArgs,
383
+ hint: TYPECHECK_HINT,
384
+ },
385
+ { name: 'lint', cmd: 'npm', args: ['run', 'lint'] },
386
+ ...buildTestGateEntry(config),
387
+ {
388
+ // Gate name kept generic ("format") so the close-orchestrator log line
389
+ // and the per-gate phase-timer key don't shift when a repo swaps biome
390
+ // for Prettier / dprint via `project.commands.formatCheck`. The
391
+ // actual command and the remediation hint resolve from config.
392
+ name: 'format',
393
+ cmd: formatCmd,
394
+ args: formatArgs,
395
+ hint: buildFormatHint(formatWriteString),
396
+ ...(formatChangedFileScope
397
+ ? { changedFileScope: formatChangedFileScope }
398
+ : {}),
399
+ },
400
+ {
401
+ name: 'coverage-capture',
402
+ cmd: 'node',
403
+ args: ['.agents/scripts/coverage-capture.js'],
404
+ hint: 'Coverage capture failed — `npm run test:coverage` exited non-zero. Fix failing tests or coverage-threshold breaches, then re-run close.',
405
+ },
406
+ {
407
+ // Story #2210 — unified `check-baselines` gate is the only path for
408
+ // per-kind regression enforcement. The legacy per-kind in-process
409
+ // gates (`check-maintainability`, `check-crap`, `check-mutation`)
410
+ // were retired with `buildInProcessBaselineGate` because their
411
+ // regression-compare semantics are fully subsumed by this gate's
412
+ // attribution-wired floor + tolerance + schema enforcement, and
413
+ // running both paths in series was redundant and conflict-prone.
414
+ //
415
+ // `check-baselines.js` self-skips per-kind gates whose
416
+ // `enabled === false` is configured, so registering it
417
+ // unconditionally is safe.
418
+ name: 'check-baselines',
419
+ cmd: 'node',
420
+ args: ['.agents/scripts/check-baselines.js', '--format', 'text'],
421
+ hint: 'Unified baselines gate breached. Inspect the JSON report (`node .agents/scripts/check-baselines.js`) to see which kind/component/axis fell below floor; remediate the underlying file(s) or — when the regression is intentional — refresh the relevant baseline through its per-kind update script and commit with a `baseline-refresh:` tagged subject.',
422
+ ...(baselinesGateEnv ? { env: baselinesGateEnv } : {}),
423
+ },
424
+ ];
425
+ }
426
+
427
+ /**
428
+ * Default gate list resolved with no consumer config — uses the
429
+ * `npm run typecheck` fallback for the typecheck gate. Call sites that have a
430
+ * resolved config object in scope (e.g. `story-close.js`) should
431
+ * prefer `buildDefaultGates({ config })` so a configured
432
+ * `project.commands.typecheck` is honoured.
433
+ *
434
+ * @type {Gate[]}
435
+ */
436
+ export const DEFAULT_GATES = buildDefaultGates();
437
+
438
+ /**
439
+ * Gates whose I/O is read-only against the working tree (no shared mutable
440
+ * state, no overlapping ports/sockets). Safe to run concurrently — see
441
+ * `runCloseValidation` for the Promise.all + AbortController plumbing.
442
+ */
443
+ export const INDEPENDENT_GATE_NAMES = new Set(['lint', 'format', 'typecheck']);
444
+
445
+ /**
446
+ * Partition a gate list into the parallel-safe set and the order-sensitive
447
+ * remainder. Order is preserved within each bucket so the serial walk stays
448
+ * cheapest-fast-fail-first (test → coverage-capture → check-baselines).
449
+ *
450
+ * @param {Gate[]} gates
451
+ * @returns {{ independent: Gate[], serial: Gate[] }}
452
+ */
453
+ export function partitionGates(gates) {
454
+ const independent = [];
455
+ const serial = [];
456
+ for (const gate of gates) {
457
+ if (INDEPENDENT_GATE_NAMES.has(gate.name)) independent.push(gate);
458
+ else serial.push(gate);
459
+ }
460
+ return { independent, serial };
461
+ }
462
+
463
+ /**
464
+ * Pipe a child stream's output line-by-line through `emit`, prepending
465
+ * `prefix` to each line. Tail bytes without a trailing newline flush on
466
+ * `end` so the operator never loses the last line of a gate's output.
467
+ */
468
+ function pipePrefixed(stream, prefix, emit) {
469
+ let buf = '';
470
+ stream.setEncoding('utf8');
471
+ stream.on('data', (chunk) => {
472
+ buf += chunk;
473
+ while (true) {
474
+ const nl = buf.indexOf('\n');
475
+ if (nl === -1) break;
476
+ emit(prefix + buf.slice(0, nl));
477
+ buf = buf.slice(nl + 1);
478
+ }
479
+ });
480
+ stream.on('end', () => {
481
+ if (buf.length > 0) emit(prefix + buf);
482
+ });
483
+ }
484
+
485
+ /**
486
+ * Default async gate runner — used by `runCloseValidation` when no `runner`
487
+ * is injected. Spawns the gate via `child_process.spawn`, prefixes every
488
+ * stdout/stderr line with `[gate-name] ` (so concurrent gates don't bleed
489
+ * into each other in the operator's terminal), and resolves only when the
490
+ * child exits.
491
+ *
492
+ * Honours `opts.signal`: a TERM is delivered to the child the moment the
493
+ * signal fires, so a sibling gate's failure aborts the rest of the wave
494
+ * promptly. The promise still resolves (rather than rejecting) on abort —
495
+ * `runCloseValidation` sees a non-zero status and folds it into the
496
+ * already-recorded first-failure.
497
+ *
498
+ * @param {string} cmd
499
+ * @param {string[]} args
500
+ * @param {{ cwd: string, signal?: AbortSignal, gateName?: string, log?: (m: string) => void, env?: Record<string, string> }} opts
501
+ * @returns {Promise<{ status: number }>}
502
+ */
503
+ /** Wire the AbortSignal so an abort kills the child. Returns the cleanup fn. */
504
+ export function attachGateAbortHandler(child, signal) {
505
+ if (!signal) return () => {};
506
+ const killChild = () => {
507
+ try {
508
+ child.kill('SIGTERM');
509
+ } catch {
510
+ /* race: already exited */
511
+ }
512
+ };
513
+ if (signal.aborted) {
514
+ killChild();
515
+ return () => {};
516
+ }
517
+ signal.addEventListener('abort', killChild, { once: true });
518
+ return () => signal.removeEventListener('abort', killChild);
519
+ }
520
+
521
+ /** SIGTERM (no exit code) on abort → non-zero so the gate counts as failed. */
522
+ export function gateExitCode(code, sig) {
523
+ if (typeof code === 'number') return code;
524
+ return sig ? 143 : 1;
525
+ }
526
+
527
+ function defaultGateRunner(cmd, args, opts = {}) {
528
+ const { cwd, signal, gateName, log, env } = opts;
529
+ const child = spawn(cmd, args, {
530
+ cwd,
531
+ shell: process.platform === 'win32',
532
+ stdio: ['ignore', 'pipe', 'pipe'],
533
+ // Per-gate env overlay (Story #3890): merged over the inherited
534
+ // environment so a gate-scoped `BASELINE_REF` reaches the spawned
535
+ // `check-baselines` child without mutating the parent process env.
536
+ ...(env ? { env: { ...process.env, ...env } } : {}),
537
+ });
538
+ const prefix = gateName ? `[${gateName}] ` : '';
539
+ const emit =
540
+ typeof log === 'function' ? log : (m) => process.stdout.write(`${m}\n`);
541
+ pipePrefixed(child.stdout, prefix, emit);
542
+ pipePrefixed(child.stderr, prefix, emit);
543
+ const detach = attachGateAbortHandler(child, signal);
544
+ return new Promise((resolve) => {
545
+ child.on('exit', (code, sig) => {
546
+ detach();
547
+ resolve({ status: gateExitCode(code, sig) });
548
+ });
549
+ child.on('error', () => {
550
+ detach();
551
+ resolve({ status: 1 });
552
+ });
553
+ });
554
+ }
555
+
556
+ /**
557
+ * Run every gate sequentially. Stops collecting after the first failure but
558
+ * still returns a summary so the caller decides how to surface the result.
559
+ *
560
+ * Worktree locality (Story #1120): when `worktreePath` is supplied, every
561
+ * gate runner is spawned with `cwd: worktreePath` so the gate sees the
562
+ * Story branch's post-rebase tree. Evidence reads/writes still key against
563
+ * `cwd` (the main checkout) because the per-Epic temp tree lives under
564
+ * the main `.git/`. Failure messages name the worktree path.
565
+ *
566
+ * Evidence-aware: when both `storyId` and `epicId` are provided and
567
+ * `useEvidence !== false`, each gate consults `validation-evidence
568
+ * .shouldSkip()` against current HEAD + the gate's command-config hash. A
569
+ * matching record skips the gate; a successful run is recorded so the
570
+ * next caller in the local hot path can skip in turn.
571
+ *
572
+ * `onGateStart` is invoked immediately before each gate's runner spawn.
573
+ * story-close uses it to drive `phaseTimer.mark(...)` for per-gate
574
+ * wall-clock telemetry. Errors thrown from the hook propagate.
575
+ *
576
+ * @param {{
577
+ * cwd: string,
578
+ * worktreePath?: string,
579
+ * gates?: Gate[],
580
+ * runner?: (cmd: string, args: string[], opts: { cwd: string, signal?: AbortSignal, gateName?: string, log?: (m: string) => void }) => Promise<{ status: number }> | { status: number },
581
+ * log?: (m: string) => void,
582
+ * onGateStart?: (gate: Gate) => void,
583
+ * storyId?: number|null,
584
+ * epicId?: number|null,
585
+ * useEvidence?: boolean,
586
+ * evidenceClock?: () => number,
587
+ * getHeadSha?: (cwd: string) => string|null,
588
+ * recordPass?: typeof defaultRecordPass,
589
+ * shouldSkip?: typeof defaultShouldSkip,
590
+ * }} opts
591
+ * @returns {{ ok: boolean, failed: Array<{ gate: Gate, status: number, cwd: string }>, skipped: Array<{ gate: Gate, reason: string }> }}
592
+ */
593
+ export async function runCloseValidation({
594
+ cwd,
595
+ worktreePath,
596
+ gates = DEFAULT_GATES,
597
+ runner = defaultGateRunner,
598
+ log = () => {},
599
+ onGateStart,
600
+ storyId = null,
601
+ epicId = null,
602
+ useEvidence = true,
603
+ evidenceClock = () => Date.now(),
604
+ getHeadSha = (resolvedCwd) => defaultGetHeadSha(resolvedCwd),
605
+ recordPass = defaultRecordPass,
606
+ shouldSkip = defaultShouldSkip,
607
+ } = {}) {
608
+ const failed = [];
609
+ const skipped = [];
610
+ const evidenceActive = useEvidence && storyId != null && epicId != null;
611
+ // Evidence keys against the main checkout's HEAD because the per-Epic
612
+ // evidence file lives under the main `.git/`. Gate spawn, in contrast,
613
+ // runs in the worktree when one is supplied — that's the whole point of
614
+ // Story #1120.
615
+ const spawnCwd = worktreePath ?? cwd;
616
+ const headSha = evidenceActive ? getHeadSha(spawnCwd) : null;
617
+
618
+ // Helper closures so the parallel and serial passes share evidence
619
+ // bookkeeping bit-for-bit.
620
+
621
+ /** Returns a `{ skip: true }` verdict when evidence makes the gate redundant. */
622
+ const evidenceVerdict = (gate, configHash) => {
623
+ if (!(evidenceActive && headSha)) return { skip: false };
624
+ const verdict = shouldSkip(
625
+ {
626
+ storyId,
627
+ gateName: gate.name,
628
+ currentSha: headSha,
629
+ configHash,
630
+ inputFingerprint: gate.inputFingerprint ?? null,
631
+ },
632
+ { cwd, epicId },
633
+ );
634
+ if (verdict.skip) {
635
+ const tsHint = verdict.record?.timestamp
636
+ ? ` recorded ${verdict.record.timestamp}`
637
+ : '';
638
+ log(
639
+ `[close-validation] ⏭ ${gate.name} skipped (${verdict.reason}: SHA=${headSha.slice(0, 7)}${tsHint})`,
640
+ );
641
+ }
642
+ return verdict;
643
+ };
644
+
645
+ const recordIfActive = (gate, configHash, durationMs) => {
646
+ if (!(evidenceActive && headSha)) return;
647
+ try {
648
+ recordPass(
649
+ {
650
+ storyId,
651
+ gateName: gate.name,
652
+ sha: headSha,
653
+ configHash,
654
+ exitCode: 0,
655
+ durationMs,
656
+ inputFingerprint: gate.inputFingerprint ?? null,
657
+ },
658
+ { cwd, epicId },
659
+ );
660
+ } catch (err) {
661
+ log(
662
+ `[close-validation] ⚠ failed to record evidence for ${gate.name}: ${err?.message ?? err}`,
663
+ );
664
+ }
665
+ };
666
+
667
+ /**
668
+ * Run a single gate. When `gate.run` is a function the gate executes
669
+ * **in process** (Story #1973 / Task #1984 — per-kind baseline gates
670
+ * removed their `child_process.spawn(node check-<kind>.js)` arm and
671
+ * call `compare(head, base)` directly). The `run` callable receives
672
+ * the same `(cmd, args, opts)` argv shape as `runner` so it slots into
673
+ * the existing contract without churn at the runner boundary.
674
+ * Otherwise the supplied `runner` is used (default: spawn).
675
+ *
676
+ * @returns {Promise<{ status: number }>}
677
+ */
678
+ const dispatchGate = async (gate, signal) => {
679
+ log(
680
+ `[close-validation] ▶ ${gate.name}${worktreePath ? ` (cwd=${worktreePath})` : ''}`,
681
+ );
682
+ if (typeof onGateStart === 'function') onGateStart(gate);
683
+ const dispatcher = typeof gate.run === 'function' ? gate.run : runner;
684
+ const result = await dispatcher(gate.cmd, gate.args, {
685
+ cwd: spawnCwd,
686
+ gateName: gate.name,
687
+ log,
688
+ signal,
689
+ ...(gate.env ? { env: gate.env } : {}),
690
+ });
691
+ return { status: result?.status ?? 1 };
692
+ };
693
+
694
+ const { independent, serial } = partitionGates(gates);
695
+
696
+ // ── Phase 1: independent gates in parallel ──────────────────────────
697
+ // First non-zero exit pins `firstFailure` and aborts every in-flight
698
+ // sibling via SIGTERM. Other gates' results are still awaited (so we
699
+ // never leak children) but their non-zero status is intentionally
700
+ // dropped: only one error surfaces.
701
+ const ac = new AbortController();
702
+ let firstIndepFailure = null;
703
+
704
+ const indepTasks = independent.map(async (gate) => {
705
+ let execution;
706
+ try {
707
+ execution = applyChangedFileScope({ gate, spawnCwd, log });
708
+ } catch (err) {
709
+ if (!firstIndepFailure) {
710
+ firstIndepFailure = { gate, status: 1, cwd: spawnCwd };
711
+ log(
712
+ `[close-validation] ✖ ${gate.name} failed to resolve changed-file scope: ${err?.message ?? err}`,
713
+ );
714
+ ac.abort();
715
+ }
716
+ return;
717
+ }
718
+ if (execution.skip) {
719
+ skipped.push({ gate, reason: 'no-changed-files' });
720
+ return;
721
+ }
722
+ const configHash = hashCommandConfig({
723
+ cmd: execution.cmd,
724
+ args: execution.args,
725
+ cwd: spawnCwd,
726
+ });
727
+ const verdict = evidenceVerdict(gate, configHash);
728
+ if (verdict.skip) {
729
+ skipped.push({ gate, reason: verdict.reason });
730
+ return;
731
+ }
732
+ const startedAt = evidenceActive ? evidenceClock() : 0;
733
+ let result;
734
+ try {
735
+ result = await dispatchGate(
736
+ { ...gate, cmd: execution.cmd, args: execution.args },
737
+ ac.signal,
738
+ );
739
+ } catch (err) {
740
+ result = { status: 1, error: err };
741
+ }
742
+ if (result.status !== 0) {
743
+ if (!firstIndepFailure) {
744
+ firstIndepFailure = { gate, status: result.status, cwd: spawnCwd };
745
+ ac.abort();
746
+ }
747
+ return;
748
+ }
749
+ log(`[close-validation] ✓ ${gate.name}`);
750
+ recordIfActive(
751
+ gate,
752
+ configHash,
753
+ evidenceActive ? evidenceClock() - startedAt : 0,
754
+ );
755
+ });
756
+
757
+ await Promise.all(indepTasks);
758
+
759
+ if (firstIndepFailure) {
760
+ failed.push(firstIndepFailure);
761
+ log(
762
+ `[close-validation] ✖ ${firstIndepFailure.gate.name} failed (exit ${firstIndepFailure.status}) in ${spawnCwd}`,
763
+ );
764
+ if (firstIndepFailure.gate.hint) {
765
+ log(`[close-validation] hint: ${firstIndepFailure.gate.hint}`);
766
+ }
767
+ return { ok: false, failed, skipped };
768
+ }
769
+
770
+ // ── Phase 2: serial gates in declared order ─────────────────────────
771
+ for (const gate of serial) {
772
+ let execution;
773
+ try {
774
+ execution = applyChangedFileScope({ gate, spawnCwd, log });
775
+ } catch (err) {
776
+ failed.push({ gate, status: 1, cwd: spawnCwd });
777
+ log(
778
+ `[close-validation] ✖ ${gate.name} failed to resolve changed-file scope: ${err?.message ?? err}`,
779
+ );
780
+ if (gate.hint) log(`[close-validation] hint: ${gate.hint}`);
781
+ break;
782
+ }
783
+ if (execution.skip) {
784
+ skipped.push({ gate, reason: 'no-changed-files' });
785
+ continue;
786
+ }
787
+ const configHash = hashCommandConfig({
788
+ cmd: execution.cmd,
789
+ args: execution.args,
790
+ cwd: spawnCwd,
791
+ });
792
+ const verdict = evidenceVerdict(gate, configHash);
793
+ if (verdict.skip) {
794
+ skipped.push({ gate, reason: verdict.reason });
795
+ continue;
796
+ }
797
+ const startedAt = evidenceActive ? evidenceClock() : 0;
798
+ const result = await dispatchGate({
799
+ ...gate,
800
+ cmd: execution.cmd,
801
+ args: execution.args,
802
+ });
803
+ if (result.status !== 0) {
804
+ failed.push({ gate, status: result.status, cwd: spawnCwd });
805
+ log(
806
+ `[close-validation] ✖ ${gate.name} failed (exit ${result.status}) in ${spawnCwd}`,
807
+ );
808
+ if (gate.hint) log(`[close-validation] hint: ${gate.hint}`);
809
+ break;
810
+ }
811
+ log(`[close-validation] ✓ ${gate.name}`);
812
+ recordIfActive(
813
+ gate,
814
+ configHash,
815
+ evidenceActive ? evidenceClock() - startedAt : 0,
816
+ );
817
+ }
818
+
819
+ return { ok: failed.length === 0, failed, skipped };
820
+ }
821
+
822
+ /**
823
+ * Pre-merge MI ceiling projection helpers — `projectMaintainabilityRegressions`
824
+ * and `formatMaintainabilityProjection` were extracted to
825
+ * `close-validation/projections/maintainability.js` (Story #1850) so the
826
+ * parent module stays below the 700-LOC ceiling and the inline guard
827
+ * cascade collapses into the shared `validateProjectionInputs` predicate.
828
+ * The re-export below preserves the public contract — every existing call
829
+ * site continues to import from `./close-validation.js`.
830
+ */
831
+ export {
832
+ formatMaintainabilityProjection,
833
+ projectMaintainabilityRegressions,
834
+ } from './close-validation/projections/maintainability.js';
835
+
836
+ /**
837
+ * Throw-away ghSpawnCount emitter (Story #1795 / Epic #1788).
838
+ *
839
+ * Writes the current `gh-exec` spawn counter to
840
+ * `temp/epic-<eid>/stories/story-<sid>/gh-spawn-count.json` so the
841
+ * `analyze-execution.js` child process can read it and emit a
842
+ * `ghSpawnCount` field on the `story-perf-summary` payload. The Story-
843
+ * close orchestrator calls this inside `runPostMergeClose` right before
844
+ * the perf-summary phase, capturing every `gh` invocation from preflight
845
+ * through the merge in one counter snapshot.
846
+ *
847
+ * @param {object} opts
848
+ * @param {number|string} opts.epicId
849
+ * @param {number|string} opts.storyId
850
+ * @param {object} [opts.config] - Resolved config bag so `tempRoot`
851
+ * resolution honours the consumer's configured path.
852
+ * @param {() => number} [opts.getSpawnCountFn=defaultGetSpawnCount] - Test seam.
853
+ * @param {typeof defaultWriteFile} [opts.writeFileFn=defaultWriteFile] - Test seam.
854
+ * @param {{ warn?: (s: string) => void }} [opts.logger] - Best-effort
855
+ * failure-path logger; never throws.
856
+ * @returns {Promise<{ status: 'ok'|'failed', path?: string, ghSpawnCount?: number, reason?: string }>}
857
+ */
858
+ export async function emitGhSpawnCount({
859
+ epicId,
860
+ storyId,
861
+ config,
862
+ getSpawnCountFn = defaultGetSpawnCount,
863
+ writeFileFn = defaultWriteFile,
864
+ logger,
865
+ } = {}) {
866
+ const eid = Number(epicId);
867
+ const sid = Number(storyId);
868
+ if (!Number.isInteger(eid) || eid < 1 || !Number.isInteger(sid) || sid < 1) {
869
+ return { status: 'failed', reason: 'invalid-ids' };
870
+ }
871
+ let ghSpawnCount;
872
+ try {
873
+ ghSpawnCount = getSpawnCountFn();
874
+ } catch (err) {
875
+ logger?.warn?.(
876
+ `[close-validation] gh-spawn-count read failed: ${err?.message ?? err}`,
877
+ );
878
+ return { status: 'failed', reason: 'counter-read-failed' };
879
+ }
880
+ const targetPath = storyArtifactPath(eid, sid, 'gh-spawn-count.json', config);
881
+ const payload = {
882
+ kind: 'gh-spawn-count',
883
+ epicId: eid,
884
+ storyId: sid,
885
+ ghSpawnCount,
886
+ capturedAt: new Date().toISOString(),
887
+ };
888
+ try {
889
+ await writeFileFn(targetPath, JSON.stringify(payload, null, 2));
890
+ return { status: 'ok', path: targetPath, ghSpawnCount };
891
+ } catch (err) {
892
+ logger?.warn?.(
893
+ `[close-validation] gh-spawn-count emit failed: ${err?.message ?? err}`,
894
+ );
895
+ return { status: 'failed', reason: 'write-failed' };
896
+ }
897
+ }