mandrel 1.93.0 → 2.0.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 (463) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/acceptance-critic.md +129 -0
  3. package/.agents/agents/story-worker.md +161 -0
  4. package/.agents/docs/SDLC.md +489 -1285
  5. package/.agents/docs/agentrc-reference.json +177 -67
  6. package/.agents/docs/configuration.md +108 -136
  7. package/.agents/docs/execution-reference.md +44 -22
  8. package/.agents/docs/quality-gates.md +13 -19
  9. package/.agents/docs/workflows.md +3 -3
  10. package/.agents/instructions.md +107 -108
  11. package/.agents/rules/ci-remediation.md +8 -12
  12. package/.agents/rules/git-conventions-reference.md +224 -0
  13. package/.agents/rules/git-conventions.md +42 -223
  14. package/.agents/rules/security-baseline.md +5 -0
  15. package/.agents/rules/testing-standards.md +106 -13
  16. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  17. package/.agents/schemas/agentrc.schema.json +71 -201
  18. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  20. package/.agents/schemas/risk-verdict.schema.json +0 -13
  21. package/.agents/scripts/acceptance-eval.js +62 -18
  22. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  23. package/.agents/scripts/analyze-execution.js +1 -1
  24. package/.agents/scripts/audit-to-stories.js +7 -7
  25. package/.agents/scripts/boot-sweep.js +1 -1
  26. package/.agents/scripts/check-context-budget.js +62 -5
  27. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  28. package/.agents/scripts/check-prepush-recovery.js +1 -1
  29. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  30. package/.agents/scripts/diagnose-friction.js +0 -6
  31. package/.agents/scripts/lib/Logger.js +6 -10
  32. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  33. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  34. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  35. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  36. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  37. package/.agents/scripts/lib/baselines/reader.js +1 -6
  38. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  39. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  40. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  41. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  42. package/.agents/scripts/lib/checks/index.js +2 -1
  43. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  44. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  45. package/.agents/scripts/lib/cli-args.js +16 -7
  46. package/.agents/scripts/lib/close-validation/gates.js +160 -22
  47. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  48. package/.agents/scripts/lib/config/ci.js +6 -31
  49. package/.agents/scripts/lib/config/delivery-routing.js +103 -0
  50. package/.agents/scripts/lib/config/explain.js +57 -36
  51. package/.agents/scripts/lib/config/limits.js +17 -58
  52. package/.agents/scripts/lib/config/paths.js +0 -2
  53. package/.agents/scripts/lib/config/quality.js +1 -1
  54. package/.agents/scripts/lib/config/runners.js +17 -50
  55. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  56. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  57. package/.agents/scripts/lib/config-resolver.js +3 -8
  58. package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
  59. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -14
  60. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  61. package/.agents/scripts/lib/dependency-parser.js +3 -2
  62. package/.agents/scripts/lib/doc-tiers.js +39 -4
  63. package/.agents/scripts/lib/duplicate-search.js +211 -41
  64. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  65. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  66. package/.agents/scripts/lib/framework-version.js +2 -3
  67. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  68. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  69. package/.agents/scripts/lib/git-utils.js +32 -6
  70. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  71. package/.agents/scripts/lib/label-constants.js +10 -23
  72. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  73. package/.agents/scripts/lib/observability/active-story-env.js +112 -3
  74. package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
  75. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  76. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  77. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  78. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  79. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  80. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  81. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
  82. package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
  83. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  84. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  85. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  86. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  87. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  88. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  89. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  90. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  91. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  92. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  93. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  94. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  95. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  96. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  97. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  98. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  99. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  100. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  102. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  103. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  104. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  105. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  106. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  107. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  108. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +14 -14
  109. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  110. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  111. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  112. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  113. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  114. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  115. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  116. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  117. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  118. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  119. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  120. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  121. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  122. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  123. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  124. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  131. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  132. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  134. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  135. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  136. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  137. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  138. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  139. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  140. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  141. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  142. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  143. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  144. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  145. package/.agents/scripts/lib/orchestration/ticket-validator.js +3 -8
  146. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  147. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  148. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  149. package/.agents/scripts/lib/planning-corpus.js +16 -11
  150. package/.agents/scripts/lib/preflight-runner.js +2 -2
  151. package/.agents/scripts/lib/provider-factory.js +1 -1
  152. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  153. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  154. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  155. package/.agents/scripts/lib/story-adjacency.js +11 -14
  156. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  157. package/.agents/scripts/lib/story-plan.js +2 -4
  158. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  159. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  160. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  161. package/.agents/scripts/lib/validation-evidence.js +1 -1
  162. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  163. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  164. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  165. package/.agents/scripts/lint-issue-body.js +71 -40
  166. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  167. package/.agents/scripts/notify.js +4 -3
  168. package/.agents/scripts/plan-context.js +64 -74
  169. package/.agents/scripts/plan-persist.js +121 -280
  170. package/.agents/scripts/plan-run-epilogue.js +97 -0
  171. package/.agents/scripts/post-structured-comment.js +38 -0
  172. package/.agents/scripts/providers/github/issues.js +17 -33
  173. package/.agents/scripts/providers/github/mappers.js +0 -12
  174. package/.agents/scripts/providers/github/tickets.js +2 -5
  175. package/.agents/scripts/resolve-plan-run.js +117 -0
  176. package/.agents/scripts/signals-view.js +24 -19
  177. package/.agents/scripts/single-story-close.js +11 -14
  178. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  179. package/.agents/scripts/single-story-init.js +29 -20
  180. package/.agents/scripts/stories-wave-tick.js +6 -6
  181. package/.agents/scripts/story-plan.js +26 -47
  182. package/.agents/scripts/sync-claude-agents.js +165 -0
  183. package/.agents/scripts/update-ticket-state.js +37 -15
  184. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  185. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  186. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  187. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  188. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  189. package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
  190. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  191. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  192. package/.agents/skills/skills.index.json +8 -418
  193. package/.agents/starter-agentrc.json +0 -5
  194. package/.agents/templates/agent-protocol.md +9 -10
  195. package/.agents/workflows/audit-architecture.md +3 -3
  196. package/.agents/workflows/audit-clean-code.md +3 -3
  197. package/.agents/workflows/audit-dependencies.md +3 -3
  198. package/.agents/workflows/audit-devops.md +3 -3
  199. package/.agents/workflows/audit-documentation.md +5 -5
  200. package/.agents/workflows/audit-lighthouse.md +3 -3
  201. package/.agents/workflows/audit-navigability.md +3 -2
  202. package/.agents/workflows/audit-performance.md +3 -3
  203. package/.agents/workflows/audit-privacy.md +3 -3
  204. package/.agents/workflows/audit-quality.md +3 -3
  205. package/.agents/workflows/audit-security.md +3 -3
  206. package/.agents/workflows/audit-seo.md +3 -3
  207. package/.agents/workflows/audit-sre.md +3 -3
  208. package/.agents/workflows/audit-to-stories.md +20 -20
  209. package/.agents/workflows/audit-ux-ui.md +3 -3
  210. package/.agents/workflows/deliver.md +122 -131
  211. package/.agents/workflows/git-cleanup.md +3 -4
  212. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  213. package/.agents/workflows/helpers/acceptance-self-eval.md +52 -40
  214. package/.agents/workflows/helpers/code-review.md +70 -193
  215. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  216. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  217. package/.agents/workflows/helpers/diagnose.md +10 -10
  218. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  219. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  220. package/.agents/workflows/helpers/signals.md +16 -16
  221. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  222. package/.agents/workflows/mandrel-update.md +3 -2
  223. package/.agents/workflows/plan.md +112 -145
  224. package/.agents/workflows/qa-assist.md +24 -30
  225. package/.agents/workflows/qa-explore.md +29 -38
  226. package/.agents/workflows/qa-run.md +2 -2
  227. package/README.md +9 -8
  228. package/docs/CHANGELOG.md +46 -0
  229. package/lib/cli/registry.js +95 -0
  230. package/lib/migrations/index.js +6 -5
  231. package/package.json +5 -3
  232. package/.agents/personas/architect.md +0 -113
  233. package/.agents/personas/devops-engineer.md +0 -38
  234. package/.agents/personas/engineer-mobile.md +0 -120
  235. package/.agents/personas/engineer-web.md +0 -111
  236. package/.agents/personas/engineer.md +0 -119
  237. package/.agents/personas/product.md +0 -94
  238. package/.agents/personas/project-manager.md +0 -114
  239. package/.agents/personas/qa-engineer.md +0 -95
  240. package/.agents/personas/refactorer.md +0 -113
  241. package/.agents/personas/security-engineer.md +0 -112
  242. package/.agents/personas/sre.md +0 -86
  243. package/.agents/personas/technical-writer.md +0 -101
  244. package/.agents/personas/ux-designer.md +0 -95
  245. package/.agents/schemas/dispatch-manifest.json +0 -232
  246. package/.agents/schemas/epic-spec.schema.json +0 -153
  247. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  248. package/.agents/scripts/dispatcher.js +0 -295
  249. package/.agents/scripts/epic-audit-prepare.js +0 -497
  250. package/.agents/scripts/epic-audit-recheck.js +0 -274
  251. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  252. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  253. package/.agents/scripts/epic-deliver-prepare.js +0 -590
  254. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  255. package/.agents/scripts/epic-plan-clarity.js +0 -211
  256. package/.agents/scripts/epic-plan-decompose.js +0 -54
  257. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  258. package/.agents/scripts/epic-plan-spec.js +0 -64
  259. package/.agents/scripts/epic-reconcile.js +0 -625
  260. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  261. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  262. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  263. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  264. package/.agents/scripts/lib/config/preflight.js +0 -58
  265. package/.agents/scripts/lib/config/retro.js +0 -77
  266. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  267. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  268. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  269. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -660
  270. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  271. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  272. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  273. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  274. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  275. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  276. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  277. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  278. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  279. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  280. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  281. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  282. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  283. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  284. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  285. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  286. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  287. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  288. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -388
  289. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  290. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  291. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  292. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  293. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  294. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  295. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  296. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  297. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  298. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  299. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  300. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  301. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  302. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  303. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  304. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  305. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  306. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  307. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  308. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  309. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  310. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  311. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  312. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  313. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
  314. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  315. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  316. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  317. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -278
  318. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  319. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  320. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  321. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  322. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  323. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  324. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  325. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  326. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  327. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  328. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  329. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  330. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  331. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  332. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  333. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  334. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  335. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  336. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  337. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  338. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  339. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  340. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  341. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  342. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  343. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  344. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  345. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  346. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  347. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  348. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  349. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  350. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  351. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  352. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  353. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  354. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  355. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  356. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  357. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  358. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  359. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  360. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  361. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -301
  362. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  363. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  364. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  365. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  366. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  367. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  368. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  369. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  370. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  371. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  372. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  373. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  374. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  375. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  376. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  377. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  378. package/.agents/scripts/lib/spec/index.js +0 -36
  379. package/.agents/scripts/lib/spec/loader.js +0 -425
  380. package/.agents/scripts/lib/spec/state.js +0 -208
  381. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  382. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  383. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  384. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  385. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  386. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  387. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  388. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  389. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  390. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  391. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  392. package/.agents/scripts/lifecycle-emit.js +0 -510
  393. package/.agents/scripts/plan-critics.js +0 -199
  394. package/.agents/scripts/retro-run.js +0 -218
  395. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  396. package/.agents/scripts/story-close.js +0 -294
  397. package/.agents/scripts/story-init.js +0 -599
  398. package/.agents/scripts/story-phase.js +0 -369
  399. package/.agents/scripts/wave-tick.js +0 -335
  400. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  401. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  402. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  403. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  404. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  405. package/.agents/skills/core/context-engineering/examples.md +0 -58
  406. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  407. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  408. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  409. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  410. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  411. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  412. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  413. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -413
  414. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  415. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  416. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  417. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  418. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  419. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  420. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  421. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  422. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  423. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  424. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  425. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  426. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  427. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  428. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  429. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  430. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  431. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  432. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  433. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  434. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  435. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  436. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  437. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  438. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  439. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  440. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  441. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  442. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  443. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  444. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  445. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  446. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  447. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  448. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  449. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  450. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  451. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  452. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
  453. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
  454. package/.agents/workflows/helpers/deliver-epic.md +0 -955
  455. package/.agents/workflows/helpers/deliver-stories.md +0 -440
  456. package/.agents/workflows/helpers/epic-audit.md +0 -189
  457. package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
  458. package/.agents/workflows/helpers/epic-testing.md +0 -125
  459. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  460. package/.agents/workflows/helpers/plan-epic.md +0 -351
  461. package/.agents/workflows/helpers/plan-story.md +0 -251
  462. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  463. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -1,625 +0,0 @@
