mandrel 1.58.0 → 1.60.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 (319) hide show
  1. package/.agents/README.md +100 -98
  2. package/.agents/docs/SDLC.md +140 -141
  3. package/.agents/docs/configuration.md +16 -16
  4. package/.agents/docs/workflows.md +7 -8
  5. package/.agents/instructions.md +12 -11
  6. package/.agents/personas/architect.md +1 -1
  7. package/.agents/personas/product.md +1 -1
  8. package/.agents/personas/project-manager.md +14 -14
  9. package/.agents/personas/technical-writer.md +1 -1
  10. package/.agents/rules/changelog-style.md +5 -5
  11. package/.agents/rules/git-conventions.md +3 -3
  12. package/.agents/schemas/agentrc.schema.json +3 -3
  13. package/.agents/schemas/audit-rules.json +20 -0
  14. package/.agents/schemas/dispatch-manifest.json +4 -4
  15. package/.agents/schemas/epic-spec.schema.json +15 -45
  16. package/.agents/schemas/lifecycle/README.md +1 -1
  17. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
  18. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
  20. package/.agents/schemas/validation-evidence.schema.json +1 -1
  21. package/.agents/scripts/README.md +1 -1
  22. package/.agents/scripts/acceptance-eval.js +21 -4
  23. package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
  24. package/.agents/scripts/analyze-execution.js +2 -2
  25. package/.agents/scripts/assert-branch.js +1 -3
  26. package/.agents/scripts/audit-to-stories.js +1 -1
  27. package/.agents/scripts/bootstrap.js +1 -1
  28. package/.agents/scripts/check-arch-cycles.js +360 -0
  29. package/.agents/scripts/check-doc-links.js +2 -3
  30. package/.agents/scripts/coverage-capture.js +24 -3
  31. package/.agents/scripts/diagnose-friction.js +1 -1
  32. package/.agents/scripts/dispatcher.js +2 -2
  33. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  34. package/.agents/scripts/epic-audit-prepare.js +3 -3
  35. package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
  36. package/.agents/scripts/epic-deliver-preflight.js +11 -9
  37. package/.agents/scripts/epic-deliver-prepare.js +13 -5
  38. package/.agents/scripts/epic-execute-record-wave.js +5 -5
  39. package/.agents/scripts/epic-plan-healthcheck.js +6 -10
  40. package/.agents/scripts/epic-plan-spec-validate.js +1 -1
  41. package/.agents/scripts/epic-reconcile.js +11 -29
  42. package/.agents/scripts/evidence-gate.js +2 -2
  43. package/.agents/scripts/generate-workflows-doc.js +1 -1
  44. package/.agents/scripts/git-rebase-and-resolve.js +1 -1
  45. package/.agents/scripts/hierarchy-gate.js +40 -24
  46. package/.agents/scripts/lib/ITicketingProvider.js +1 -1
  47. package/.agents/scripts/lib/audit-suite/selector.js +1 -1
  48. package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
  49. package/.agents/scripts/lib/baseline-snapshot.js +7 -7
  50. package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
  51. package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
  52. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
  53. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
  54. package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
  55. package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
  56. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
  57. package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
  58. package/.agents/scripts/lib/baselines/writer.js +1 -1
  59. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  60. package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
  61. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
  62. package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
  63. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
  64. package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
  65. package/.agents/scripts/lib/close-validation/commands.js +188 -0
  66. package/.agents/scripts/lib/close-validation/gates.js +235 -0
  67. package/.agents/scripts/lib/close-validation/process.js +101 -0
  68. package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
  69. package/.agents/scripts/lib/close-validation/runner.js +325 -0
  70. package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
  71. package/.agents/scripts/lib/codebase-snapshot.js +1 -1
  72. package/.agents/scripts/lib/config/explain.js +1 -1
  73. package/.agents/scripts/lib/config/quality.js +6 -6
  74. package/.agents/scripts/lib/config/runners.js +2 -2
  75. package/.agents/scripts/lib/config/runtime.js +1 -1
  76. package/.agents/scripts/lib/config/temp-paths.js +2 -2
  77. package/.agents/scripts/lib/config-resolver.js +2 -5
  78. package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
  79. package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
  80. package/.agents/scripts/lib/config-settings-schema.js +3 -3
  81. package/.agents/scripts/lib/coverage-capture.js +147 -4
  82. package/.agents/scripts/lib/cpu-pool.js +14 -0
  83. package/.agents/scripts/lib/crap-utils.js +6 -11
  84. package/.agents/scripts/lib/duplicate-search.js +1 -1
  85. package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
  86. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
  87. package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
  88. package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
  89. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
  90. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
  91. package/.agents/scripts/lib/findings/classify-finding.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +10 -10
  93. package/.agents/scripts/lib/git-utils.js +24 -22
  94. package/.agents/scripts/lib/label-constants.js +3 -4
  95. package/.agents/scripts/lib/label-taxonomy.js +3 -8
  96. package/.agents/scripts/lib/maintainability-engine.js +1 -1
  97. package/.agents/scripts/lib/maintainability-utils.js +4 -187
  98. package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
  99. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
  100. package/.agents/scripts/lib/orchestration/code-review.js +95 -82
  101. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
  102. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
  103. package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
  104. package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
  105. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
  106. package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
  107. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
  108. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
  109. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
  110. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
  111. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
  112. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
  113. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
  114. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
  115. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
  116. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
  117. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
  118. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
  119. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
  120. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
  121. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
  122. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
  123. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
  124. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
  125. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
  126. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
  127. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
  128. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
  129. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
  130. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
  131. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
  132. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
  133. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
  134. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
  135. package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
  136. package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
  137. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
  138. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
  139. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
  140. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
  141. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
  142. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
  143. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
  144. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
  145. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
  146. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
  147. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
  148. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
  149. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
  150. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
  151. package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
  152. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
  153. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
  154. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
  155. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
  156. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
  157. package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
  158. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
  159. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
  160. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
  161. package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
  162. package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
  163. package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
  164. package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
  165. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
  166. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
  167. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
  170. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
  171. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
  172. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
  173. package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
  174. package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
  175. package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
  176. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
  177. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
  178. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
  179. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
  180. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
  181. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
  182. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
  183. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
  184. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
  185. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
  186. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
  187. package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
  188. package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
  189. package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
  190. package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
  191. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
  192. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
  193. package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
  194. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
  195. package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
  196. package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
  197. package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
  198. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  199. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
  200. package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
  201. package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
  202. package/.agents/scripts/lib/preflight-runner.js +1 -1
  203. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
  204. package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
  205. package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
  206. package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
  207. package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
  208. package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
  209. package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
  210. package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
  211. package/.agents/scripts/lib/project-root.js +17 -0
  212. package/.agents/scripts/lib/signals/schema.js +1 -1
  213. package/.agents/scripts/lib/spec/index.js +1 -1
  214. package/.agents/scripts/lib/spec/loader.js +2 -2
  215. package/.agents/scripts/lib/spec/state.js +7 -16
  216. package/.agents/scripts/lib/story-adjacency.js +76 -0
  217. package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
  218. package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
  219. package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
  220. package/.agents/scripts/lib/story-lifecycle.js +9 -9
  221. package/.agents/scripts/lib/story-plan.js +1 -1
  222. package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
  223. package/.agents/scripts/lib/transpile.js +93 -0
  224. package/.agents/scripts/lib/wave-runner/tick.js +4 -153
  225. package/.agents/scripts/lib/workers/crap-worker.js +1 -1
  226. package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
  228. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
  229. package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
  230. package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
  231. package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
  232. package/.agents/scripts/lifecycle-emit.js +1 -1
  233. package/.agents/scripts/providers/github/board-add.js +1 -1
  234. package/.agents/scripts/providers/github/errors.js +1 -1
  235. package/.agents/scripts/providers/github/mappers.js +2 -2
  236. package/.agents/scripts/providers/github/tickets.js +114 -10
  237. package/.agents/scripts/resync-status-column.js +1 -1
  238. package/.agents/scripts/retro-run.js +2 -2
  239. package/.agents/scripts/run-lint.js +10 -1
  240. package/.agents/scripts/run-tests.js +24 -4
  241. package/.agents/scripts/single-story-init.js +1 -1
  242. package/.agents/scripts/stories-wave-tick.js +13 -10
  243. package/.agents/scripts/story-close.js +1 -1
  244. package/.agents/scripts/story-init.js +162 -26
  245. package/.agents/scripts/story-phase.js +5 -5
  246. package/.agents/scripts/story-plan.js +3 -3
  247. package/.agents/scripts/sync-branch-from-base.js +2 -2
  248. package/.agents/scripts/validate-docs-freshness.js +1 -1
  249. package/.agents/scripts/wave-tick.js +1 -1
  250. package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
  251. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
  252. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
  253. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
  254. package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
  255. package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
  256. package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
  257. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
  258. package/.agents/skills/core/scope-triage/SKILL.md +9 -10
  259. package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
  260. package/.agents/skills/skills.index.json +7 -7
  261. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
  262. package/.agents/templates/agent-protocol.md +2 -2
  263. package/.agents/workflows/agents-update.md +2 -2
  264. package/.agents/workflows/audit-architecture.md +2 -2
  265. package/.agents/workflows/audit-clean-code.md +2 -2
  266. package/.agents/workflows/audit-dependencies.md +1 -1
  267. package/.agents/workflows/audit-devops.md +1 -1
  268. package/.agents/workflows/audit-documentation.md +226 -0
  269. package/.agents/workflows/audit-lighthouse.md +1 -1
  270. package/.agents/workflows/audit-performance.md +2 -2
  271. package/.agents/workflows/audit-privacy.md +1 -1
  272. package/.agents/workflows/audit-quality.md +2 -2
  273. package/.agents/workflows/audit-security.md +2 -2
  274. package/.agents/workflows/audit-seo.md +1 -1
  275. package/.agents/workflows/audit-sre.md +1 -1
  276. package/.agents/workflows/audit-to-stories.md +10 -10
  277. package/.agents/workflows/audit-ux-ui.md +1 -1
  278. package/.agents/workflows/deliver.md +85 -0
  279. package/.agents/workflows/explain.md +3 -3
  280. package/.agents/workflows/git-merge-pr.md +1 -1
  281. package/.agents/workflows/git-pr-all.md +13 -10
  282. package/.agents/workflows/git-push.md +6 -3
  283. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  284. package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
  285. package/.agents/workflows/helpers/code-review.md +5 -5
  286. package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
  287. package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
  288. package/.agents/workflows/helpers/diagnose.md +1 -1
  289. package/.agents/workflows/helpers/epic-audit.md +6 -6
  290. package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
  291. package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
  292. package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
  293. package/.agents/workflows/helpers/epic-testing.md +3 -3
  294. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  295. package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
  296. package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
  297. package/.agents/workflows/helpers/signals.md +1 -1
  298. package/.agents/workflows/helpers/single-story-deliver.md +12 -11
  299. package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
  300. package/.agents/workflows/onboard.md +21 -20
  301. package/.agents/workflows/plan.md +89 -0
  302. package/.agents/workflows/qa-explore.md +1 -1
  303. package/.agents/workflows/qa-run-harness.md +1 -1
  304. package/README.md +17 -20
  305. package/docs/CHANGELOG.md +1149 -0
  306. package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
  307. package/lib/cli/__tests__/update-reexec.test.js +513 -0
  308. package/lib/cli/init.js +338 -0
  309. package/lib/cli/update.js +413 -52
  310. package/package.json +3 -1
  311. package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
  312. package/.agents/scripts/lib/close-validation.js +0 -897
  313. package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
  314. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
  315. package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
  316. package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
  317. package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
  318. package/.agents/scripts/lib/task-utils.js +0 -26
  319. package/.agents/scripts/story-deliver-prepare.js +0 -267
