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,797 @@
1
+ /**
2
+ * lib/orchestration/epic-spec-reconciler-apply.js — apply engine for the
3
+ * epic-spec reconciler (Epic #1182 / Tech Spec #1483 / Story #1494).
4
+ *
5
+ * Consumes a `Plan` produced by `epic-spec-reconciler-diff.js#diff()` and
6
+ * materialises the operations against an `ITicketingProvider` with
7
+ * bounded concurrency. The apply engine is the only place in the
8
+ * structural reconciler that touches the world — every other module in
9
+ * the surface (`epic-spec-reconciler-ops.js`,
10
+ * `epic-spec-reconciler-diff.js`,
11
+ * `epic-spec-reconciler-discriminator.js`,
12
+ * `epic-spec-reconciler-format.js`) is intentionally I/O-free so the
13
+ * data path can be unit-tested in isolation.
14
+ *
15
+ * ## Contract
16
+ *
17
+ * - `apply(plan, opts)` — executes the plan and returns a typed result
18
+ * envelope describing what was created, updated, closed, or
19
+ * relinked. The envelope's shape is stable and machine-readable.
20
+ * - Concurrency is bounded at 4 via `concurrentMap` from
21
+ * `lib/util/concurrent-map.js`. The cap matches the
22
+ * `RECONCILE_CONCURRENCY` constant in
23
+ * `lib/orchestration/reconciler.js:18` so structural and
24
+ * label-hygiene reconciliation operate at the same provider load.
25
+ * - `opts.dryRun === true` short-circuits with zero provider calls.
26
+ * The returned envelope echoes the plan's intent (`created`,
27
+ * `updated`, `closed`, `relinked` arrays populated from the plan
28
+ * ops, marked `dryRun: true`).
29
+ * - The discriminator gates (`mayClose`, `mayUpdate`) are re-asserted
30
+ * before any mutation runs. A plan op that fails the gate aborts
31
+ * `apply` synchronously (Promise rejection) **before** any provider
32
+ * call is dispatched, so partial failure of a forbidden op is
33
+ * impossible.
34
+ * - `assertPlanLabelAllowList` is invoked unconditionally at entry to
35
+ * re-prove the safety net even on hand-built plans (diff already
36
+ * asserts; we double-check at apply because tests and CLI callers
37
+ * may bypass the diff path).
38
+ *
39
+ * ## Result envelope
40
+ *
41
+ * @typedef {object} ApplyResultEntry
42
+ * @property {string} slug
43
+ * @property {string} entity
44
+ * @property {string} kind 'create'|'update'|'close'|'relink'
45
+ * @property {number} [issueNumber] Resulting issue number (post-create)
46
+ * or the targeted issue number for
47
+ * update/close/relink.
48
+ * @property {string} [url] Issue URL when the provider returned one.
49
+ *
50
+ * @typedef {object} ApplyResult
51
+ * @property {boolean} dryRun
52
+ * @property {ApplyResultEntry[]} created
53
+ * @property {ApplyResultEntry[]} updated
54
+ * @property {ApplyResultEntry[]} closed
55
+ * @property {ApplyResultEntry[]} relinked
56
+ * @property {Record<string, number>} slugToIssue
57
+ * Post-apply slug → issue mapping. On full success, this is the
58
+ * complete map; on partial failure, it reflects creates that landed
59
+ * before the failure.
60
+ * @property {object} [state] The state object the apply
61
+ * pipeline persisted via the
62
+ * `writeState` hook. Omitted when
63
+ * `opts.spec` was not supplied.
64
+ * @property {string} [statePath] Absolute path the default
65
+ * writer returned; omitted when a
66
+ * custom `writeState` hook ran or
67
+ * when no spec was supplied.
68
+ * @property {Error} [failure] Present only when apply exited
69
+ * via partial-failure: the
70
+ * provider error that aborted
71
+ * further dispatch. The state file
72
+ * was still written to reflect the
73
+ * successful ops so the operator
74
+ * can resume cleanly.
75
+ *
76
+ * @typedef {object} ApplyOptions
77
+ * @property {boolean} [dryRun] Skip provider calls; echo plan.
78
+ * @property {number} [concurrency] Override the default cap (4).
79
+ * Tests use 1 to assert order.
80
+ * @property {number} [epicId] Required for create ops — the
81
+ * parent Epic issue number that
82
+ * feeds into createTicket's
83
+ * ticketData.epicId field.
84
+ * @property {Record<string, number>} [slugToIssue]
85
+ * Pre-seeded slug → issue map.
86
+ * Apply populates it as creates
87
+ * land so child creates can
88
+ * resolve parentSlug → parentId.
89
+ * Callers (state writer) may
90
+ * read the post-apply state from
91
+ * the returned envelope's
92
+ * `slugToIssue` field.
93
+ * @property {object} [storySnapshots] Optional snapshot map keyed
94
+ * by slug carrying live
95
+ * execution state for the
96
+ * close-discriminator. When
97
+ * absent, close ops require
98
+ * `explicitDelete: true` per
99
+ * the discriminator's default.
100
+ * @property {boolean} [explicitDelete] Operator opt-in: passes
101
+ * through to `mayClose`.
102
+ * @property {object} [spec] Parsed spec used to derive
103
+ * the post-apply state-file
104
+ * mapping (Task #1518). When
105
+ * omitted, apply does not
106
+ * touch state.json — useful
107
+ * for tests that only want to
108
+ * drive the provider surface.
109
+ * @property {object} [priorState] Prior state loaded from
110
+ * `<epicId>.state.json` (carries
111
+ * pre-existing slug → issue
112
+ * mappings and observed agent
113
+ * states for slugs unchanged
114
+ * by this apply).
115
+ * @property {string} [stateNow] Override for the
116
+ * `lastReconciledAt` timestamp.
117
+ * Tests pass a fixed string so
118
+ * the rendered state is
119
+ * byte-stable.
120
+ * @property {(epicId: number, state: object) => string} [writeState]
121
+ * Optional state writer
122
+ * injection point. Defaults to
123
+ * `lib/spec/loader.js#writeState`.
124
+ * Tests pass an in-memory
125
+ * spy so apply does not touch
126
+ * the real on-disk file.
127
+ * @property {{epicsDir?: string}} [writeStateOpts]
128
+ * Forwarded to the default
129
+ * `writeState` adapter so tests
130
+ * can redirect the on-disk
131
+ * output to a tmp dir.
132
+ */
133
+
134
+ import { Logger } from '../Logger.js';
135
+ import { writeState as defaultWriteState } from '../spec/loader.js';
136
+ import { buildState } from '../spec/state.js';
137
+ import { concurrentMap } from '../util/concurrent-map.js';
138
+ import {
139
+ assertPlanLabelAllowList,
140
+ mayClose,
141
+ mayUpdate,
142
+ } from './epic-spec-reconciler-discriminator.js';
143
+ import { isPlan, OP_KINDS } from './epic-spec-reconciler-ops.js';
144
+
145
+ /**
146
+ * Default concurrency cap. Hard-pinned to 4 to match
147
+ * `RECONCILE_CONCURRENCY` declared in
148
+ * `.agents/scripts/lib/orchestration/reconciler.js:18`. That constant is
149
+ * file-local in `reconciler.js`; rather than widen its surface for one
150
+ * caller, we duplicate the value with a documented cross-reference. The
151
+ * sibling `concurrency-wiring.test.js` suite owns the invariant that
152
+ * these two values stay in sync.
153
+ */
154
+ export const APPLY_CONCURRENCY = 4;
155
+
156
+ /**
157
+ * Error class thrown when a plan operation fails a discriminator gate.
158
+ * Carrying structured metadata (`slug`, `field`, `reason`) lets the CLI
159
+ * report which op was rejected without re-parsing the message.
160
+ */
161
+ export class ApplyGateViolation extends Error {
162
+ /**
163
+ * @param {string} message
164
+ * @param {{slug?: string, kind?: string, field?: string, reason?: string}} [meta]
165
+ */
166
+ constructor(message, meta = {}) {
167
+ super(message);
168
+ this.name = 'ApplyGateViolation';
169
+ if (meta.slug !== undefined) this.slug = meta.slug;
170
+ if (meta.kind !== undefined) this.kind = meta.kind;
171
+ if (meta.field !== undefined) this.field = meta.field;
172
+ if (meta.reason !== undefined) this.reason = meta.reason;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Pre-flight gate check. Walks every op in the plan and asserts each one
178
+ * passes its discriminator. Throws `ApplyGateViolation` on the first
179
+ * failure so apply aborts before dispatching any provider call.
180
+ *
181
+ * Update ops are checked field-by-field: every key in `op.changes` must
182
+ * pass `mayUpdate(_, field)`. The diff engine already constrains keys to
183
+ * the structural allow-list, but we re-check here so a hand-built plan
184
+ * cannot bypass the safety net.
185
+ *
186
+ * Close ops consult `mayClose(snapshot, { explicitDelete })`. The
187
+ * snapshot is looked up from `opts.storySnapshots` keyed by slug;
188
+ * absence is fine — `mayClose` defaults to the conservative "require
189
+ * explicit delete" path.
190
+ *
191
+ * @param {import('./epic-spec-reconciler-ops.js').Plan} plan
192
+ * @param {ApplyOptions} opts
193
+ * @returns {void}
194
+ */
195
+ function assertGates(plan, opts) {
196
+ for (const op of plan.updates) {
197
+ for (const field of Object.keys(op.changes ?? {})) {
198
+ const result = mayUpdate(undefined, field);
199
+ if (!result.allowed) {
200
+ throw new ApplyGateViolation(
201
+ `update for slug=${op.slug} field=${field} blocked: ${result.reason}`,
202
+ { slug: op.slug, kind: 'update', field, reason: result.reason },
203
+ );
204
+ }
205
+ }
206
+ }
207
+ for (const op of plan.closes) {
208
+ const snapshot = opts.storySnapshots?.[op.slug];
209
+ const result = mayClose(snapshot, {
210
+ explicitDelete: opts.explicitDelete === true,
211
+ });
212
+ if (!result.allowed) {
213
+ throw new ApplyGateViolation(
214
+ `close for slug=${op.slug} blocked: ${result.reason}`,
215
+ { slug: op.slug, kind: 'close', reason: result.reason },
216
+ );
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Build a dry-run envelope that echoes the plan's intent without making
223
+ * provider calls. Useful for CLI `--dry-run` output and for the apply
224
+ * pipeline's preview path.
225
+ *
226
+ * @param {import('./epic-spec-reconciler-ops.js').Plan} plan
227
+ * @param {Record<string, number>} slugToIssue
228
+ * @returns {ApplyResult}
229
+ */
230
+ /**
231
+ * Seed `slugToIssue.epic` so child features can resolve
232
+ * `parentSlug: 'epic'` through `resolveParentId`. Story #1820: the CLI
233
+ * seeds `state.mapping.epic` so diff no longer emits a Create for the
234
+ * epic; without this matching seed on the apply side, `slugToIssue.epic`
235
+ * is never populated and the first feature create throws "parent slug
236
+ * epic has no mapped issue number". Priority order:
237
+ * 1. Existing `slugToIssue.epic` (caller-supplied) — wins.
238
+ * 2. `opts.priorState.mapping.epic.issueNumber`.
239
+ * 3. `opts.epicId`.
240
+ *
241
+ * @param {Record<string, number>} slugToIssue cloned mapping (mutated in place)
242
+ * @param {ApplyOptions} opts
243
+ * @returns {Record<string, number>} same mapping, returned for chaining
244
+ */
245
+ function seedEpicSlug(slugToIssue, opts) {
246
+ if (typeof slugToIssue.epic === 'number') return slugToIssue;
247
+ const priorEpicNumber = opts.priorState?.mapping?.epic?.issueNumber;
248
+ const seed =
249
+ typeof priorEpicNumber === 'number'
250
+ ? priorEpicNumber
251
+ : typeof opts.epicId === 'number'
252
+ ? opts.epicId
253
+ : null;
254
+ if (seed !== null) slugToIssue.epic = seed;
255
+ return slugToIssue;
256
+ }
257
+
258
+ function buildDryRunResult(plan, slugToIssue) {
259
+ return {
260
+ dryRun: true,
261
+ created: plan.creates.map((op) => ({
262
+ slug: op.slug,
263
+ entity: op.entity,
264
+ kind: OP_KINDS.CREATE,
265
+ })),
266
+ updated: plan.updates.map((op) => ({
267
+ slug: op.slug,
268
+ entity: op.entity,
269
+ kind: OP_KINDS.UPDATE,
270
+ issueNumber: op.issueNumber,
271
+ })),
272
+ closed: plan.closes.map((op) => ({
273
+ slug: op.slug,
274
+ entity: op.entity,
275
+ kind: OP_KINDS.CLOSE,
276
+ issueNumber: op.issueNumber,
277
+ })),
278
+ relinked: plan.relinks.map((op) => ({
279
+ slug: op.slug,
280
+ entity: op.entity,
281
+ kind: OP_KINDS.RELINK,
282
+ issueNumber: op.issueNumber,
283
+ })),
284
+ slugToIssue: { ...slugToIssue },
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Resolve a parent slug to an issue number using the running map. Throws
290
+ * a structured error if the slug is required but unknown — apply must
291
+ * not silently drop a parent edge.
292
+ *
293
+ * @param {string|undefined} slug
294
+ * @param {Record<string, number>} slugToIssue
295
+ * @returns {number|undefined}
296
+ */
297
+ function resolveParentId(slug, slugToIssue) {
298
+ if (!slug) return undefined;
299
+ const id = slugToIssue[slug];
300
+ if (typeof id !== 'number') {
301
+ throw new ApplyGateViolation(
302
+ `apply: parent slug ${slug} has no mapped issue number`,
303
+ { slug, kind: 'create', reason: 'unmapped-parent' },
304
+ );
305
+ }
306
+ return id;
307
+ }
308
+
309
+ /**
310
+ * Resolve a dependsOn slug list to its issue numbers, failing loud on any
311
+ * unresolved slug. The reconciler hands the resolved numbers to
312
+ * `createTicket` as `ticketData.dependencies`; the provider's
313
+ * `composeStoryBody` is the single owner that renders the canonical
314
+ * `blocked by #N` footer from them (Story #3958). The reconciler must NOT
315
+ * also pre-append a footer to the body — doing so doubled every dependency
316
+ * line, since `composeStoryBody` appends from `dependencies` independently.
317
+ *
318
+ * Fail-loud is preserved here (not delegated to the body composer): an
319
+ * unresolved slug means `topoSortCreates` failed to order a dependency
320
+ * ahead of its dependent, which would otherwise be silently dropped by
321
+ * `dependencies`' number filter. Returns `[]` when no deps.
322
+ *
323
+ * @param {string[]|undefined} dependsOn
324
+ * @param {Record<string, number>} slugToIssue
325
+ * @returns {number[]}
326
+ */
327
+ function resolveDependencies(dependsOn, slugToIssue) {
328
+ if (!Array.isArray(dependsOn) || dependsOn.length === 0) return [];
329
+ const resolved = [];
330
+ const unresolved = [];
331
+ for (const slug of dependsOn) {
332
+ const issueNumber = slugToIssue[slug];
333
+ if (typeof issueNumber === 'number') {
334
+ resolved.push(issueNumber);
335
+ } else {
336
+ unresolved.push(slug);
337
+ }
338
+ }
339
+ if (unresolved.length > 0) {
340
+ throw new Error(
341
+ `[reconciler] resolveDependencies: unresolved dependsOn slugs: ${unresolved.join(', ')}. ` +
342
+ `topoSortCreates must order dependencies ahead of dependents.`,
343
+ );
344
+ }
345
+ return resolved;
346
+ }
347
+
348
+ /**
349
+ * Materialise a single create op. The provider returns
350
+ * `{ id, url }`; we record the slug → id mapping and reflect it in the
351
+ * envelope.
352
+ *
353
+ * @param {import('./epic-spec-reconciler-ops.js').CreateOp} op
354
+ * @param {object} provider
355
+ * @param {ApplyOptions} opts
356
+ * @param {Record<string, number>} slugToIssue
357
+ * @returns {Promise<ApplyResultEntry>}
358
+ */
359
+ async function applyCreate(op, provider, opts, slugToIssue) {
360
+ const parentId = resolveParentId(op.parentSlug, slugToIssue);
361
+ const epicId = typeof opts.epicId === 'number' ? opts.epicId : parentId;
362
+ // Story #3958 — resolve (and fail-loud validate) the dependency issue
363
+ // numbers, but do NOT pre-append a `blocked by #N` footer to the body.
364
+ // `composeStoryBody` (the provider's createTicket renderer) is the single
365
+ // owner of that footer; appending here too doubled every dependency line.
366
+ const dependencies = resolveDependencies(op.dependsOn, slugToIssue);
367
+ const ticketData = {
368
+ epicId,
369
+ title: op.title,
370
+ body: op.body ?? '',
371
+ labels: op.labels ?? [],
372
+ dependencies,
373
+ };
374
+ // Epic-level create has no parent — the provider's createTicket
375
+ // expects a parent for sub-issue linkage. For the epic op we route to
376
+ // the same surface but the parentId fallback (epicId) is fine since
377
+ // the epic *is* its own anchor; the diff engine never emits this in
378
+ // practice (the epic is bootstrapped before reconciliation), but we
379
+ // keep the path safe.
380
+ const created = await provider.createTicket(parentId ?? epicId, ticketData);
381
+ if (created && typeof created.id === 'number') {
382
+ slugToIssue[op.slug] = created.id;
383
+ }
384
+ // Defence — Story #2063. GitHubProvider.createTicket captures a
385
+ // failed addSubIssue mutation into `{ subIssueLinked: false,
386
+ // subIssueError }` rather than throwing, so a transient GraphQL
387
+ // failure (secondary rate-limit, "Something went wrong" envelope,
388
+ // etc.) leaves the issue created on GH but with no native sub-issue
389
+ // link to its parent. The previous behaviour swallowed this
390
+ // entirely — a partial backlog persisted with no operator-visible
391
+ // signal. Surface it here at WARN so the breadcrumb is visible; the
392
+ // canonical repair runs in `runDecomposePhase` via
393
+ // `reconcileSubIssueLinks(epicId)` after every apply pass.
394
+ if (created && created.subIssueLinked === false) {
395
+ const reason =
396
+ created.subIssueError?.message ?? 'no error message captured';
397
+ Logger.warn(
398
+ `[reconciler.apply] sub-issue link failed for child #${created.id} (parent #${parentId ?? epicId}): ${reason}. The runDecomposePhase safety net will retry.`,
399
+ );
400
+ }
401
+ return {
402
+ slug: op.slug,
403
+ entity: op.entity,
404
+ kind: OP_KINDS.CREATE,
405
+ issueNumber: created?.id,
406
+ url: created?.url,
407
+ subIssueLinked: created?.subIssueLinked,
408
+ };
409
+ }
410
+
411
+ /**
412
+ * Materialise a single update op. Translates the plan's `changes` map
413
+ * into the provider's `mutations` shape: `title`/`body` map directly,
414
+ * `labels` becomes `{ add, remove }` derived from the before/after
415
+ * difference, and `wave` is appended to the body marker (the wave
416
+ * integer lives in the body, not on a label).
417
+ *
418
+ * @param {import('./epic-spec-reconciler-ops.js').UpdateOp} op
419
+ * @param {object} provider
420
+ * @returns {Promise<ApplyResultEntry>}
421
+ */
422
+ async function applyUpdate(op, provider) {
423
+ const mutations = {};
424
+ const changes = op.changes ?? {};
425
+ if (changes.title) {
426
+ mutations.title = changes.title.after;
427
+ }
428
+ if (changes.body) {
429
+ mutations.body = changes.body.after;
430
+ }
431
+ if (changes.labels) {
432
+ const before = new Set(changes.labels.before ?? []);
433
+ const after = new Set(changes.labels.after ?? []);
434
+ const add = [...after].filter((l) => !before.has(l));
435
+ const remove = [...before].filter((l) => !after.has(l));
436
+ if (add.length || remove.length) {
437
+ mutations.labels = { add, remove };
438
+ }
439
+ }
440
+ await provider.updateTicket(op.issueNumber, mutations);
441
+ return {
442
+ slug: op.slug,
443
+ entity: op.entity,
444
+ kind: OP_KINDS.UPDATE,
445
+ issueNumber: op.issueNumber,
446
+ };
447
+ }
448
+
449
+ /**
450
+ * Materialise a single close op. The provider's `updateTicket`
451
+ * mutation surface accepts `{ state: 'closed' }`, matching the mock
452
+ * provider in `tests/fixtures/mock-provider.js`.
453
+ *
454
+ * @param {import('./epic-spec-reconciler-ops.js').CloseOp} op
455
+ * @param {object} provider
456
+ * @returns {Promise<ApplyResultEntry>}
457
+ */
458
+ async function applyClose(op, provider) {
459
+ await provider.updateTicket(op.issueNumber, { state: 'closed' });
460
+ return {
461
+ slug: op.slug,
462
+ entity: op.entity,
463
+ kind: OP_KINDS.CLOSE,
464
+ issueNumber: op.issueNumber,
465
+ };
466
+ }
467
+
468
+ /**
469
+ * Materialise a single relink op. Parent edge changes are written by
470
+ * removing the existing sub-issue link (when present) and adding the
471
+ * new one. DependsOn edge changes rewrite the body's `blocked by`
472
+ * footer; we surface the new edge list to the provider via an
473
+ * `updateTicket` body mutation. The plan carries before/after so the
474
+ * caller can render the body locally without re-fetching the ticket
475
+ * (`opts.bodyRenderer` injects the renderer; absence = no-op on body).
476
+ *
477
+ * @param {import('./epic-spec-reconciler-ops.js').RelinkOp} op
478
+ * @param {object} provider
479
+ * @param {Record<string, number>} slugToIssue
480
+ * @returns {Promise<ApplyResultEntry>}
481
+ */
482
+ async function applyRelink(op, provider, slugToIssue) {
483
+ if (op.parent) {
484
+ const before = op.parent.before;
485
+ const after = op.parent.after;
486
+ if (before) {
487
+ const beforeId = slugToIssue[before];
488
+ if (typeof beforeId === 'number') {
489
+ await provider.removeSubIssue(beforeId, op.issueNumber);
490
+ }
491
+ }
492
+ if (after) {
493
+ const afterId = slugToIssue[after];
494
+ if (typeof afterId === 'number') {
495
+ await provider.addSubIssue(afterId, op.issueNumber);
496
+ }
497
+ }
498
+ }
499
+ // Story #2982 — dependsOn edge changes no longer write the body here.
500
+ // The diff engine recomposes the canonical orchestrator footer
501
+ // (`---\nparent:`/`Epic:`/`blocked by`) and routes a body change
502
+ // through `applyUpdate`. Writing it from the relink path stripped
503
+ // description + parent + Epic on every dep change. The relink op
504
+ // remains the authoritative carrier of the dependsOn delta for the
505
+ // state writer and for the parent sub-issue add/remove above.
506
+ return {
507
+ slug: op.slug,
508
+ entity: op.entity,
509
+ kind: OP_KINDS.RELINK,
510
+ issueNumber: op.issueNumber,
511
+ };
512
+ }
513
+
514
+ /**
515
+ * Apply a plan against an `ITicketingProvider`.
516
+ *
517
+ * Execution order: creates → updates → closes → relinks. Creates run
518
+ * first so subsequent updates/relinks can target the newly minted
519
+ * issue numbers (the running `slugToIssue` map propagates IDs across
520
+ * phases). Within each phase, ops are dispatched through `concurrentMap`
521
+ * at cap=4.
522
+ *
523
+ * Errors:
524
+ * - `ApplyGateViolation` — pre-flight gate failed; no provider call
525
+ * was issued.
526
+ * - Provider errors — `concurrentMap`'s first-rejection-wins semantics
527
+ * surface the first failure; later rejections are swallowed. The
528
+ * `slugToIssue` map reflects whatever creates completed before the
529
+ * failure, so the caller (state writer) can persist a partial
530
+ * mapping if desired.
531
+ *
532
+ * @param {import('./epic-spec-reconciler-ops.js').Plan} plan
533
+ * @param {object} provider ITicketingProvider instance.
534
+ * @param {ApplyOptions} [opts]
535
+ * @returns {Promise<ApplyResult>}
536
+ */
537
+ export async function apply(plan, provider, opts = {}) {
538
+ if (!isPlan(plan)) {
539
+ throw new TypeError('apply: plan must conform to the Plan shape');
540
+ }
541
+ if (!provider || typeof provider !== 'object') {
542
+ throw new TypeError('apply: provider is required');
543
+ }
544
+ // Re-prove the label allow-list safety net at the apply boundary.
545
+ assertPlanLabelAllowList(plan);
546
+
547
+ const concurrency =
548
+ typeof opts.concurrency === 'number' && opts.concurrency > 0
549
+ ? opts.concurrency
550
+ : APPLY_CONCURRENCY;
551
+ const slugToIssue = seedEpicSlug({ ...(opts.slugToIssue ?? {}) }, opts);
552
+
553
+ if (opts.dryRun === true) {
554
+ return buildDryRunResult(plan, slugToIssue);
555
+ }
556
+
557
+ // Pre-flight gates. Throws synchronously before any provider call.
558
+ assertGates(plan, opts);
559
+
560
+ const created = [];
561
+ const updated = [];
562
+ const closed = [];
563
+ const relinked = [];
564
+ let failure = null;
565
+
566
+ // Phase 1: creates. Topo-batched so parent issues materialise before
567
+ // their children.
568
+ try {
569
+ const orderedCreates = topoSortCreates(plan.creates, slugToIssue);
570
+ for (const batch of orderedCreates) {
571
+ const batchResults = await concurrentMap(
572
+ batch,
573
+ (op) => applyCreate(op, provider, opts, slugToIssue),
574
+ { concurrency },
575
+ );
576
+ created.push(...batchResults);
577
+ }
578
+ // Phase 2: updates. Independent — run in parallel.
579
+ updated.push(
580
+ ...(await concurrentMap(plan.updates, (op) => applyUpdate(op, provider), {
581
+ concurrency,
582
+ })),
583
+ );
584
+ // Phase 3: closes. Independent — run in parallel. Note we remove the
585
+ // slug from `slugToIssue` so the projected state mapping drops it
586
+ // (matches the AC: closes disappear from state).
587
+ closed.push(
588
+ ...(await concurrentMap(
589
+ plan.closes,
590
+ async (op) => {
591
+ const entry = await applyClose(op, provider);
592
+ delete slugToIssue[op.slug];
593
+ return entry;
594
+ },
595
+ { concurrency },
596
+ )),
597
+ );
598
+ // Phase 4: relinks. Independent — run in parallel.
599
+ relinked.push(
600
+ ...(await concurrentMap(
601
+ plan.relinks,
602
+ (op) => applyRelink(op, provider, slugToIssue),
603
+ { concurrency },
604
+ )),
605
+ );
606
+ } catch (err) {
607
+ failure = err;
608
+ }
609
+
610
+ const result = {
611
+ dryRun: false,
612
+ created,
613
+ updated,
614
+ closed,
615
+ relinked,
616
+ slugToIssue: { ...slugToIssue },
617
+ };
618
+
619
+ // State writer integration (Task #1518). When the caller provided a
620
+ // spec, project the resulting state and persist it. On partial failure
621
+ // the state reflects only completed operations — the `slugToIssue` map
622
+ // above already encodes that (failed creates never landed; closed
623
+ // slugs were removed; failed closes leave the slug present).
624
+ if (opts.spec && typeof opts.spec === 'object') {
625
+ const writeStateFn = opts.writeState ?? defaultWriteState;
626
+ const epicId =
627
+ typeof opts.epicId === 'number'
628
+ ? opts.epicId
629
+ : typeof opts.spec?.epic?.id === 'number'
630
+ ? opts.spec.epic.id
631
+ : undefined;
632
+ const state = projectStateForWrite({
633
+ spec: opts.spec,
634
+ priorState: opts.priorState,
635
+ slugToIssue,
636
+ now: opts.stateNow,
637
+ });
638
+ const writeArgs = [epicId, state];
639
+ if (opts.writeStateOpts) writeArgs.push(opts.writeStateOpts);
640
+ const writePath = writeStateFn(...writeArgs);
641
+ result.state = state;
642
+ if (typeof writePath === 'string') {
643
+ result.statePath = writePath;
644
+ }
645
+ }
646
+
647
+ if (failure) {
648
+ result.failure = failure;
649
+ }
650
+ return result;
651
+ }
652
+
653
+ /**
654
+ * Project a state object from `(spec, priorState, slugToIssue)` for the
655
+ * state writer. Layers the post-apply slug → issue map onto the
656
+ * canonical `buildState` output so newly-created issues get their fresh
657
+ * numbers, dropped slugs disappear, and pre-existing observed agent
658
+ * state is preserved for slugs unchanged by this apply.
659
+ *
660
+ * Idempotency contract (AC: "successful apply followed by an immediate
661
+ * second apply is a no-op"). The diff engine's empty plan, the projected
662
+ * mapping, and the byte-identical state writer compose to produce the
663
+ * same on-disk state across re-runs.
664
+ *
665
+ * @param {{spec: object, priorState?: object, slugToIssue: Record<string, number>, now?: string}} input
666
+ * @returns {{epicId: number, lastReconciledAt: string, mapping: object}}
667
+ */
668
+ function projectStateForWrite({ spec, priorState, slugToIssue, now }) {
669
+ const base = buildState(spec, priorState ?? {}, now ? { now } : {});
670
+ // `buildState` projects feature/story/task slugs — `iterSpecEntries`
671
+ // intentionally walks features down, so the epic slug is not yielded.
672
+ // The reconciler's contract (Tech Spec §"state.json") expects the
673
+ // epic to appear in the mapping too: layer it on explicitly here so
674
+ // a follow-up diff sees the epic mapped and emits no create for it.
675
+ if (spec?.epic && typeof spec.epic.id === 'number' && !base.mapping.epic) {
676
+ const priorEpic = priorState?.mapping?.epic ? priorState.mapping.epic : {};
677
+ base.mapping.epic = {
678
+ issueNumber:
679
+ typeof slugToIssue.epic === 'number'
680
+ ? slugToIssue.epic
681
+ : typeof priorEpic.issueNumber === 'number'
682
+ ? priorEpic.issueNumber
683
+ : spec.epic.id,
684
+ contentHash:
685
+ typeof priorEpic.contentHash === 'string' ? priorEpic.contentHash : '',
686
+ lastObservedAgentState:
687
+ typeof priorEpic.lastObservedAgentState === 'string'
688
+ ? priorEpic.lastObservedAgentState
689
+ : null,
690
+ entity: 'epic',
691
+ parentSlug: null,
692
+ };
693
+ }
694
+ // Layer structural edge metadata (entity, parentSlug, dependsOn) onto
695
+ // each mapping entry. The diff engine reads these fields to decide
696
+ // whether a Relink op should fire; without them every follow-up diff
697
+ // would re-emit a relink for the parent edge that already exists in
698
+ // GH. The AC's idempotency invariant requires this layering — the
699
+ // pure mapping projection (`spec/state.js`) intentionally does not
700
+ // carry edge data so the hashing path can stay isolated.
701
+ const edgeLookup = collectStructuralEdges(spec);
702
+ for (const slug of Object.keys(base.mapping)) {
703
+ const entry = base.mapping[slug];
704
+ const issueNumber = slugToIssue[slug];
705
+ if (typeof issueNumber === 'number') {
706
+ entry.issueNumber = issueNumber;
707
+ }
708
+ const edges = edgeLookup[slug];
709
+ if (edges) {
710
+ entry.entity = edges.entity;
711
+ entry.parentSlug = edges.parentSlug;
712
+ if (edges.dependsOn) entry.dependsOn = edges.dependsOn;
713
+ }
714
+ }
715
+ // Closed slugs (dropped from spec) are absent from `base.mapping`
716
+ // because `buildState` walks the spec. That matches the AC: closed
717
+ // entries do not appear in state.
718
+ return base;
719
+ }
720
+
721
+ /**
722
+ * Walk the spec once and emit `{ slug → { entity, parentSlug, dependsOn? } }`
723
+ * so `projectStateForWrite` can layer the edges back onto the state
724
+ * mapping. Pure / allocation-bounded; runs O(spec size).
725
+ *
726
+ * @param {object} spec
727
+ * @returns {Record<string, {entity: string, parentSlug: string|null, dependsOn?: string[]}>}
728
+ */
729
+ function collectStructuralEdges(spec) {
730
+ const out = {};
731
+ if (!spec || typeof spec !== 'object') return out;
732
+ const epicSlug = 'epic';
733
+ for (const feature of spec.features ?? []) {
734
+ if (!feature?.slug) continue;
735
+ out[feature.slug] = { entity: 'feature', parentSlug: epicSlug };
736
+ for (const story of feature.stories ?? []) {
737
+ if (!story?.slug) continue;
738
+ out[story.slug] = {
739
+ entity: 'story',
740
+ parentSlug: feature.slug,
741
+ dependsOn: [...(story.dependsOn ?? [])].sort(),
742
+ };
743
+ for (const task of story.tasks ?? []) {
744
+ if (!task?.slug) continue;
745
+ out[task.slug] = { entity: 'task', parentSlug: story.slug };
746
+ }
747
+ }
748
+ }
749
+ return out;
750
+ }
751
+
752
+ /**
753
+ * Topologically sort the create ops into dependency batches. Each batch
754
+ * is a list of ops whose parents are already in `slugToIssue` or in an
755
+ * earlier batch. This keeps parent creates ahead of child creates
756
+ * without forcing a global single-file execution.
757
+ *
758
+ * @param {import('./epic-spec-reconciler-ops.js').CreateOp[]} creates
759
+ * @param {Record<string, number>} slugToIssue
760
+ * @returns {import('./epic-spec-reconciler-ops.js').CreateOp[][]}
761
+ */
762
+ function topoSortCreates(creates, slugToIssue) {
763
+ if (!creates.length) return [];
764
+ const remaining = [...creates];
765
+ const knownSlugs = new Set(Object.keys(slugToIssue));
766
+ // dependsOn slugs may reference siblings that are themselves in the
767
+ // create batch — those become known once their batch lands. Any slug
768
+ // that is neither pre-known nor will be created here is an external
769
+ // reference we can't satisfy via topo order; treat it as "satisfiable
770
+ // outside the batch" so we don't deadlock. The fail-loud renderer
771
+ // catches truly unresolved slugs at apply time.
772
+ const createSlugs = new Set(creates.map((op) => op.slug));
773
+ const batches = [];
774
+ while (remaining.length) {
775
+ const ready = remaining.filter((op) => {
776
+ const parentReady = !op.parentSlug || knownSlugs.has(op.parentSlug);
777
+ const depsReady =
778
+ !Array.isArray(op.dependsOn) ||
779
+ op.dependsOn.every((d) => knownSlugs.has(d) || !createSlugs.has(d));
780
+ return parentReady && depsReady;
781
+ });
782
+ if (ready.length === 0) {
783
+ // Cycle or missing parent — break out by emitting the rest as one
784
+ // batch and letting `resolveParentId` / footer renderer raise the
785
+ // structured error.
786
+ batches.push(remaining.splice(0));
787
+ break;
788
+ }
789
+ batches.push(ready);
790
+ for (const op of ready) knownSlugs.add(op.slug);
791
+ for (const op of ready) {
792
+ const idx = remaining.indexOf(op);
793
+ if (idx >= 0) remaining.splice(idx, 1);
794
+ }
795
+ }
796
+ return batches;
797
+ }