1
- #!/usr/bin/env node
2
- /* node:coverage ignore file */
3
-
4
- /**
5
- * epic-reconcile.js — CLI surface for the structural epic-spec reconciler.
6
- *
7
- * Story #1496 (Epic #1182 / Tech Spec #1483). Thin wrapper that
8
- * composes the existing reconciler modules into the operator-facing
9
- * surface specified in the Tech Spec:
10
- *
11
- * node .agents/scripts/epic-reconcile.js <epicId>
12
- * [--dry-run] (default)
13
- * [--apply]
14
- * [--explicit-delete]
15
- * [--yes]
16
- *
17
- * Control flow (Tech Spec §"Reconciler control flow"):
18
- *
19
- * 1. Load `.agents/epics/<id>.yaml` via `lib/spec/loader.js`. Validation
20
- * failure exits 1 with the failing JSON Pointer.
21
- * 2. Load `.agents/epics/<id>.state.json` (absent → empty mapping).
22
- * 3. Fetch the live GH state via the configured `ITicketingProvider`.
23
- * 4. Compute the plan via `epic-spec-reconciler-diff.js#diff`.
24
- * 5. Render the plan via `epic-spec-reconciler-format.js#formatPlan`.
25
- * 6. On `--dry-run` (default) — print and exit 0.
26
- * On `--apply`:
27
- * - When the plan carries close ops without `--explicit-delete`,
28
- * exit 2.
29
- * - When the plan is non-empty, in a TTY, prompt unless `--yes`.
30
- * Non-TTY without `--yes` aborts (exit 1).
31
- * - Invoke `apply()` with the appropriate gate flags.
32
- *
33
- * Exit codes (Story #1496 / Task #1521):
34
- * - 0 No diff or successful apply.
35
- * - 1 Validation error (`SpecValidationError`, `SpecParseError`,
36
- * `SpecNotFoundError`), or a non-TTY `--apply` without `--yes`,
37
- * or an apply-phase provider failure.
38
- * - 2 Close operation requires `--explicit-delete` but the flag was
39
- * absent.
40
- *
41
- * The CLI is intentionally thin: every behaviour that matters for
42
- * downstream automation is rooted in the underlying modules. This file
43
- * adds three things on top: argument parsing, the confirmation gate,
44
- * and the exit-code contract.
45
- */
46
-
47
- import readline from 'node:readline';
48
- import { parseArgs } from 'node:util';
49
-
50
- import { runAsCli } from './lib/cli-utils.js';
51
- import { resolveConfig } from './lib/config-resolver.js';
52
- import { Logger } from './lib/Logger.js';
53
- import {
54
- ApplyGateViolation,
55
- apply,
56
- } from './lib/orchestration/epic-spec-reconciler-apply.js';
57
- import { diff } from './lib/orchestration/epic-spec-reconciler-diff.js';
58
- import { formatPlan } from './lib/orchestration/epic-spec-reconciler-format.js';
59
- import { isEmptyPlan } from './lib/orchestration/epic-spec-reconciler-ops.js';
60
- import { createProvider } from './lib/provider-factory.js';
61
- import {
62
- loadSpec,
63
- loadState,
64
- SpecNotFoundError,
65
- SpecParseError,
66
- SpecValidationError,
67
- } from './lib/spec/index.js';
68
-
69
- /**
70
- * Exit-code contract (see header). Exported for tests so they can assert
71
- * the constants without re-deriving the integers from prose.
72
- */
73
- export const EXIT_CODES = Object.freeze({
74
- OK: 0,
75
- VALIDATION_ERROR: 1,
76
- EXPLICIT_DELETE_REQUIRED: 2,
77
- });
78
-
79
- /**
80
- * Default writer for log/output streams. Tests inject sinks so they can
81
- * inspect what the CLI printed without intercepting process.stdout.
82
- */
83
- function defaultStdout(line) {
84
- process.stdout.write(`${line}\n`);
85
- }
86
-
87
- function defaultStderr(line) {
88
- process.stderr.write(`${line}\n`);
89
- }
90
-
91
- /**
92
- * Parse the CLI argv into a typed bag. Exported for tests.
93
- *
94
- * @param {string[]} argv argv slice (excluding `node` and the script path).
95
- * @returns {{
96
- * epicId: number|null,
97
- * dryRun: boolean,
98
- * apply: boolean,
99
- * explicitDelete: boolean,
100
- * yes: boolean,
101
- * raw: object
102
- * }}
103
- */
104
- export function parseCli(argv) {
105
- const { values, positionals } = parseArgs({
106
- args: argv,
107
- allowPositionals: true,
108
- options: {
109
- 'dry-run': { type: 'boolean', default: false },
110
- apply: { type: 'boolean', default: false },
111
- 'explicit-delete': { type: 'boolean', default: false },
112
- yes: { type: 'boolean', default: false },
113
- },
114
- strict: false,
115
- });
116
-
117
- // Default behaviour: when neither --apply nor --dry-run is set, the
118
- // CLI runs in dry-run mode (Tech Spec: "dry-run default"). When both
119
- // are passed, --dry-run wins — safer to no-op than to apply by accident.
120
- const explicitApply = values.apply === true;
121
- const explicitDryRun = values['dry-run'] === true;
122
- const dryRun = explicitDryRun || !explicitApply;
123
-
124
- const epicArg = positionals[0];
125
- const epicId =
126
- epicArg != null && /^\d+$/.test(String(epicArg))
127
- ? Number.parseInt(String(epicArg), 10)
128
- : null;
129
-
130
- return {
131
- epicId,
132
- dryRun,
133
- apply: explicitApply && !explicitDryRun,
134
- explicitDelete: values['explicit-delete'] === true,
135
- yes: values.yes === true,
136
- raw: values,
137
- };
138
- }
139
-
140
- /**
141
- * Project the GH state observation into the shape the diff engine
142
- * expects: `{ [issueNumber]: { title, body?, labels?, state } }`.
143
- *
144
- * Enumerates the Epic's children via the **server-side-scoped**
145
- * `getSubTickets(epicId)` (native sub-issue GraphQL graph → checklist
146
- * links → reverse-search) rather than `getTickets(epicId)`'s repo-wide
147
- * `state=all` scan-and-filter (Story #3455). The structural diff engine
148
- * never branches on `obs.state` — Close detection is driven by
149
- * spec-vs-mapping, `fieldChanges` compares only `title/body/labels/wave`,
150
- * and `ghState` is consulted only to look up an issue by the number
151
- * already in the state-file mapping — so dropping the repo-wide
152
- * closed-issue over-fetch changes nothing but cost (and removes the
153
- * latent spurious-Update risk on a re-plan of a partially-delivered
154
- * Epic). The provider's returned children come back as full ticket
155
- * objects (`id, title, body, labels, state`); the diff engine ignores
156
- * fields it doesn't use.
157
- *
158
- * @param {object} provider
159
- * @param {number} epicId
160
- * @returns {Promise<Record<number, {title:string,body?:string,labels?:string[],state?:string}>>}
161
- */
162
- export async function fetchGhState(provider, epicId) {
163
- const ghState = {};
164
- // Epic itself (best-effort — older providers may stub getEpic; treat
165
- // a thrown call as "epic not observable" and continue with children).
166
- try {
167
- if (typeof provider.getEpic === 'function') {
168
- const epic = await provider.getEpic(epicId);
169
- if (epic && typeof epic.id === 'number') {
170
- ghState[epic.id] = {
171
- title: epic.title,
172
- body: epic.body ?? '',
173
- labels: epic.labels ?? [],
174
- state: epic.state ?? 'open',
175
- };
176
- }
177
- }
178
- } catch (_err) {
179
- /* swallow — epic body observation is non-fatal for diff */
180
- }
181
-
182
- if (typeof provider.getSubTickets === 'function') {
183
- const tickets = await provider.getSubTickets(epicId);
184
- for (const t of tickets ?? []) {
185
- if (typeof t.id !== 'number') continue;
186
- ghState[t.id] = {
187
- title: t.title,
188
- body: t.body ?? '',
189
- labels: t.labels ?? [],
190
- state: t.state ?? 'open',
191
- };
192
- }
193
- }
194
- return ghState;
195
- }
196
-
197
- /**
198
- * Walk the spec and yield one `{ slug, entity, title, parentSlug,
199
- * dependsOn }` record per logical entity (epic → story), mirroring the
200
- * diff engine's `flattenSpec`. Local to the CLI so the reseed pass
201
- * (below) can map spec slugs onto live GH issues without importing the
202
- * diff engine's private walker. Pure.
203
- *
204
- * @param {object} spec
205
- * @returns {Array<{slug: string, entity: string, title: string, parentSlug: string|null, dependsOn: string[]}>}
206
- */
207
- export function flattenSpecForReseed(spec) {
208
- const out = [];
209
- if (!spec || typeof spec !== 'object') return out;
210
- if (spec.epic && typeof spec.epic === 'object') {
211
- out.push({
212
- slug: 'epic',
213
- entity: 'epic',
214
- title: String(spec.epic.title ?? ''),
215
- parentSlug: null,
216
- dependsOn: [],
217
- });
218
- }
219
- for (const story of spec.stories ?? []) {
220
- out.push({
221
- slug: story.slug,
222
- entity: 'story',
223
- title: String(story.title ?? ''),
224
- parentSlug: 'epic',
225
- dependsOn: story.dependsOn ?? [],
226
- });
227
- }
228
- return out;
229
- }
230
-
231
- /**
232
- * Story #3905 — recover the slug→issue mapping from live GitHub state
233
- * when `state.json` is missing or incomplete.
234
- *
235
- * The reconciler's idempotency on `--resume` rests entirely on the
236
- * gitignored `temp/epic-<id>/<id>.state.json` slug→issue map. When that
237
- * file is absent (a fresh checkout, a reaped temp dir, the exact
238
- * situation `--resume` exists to recover from), `loadState` returns an
239
- * empty mapping, the diff engine sees every spec slug as unmapped, and
240
- * `apply` recreates the entire Story set on top of the existing
241
- * one — duplicating every child. This is precisely the failure `--resume`
242
- * is meant to prevent.
243
- *
244
- * This pass closes the gap: for every spec slug that is **not** already
245
- * in the mapping, it looks for an **open** GH issue whose title matches
246
- * the spec entity's title (and whose issue number is not already claimed
247
- * by another mapping entry). A match seeds a mapping entry carrying the
248
- * structural edges (`entity`, `parentSlug`, `dependsOn`) the diff engine
249
- * reads, so the slug diffs as an Update/no-op rather than a Create.
250
- *
251
- * Pure: it mutates and returns a shallow clone of `state.mapping`; it
252
- * performs no I/O (the caller supplies the already-fetched `ghState`).
253
- * Title matching is intentionally conservative — an unmatched slug is
254
- * left unmapped so a genuinely-missing child is still created (the
255
- * partial-persist case `--resume` also serves). Returns the reseeded
256
- * state plus the list of slugs it recovered so the caller can log them.
257
- *
258
- * @param {{epicId: number, mapping: Record<string, object>}} state
259
- * @param {object} spec
260
- * @param {Record<string|number, {title?: string, state?: string}>} ghState
261
- * @returns {{ state: object, reseeded: Array<{slug: string, issueNumber: number}> }}
262
- */
263
- export function reseedMappingFromGh(state, spec, ghState) {
264
- const reseeded = [];
265
- const mapping = { ...(state?.mapping ?? {}) };
266
- if (!spec || typeof spec !== 'object' || !ghState) {
267
- return { state: { ...state, mapping }, reseeded };
268
- }
269
-
270
- // Issue numbers already claimed by the current mapping must not be
271
- // re-bound to a second slug.
272
- const claimed = new Set();
273
- for (const entry of Object.values(mapping)) {
274
- if (entry && typeof entry.issueNumber === 'number') {
275
- claimed.add(entry.issueNumber);
276
- }
277
- }
278
-
279
- // Index OPEN gh issues by title → [issueNumber]. Closed issues are
280
- // skipped: a `--force`/`--resume` should never re-bind a slug to a
281
- // tombstoned issue.
282
- const openByTitle = new Map();
283
- for (const [num, obs] of Object.entries(ghState)) {
284
- const issueNumber = Number(num);
285
- if (!Number.isInteger(issueNumber)) continue;
286
- if (obs?.state && obs.state !== 'open') continue;
287
- const title = typeof obs?.title === 'string' ? obs.title : null;
288
- if (title == null) continue;
289
- if (!openByTitle.has(title)) openByTitle.set(title, []);
290
- openByTitle.get(title).push(issueNumber);
291
- }
292
-
293
- for (const entity of flattenSpecForReseed(spec)) {
294
- // Epic #4474 (PR3) — a mapping entry WITHOUT an issue number is a
295
- // partial-failure tombstone (the apply's state writer persists every
296
- // spec slug; only completed creations carry `issueNumber`). Treat it
297
- // as unmapped here so a title-matched live issue reseeds it instead
298
- // of leaving a numberless entry the diff engine can neither update
299
- // nor (post-fix) skip-create.
300
- if (Number.isInteger(mapping[entity.slug]?.issueNumber)) continue; // already mapped
301
- const candidates = openByTitle.get(entity.title) ?? [];
302
- // Pick the lowest unclaimed candidate for determinism.
303
- const match = candidates
304
- .filter((n) => !claimed.has(n))
305
- .sort((a, b) => a - b)[0];
306
- if (typeof match !== 'number') continue;
307
- claimed.add(match);
308
- mapping[entity.slug] = {
309
- issueNumber: match,
310
- contentHash: '',
311
- lastObservedAgentState: null,
312
- entity: entity.entity,
313
- parentSlug: entity.parentSlug,
314
- ...(entity.dependsOn.length > 0 ? { dependsOn: entity.dependsOn } : {}),
315
- };
316
- reseeded.push({ slug: entity.slug, issueNumber: match });
317
- }
318
-
319
- return { state: { ...state, mapping }, reseeded };
320
- }
321
-
322
- /**
323
- * Prompt the operator for confirmation on the supplied plan. Resolves
324
- * `true` when the user answers `y`/`yes` (case-insensitive), `false`
325
- * otherwise. Uses `readline` against `process.stdin`/`process.stdout` so
326
- * stubs (test) and the real TTY both work.
327
- *
328
- * @param {object} [opts]
329
- * @param {NodeJS.ReadableStream} [opts.input]
330
- * @param {NodeJS.WritableStream} [opts.output]
331
- * @returns {Promise<boolean>}
332
- */
333
- export function confirmInteractive(opts = {}) {
334
- const input = opts.input ?? process.stdin;
335
- const output = opts.output ?? process.stdout;
336
- return new Promise((resolve) => {
337
- const rl = readline.createInterface({ input, output });
338
- rl.question('Apply this plan? [y/N] ', (answer) => {
339
- rl.close();
340
- const trimmed = String(answer ?? '')
341
- .trim()
342
- .toLowerCase();
343
- resolve(trimmed === 'y' || trimmed === 'yes');
344
- });
345
- });
346
- }
347
-
348
- /**
349
- * Identify whether the plan carries any close ops. The exit-code
350
- * contract gates close ops behind `--explicit-delete`; this predicate
351
- * is the canonical "needs --explicit-delete" check. Exported so the
352
- * contract tests can pin the predicate without re-deriving it.
353
- *
354
- * @param {object} plan
355
- * @returns {boolean}
356
- */
357
- export function planHasCloses(plan) {
358
- return Array.isArray(plan?.closes) && plan.closes.length > 0;
359
- }
360
-
361
- /**
362
- * Render the operator-facing message printed on the exit-2 path. Names
363
- * every close-op slug so the operator can audit precisely what would be
364
- * removed before re-running with `--explicit-delete`. Exported so the
365
- * contract tests assert the rendering without re-implementing it.
366
- *
367
- * @param {object} plan
368
- * @returns {string}
369
- */
370
- export function renderExplicitDeleteMessage(plan) {
371
- const closes = Array.isArray(plan?.closes) ? plan.closes : [];
372
- const slugList = closes
373
- .map((op) => `#${op.issueNumber ?? '?'} (${op.slug})`)
374
- .join(', ');
375
- return (
376
- `Plan carries ${closes.length} close operation(s): ${slugList}. ` +
377
- `Re-run with --explicit-delete to apply.`
378
- );
379
- }
380
-
381
- /**
382
- * Format the spec-validation failure for stderr — names the failing
383
- * JSON Pointer and the underlying schema message so operators can fix
384
- * the spec without re-running with --verbose.
385
- *
386
- * @param {SpecValidationError|SpecParseError|SpecNotFoundError} err
387
- * @returns {string}
388
- */
389
- function formatSpecError(err) {
390
- if (err instanceof SpecValidationError) {
391
- // Render every issue on its own line so scripts that grep stderr for
392
- // a JSON Pointer find each failure individually. The first issue is
393
- // duplicated into the headline so existing log scrapers reading line 1
394
- // keep working.
395
- const issues = Array.isArray(err.issues) ? err.issues : [];
396
- if (issues.length === 0) {
397
- return `Spec validation failed: ${err.message}`;
398
- }
399
- const head = issues[0];
400
- const lines = [
401
- `Spec validation failed at ${head.path}: ${head.message}`,
402
- ...issues.slice(1).map((iss) => ` · ${iss.path}: ${iss.message}`),
403
- ];
404
- return lines.join('\n');
405
- }
406
- if (err instanceof SpecParseError) {
407
- return `Spec YAML parse error in ${err.filePath}: ${err.cause?.message ?? err.message}`;
408
- }
409
- if (err instanceof SpecNotFoundError) {
410
- return `Spec file missing for epic ${err.epicId}: ${err.filePath}`;
411
- }
412
- return `Spec error: ${err.message ?? err}`;
413
- }
414
-
415
- /**
416
- * Run one reconcile cycle for a given Epic. Pure dependency-injection
417
- * surface — every external collaborator (provider, loaders, formatter,
418
- * apply, confirm prompt, log sinks) is overridable so the contract tests
419
- * can drive the CLI without hitting the network, the file system, or a
420
- * real TTY.
421
- *
422
- * @param {{
423
- * epicId: number,
424
- * dryRun: boolean,
425
- * apply: boolean,
426
- * explicitDelete: boolean,
427
- * yes: boolean,
428
- * }} args
429
- * @param {{
430
- * provider?: object,
431
- * loadSpec?: typeof loadSpec,
432
- * loadState?: typeof loadState,
433
- * diff?: typeof diff,
434
- * apply?: typeof apply,
435
- * formatPlan?: typeof formatPlan,
436
- * fetchGhState?: typeof fetchGhState,
437
- * confirm?: typeof confirmInteractive,
438
- * isTty?: () => boolean,
439
- * stdout?: (line: string) => void,
440
- * stderr?: (line: string) => void,
441
- * loaderOpts?: object,
442
- * }} [deps]
443
- * @returns {Promise<{exitCode: number, plan?: object, applyResult?: object}>}
444
- */
445
- export async function runReconcile(args, deps = {}) {
446
- const stdout = deps.stdout ?? defaultStdout;
447
- const stderr = deps.stderr ?? defaultStderr;
448
- const loadSpecFn = deps.loadSpec ?? loadSpec;
449
- const loadStateFn = deps.loadState ?? loadState;
450
- const diffFn = deps.diff ?? diff;
451
- const applyFn = deps.apply ?? apply;
452
- const formatFn = deps.formatPlan ?? formatPlan;
453
- const fetchGhStateFn = deps.fetchGhState ?? fetchGhState;
454
- const confirmFn = deps.confirm ?? confirmInteractive;
455
- const isTtyFn = deps.isTty ?? (() => Boolean(process.stdin.isTTY));
456
- const loaderOpts = deps.loaderOpts ?? {};
457
-
458
- if (!Number.isInteger(args.epicId) || args.epicId <= 0) {
459
- stderr('[epic-reconcile] Error: epic id is required and must be positive');
460
- stderr(
461
- 'Usage: epic-reconcile.js <epicId> [--dry-run] [--apply] [--explicit-delete] [--yes]',
462
- );
463
- return { exitCode: EXIT_CODES.VALIDATION_ERROR };
464
- }
465
-
466
- // 1. Load + validate the spec.
467
- let spec;
468
- try {
469
- spec = loadSpecFn(args.epicId, loaderOpts);
470
- } catch (err) {
471
- stderr(`[epic-reconcile] ${formatSpecError(err)}`);
472
- return { exitCode: EXIT_CODES.VALIDATION_ERROR };
473
- }
474
-
475
- // 2. Load the state (absent file → empty mapping).
476
- let state;
477
- try {
478
- state = loadStateFn(args.epicId, loaderOpts);
479
- } catch (_err) {
480
- state = { epicId: args.epicId, mapping: {} };
481
- }
482
- if (!state || typeof state !== 'object') {
483
- state = { epicId: args.epicId, mapping: {} };
484
- }
485
-
486
- // 2a. Seed the synthetic `epic` slug mapping. The diff engine's
487
- // `flattenSpec` unconditionally yields an entity for the epic, and
488
- // `applyCreate` carries a long-standing comment asserting the epic
489
- // "is bootstrapped before reconciliation". On a fresh apply with no
490
- // state.json, nothing previously did that — so diff emitted a Create
491
- // op for the epic and `provider.createTicket` materialised a duplicate
492
- // GH issue (Story #1820). The CLI is the right seam to seed it: keeps
493
- // diff/apply pure and one-shot per reconcile run.
494
- if (!state.mapping || typeof state.mapping !== 'object') {
495
- state.mapping = {};
496
- }
497
- if (!state.mapping.epic) {
498
- state.mapping.epic = {
499
- issueNumber: args.epicId,
500
- contentHash: '',
501
- lastObservedAgentState: null,
502
- entity: 'epic',
503
- parentSlug: null,
504
- };
505
- }
506
-
507
- // 3. Resolve provider, fetch GH state.
508
- let provider = deps.provider;
509
- if (!provider) {
510
- const config = deps.config ?? resolveConfig({});
511
- provider = createProvider(config);
512
- }
513
- const ghState = await fetchGhStateFn(provider, args.epicId);
514
-
515
- // 3a. Reseed the slug→issue mapping from live GH state for any spec
516
- // slug the state file did not cover (Story #3905). Without this, a
517
- // `--resume` (or any apply) run with a missing/empty `state.json` but
518
- // open children present would diff every spec slug as a Create and
519
- // duplicate the entire Feature/Story tree. Title-matched recovery turns
520
- // those spurious Creates into Updates/no-ops. Pure — no I/O.
521
- const { state: reseededState, reseeded } = reseedMappingFromGh(
522
- state,
523
- spec,
524
- ghState,
525
- );
526
- state = reseededState;
527
- if (reseeded.length > 0) {
528
- stderr(
529
- `[epic-reconcile] Reseeded ${reseeded.length} slug→issue mapping(s) from live GitHub state ` +
530
- `(state.json was missing/incomplete): ${reseeded
531
- .map((r) => `${r.slug}→#${r.issueNumber}`)
532
- .join(', ')}.`,
533
- );
534
- }
535
-
536
- // 4. Compute the plan.
537
- const plan = diffFn({ spec, state, ghState });
538
-
539
- // 5. Render.
540
- stdout(formatFn(plan));
541
-
542
- // 6a. Dry-run path — print and exit 0 regardless of plan size.
543
- if (args.dryRun) {
544
- return { exitCode: EXIT_CODES.OK, plan };
545
- }
546
-
547
- // 6b. Empty plan: nothing to apply. Exit 0 without prompting.
548
- if (isEmptyPlan(plan)) {
549
- return { exitCode: EXIT_CODES.OK, plan };
550
- }
551
-
552
- // 6c. Explicit-delete pre-flight: any close op requires the flag. We
553
- // gate at the CLI surface (before apply) so the exit code is stable
554
- // regardless of whether apply's discriminator would also reject — the
555
- // task contract pins exit 2 to "would close without --explicit-delete",
556
- // not "apply engine rejected".
557
- if (planHasCloses(plan) && !args.explicitDelete) {
558
- stderr(`[epic-reconcile] ${renderExplicitDeleteMessage(plan)}`);
559
- return { exitCode: EXIT_CODES.EXPLICIT_DELETE_REQUIRED, plan };
560
- }
561
-
562
- // 6d. Confirmation gate.
563
- if (!args.yes) {
564
- if (!isTtyFn()) {
565
- stderr(
566
- '[epic-reconcile] --apply requires either --yes or an interactive TTY.',
567
- );
568
- return { exitCode: EXIT_CODES.VALIDATION_ERROR, plan };
569
- }
570
- const confirmed = await confirmFn();
571
- if (!confirmed) {
572
- stdout('[epic-reconcile] Aborted by operator.');
573
- return { exitCode: EXIT_CODES.OK, plan };
574
- }
575
- }
576
-
577
- // 6e. Apply.
578
- let applyResult;
579
- try {
580
- applyResult = await applyFn(plan, provider, {
581
- epicId: args.epicId,
582
- spec,
583
- priorState: state,
584
- explicitDelete: args.explicitDelete,
585
- });
586
- } catch (err) {
587
- if (
588
- err instanceof ApplyGateViolation &&
589
- err.reason === 'explicit-delete-required'
590
- ) {
591
- stderr(`[epic-reconcile] ${err.message}`);
592
- return { exitCode: EXIT_CODES.EXPLICIT_DELETE_REQUIRED, plan };
593
- }
594
- stderr(`[epic-reconcile] Apply failed: ${err.message ?? err}`);
595
- return { exitCode: EXIT_CODES.VALIDATION_ERROR, plan };
596
- }
597
-
598
- if (applyResult?.failure) {
599
- stderr(
600
- `[epic-reconcile] Apply completed with partial failure: ${applyResult.failure.message ?? applyResult.failure}`,
601
- );
602
- return { exitCode: EXIT_CODES.VALIDATION_ERROR, plan, applyResult };
603
- }
604
-
605
- stdout('[epic-reconcile] Apply complete.');
606
- return { exitCode: EXIT_CODES.OK, plan, applyResult };
607
- }
608
-
609
- /**
610
- * CLI entry point. Parses argv, delegates to `runReconcile`, and exits
611
- * with the contract-defined integer.
612
- */
613
- async function main(argv = process.argv.slice(2)) {
614
- const args = parseCli(argv);
615
- const { exitCode } = await runReconcile(args);
616
- process.exit(exitCode);
617
- }
618
-
619
- runAsCli(import.meta.url, main, {
620
- source: 'epic-reconcile',
621
- onError: (err) => {
622
- Logger.error('[epic-reconcile] Fatal error:', err?.message ?? err);
623
- process.exit(EXIT_CODES.VALIDATION_ERROR);
624
- },
625
- });