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,209 +0,0 @@
1
- /**
2
- * push-epic-retry.js — Bounded retry for concurrent epic-branch pushes.
3
- *
4
- * story-close holds a per-Epic filesystem lock for the rebase / merge /
5
- * push sequence, but that lock is local to one machine. N sprint sessions on
6
- * different machines (or different Claude Code web workers) will not see each
7
- * other's lock and can race on `origin/epic/<id>`. Whoever pushes second will
8
- * be rejected with a non-fast-forward error.
9
- *
10
- * This module provides `pushEpicWithRetry`: first push attempt is a direct
11
- * `git push` — single-session behaviour is byte-identical to pre-change code.
12
- * Only on a non-fast-forward rejection do we enter the retry loop: fetch the
13
- * advanced remote, reset local epic onto `origin/<epic>`, reapply the story
14
- * merge, and retry the push. A real content conflict during re-apply is
15
- * non-recoverable: we abort the merge (restoring a clean tree) and throw a
16
- * `PushRetryConflictError` naming the conflicting files.
17
- */
18
-
19
- import { DEFAULT_STORY_MERGE_RETRY } from './config/runners.js';
20
-
21
- /**
22
- * Stderr patterns git emits when `git push` is rejected because the remote
23
- * tip has advanced beyond our local branch head. Only these patterns trigger
24
- * a retry; every other push error surfaces immediately so we do not mask
25
- * permission denials, protected-branch rejections, or network errors behind
26
- * a retry loop.
27
- */
28
- const NON_FAST_FORWARD_PATTERNS = [
29
- /non-fast-forward/i,
30
- /fetch first/i,
31
- /failed to push some refs/i,
32
- /\[rejected\]/,
33
- /Updates were rejected/i,
34
- ];
35
-
36
- export function isNonFastForwardPush(stderr) {
37
- if (!stderr) return false;
38
- return NON_FAST_FORWARD_PATTERNS.some((p) => p.test(stderr));
39
- }
40
-
41
- /**
42
- * Thrown when the retry loop's re-apply step (`git merge --no-ff story-<id>`
43
- * onto the freshly-fetched epic tip) hits a real content conflict. The
44
- * caller should surface this to the operator — we do not attempt automated
45
- * resolution. The merge is aborted before throwing so the working tree is
46
- * clean and recoverable.
47
- */
48
- export class PushRetryConflictError extends Error {
49
- constructor(conflictFiles, gitStderr) {
50
- const fileList = conflictFiles.length
51
- ? conflictFiles.join(', ')
52
- : '(unknown)';
53
- super(
54
- `Content conflict while reapplying story merge onto origin/epic. ` +
55
- `Conflicting file(s): ${fileList}. ` +
56
- `The merge has been aborted and the working tree is clean — no ` +
57
- `half-merged files remain. Resolve manually by rebasing the story ` +
58
- `branch onto the updated epic, committing, and re-running ` +
59
- `story-close.\n\n` +
60
- `git stderr:\n${gitStderr}`,
61
- );
62
- this.name = 'PushRetryConflictError';
63
- this.conflictFiles = conflictFiles;
64
- }
65
- }
66
-
67
- /** Re-apply the story merge during a non-ff retry, optionally pinning the
68
- * conventional-commit subject so the post-merge `(resolves #N)` grep keeps
69
- * finding the merge commit (without it, git falls back to its default
70
- * `Merge branch …` subject and the grep misses). */
71
- function runRetryMerge({ cwd, git, storyBranch, mergeMessage }) {
72
- return mergeMessage
73
- ? git.gitSpawn(cwd, 'merge', '--no-ff', '-m', mergeMessage, storyBranch)
74
- : git.gitSpawn(cwd, 'merge', '--no-ff', '--no-edit', storyBranch);
75
- }
76
-
77
- /** Collect the list of files with unmerged paths after a failed merge. */
78
- function collectConflictFiles(cwd, git) {
79
- const result = git.gitSpawn(cwd, 'diff', '--name-only', '--diff-filter=U');
80
- if (result.status !== 0 || !result.stdout) return [];
81
- return result.stdout
82
- .split(/\r?\n/)
83
- .map((s) => s.trim())
84
- .filter(Boolean);
85
- }
86
-
87
- /**
88
- * Push `epicBranch` with a bounded retry on non-fast-forward rejections.
89
- *
90
- * The first attempt is a direct push (no fetch, no reapply) so single-session
91
- * runs behave identically to the pre-change path. Retries fetch the advanced
92
- * remote, reset local `<epicBranch>` to `origin/<epicBranch>`, and reapply
93
- * `<storyBranch>` via `merge --no-ff --no-edit`.
94
- *
95
- * @param {object} opts
96
- * @param {string} opts.cwd
97
- * @param {string} opts.epicBranch
98
- * @param {string} opts.storyBranch
99
- * @param {{ maxAttempts?: number, backoffMs?: number[] }} [opts.storyMergeRetry]
100
- * @param {{ gitSpawn: (cwd: string, ...args: string[]) => { status: number, stdout: string, stderr: string } }} opts.git
101
- * @param {(ms: number) => Promise<void>} [opts.sleep]
102
- * @param {(msg: string) => void} [opts.log]
103
- * @returns {Promise<{ ok: boolean, attempts: number, reason?: string, result: object }>}
104
- * @throws {PushRetryConflictError} On content conflict during re-apply.
105
- */
106
- export async function pushEpicWithRetry({
107
- cwd,
108
- epicBranch,
109
- storyBranch,
110
- storyMergeRetry,
111
- git,
112
- sleep = (ms) => new Promise((r) => setTimeout(r, ms)),
113
- log = () => {},
114
- mergeMessage = null,
115
- }) {
116
- if (!cwd) throw new Error('pushEpicWithRetry: cwd is required');
117
- if (!epicBranch) throw new Error('pushEpicWithRetry: epicBranch is required');
118
- if (!storyBranch)
119
- throw new Error('pushEpicWithRetry: storyBranch is required');
120
- if (!git || typeof git.gitSpawn !== 'function') {
121
- throw new Error('pushEpicWithRetry: git.gitSpawn injection is required');
122
- }
123
-
124
- const maxAttempts =
125
- storyMergeRetry?.maxAttempts ?? DEFAULT_STORY_MERGE_RETRY.maxAttempts;
126
- const backoffMs =
127
- storyMergeRetry?.backoffMs ?? DEFAULT_STORY_MERGE_RETRY.backoffMs;
128
-
129
- let lastResult;
130
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
131
- lastResult = git.gitSpawn(cwd, 'push', '--no-verify', 'origin', epicBranch);
132
- if (lastResult.status === 0) {
133
- return { ok: true, attempts: attempt, result: lastResult };
134
- }
135
-
136
- if (!isNonFastForwardPush(lastResult.stderr)) {
137
- return {
138
- ok: false,
139
- attempts: attempt,
140
- result: lastResult,
141
- reason: 'non-retryable-push-error',
142
- };
143
- }
144
-
145
- if (attempt === maxAttempts) {
146
- return {
147
- ok: false,
148
- attempts: attempt,
149
- result: lastResult,
150
- reason: 'retry-exhausted',
151
- };
152
- }
153
-
154
- const backoff = backoffMs[Math.min(attempt - 1, backoffMs.length - 1)] ?? 0;
155
- log(
156
- `[push-epic-retry] Push rejected (non-fast-forward) on attempt ` +
157
- `${attempt}/${maxAttempts}; fetching remote and reapplying after ` +
158
- `${backoff}ms backoff.`,
159
- );
160
- await sleep(backoff);
161
-
162
- const fetchResult = git.gitSpawn(cwd, 'fetch', 'origin', epicBranch);
163
- if (fetchResult.status !== 0) {
164
- return {
165
- ok: false,
166
- attempts: attempt,
167
- result: fetchResult,
168
- reason: 'fetch-failed',
169
- };
170
- }
171
-
172
- const resetResult = git.gitSpawn(
173
- cwd,
174
- 'reset',
175
- '--hard',
176
- `origin/${epicBranch}`,
177
- );
178
- if (resetResult.status !== 0) {
179
- return {
180
- ok: false,
181
- attempts: attempt,
182
- result: resetResult,
183
- reason: 'reset-failed',
184
- };
185
- }
186
-
187
- const mergeResult = runRetryMerge({
188
- cwd,
189
- git,
190
- storyBranch,
191
- mergeMessage,
192
- });
193
- if (mergeResult.status !== 0) {
194
- const conflicts = collectConflictFiles(cwd, git);
195
- git.gitSpawn(cwd, 'merge', '--abort');
196
- throw new PushRetryConflictError(
197
- conflicts,
198
- mergeResult.stderr || mergeResult.stdout || '',
199
- );
200
- }
201
- }
202
-
203
- return {
204
- ok: false,
205
- attempts: maxAttempts,
206
- result: lastResult,
207
- reason: 'retry-exhausted',
208
- };
209
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * lib/spec/index.js — public surface for the spec I/O module.
3
- *
4
- * Re-exports the loader (and, in Wave 1, future spec utilities) so
5
- * downstream consumers — the reconciler, the rewritten /plan, the
6
- * wave-runner — can `import * from '../lib/spec/index.js'` without
7
- * reaching into individual submodules.
8
- *
9
- * The module intentionally re-exports only the public surface defined
10
- * by Story #1491; the loader's internal helpers (`_resetValidatorCacheForTests`,
11
- * `sortKeysDeep`, `renderStateJson`) remain importable from
12
- * `./loader.js` directly for tests but are not part of the consumer
13
- * contract.
14
- */
15
-
16
- export {
17
- loadSpec,
18
- loadState,
19
- SpecNotFoundError,
20
- SpecParseError,
21
- SpecValidationError,
22
- specPath,
23
- statePath,
24
- writeSpec,
25
- writeState,
26
- } from './loader.js';
27
-
28
- export {
29
- buildState,
30
- canonicalise,
31
- canonicalStringify,
32
- hashSpecEntry,
33
- iterSpecEntries,
34
- projectMapping,
35
- sha256Hex,
36
- } from './state.js';
@@ -1,425 +0,0 @@
1
- /**
2
- * lib/spec/loader.js — spec + state file I/O for the epic-spec reconciler.
3
- *
4
- * Owns the two files that bracket the structural SSOT migration (Epic
5
- * #1182 / Tech Spec #1483):
6
- *
7
- * - `temp/epic-<epic-id>/<epic-id>.yaml` — declarative spec (regenerated)
8
- * - `temp/epic-<epic-id>/<epic-id>.state.json` — slug→issue mapping (observed)
9
- *
10
- * Both files live under the per-Epic ephemeral tree `temp/epic-<id>/`
11
- * (already gitignored) so /plan reruns don't churn a tracked path
12
- * and so concurrent Epics never collide on a single shared directory.
13
- * Tests inject `opts.epicsDir` to point at a sandbox; the default for
14
- * production callers is derived from `lib/config/temp-paths.js#epicTempDir`.
15
- *
16
- * The module is intentionally a thin, dependency-light I/O layer:
17
- *
18
- * • `loadSpec(epicId)` → parses YAML, validates against
19
- * `.agents/schemas/epic-spec.schema.json` (Ajv2020). Throws a
20
- * `SpecValidationError` carrying the offending JSON Pointer paths
21
- * when the spec is structurally invalid. Missing file throws
22
- * `SpecNotFoundError`.
23
- * • `loadState(epicId)` → returns `{ epicId, mapping: {} }` (with
24
- * `lastReconciledAt` omitted) when the state file is missing, so
25
- * callers can start reconciling against a fresh Epic without a
26
- * pre-existing state file.
27
- * • `writeState(epicId, state)` → writes pretty-printed JSON with
28
- * deterministically sorted keys (recursive). Repeated writes of an
29
- * equivalent state produce a byte-identical file. Trailing newline
30
- * included so the file behaves well under git + POSIX `cat`.
31
- *
32
- * The loader does **not** make any GitHub calls; it is pure file I/O
33
- * over the two on-disk artefacts. The reconciler (Wave 1) layers diff
34
- * + apply on top of this surface.
35
- *
36
- * All public functions accept an optional `{ epicsDir, schemaPath, fs }`
37
- * options bag so tests can point the loader at a sandbox directory
38
- * without monkey-patching `process.cwd()` or the project schema path.
39
- *
40
- * Cross-references:
41
- * - Schema: `.agents/schemas/epic-spec.schema.json` (Story #1490)
42
- * - Fixtures: `tests/fixtures/epic-specs/*.json` (Story #1490)
43
- * - Tech Spec §"`.agents/epics/<epic-id>.yaml` (spec)" + §"state.json"
44
- */
45
-
46
- import {
47
- existsSync as defaultExistsSync,
48
- mkdirSync as defaultMkdirSync,
49
- readFileSync as defaultReadFileSync,
50
- writeFileSync as defaultWriteFileSync,
51
- } from 'node:fs';
52
- import path from 'node:path';
53
- import { fileURLToPath } from 'node:url';
54
- import Ajv2020 from 'ajv/dist/2020.js';
55
- import addFormats from 'ajv-formats';
56
- import yaml from 'js-yaml';
57
-
58
- import { epicTempDir } from '../config/temp-paths.js';
59
-
60
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
61
-
62
- // scripts/lib/spec/ → scripts/lib/ → scripts/ → .agents/
63
- const PROJECT_AGENTS_DIR = path.resolve(__dirname, '..', '..', '..');
64
- const DEFAULT_SCHEMA_PATH = path.join(
65
- PROJECT_AGENTS_DIR,
66
- 'schemas',
67
- 'epic-spec.schema.json',
68
- );
69
-
70
- // Resolve the default per-Epic spec directory under `temp/epic-<id>/`.
71
- // Caller-injected `opts.epicsDir` still wins for tests and any external
72
- // tooling that wants to point at a sandbox. Production callers omit the
73
- // option and route through this helper.
74
- function defaultEpicsDir(epicId) {
75
- return epicTempDir(epicId);
76
- }
77
-
78
- const defaultFsAdapter = Object.freeze({
79
- existsSync: defaultExistsSync,
80
- mkdirSync: defaultMkdirSync,
81
- readFileSync: defaultReadFileSync,
82
- writeFileSync: defaultWriteFileSync,
83
- });
84
-
85
- let cachedValidator = null;
86
- let cachedValidatorKey = null;
87
-
88
- /**
89
- * Compile (and cache) the Ajv2020 validator for the epic-spec schema.
90
- * Cached by absolute schema path so tests can swap to a sandbox schema.
91
- *
92
- * @param {string} schemaPath
93
- * @param {{ readFileSync: typeof defaultReadFileSync }} fs
94
- * @returns {(data: unknown) => boolean}
95
- */
96
- function getValidator(schemaPath, fs) {
97
- if (cachedValidator && cachedValidatorKey === schemaPath) {
98
- return cachedValidator;
99
- }
100
- const ajv = new Ajv2020({ allErrors: true, strict: false });
101
- addFormats(ajv);
102
- const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
103
- cachedValidator = ajv.compile(schema);
104
- cachedValidatorKey = schemaPath;
105
- return cachedValidator;
106
- }
107
-
108
- /**
109
- * Test-only hook: drop the cached validator so a subsequent call
110
- * recompiles. Safe to leave exported — production code never invokes it.
111
- */
112
- export function _resetValidatorCacheForTests() {
113
- cachedValidator = null;
114
- cachedValidatorKey = null;
115
- }
116
-
117
- /**
118
- * Structured error raised by `loadSpec` when the YAML parses but fails
119
- * schema validation. The Ajv error list is normalised to an array of
120
- * `{ path, message }` so callers (reconciler CLI, tests) can render the
121
- * offending JSON Pointer without re-parsing the Ajv envelope.
122
- */
123
- export class SpecValidationError extends Error {
124
- /**
125
- * @param {string} epicId
126
- * @param {Array<{path: string, message: string, params?: object}>} issues
127
- */
128
- constructor(epicId, issues) {
129
- const head = issues[0] ?? { path: '/', message: 'unknown' };
130
- super(
131
- `Spec for epic ${epicId} failed schema validation at ${head.path}: ${head.message}`,
132
- );
133
- this.name = 'SpecValidationError';
134
- this.epicId = epicId;
135
- this.issues = issues;
136
- }
137
- }
138
-
139
- /**
140
- * Raised by `loadSpec` when the on-disk YAML file does not exist.
141
- */
142
- export class SpecNotFoundError extends Error {
143
- /**
144
- * @param {string} epicId
145
- * @param {string} filePath
146
- */
147
- constructor(epicId, filePath) {
148
- super(`Spec file missing for epic ${epicId}: ${filePath}`);
149
- this.name = 'SpecNotFoundError';
150
- this.epicId = epicId;
151
- this.filePath = filePath;
152
- }
153
- }
154
-
155
- /**
156
- * Raised by `loadSpec` when the file exists but is not parseable YAML.
157
- */
158
- export class SpecParseError extends Error {
159
- /**
160
- * @param {string} epicId
161
- * @param {string} filePath
162
- * @param {Error} cause
163
- */
164
- constructor(epicId, filePath, cause) {
165
- super(
166
- `Spec file for epic ${epicId} is not valid YAML (${filePath}): ${cause.message}`,
167
- );
168
- this.name = 'SpecParseError';
169
- this.epicId = epicId;
170
- this.filePath = filePath;
171
- this.cause = cause;
172
- }
173
- }
174
-
175
- function resolveOpts(epicId, opts = {}) {
176
- return {
177
- epicsDir: opts.epicsDir ?? defaultEpicsDir(epicId),
178
- schemaPath: opts.schemaPath ?? DEFAULT_SCHEMA_PATH,
179
- fs: opts.fs ?? defaultFsAdapter,
180
- };
181
- }
182
-
183
- /**
184
- * Resolve the on-disk spec path for `epicId` under the configured
185
- * epics dir. Exported for tests and for the reconciler CLI's error
186
- * messages.
187
- *
188
- * @param {number|string} epicId
189
- * @param {{epicsDir?: string}} [opts]
190
- * @returns {string}
191
- */
192
- export function specPath(epicId, opts = {}) {
193
- const { epicsDir } = resolveOpts(epicId, opts);
194
- return path.join(epicsDir, `${String(epicId)}.yaml`);
195
- }
196
-
197
- /**
198
- * Resolve the on-disk state path for `epicId` under the configured
199
- * epics dir.
200
- *
201
- * @param {number|string} epicId
202
- * @param {{epicsDir?: string}} [opts]
203
- * @returns {string}
204
- */
205
- export function statePath(epicId, opts = {}) {
206
- const { epicsDir } = resolveOpts(epicId, opts);
207
- return path.join(epicsDir, `${String(epicId)}.state.json`);
208
- }
209
-
210
- /**
211
- * Convert Ajv's error array into the loader's `{ path, message }`
212
- * shape. Ajv2020 uses `instancePath` for the JSON Pointer into the
213
- * data; for `required` errors it leaves the missing property in
214
- * `params.missingProperty` rather than the path, so we append it so
215
- * the caller sees `/epic` instead of `` (root) for the canonical
216
- * `epic required` failure.
217
- *
218
- * @param {Array<{instancePath:string,message:string,keyword:string,params?:Record<string,unknown>}>} ajvErrors
219
- * @returns {Array<{path:string,message:string,params?:object}>}
220
- */
221
- function normaliseAjvErrors(ajvErrors) {
222
- return ajvErrors.map((err) => {
223
- let p = err.instancePath || '/';
224
- if (
225
- err.keyword === 'required' &&
226
- typeof err.params?.missingProperty === 'string'
227
- ) {
228
- const sep = p === '/' ? '' : '/';
229
- p = `${p}${sep}${err.params.missingProperty}`;
230
- }
231
- return {
232
- path: p,
233
- message: err.message ?? 'validation failed',
234
- params: err.params,
235
- };
236
- });
237
- }
238
-
239
- /**
240
- * Load and validate the spec YAML for `epicId`. Returns the parsed
241
- * JavaScript object on success. Throws `SpecNotFoundError`,
242
- * `SpecParseError`, or `SpecValidationError` otherwise.
243
- *
244
- * @param {number|string} epicId
245
- * @param {{epicsDir?: string, schemaPath?: string, fs?: typeof defaultFsAdapter}} [opts]
246
- * @returns {object}
247
- */
248
- export function loadSpec(epicId, opts = {}) {
249
- const { schemaPath, fs } = resolveOpts(epicId, opts);
250
- const filePath = specPath(epicId, opts);
251
-
252
- if (!fs.existsSync(filePath)) {
253
- throw new SpecNotFoundError(String(epicId), filePath);
254
- }
255
-
256
- const raw = fs.readFileSync(filePath, 'utf8');
257
- let parsed;
258
- try {
259
- parsed = yaml.load(raw, { filename: filePath });
260
- } catch (err) {
261
- throw new SpecParseError(String(epicId), filePath, err);
262
- }
263
-
264
- if (parsed == null || typeof parsed !== 'object') {
265
- throw new SpecValidationError(String(epicId), [
266
- {
267
- path: '/',
268
- message: 'spec root must be an object',
269
- },
270
- ]);
271
- }
272
-
273
- const validate = getValidator(schemaPath, fs);
274
- const ok = validate(parsed);
275
- if (!ok) {
276
- throw new SpecValidationError(
277
- String(epicId),
278
- normaliseAjvErrors(validate.errors ?? []),
279
- );
280
- }
281
-
282
- return parsed;
283
- }
284
-
285
- /**
286
- * Empty-state default. `loadState` returns this shape when the state
287
- * file does not exist; callers can rely on `mapping` being a plain
288
- * object (never undefined).
289
- *
290
- * @param {number|string} epicId
291
- * @returns {{epicId: number, mapping: Record<string, never>}}
292
- */
293
- function emptyState(epicId) {
294
- return { epicId: Number(epicId), mapping: {} };
295
- }
296
-
297
- /**
298
- * Load the state file for `epicId`. Returns an empty mapping when the
299
- * file is missing (the canonical "fresh Epic" case the reconciler
300
- * faces on first apply). Throws if the file exists but is not valid
301
- * JSON.
302
- *
303
- * @param {number|string} epicId
304
- * @param {{epicsDir?: string, fs?: typeof defaultFsAdapter}} [opts]
305
- * @returns {{epicId: number, mapping: object, lastReconciledAt?: string}}
306
- */
307
- export function loadState(epicId, opts = {}) {
308
- const { fs } = resolveOpts(epicId, opts);
309
- const filePath = statePath(epicId, opts);
310
-
311
- if (!fs.existsSync(filePath)) {
312
- return emptyState(epicId);
313
- }
314
-
315
- const raw = fs.readFileSync(filePath, 'utf8');
316
- return JSON.parse(raw);
317
- }
318
-
319
- /**
320
- * Recursively sort object keys (arrays preserve order). Returns a new
321
- * value with the same shape — leaves are returned unchanged.
322
- *
323
- * Exported for `state-writer.js` so the hashing path can share the
324
- * exact same canonicalisation as the file writer.
325
- *
326
- * @param {unknown} value
327
- * @returns {unknown}
328
- */
329
- export function sortKeysDeep(value) {
330
- if (Array.isArray(value)) {
331
- return value.map(sortKeysDeep);
332
- }
333
- if (value && typeof value === 'object') {
334
- const out = {};
335
- for (const key of Object.keys(value).sort()) {
336
- out[key] = sortKeysDeep(value[key]);
337
- }
338
- return out;
339
- }
340
- return value;
341
- }
342
-
343
- /**
344
- * Render `state` to deterministic JSON. Public so the test suite can
345
- * assert the byte-identical-roundtrip property without re-implementing
346
- * the formatter.
347
- *
348
- * @param {object} state
349
- * @returns {string} pretty-printed JSON, terminated by a single newline.
350
- */
351
- export function renderStateJson(state) {
352
- return `${JSON.stringify(sortKeysDeep(state), null, 2)}\n`;
353
- }
354
-
355
- /**
356
- * Write the state file for `epicId`. Creates the parent directory
357
- * lazily. Object keys are recursively sorted so re-writing the same
358
- * logical state produces a byte-identical file (AC: "diffs stay
359
- * stable", "byte-identical when written twice").
360
- *
361
- * Returns the absolute path written so callers can log it.
362
- *
363
- * @param {number|string} epicId
364
- * @param {object} state
365
- * @param {{epicsDir?: string, fs?: typeof defaultFsAdapter}} [opts]
366
- * @returns {string}
367
- */
368
- export function writeState(epicId, state, opts = {}) {
369
- const { epicsDir, fs } = resolveOpts(epicId, opts);
370
- if (!fs.existsSync(epicsDir)) {
371
- fs.mkdirSync(epicsDir, { recursive: true });
372
- }
373
- const filePath = statePath(epicId, opts);
374
- fs.writeFileSync(filePath, renderStateJson(state), 'utf8');
375
- return filePath;
376
- }
377
-
378
- /**
379
- * Write the spec YAML file for `epicId`. Creates the parent directory
380
- * lazily and emits a top-level `$schema` reference so editors with
381
- * YAML-schema autocomplete (e.g. the Red Hat YAML extension) resolve
382
- * the schema from the file itself.
383
- *
384
- * Story #1498 / Task #1525 introduced this writer so the rewritten
385
- * `/plan` halves can persist the spec from the decomposer's
386
- * ticket-array projection (`renderSpec`) without reaching into raw
387
- * `js-yaml` calls scattered across the planning scripts.
388
- *
389
- * The function validates the spec via the same Ajv2020 compiler the
390
- * loader caches — a malformed spec is rejected synchronously instead of
391
- * being persisted and tripping `loadSpec` on the next reconciler run.
392
- *
393
- * @param {number|string} epicId
394
- * @param {object} spec spec object matching `epic-spec.schema.json`.
395
- * @param {{epicsDir?: string, schemaPath?: string, fs?: typeof defaultFsAdapter}} [opts]
396
- * @returns {string} the absolute path written.
397
- */
398
- export function writeSpec(epicId, spec, opts = {}) {
399
- const { epicsDir, schemaPath, fs } = resolveOpts(epicId, opts);
400
- if (!spec || typeof spec !== 'object') {
401
- throw new TypeError('[writeSpec] spec must be an object');
402
- }
403
- const validate = getValidator(schemaPath, fs);
404
- const ok = validate(spec);
405
- if (!ok) {
406
- throw new SpecValidationError(
407
- String(epicId),
408
- normaliseAjvErrors(validate.errors ?? []),
409
- );
410
- }
411
- if (!fs.existsSync(epicsDir)) {
412
- fs.mkdirSync(epicsDir, { recursive: true });
413
- }
414
- const filePath = specPath(epicId, opts);
415
- // Lazy require: `js-yaml` is already a runtime dep of the loader, but
416
- // keeping the import top-level would force every consumer of `loader.js`
417
- // to pay the parse cost even when they only need state helpers.
418
- const yamlDump = yaml.dump(spec, {
419
- noRefs: true,
420
- sortKeys: false,
421
- lineWidth: 120,
422
- });
423
- fs.writeFileSync(filePath, yamlDump, 'utf8');
424
- return filePath;
425
- }