@@ -9,19 +9,19 @@
9
9
  * Extracted from `manifest-formatter.js` (Story #1849 Task #1869). The
10
10
  * shape projection used to be inlined in the formatter; pulling it out
11
11
  * isolates the spec → manifest projection from the Markdown renderer and
12
- * lets the per-feature / per-story guard cascade live behind a single
12
+ * lets the per-story guard cascade live behind a single
13
13
  * private predicate (`validateSpecShape`) so the orchestrator function's
14
14
  * CRAP score drops below 12.
15
15
  *
16
- * Story #3413 (3-tier cutover, final): the residual per-Task projection
16
+ * Story #3413 (2-tier cutover, final): the residual per-Task projection
17
17
  * (the Task projector, the per-Story Task array, and the Task-count
18
- * rollup) has been deleted. The walker (`projectFeatures`) now counts
18
+ * rollup) has been deleted. The walker (`projectStories`) counts
19
19
  * Stories directly, and `summary` carries Story-tier counts only
20
20
  * (`totalStories` / `doneStories` / `progressPercent`), matching the
21
21
  * canonical producer in `lib/orchestration/manifest-builder.js`.
22
22
  *
23
23
  * Story-level status surfaces on each `storyEntry.status` so downstream
24
- * renderers read the Story's own `agent::*` label directly — the 3-tier
24
+ * renderers read the Story's own `agent::*` label directly — the
25
25
  * "Stories are first-class lifecycle units" invariant.
26
26
  *
27
27
  * No fs / network access; pure transform. Caller supplies `state` from
@@ -38,8 +38,7 @@ import { AGENT_LABELS } from '../label-constants.js';
38
38
  * "is this thing iterable / object-shaped?" decisions.
39
39
  *
40
40
  * `level` describes which spec node we are validating:
41
- * - `'features'` → the spec-level `features` array
42
- * - `'stories'` → a feature's `stories` array
41
+ * - `'stories'` → the spec-level `stories` array
43
42
  * - `'story'` → a single Story object (must be a non-null object)
44
43
  *
45
44
  * Returns `true` when the node satisfies the shape contract for that
@@ -52,7 +51,6 @@ import { AGENT_LABELS } from '../label-constants.js';
52
51
  */
53
52
  function validateSpecShape(level, value) {
54
53
  switch (level) {
55
- case 'features':
56
54
  case 'stories':
57
55
  return Array.isArray(value);
58
56
  case 'story':
@@ -102,8 +100,8 @@ function buildResolvers(state) {
102
100
  * Private: project a single spec Story into a manifest Story entry. The
103
101
  * Story's status is resolved directly from the Story-level label
104
102
  * (`state.mapping[slug].lastObservedAgentState`) and surfaces on
105
- * `storyEntry.status` — under the 3-tier hierarchy Stories carry their
106
- * own lifecycle state and are leaves with no child Task tickets. Caller
103
+ * `storyEntry.status` — Stories carry their own lifecycle state and are
104
+ * leaves with no child tickets. Caller
107
105
  * filters non-object stories with `validateSpecShape('story', ...)`
108
106
  * before invoking.
109
107
  *
@@ -129,16 +127,17 @@ function projectStory(story, resolvers) {
129
127
  }
130
128
 
131
129
  /**
132
- * Private: walk every feature → story pair in a spec and collect the
133
- * per-story projections + Story-tier roll-up counters. Keeps the loop
134
- * machinery out of `buildManifestFromSpec` so the entry point reads as a
135
- * straight assembly of the result envelope.
130
+ * Private: walk every Story in a spec and collect the per-story
131
+ * projections + Story-tier roll-up counters. Keeps the loop machinery
132
+ * out of `buildManifestFromSpec` so the entry point reads as a straight
133
+ * assembly of the result envelope.
136
134
  *
137
- * Under the 3-tier hierarchy (Epic #3163) Stories are leaves, so the
138
- * rollup counts Stories directly: `totalStories` is every projected
139
- * Story and `doneStories` is the subset carrying `agent::done`.
135
+ * Under the 2-tier hierarchy (Story #4041) Stories are direct Epic
136
+ * children and leaves, so the rollup counts Stories directly:
137
+ * `totalStories` is every projected Story and `doneStories` is the
138
+ * subset carrying `agent::done`.
140
139
  *
141
- * @param {object[]} features
140
+ * @param {object[]} stories
142
141
  * @param {{ resolveId: Function, resolveStatus: Function }} resolvers
143
142
  * @returns {{
144
143
  * storyManifest: object[],
@@ -147,23 +146,18 @@ function projectStory(story, resolvers) {
147
146
  * waveSet: Set<number>,
148
147
  * }}
149
148
  */
150
- function projectFeatures(features, resolvers) {
149
+ function projectStories(stories, resolvers) {
151
150
  const storyManifest = [];
152
151
  let totalStories = 0;
153
152
  let doneStories = 0;
154
153
  const waveSet = new Set();
155
- for (const feature of features) {
156
- const stories = validateSpecShape('stories', feature?.stories)
157
- ? feature.stories
158
- : [];
159
- for (const story of stories) {
160
- if (!validateSpecShape('story', story)) continue;
161
- const { storyEntry, wave } = projectStory(story, resolvers);
162
- storyManifest.push(storyEntry);
163
- totalStories++;
164
- if (storyEntry.status === AGENT_LABELS.DONE) doneStories++;
165
- if (wave >= 0) waveSet.add(wave);
166
- }
154
+ for (const story of stories) {
155
+ if (!validateSpecShape('story', story)) continue;
156
+ const { storyEntry, wave } = projectStory(story, resolvers);
157
+ storyManifest.push(storyEntry);
158
+ totalStories++;
159
+ if (storyEntry.status === AGENT_LABELS.DONE) doneStories++;
160
+ if (wave >= 0) waveSet.add(wave);
167
161
  }
168
162
  return { storyManifest, totalStories, doneStories, waveSet };
169
163
  }
@@ -199,12 +193,12 @@ export function buildManifestFromSpec(spec, opts = {}) {
199
193
  spec?.epic && typeof spec.epic.id === 'number' ? spec.epic.id : null;
200
194
  const epicTitle =
201
195
  spec?.epic && typeof spec.epic.title === 'string' ? spec.epic.title : '';
202
- const features = validateSpecShape('features', spec?.features)
203
- ? spec.features
196
+ const stories = validateSpecShape('stories', spec?.stories)
197
+ ? spec.stories
204
198
  : [];
205
199
 
206
- const { storyManifest, totalStories, doneStories, waveSet } = projectFeatures(
207
- features,
200
+ const { storyManifest, totalStories, doneStories, waveSet } = projectStories(
201
+ stories,
208
202
  resolvers,
209
203
  );
210
204
 
@@ -144,7 +144,6 @@ function renderManifestHeader(manifest) {
144
144
 
145
145
  /**
146
146
  * Private: emit the Wave Summary table plus the per-wave H2 sections.
147
- * Filters Feature containers out of the wave-eligible set.
148
147
  *
149
148
  * @param {object} manifest
150
149
  * @returns {string[]}
@@ -155,7 +154,7 @@ function renderManifestBody(manifest) {
155
154
  manifest.stories ||
156
155
  manifest.summary?.stories ||
157
156
  [];
158
- const waveEligible = allItems.filter((s) => s.type !== 'feature');
157
+ const waveEligible = allItems;
159
158
  const lines = [];
160
159
  const waveBlock = renderWaveSections(waveEligible);
161
160
  if (waveBlock) lines.push(waveBlock);
@@ -165,7 +164,7 @@ function renderManifestBody(manifest) {
165
164
  if (nestedBlock) lines.push(nestedBlock);
166
165
  }
167
166
  // Cross-Story concurrency hazards block — only emitted when the caller
168
- // attaches `concurrencyFindings` to the manifest (i.e. `/epic-plan`
167
+ // attaches `concurrencyFindings` to the manifest (i.e. `/plan`
169
168
  // Phase 9 dispatcher dry-run forwards the validator's findings array).
170
169
  // Absent for live progress-reporter manifests where the block would
171
170
  // duplicate Story-level state already shown above.
@@ -178,7 +177,7 @@ function renderManifestBody(manifest) {
178
177
 
179
178
  /**
180
179
  * Private: emit the agent-telemetry trailer (friction count + recent
181
- * friction list) when the manifest carries one. Under the 3-tier
180
+ * friction list) when the manifest carries one. Under the 2-tier
182
181
  * hierarchy (Epic #3163) friction records are Story-scoped, so each
183
182
  * recent-friction item is keyed by its `storyId`.
184
183
  *
@@ -8,7 +8,7 @@
8
8
  * re-exports every name here so existing call-sites' import paths stay
9
9
  * unchanged.
10
10
  *
11
- * Post-3-tier (Story #3194 / #3413, Epic #3163): the helpers consume the
11
+ * Post-2-tier (Story #3194 / #3413, Epic #3163): the helpers consume the
12
12
  * Story-only manifest shape. Stories carry their lifecycle state on a
13
13
  * top-level `status` field (the parent Story's `agent::*` label) — the
14
14
  * old per-Story Task array, the per-Task id indirection, and the
@@ -16,7 +16,7 @@
16
16
  * the only HTML the manifest emits by AC — every other section is plain
17
17
  * Markdown.
18
18
  *
19
- * @param {number|string} epicId the Epic id used to substitute `/epic-deliver` examples.
19
+ * @param {number|string} epicId the Epic id used to substitute `/deliver` examples.
20
20
  * @returns {string}
21
21
  */
22
22
  export function renderProceduresAndLegendDetails(epicId) {
@@ -28,13 +28,13 @@ export function renderProceduresAndLegendDetails(epicId) {
28
28
  lines.push('### Operating Procedures');
29
29
  lines.push('');
30
30
  lines.push(
31
- `1. **Deliver**: Run \`/epic-deliver ${epicId}\`. The runner iterates waves in order, fans Stories out in parallel via \`/story-deliver\`, and only pauses when the Epic flips to \`agent::blocked\`.`,
31
+ `1. **Deliver**: Run \`/deliver ${epicId}\`. The runner iterates waves in order, fans Stories out in parallel via \`/deliver\`, and only pauses when the Epic flips to \`agent::blocked\`.`,
32
32
  );
33
33
  lines.push(
34
- '2. **Resume (granular, optional)**: Re-running `/epic-deliver` resumes from the checkpointed wave. To re-drive a single Story, run `/story-deliver <storyId>`. Re-runs are checkpoint-idempotent.',
34
+ '2. **Resume (granular, optional)**: Re-running `/deliver` resumes from the checkpointed wave. To re-drive a single Story, run `/deliver <storyId>`. Re-runs are checkpoint-idempotent.',
35
35
  );
36
36
  lines.push(
37
- `3. **Close**: \`/epic-deliver ${epicId}\` runs close-validation, code-review, retro, and PR-create in its tail. Operators merge the PR via the GitHub UI.`,
37
+ `3. **Close**: \`/deliver ${epicId}\` runs close-validation, code-review, retro, and PR-create in its tail. Operators merge the PR via the GitHub UI.`,
38
38
  );
39
39
  lines.push('');
40
40
  lines.push('### Symbol legend');
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Presentation-only: emits the per-wave `## <emoji> Wave N` H2 sections
5
5
  * (with nested per-Story H3 headings) that sit beneath the Wave Summary
6
- * table in the dispatch manifest. Under the 3-tier hierarchy (Epic
6
+ * table in the dispatch manifest. Under the 2-tier hierarchy (Epic
7
7
  * #3163) Stories are leaves with no child Task tickets, so each wave
8
8
  * counts Stories (done/total) and renders one H3 per Story. The TOC
9
9
  * links emitted by `renderWaveSections` jump directly into the H2
@@ -85,7 +85,7 @@ function pickWaveTail(status, waveIdx, sortedWaves, storyCount) {
85
85
  /**
86
86
  * Group Stories into per-wave buckets and accumulate per-wave Story
87
87
  * totals. Pure helper — keeps the bookkeeping outside the main render
88
- * loop. Under the 3-tier hierarchy (Epic #3163) Stories are leaves with
88
+ * loop. Under the 2-tier hierarchy (Epic #3163) Stories are leaves with
89
89
  * no child Task tickets, so each wave's `total` / `done` counts Stories
90
90
  * (a Story is "done" when it carries `agent::done`) — the unit
91
91
  * `deriveWaveStatus` consumes.
@@ -122,11 +122,8 @@ export function renderNestedWaveSections(storyManifest) {
122
122
  if (!validateWaveSection('storyManifest', storyManifest)) return '';
123
123
  if (storyManifest.length === 0) return '';
124
124
 
125
- const waveStories = storyManifest.filter(
126
- (s) => validateWaveSection('story', s) && s.type !== 'feature',
127
- );
128
- const featureItems = storyManifest.filter(
129
- (s) => validateWaveSection('story', s) && s.type === 'feature',
125
+ const waveStories = storyManifest.filter((s) =>
126
+ validateWaveSection('story', s),
130
127
  );
131
128
 
132
129
  const { waveGroups, waveStats } = groupStoriesByWave(waveStories);
@@ -158,22 +155,6 @@ export function renderNestedWaveSections(storyManifest) {
158
155
  }
159
156
  }
160
157
 
161
- if (featureItems.length > 0) {
162
- lines.push('## Feature Containers');
163
- lines.push('');
164
- lines.push(
165
- '> Features are organizational groupings and are **not directly executable**.',
166
- );
167
- lines.push('> Execute the Stories within each Feature instead.');
168
- lines.push('');
169
- lines.push('| Feature | Title |');
170
- lines.push('| :--- | :--- |');
171
- for (const f of featureItems) {
172
- lines.push(`| #${f.storyId} | ${f.storySlug} |`);
173
- }
174
- lines.push('');
175
- }
176
-
177
158
  return lines.join('\n');
178
159
  }
179
160
 
@@ -139,7 +139,7 @@ export async function postParkedFollowOnsComment(manifest, provider) {
139
139
 
140
140
  const storyManifest = manifest.storyManifest ?? [];
141
141
  const manifestStoryIds = storyManifest
142
- .filter((s) => s.type !== 'feature' && s.storyId !== '__ungrouped__')
142
+ .filter((s) => s.storyId !== '__ungrouped__')
143
143
  .map((s) => Number(s.storyId))
144
144
  .filter((n) => Number.isFinite(n));
145
145
 
@@ -83,9 +83,7 @@ export function printStoryDispatchTable(storyManifest, opts = {}) {
83
83
  const log = opts.logger?.log ?? ((line) => Logger.info(line));
84
84
  if (!storyManifest || storyManifest.length === 0) return;
85
85
 
86
- // Split into wave-eligible Stories and Feature containers
87
- const stories = storyManifest.filter((s) => s.type !== 'feature');
88
- const features = storyManifest.filter((s) => s.type === 'feature');
86
+ const stories = storyManifest;
89
87
 
90
88
  log('\n┌─────────┬──────────────────────────────────────┬──────┐');
91
89
  log('│ 📋 STORY DISPATCH TABLE │');
@@ -106,14 +104,7 @@ export function printStoryDispatchTable(storyManifest, opts = {}) {
106
104
  log('└─────────┴──────────────────────────────────────┴──────┘');
107
105
  log('');
108
106
  log(' 💡 Stories in the same [Wave] can be executed in parallel.');
109
- log(' 💡 Use /epic-deliver #[Story ID] to execute a Story.');
107
+ log(' 💡 Use /deliver #[Story ID] to execute a Story.');
110
108
 
111
- if (features.length > 0) {
112
- log('');
113
- log(' 📦 Feature Containers (not directly executable):');
114
- for (const f of features) {
115
- log(` #${f.storyId} — ${f.storySlug}`);
116
- }
117
- }
118
109
  log('');
119
110
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * lib/project-root.js — side-effect-free leaf module for the repository
3
+ * root path.
4
+ *
5
+ * Extracted from `config-resolver.js` (Story #3993) so the ~10 importers
6
+ * that need only the path constant no longer transitively load the
7
+ * stateful config subsystem (module-global caches + `.env` load side
8
+ * effect). `config-resolver.js` re-exports this constant, so its barrel
9
+ * surface is unchanged.
10
+ */
11
+
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+ // scripts/lib/ → scripts/ → .agents/ → project root
17
+ export const PROJECT_ROOT = path.resolve(__dirname, '../../..');
@@ -98,7 +98,7 @@ export const EVENT_KINDS = Object.freeze({
98
98
  // Story #3819 — per-criterion acceptance self-eval signal emitted by
99
99
  // acceptance-eval.js. One record per Story per eval-loop terminus,
100
100
  // carrying which acceptance items needed rework and the round count, so
101
- // the retro and /epic-plan Phase 0 feedback fetch can see acceptance
101
+ // the retro and /plan Phase 0 feedback fetch can see acceptance
102
102
  // churn alongside friction/hotspot data.
103
103
  ACCEPTANCE_EVAL: 'acceptance-eval',
104
104
  });
@@ -2,7 +2,7 @@
2
2
  * lib/spec/index.js — public surface for the spec I/O module.
3
3
  *
4
4
  * Re-exports the loader (and, in Wave 1, future spec utilities) so
5
- * downstream consumers — the reconciler, the rewritten /epic-plan, the
5
+ * downstream consumers — the reconciler, the rewritten /plan, the
6
6
  * wave-runner — can `import * from '../lib/spec/index.js'` without
7
7
  * reaching into individual submodules.
8
8
  *
@@ -8,7 +8,7 @@
8
8
  * - `temp/epic-<epic-id>/<epic-id>.state.json` — slug→issue mapping (observed)
9
9
  *
10
10
  * Both files live under the per-Epic ephemeral tree `temp/epic-<id>/`
11
- * (already gitignored) so /epic-plan reruns don't churn a tracked path
11
+ * (already gitignored) so /plan reruns don't churn a tracked path
12
12
  * and so concurrent Epics never collide on a single shared directory.
13
13
  * Tests inject `opts.epicsDir` to point at a sandbox; the default for
14
14
  * production callers is derived from `lib/config/temp-paths.js#epicTempDir`.
@@ -382,7 +382,7 @@ export function writeState(epicId, state, opts = {}) {
382
382
  * the schema from the file itself.
383
383
  *
384
384
  * Story #1498 / Task #1525 introduced this writer so the rewritten
385
- * `/epic-plan` halves can persist the spec from the decomposer's
385
+ * `/plan` halves can persist the spec from the decomposer's
386
386
  * ticket-array projection (`renderSpec`) without reaching into raw
387
387
  * `js-yaml` calls scattered across the planning scripts.
388
388
  *
@@ -108,7 +108,7 @@ export function sha256Hex(input) {
108
108
  }
109
109
 
110
110
  /**
111
- * Hash a spec entry (a Feature, Story, or Task object) deterministically.
111
+ * Hash a spec entry (a Story object) deterministically.
112
112
  * The entry is canonicalised (recursive key-sort), serialised, and
113
113
  * sha256'd. Equivalent entries (any key order, equivalent nested order
114
114
  * for the object-valued fields) hash to the same digest.
@@ -126,10 +126,9 @@ export function hashSpecEntry(entry) {
126
126
 
127
127
  /**
128
128
  * Iterate every slug-bearing entity in the spec. Yields tuples of
129
- * `[slug, entry]` where `entry` is the raw object as authored in the
130
- * spec (feature/story/task). Order is feature-major, story-minor,
131
- * task-tertiary — the natural read order, useful for deterministic
132
- * mapping iteration.
129
+ * `[slug, entry]` where `entry` is the raw Story object as authored in
130
+ * the spec. Order follows `spec.stories[]` — the natural read order,
131
+ * useful for deterministic mapping iteration.
133
132
  *
134
133
  * Exported for tests; the reconciler diff path uses `projectMapping`
135
134
  * (below) rather than this generator directly.
@@ -138,17 +137,9 @@ export function hashSpecEntry(entry) {
138
137
  * @returns {Generator<[string, object]>}
139
138
  */
140
139
  export function* iterSpecEntries(spec) {
141
- if (!spec || !Array.isArray(spec.features)) return;
142
- for (const feature of spec.features) {
143
- if (feature?.slug) yield [feature.slug, feature];
144
- if (!Array.isArray(feature?.stories)) continue;
145
- for (const story of feature.stories) {
146
- if (story?.slug) yield [story.slug, story];
147
- if (!Array.isArray(story?.tasks)) continue;
148
- for (const task of story.tasks) {
149
- if (task?.slug) yield [task.slug, task];
150
- }
151
- }
140
+ if (!spec || !Array.isArray(spec.stories)) return;
141
+ for (const story of spec.stories) {
142
+ if (story?.slug) yield [story.slug, story];
152
143
  }
153
144
  }
154
145
 
@@ -0,0 +1,76 @@
1
+ /**
2
+ * lib/story-adjacency.js — the single story-level adjacency builder.
3
+ *
4
+ * All three wave-computation wrappers bottom out in the shared
5
+ * `lib/Graph.js` kernel (`detectCycle` / `assignLayers` /
6
+ * `computeWaves`), but each historically re-implemented the step that
7
+ * turns a list of Story records into the `Map<storyId, number[]>`
8
+ * adjacency the kernel consumes. This module is now the one home for
9
+ * that step; the consumers are:
10
+ *
11
+ * - `lib/orchestration/epic-runner/phases/build-wave-dag.js`
12
+ * (`buildStoryDag` → `computeWaves`)
13
+ * - `lib/orchestration/dispatch-pipeline.js`
14
+ * (`buildStoryDispatchGraph` → `computeStoryWaves`)
15
+ * - `stories-wave-tick.js` (`buildAdjacency` → `assignLayers`)
16
+ *
17
+ * Dependency source order (must stay aligned with manifest-builder.js so
18
+ * the dispatch manifest and runtime wave scheduling never disagree):
19
+ * 1. Canonical: `blocked by #NNN` / `depends on #NNN` parsed from the
20
+ * Story body via `parseBlockedBy` (the same parser the dispatcher
21
+ * uses).
22
+ * 2. Fallback: an explicit `dependencies` (ticket shape) or
23
+ * `dependsOn` (operator-DAG shape) array on the Story record.
24
+ *
25
+ * @module lib/story-adjacency
26
+ */
27
+
28
+ import { parseBlockedBy } from './dependency-parser.js';
29
+
30
+ /**
31
+ * Build a story-level adjacency map (`Map<storyId, dependencyIds[]>`)
32
+ * from an ordered list of Story records.
33
+ *
34
+ * Each record contributes one adjacency entry keyed by
35
+ * `Number(record.id ?? record.number)`. Dependencies are the deduped
36
+ * union of body-parsed `blocked by` references and the record's
37
+ * explicit `dependencies` / `dependsOn` array, with self-edges and
38
+ * non-integer ids always dropped.
39
+ *
40
+ * @param {Array<{id?: number|string, number?: number, body?: string,
41
+ * dependencies?: Array<number|string>, dependsOn?: Array<number|string>}>} stories
42
+ * Story records (live ticket payloads, fixture tickets, or operator
43
+ * DAG nodes).
44
+ * @param {object} [opts]
45
+ * @param {boolean} [opts.dropForeign=true] When true (the default,
46
+ * matching the Epic-scoped wrappers), edges pointing at ids outside
47
+ * the supplied story set are dropped so the DAG stays closed over the
48
+ * scheduled set. `stories-wave-tick.js` passes `false` to preserve its
49
+ * historical operator-DAG contract, where a dependency on an id absent
50
+ * from the input still deepens the dependent's layer.
51
+ * @returns {Map<number, number[]>}
52
+ */
53
+ export function buildStoryAdjacency(stories, { dropForeign = true } = {}) {
54
+ const records = Array.isArray(stories) ? stories : [];
55
+ const storyIds = new Set(records.map((s) => Number(s?.id ?? s?.number)));
56
+ const adjacency = new Map();
57
+ for (const s of records) {
58
+ const id = Number(s?.id ?? s?.number);
59
+ const fromBody = parseBlockedBy(s?.body ?? '');
60
+ const fromField = Array.isArray(s?.dependencies)
61
+ ? s.dependencies.map(Number)
62
+ : Array.isArray(s?.dependsOn)
63
+ ? s.dependsOn.map(Number)
64
+ : [];
65
+ const merged = [...new Set([...fromBody, ...fromField])]
66
+ .map(Number)
67
+ .filter(
68
+ (dep) =>
69
+ Number.isInteger(dep) &&
70
+ dep !== id &&
71
+ (!dropForeign || storyIds.has(dep)),
72
+ );
73
+ adjacency.set(id, merged);
74
+ }
75
+ return adjacency;
76
+ }
@@ -21,7 +21,7 @@ import { resolveStoryHierarchy } from '../story-lifecycle.js';
21
21
  * @param {number} deps.input.storyId
22
22
  * @param {number|null} [deps.input.recutOf]
23
23
  * @param {boolean} [deps.input.dryRun]
24
- * @returns {Promise<{ story: object, body: string, epicId: number, featureId: number|null }>}
24
+ * @returns {Promise<{ story: object, body: string, epicId: number, parentId: number|null }>}
25
25
  */
26
26
  export async function resolveContext({ provider, logger, input }) {
27
27
  const { storyId, recutOf = null, dryRun = false } = input;
@@ -43,7 +43,7 @@ export async function resolveContext({ provider, logger, input }) {
43
43
  }
44
44
 
45
45
  let body = story.body ?? '';
46
- const { epicId, featureId } = resolveStoryHierarchy(body);
46
+ const { epicId, parentId } = resolveStoryHierarchy(body);
47
47
 
48
48
  if (!epicId) {
49
49
  throw new Error(
@@ -88,5 +88,5 @@ export async function resolveContext({ provider, logger, input }) {
88
88
  }
89
89
  }
90
90
 
91
- return { story, body, epicId, featureId };
91
+ return { story, body, epicId, parentId };
92
92
  }
@@ -2,8 +2,8 @@
2
2
  * state-transitioner.js — Stage 6 of the story-init pipeline.
3
3
  *
4
4
  * Flips the Story ticket to `agent::executing` at init time. Under the
5
- * 3-tier hierarchy the Story has inline acceptance and no child Task
6
- * lifecycle — `/story-deliver` runs a single Story-implementation phase.
5
+ * 2-tier hierarchy the Story has inline acceptance and no child Task
6
+ * lifecycle — `/deliver` runs a single Story-implementation phase.
7
7
  */
8
8
 
9
9
  import {
@@ -14,7 +14,7 @@ import { fetchChildTickets } from '../story-lifecycle.js';
14
14
 
15
15
  /**
16
16
  * Detect whether a Story body carries inline acceptance criteria, the
17
- * structural signal that the Story is authored in the 3-tier
17
+ * structural signal that the Story is authored in the 2-tier
18
18
  * (Story-with-inline-acceptance) shape and therefore should not be expected
19
19
  * to enumerate child Task tickets. Recognises both `## Acceptance` and
20
20
  * `## Acceptance Criteria` headings, with at least one list bullet under
@@ -67,15 +67,15 @@ function sortTasksByDependencies(tasks) {
67
67
  * @param {object} deps.input
68
68
  * @param {number} deps.input.storyId
69
69
  * @param {string} [deps.input.storyBody] Story body — used to detect
70
- * whether the Story carries inline acceptance (3-tier shape) so the
70
+ * whether the Story carries inline acceptance (2-tier shape) so the
71
71
  * empty-Task-list path is treated as expected rather than as a warning.
72
72
  *
73
73
  * Task #3154 (Epic #3078) deleted the `planning.hierarchy` flag; the
74
- * 3-tier vs 4-tier mode is now derived entirely from the ticket shape —
75
- * inline acceptance + zero Tasks resolves to `'3-tier'`, otherwise
74
+ * 2-tier vs 4-tier mode is now derived entirely from the ticket shape —
75
+ * inline acceptance + zero Tasks resolves to `'2-tier'`, otherwise
76
76
  * `'4-tier'`.
77
77
  *
78
- * @returns {Promise<{ sortedTasks: Array<object>, mode: '3-tier'|'4-tier' }>}
78
+ * @returns {Promise<{ sortedTasks: Array<object>, mode: '2-tier'|'4-tier' }>}
79
79
  */
80
80
  export async function buildTaskGraph({ provider, logger, input }) {
81
81
  const { storyId, storyBody = '' } = input;
@@ -85,13 +85,13 @@ export async function buildTaskGraph({ provider, logger, input }) {
85
85
  const tasks = await fetchChildTickets(provider, storyId);
86
86
 
87
87
  const inlineAcceptance = hasInlineAcceptance(storyBody);
88
- const mode = tasks.length === 0 && inlineAcceptance ? '3-tier' : '4-tier';
88
+ const mode = tasks.length === 0 && inlineAcceptance ? '2-tier' : '4-tier';
89
89
 
90
90
  if (tasks.length === 0) {
91
91
  if (inlineAcceptance) {
92
92
  progress(
93
93
  'TASKS',
94
- `Story #${storyId} has inline acceptance — no child Tasks expected (3-tier shape).`,
94
+ `Story #${storyId} has inline acceptance — no child Tasks expected (2-tier shape).`,
95
95
  );
96
96
  } else {
97
97
  warn(
@@ -10,7 +10,7 @@
10
10
  * (branch bootstrap, merge, cascade, notifications). Expanding this module to
11
11
  * cover those would over-abstract — they are genuinely different concerns.
12
12
  *
13
- * Under the 3-tier hierarchy (Epic #3078) Stories have no child tickets, so
13
+ * Under the 2-tier hierarchy (Epic #3078) Stories have no child tickets, so
14
14
  * `fetchChildTickets` typically returns `[]` — but the helper remains in
15
15
  * place so legacy callers still resolve the Story's direct children cleanly.
16
16
  */
@@ -24,7 +24,7 @@ import {
24
24
  * Parse the `Epic: #N` and `parent: #N` references from a Story body.
25
25
  *
26
26
  * @param {string} body Raw Story body Markdown.
27
- * @returns {{ epicId: number|null, featureId: number|null }}
27
+ * @returns {{ epicId: number|null, parentId: number|null }}
28
28
  */
29
29
  export function resolveStoryHierarchy(body) {
30
30
  const source = body ?? '';
@@ -32,25 +32,25 @@ export function resolveStoryHierarchy(body) {
32
32
  const parentMatch = source.match(/(?:^\s*parent:\s*#(\d+))/im);
33
33
  return {
34
34
  epicId: epicMatch ? Number.parseInt(epicMatch[1], 10) : null,
35
- featureId: parentMatch ? Number.parseInt(parentMatch[1], 10) : null,
35
+ parentId: parentMatch ? Number.parseInt(parentMatch[1], 10) : null,
36
36
  };
37
37
  }
38
38
 
39
39
  /**
40
40
  * Fetch the Story's direct child tickets via the provider.
41
41
  *
42
- * Under the 3-tier hierarchy (Epic #3078) Stories no longer enumerate
42
+ * Under the 2-tier hierarchy (Epic #3078) Stories no longer enumerate
43
43
  * child Task tickets — acceptance criteria and verification steps live
44
- * inline on the Story body, and decomposers emit only Epic / Feature /
45
- * Story issues. For those Stories this helper resolves to an empty
44
+ * inline on the Story body, and decomposers emit only Epic / Story
45
+ * issues. For those Stories this helper resolves to an empty
46
46
  * array because `provider.getSubTickets(storyId)` itself returns no
47
47
  * rows. The helper is retained as a thin pass-through so the three
48
- * orchestration callers (`story-deliver-prepare`, `task-graph-builder`,
48
+ * orchestration callers (`story-init` prepare, `task-graph-builder`,
49
49
  * and `locked-pipeline`) keep a single, named seam for sub-ticket
50
50
  * hydration that is easy to mock in tests and to instrument in the
51
51
  * provider layer.
52
52
  *
53
- * Legacy ticket trees that were planned before the 3-tier cutover may
53
+ * Legacy ticket trees that were planned before the 2-tier cutover may
54
54
  * still carry sub-tickets (PRD/Tech-Spec links recorded as direct
55
55
  * children, for example). The helper deliberately does **not** filter
56
56
  * those out — the caller is responsible for classifying anything that
@@ -59,7 +59,7 @@ export function resolveStoryHierarchy(body) {
59
59
  *
60
60
  * @param {object} provider ITicketingProvider instance.
61
61
  * @param {number} storyId Story ticket number to hydrate children for.
62
- * @returns {Promise<object[]>} Array of child tickets (empty under 3-tier).
62
+ * @returns {Promise<object[]>} Array of child tickets (empty under 2-tier).
63
63
  */
64
64
  export async function fetchChildTickets(provider, storyId) {
65
65
  return provider.getSubTickets(storyId);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * story-plan.js — helpers for `/story-plan`.
2
+ * story-plan.js — helpers for `/plan`.
3
3
  *
4
4
  * Pure functions used by `.agents/scripts/story-plan.js` (the CLI).
5
5
  * Kept side-effect-free so the CLI stays a thin orchestrator and these