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
@@ -11,6 +11,7 @@
11
11
  * `isCoverageFresh` and decide whether to delegate to `runCapture`.
12
12
  */
13
13
  import { spawnSync } from 'node:child_process';
14
+ import crypto from 'node:crypto';
14
15
  import fs from 'node:fs';
15
16
  import path from 'node:path';
16
17
 
@@ -65,10 +66,130 @@ export function newestSourceMtime(cwd, targetDirs, io = {}) {
65
66
  }
66
67
 
67
68
  /**
68
- * Decide whether the existing coverage artifact is "fresh" present and at
69
- * least as new as the newest source file under `targetDirs`. Missing files,
70
- * missing target dirs, or any IO error resolve to `false` so the caller
71
- * captures rather than trusting stale data.
69
+ * Resolve the capture-stamp path that sits next to the coverage artifact.
70
+ * The stamp persists the content digest of the CRAP-target sources at the
71
+ * moment coverage was last captured, so freshness can be decided by content
72
+ * rather than mtime (mtime churns on branch switches / checkouts even when
73
+ * content is unchanged).
74
+ *
75
+ * @param {string} cwd Absolute repo root.
76
+ * @param {string} coveragePath Repo-relative coverage artifact path.
77
+ * @returns {string} Absolute stamp path (`<coverage-dir>/.capture-stamp.json`).
78
+ */
79
+ export function captureStampPath(cwd, coveragePath) {
80
+ return path.join(
81
+ path.dirname(path.resolve(cwd, coveragePath)),
82
+ '.capture-stamp.json',
83
+ );
84
+ }
85
+
86
+ const SOURCE_EXT_RE = /\.(?:js|mjs)$/;
87
+
88
+ /**
89
+ * Compute a stable content digest of the `.js`/`.mjs` sources under
90
+ * `targetDirs`: the `git ls-files -s` listing (mode + blob SHA + path) of
91
+ * tracked content, plus the on-disk bytes of any dirty working-tree files.
92
+ * Checkout/branch churn leaves blob SHAs untouched, so the digest only moves
93
+ * when content actually changes.
94
+ *
95
+ * Returns `null` when the digest cannot be computed (git unavailable, not a
96
+ * repo, empty target list) so callers can fall back to the mtime heuristic.
97
+ *
98
+ * @param {string} cwd Absolute repo root.
99
+ * @param {string[]} targetDirs Repo-relative directories to digest.
100
+ * @param {{ spawnSync?: typeof spawnSync, readFileSync?: typeof fs.readFileSync }} [io]
101
+ * @returns {string | null} Hex SHA-256 digest, or null when unavailable.
102
+ */
103
+ export function computeContentDigest(cwd, targetDirs, io = {}) {
104
+ const spawn = io.spawnSync ?? spawnSync;
105
+ const readFileSync = io.readFileSync ?? fs.readFileSync;
106
+ const dirs = (targetDirs ?? []).filter(
107
+ (d) => typeof d === 'string' && d.length > 0,
108
+ );
109
+ if (dirs.length === 0) return null;
110
+
111
+ const git = (...args) => {
112
+ const res = spawn('git', args, { cwd, encoding: 'utf8' });
113
+ if (res?.error || res?.status !== 0) {
114
+ throw res?.error ?? new Error(res?.stderr || `git ${args[0]} failed`);
115
+ }
116
+ return res.stdout ?? '';
117
+ };
118
+
119
+ try {
120
+ const hash = crypto.createHash('sha256');
121
+ const tracked = git('ls-files', '-s', '--', ...dirs)
122
+ .split('\n')
123
+ .filter((line) => SOURCE_EXT_RE.test(line.trimEnd()));
124
+ hash.update(tracked.join('\n'));
125
+
126
+ // Dirty working-tree files are not represented by their index blob SHA,
127
+ // so fold in their on-disk bytes (or absence) explicitly.
128
+ const dirty = git('status', '--porcelain', '--', ...dirs)
129
+ .split('\n')
130
+ .filter((line) => line.length > 3);
131
+ for (const line of dirty) {
132
+ let file = line.slice(3).trim();
133
+ if (file.includes(' -> ')) file = file.split(' -> ').pop();
134
+ file = file.replace(/^"|"$/g, '');
135
+ if (!SOURCE_EXT_RE.test(file)) continue;
136
+ hash.update(`\0${file}\0`);
137
+ try {
138
+ hash.update(readFileSync(path.resolve(cwd, file)));
139
+ } catch {
140
+ hash.update('<absent>');
141
+ }
142
+ }
143
+ return hash.digest('hex');
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Persist the capture stamp next to the coverage artifact. Best-effort: a
151
+ * write failure returns `false` rather than throwing — the worst case is a
152
+ * fall back to the mtime heuristic on the next freshness check.
153
+ *
154
+ * @param {{
155
+ * cwd: string,
156
+ * coveragePath: string,
157
+ * digest: string,
158
+ * writeFileSync?: typeof fs.writeFileSync,
159
+ * }} opts
160
+ * @returns {boolean} True when the stamp was written.
161
+ */
162
+ export function writeCaptureStamp({
163
+ cwd,
164
+ coveragePath,
165
+ digest,
166
+ writeFileSync = fs.writeFileSync,
167
+ }) {
168
+ if (typeof digest !== 'string' || digest.length === 0) return false;
169
+ try {
170
+ writeFileSync(
171
+ captureStampPath(cwd, coveragePath),
172
+ `${JSON.stringify({ digest, capturedAt: new Date().toISOString() }, null, 2)}\n`,
173
+ );
174
+ return true;
175
+ } catch {
176
+ return false;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Decide whether the existing coverage artifact is "fresh".
182
+ *
183
+ * Primary test (content-aware, Story #3982): when a capture stamp exists
184
+ * next to the artifact, compare its persisted digest against the current
185
+ * content digest of `targetDirs`. Equal digests → fresh; different → stale.
186
+ * Branch switches and checkouts that bump mtimes without changing content
187
+ * no longer invalidate coverage.
188
+ *
189
+ * Fallback (stamp absent / unreadable / digest unavailable): the original
190
+ * mtime heuristic — artifact at least as new as the newest source file
191
+ * under `targetDirs`. Missing files, missing target dirs, or any IO error
192
+ * resolve to `false` so the caller captures rather than trusting stale data.
72
193
  *
73
194
  * @param {{
74
195
  * coveragePath: string,
@@ -77,6 +198,8 @@ export function newestSourceMtime(cwd, targetDirs, io = {}) {
77
198
  * statSync?: typeof fs.statSync,
78
199
  * readdirSync?: typeof fs.readdirSync,
79
200
  * existsSync?: typeof fs.existsSync,
201
+ * readFileSync?: typeof fs.readFileSync,
202
+ * computeDigest?: typeof computeContentDigest,
80
203
  * }} opts
81
204
  * @returns {{ fresh: boolean, reason: 'missing' | 'stale' | 'fresh' | 'no-sources' }}
82
205
  */
@@ -87,10 +210,30 @@ export function isCoverageFresh({
87
210
  statSync = fs.statSync,
88
211
  readdirSync = fs.readdirSync,
89
212
  existsSync = fs.existsSync,
213
+ readFileSync = fs.readFileSync,
214
+ computeDigest = computeContentDigest,
90
215
  }) {
91
216
  const absCoverage = path.resolve(cwd, coveragePath);
92
217
  if (!existsSync(absCoverage)) return { fresh: false, reason: 'missing' };
93
218
 
219
+ const stampPath = captureStampPath(cwd, coveragePath);
220
+ if (existsSync(stampPath)) {
221
+ let stamp = null;
222
+ try {
223
+ stamp = JSON.parse(readFileSync(stampPath, 'utf8'));
224
+ } catch {
225
+ // Corrupt/unreadable stamp → fall through to the mtime heuristic.
226
+ }
227
+ if (typeof stamp?.digest === 'string' && stamp.digest.length > 0) {
228
+ const current = computeDigest(cwd, targetDirs);
229
+ if (typeof current === 'string' && current.length > 0) {
230
+ return current === stamp.digest
231
+ ? { fresh: true, reason: 'fresh' }
232
+ : { fresh: false, reason: 'stale' };
233
+ }
234
+ }
235
+ }
236
+
94
237
  let coverageMtime;
95
238
  try {
96
239
  coverageMtime = statSync(absCoverage).mtimeMs;
@@ -54,6 +54,20 @@ import { Worker } from 'node:worker_threads';
54
54
  /** Default factory: spawn a real `worker_threads.Worker`. */
55
55
  const defaultWorkerFactory = (script, options) => new Worker(script, options);
56
56
 
57
+ /**
58
+ * Pool-vs-serial cutover for `runOnPool` callers.
59
+ *
60
+ * Below this batch size the pool's worker spawn overhead dominates, so
61
+ * callers fall back to in-process serial scoring. Tuned against the test
62
+ * suite's tmpdir fixtures (n=2 stays serial; the full repo n≈200–470
63
+ * takes the pool path). Single-sourced here so the maintainability
64
+ * baseline scan (`maintainability-utils.js`), the CRAP scanner
65
+ * (`crap-utils.js`), and the native review provider
66
+ * (`review-providers/native.js`) cannot silently desynchronize on a
67
+ * retune.
68
+ */
69
+ export const POOL_SERIAL_THRESHOLD = 8;
70
+
57
71
  /**
58
72
  * @template TItem, TResult
59
73
  * @param {string|URL} workerScript - File URL or path to the worker entry.
@@ -6,22 +6,17 @@ import {
6
6
  coverageForMethodInEntry,
7
7
  findCoverageEntry,
8
8
  } from './coverage-utils.js';
9
- import { runOnPool } from './cpu-pool.js';
9
+ import { POOL_SERIAL_THRESHOLD, runOnPool } from './cpu-pool.js';
10
10
  import { crapFormula } from './crap-engine.js';
11
11
  import { Logger } from './Logger.js';
12
- import {
13
- resolveTsTranspilerVersion,
14
- scanDirectory,
15
- transpileIfNeeded,
16
- } from './maintainability-utils.js';
12
+ import { scanDirectory } from './maintainability-utils.js';
13
+ import { resolveTsTranspilerVersion, transpileIfNeeded } from './transpile.js';
17
14
 
18
15
  const CRAP_WORKER_URL = new URL('./workers/crap-worker.js', import.meta.url);
19
16
 
20
- // Below this batch size the pool's spawn overhead dominates — fall
21
- // back to in-process serial scoring. The full repo (~200+ files)
22
- // always takes the pool path; tests with handful-of-files fixtures
23
- // stay serial and remain byte-identical to the pre-pool output.
24
- const SERIAL_THRESHOLD = 8;
17
+ // Pool-vs-serial cutover single-sourced in cpu-pool.js (see the
18
+ // POOL_SERIAL_THRESHOLD docstring for the tuning rationale).
19
+ const SERIAL_THRESHOLD = POOL_SERIAL_THRESHOLD;
25
20
  // 1.1.0 — TypeScript support landed in 5.29.0. Bumped from 1.0.0 because
26
21
  // the scanner now emits CRAP rows for TS/TSX paths that the previous
27
22
  // kernel could never reach. The CRAP formula and per-method scoring
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * duplicate-search.js — Cross-Epic Duplicate Detection
3
3
  *
4
- * Used by `/epic-plan` Phase 2 (s-plan-ideation) to surface open Epics
4
+ * Used by `/plan` Phase 2 (s-plan-ideation) to surface open Epics
5
5
  * whose scope overlaps with a sharpened one-pager before a new Epic is
6
6
  * created. Returns ranked candidates with an overlap score and URL so
7
7
  * the host LLM can pause for HITL confirmation.
@@ -26,7 +26,7 @@
26
26
  *
27
27
  * Both paths MUST emit the identical per-lens report contract
28
28
  * (`{{auditOutputDir}}/<lens>-results.md`), so downstream consumers
29
- * (`/epic-deliver` Phase 4 epic-audit, `audit-to-stories`) are agnostic to
29
+ * (`/deliver` Phase 4 epic-audit, `audit-to-stories`) are agnostic to
30
30
  * which path produced it.
31
31
  *
32
32
  * ## Why this is capability-degradation, not a contract shim
@@ -0,0 +1,87 @@
1
+ // .agents/scripts/lib/dynamic-workflow/documentation-report-contract.js
2
+ import { assertSectionsContract } from './report-contract-core.js';
3
+
4
+ /**
5
+ * The `audit-documentation` report contract (Story #4024).
6
+ *
7
+ * This is the **single source of truth** for the report shape that BOTH the
8
+ * sequential lens (`.agents/workflows/audit-documentation.md` Step 3) and the
9
+ * orchestrated dynamic-workflow path
10
+ * (`.claude/workflows/audit-documentation.workflow.js`) MUST emit to
11
+ * `{{auditOutputDir}}/audit-documentation-results.md`. Keeping it here lets
12
+ * the contract-tier test assert report conformance against one definition
13
+ * rather than re-deriving headings from prose in two places.
14
+ *
15
+ * @module dynamic-workflow/documentation-report-contract
16
+ */
17
+
18
+ /** The artifact filename the lens writes under `auditOutputDir`. */
19
+ export const REPORT_ARTIFACT_BASENAME = 'audit-documentation-results.md';
20
+
21
+ /**
22
+ * The required top-level (`##`) section headings, in document order, that the
23
+ * lens markdown's Step 3 template defines. A conformant report MUST contain
24
+ * each of these headings; the orchestrated path assembles its verified
25
+ * findings into exactly this skeleton.
26
+ */
27
+ export const REQUIRED_SECTIONS = Object.freeze([
28
+ 'Executive Summary',
29
+ 'Target Set Coverage',
30
+ 'Detailed Findings',
31
+ ]);
32
+
33
+ /** The H1 title the report opens with. */
34
+ export const REPORT_TITLE = 'Documentation Audit Report';
35
+
36
+ /**
37
+ * The required field labels inside each `### <finding>` block under
38
+ * `## Detailed Findings`. Mirrors the strict per-finding structure in the
39
+ * lens template.
40
+ */
41
+ export const FINDING_FIELDS = Object.freeze([
42
+ 'Category',
43
+ 'Impact',
44
+ 'Current State',
45
+ 'Recommendation & Rationale',
46
+ 'Agent Prompt',
47
+ ]);
48
+
49
+ /**
50
+ * The finding categories the per-finding `Category` field draws from. These
51
+ * mirror the lens's Step 3 template enumeration: deterministic-gate findings
52
+ * (Generator Drift, Link Integrity) plus semantic-verification findings
53
+ * (Broken Instruction, Stale Description, Missing Coverage).
54
+ */
55
+ export const FINDING_CATEGORIES = Object.freeze([
56
+ 'Broken Instruction',
57
+ 'Stale Description',
58
+ 'Missing Coverage',
59
+ 'Generator Drift',
60
+ 'Link Integrity',
61
+ ]);
62
+
63
+ /**
64
+ * The impact buckets the Executive Summary and per-finding `Impact` field draw
65
+ * from. The lens speaks in High/Medium/Low; this list is the canonical
66
+ * taxonomy the downstream `audit-to-stories` consumer maps onto Story
67
+ * priority.
68
+ */
69
+ export const IMPACT_LEVELS = Object.freeze(['High', 'Medium', 'Low']);
70
+
71
+ /**
72
+ * Assert that a rendered markdown report conforms to the contract: it has the
73
+ * H1 title and every required `##` section heading. Returns a structured
74
+ * result rather than throwing, so callers (tests, the orchestrated path's
75
+ * self-check) can report precisely which sections are missing.
76
+ *
77
+ * Pure function — string analysis only.
78
+ *
79
+ * @param {string} markdown - The rendered report body.
80
+ * @returns {{ conformant: boolean, missingSections: string[], hasTitle: boolean }}
81
+ */
82
+ export function assertReportContract(markdown) {
83
+ return assertSectionsContract(markdown, {
84
+ title: REPORT_TITLE,
85
+ requiredSections: REQUIRED_SECTIONS,
86
+ });
87
+ }
@@ -10,7 +10,7 @@
10
10
  * Verdict rule: `clear` requires **both** (a) ≥ 4 of 5 canonical sections
11
11
  * present, **and** (b) the **Acceptance Criteria** section present. The
12
12
  * Acceptance-Criteria requirement is load-bearing: a downstream
13
- * `/epic-deliver` start gate and the close-time acceptance-spec reconciler
13
+ * `/deliver` start gate and the close-time acceptance-spec reconciler
14
14
  * both assume the Epic carries acceptance criteria, so a gate that passed an
15
15
  * Epic with no Acceptance Criteria (the pre-Story-#3910 `≥ 4 of 5` behaviour)
16
16
  * advertised a clarity guarantee it did not provide. AC is now a required
@@ -1,5 +1,5 @@
1
1
  /**
2
- * epic-plan-ideation.js — Phase 3/4 helpers for /epic-plan
2
+ * epic-plan-ideation.js — Phase 3/4 helpers for /plan
3
3
  *
4
4
  * Phase 3: render an Epic body from a sharpened ideation one-pager
5
5
  * using the canonical template at `.agents/templates/epic-from-idea.md`.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * memory-freshness.js — walker + verifier for the `/epic-plan` Phase 0
2
+ * memory-freshness.js — walker + verifier for the `/plan` Phase 0
3
3
  * memory-freshness pre-flight. Story #2557 / Epic #2547. Tech Spec #2550.
4
4
  *
5
5
  * Walks every `.md` file under a memory directory (typically
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * prior-feedback-fetcher.js — gh-CLI-backed fetcher for open meta feedback
3
- * issues that feed the `/epic-plan` Phase 0 planner context.
3
+ * issues that feed the `/plan` Phase 0 planner context.
4
4
  *
5
5
  * Story #2554 / Epic #2547. Tech Spec #2550 specifies that the fetcher MUST
6
6
  * return open issues carrying the `meta::framework-gap` and
@@ -51,7 +51,7 @@ export const FINDING_CLASSES = Object.freeze([
51
51
  /**
52
52
  * Class → label-set routing table. Each class maps to exactly one label set.
53
53
  * `tooling-dx` is the framework-gap path: it carries `meta::framework-gap` so
54
- * the `/epic-plan` Phase 0 feedback fetcher surfaces it to the planner.
54
+ * the `/plan` Phase 0 feedback fetcher surfaces it to the planner.
55
55
  */
56
56
  const CLASS_TO_LABELS = Object.freeze({
57
57
  'product-bug': [FOCUS_LABELS.PRODUCT],
@@ -5,8 +5,8 @@
5
5
  * tail of the exploratory-QA Triage path: once an operator has dispositioned a
6
6
  * session's ledger items (see `.agents/schemas/qa-ledger.schema.json` and
7
7
  * `lib/qa/qa-session.js`), the still-untriaged backlog is clustered and each
8
- * cluster is promoted to a follow-up ticket — a single Story (via `/story-plan`)
9
- * for a tight, one-deliverable cluster, or an Epic (via `/epic-plan --idea`) for
8
+ * cluster is promoted to a follow-up ticket — a single Story (via `/plan`)
9
+ * for a tight, one-deliverable cluster, or an Epic (via `/plan --idea`) for
10
10
  * a broad cluster that spans multiple coverage surfaces. Each contributing
11
11
  * ledger item then has the resulting `routedTo` issue link written back onto it
12
12
  * so a resume run sees the item as filed rather than re-promoting it.
@@ -23,7 +23,7 @@
23
23
  * Pure orchestration: **no network I/O lives here.** Every GitHub side-effect
24
24
  * (issue search, ticket creation) flows through INJECTED PORTS so the unit test
25
25
  * runs with no network. Production wires the ports to the GitHub provider /
26
- * `/story-plan` / `/epic-plan` surfaces; tests pass in-memory stubs.
26
+ * `/plan` / `/plan` surfaces; tests pass in-memory stubs.
27
27
  */
28
28
 
29
29
  import { fingerprintFinding, routeFinding } from './route-finding.js';
@@ -40,8 +40,8 @@ export const PROMOTION_TARGETS = Object.freeze({
40
40
 
41
41
  /**
42
42
  * A cluster of more than this many distinct coverage surfaces is broad enough
43
- * to warrant an Epic (`/epic-plan --idea`) rather than a single Story
44
- * (`/story-plan`). One or two surfaces is a tight, single-deliverable cluster.
43
+ * to warrant an Epic (`/plan --idea`) rather than a single Story
44
+ * (`/plan`). One or two surfaces is a tight, single-deliverable cluster.
45
45
  */
46
46
  const EPIC_COVERAGE_THRESHOLD = 2;
47
47
 
@@ -159,8 +159,8 @@ export function clusterLedgerItems(items) {
159
159
  /**
160
160
  * Decide a cluster's promotion target. A cluster that spans more than
161
161
  * {@link EPIC_COVERAGE_THRESHOLD} distinct coverage surfaces is broad enough to
162
- * warrant an Epic (`/epic-plan --idea`); otherwise it is a single-deliverable
163
- * Story (`/story-plan`).
162
+ * warrant an Epic (`/plan --idea`); otherwise it is a single-deliverable
163
+ * Story (`/plan`).
164
164
  *
165
165
  * @param {{ coverages: string[] }} cluster
166
166
  * @returns {'story'|'epic'}
@@ -233,7 +233,7 @@ function routedToLink(issue, kind) {
233
233
  * shared `routeFinding` against existing Issues (via the injected search
234
234
  * port). This dedups against work already filed.
235
235
  * 2. On a `new` decision, open the follow-up ticket through the injected
236
- * `createStory` (`/story-plan`) or `createEpic` (`/epic-plan --idea`) port,
236
+ * `createStory` (`/plan`) or `createEpic` (`/plan --idea`) port,
237
237
  * chosen by {@link targetForCluster}. On any other decision, link back to
238
238
  * the matched Issue rather than creating a duplicate.
239
239
  * 3. Stamp the resolved `routedTo` link onto every contributing ledger item
@@ -250,9 +250,9 @@ function routedToLink(issue, kind) {
250
250
  * @param {(finding: object) => Promise<Array<{ number: number, state: string, title?: string, body?: string }>>} [ports.searchCandidates]
251
251
  * Optional semantic candidate search, forwarded to `routeFinding`.
252
252
  * @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createStory
253
- * Opens a single Story (`/story-plan`) for a tight cluster.
253
+ * Opens a single Story (`/plan`) for a tight cluster.
254
254
  * @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createEpic
255
- * Opens an Epic (`/epic-plan --idea`) for a broad cluster.
255
+ * Opens an Epic (`/plan --idea`) for a broad cluster.
256
256
  * @returns {Promise<{
257
257
  * promotions: Array<{
258
258
  * clusterKey: string,
@@ -231,25 +231,28 @@ export function __setSleep(fn, opts = {}) {
231
231
  }
232
232
 
233
233
  /**
234
- * Run `git fetch …` with a bounded retry loop that only triggers on known
235
- * packed-refs lock-contention signatures. Non-contention failures surface
236
- * immediately (no retry). Success short-circuits the loop.
234
+ * Shared bounded retry loop for git commands that can hit packed-refs lock
235
+ * contention. Only contention signatures trigger a retry — non-contention
236
+ * failures surface immediately, and success short-circuits the loop.
237
237
  *
238
238
  * Backoff schedule: 250ms, 500ms, 1000ms (3 retries → 4 attempts total).
239
239
  * Deliberately no global lock — a mutex would erase the parallelism the
240
- * worktree-isolation model is designed to enable.
240
+ * worktree-isolation model is designed to enable. The schedule and the
241
+ * jitter policy (`_sleep` / `_jitterFactor` seams) live only here so a
242
+ * backoff tuning change has a single point of application.
241
243
  *
242
244
  * @param {string} cwd
243
- * @param {...string} args - Arguments after `fetch` (e.g. `'origin'`).
245
+ * @param {string[]} argvPrefix - Leading git argv (e.g. `['fetch']`).
246
+ * @param {string[]} args - Trailing arguments (e.g. `['origin']`).
244
247
  * @returns {Promise<{ status: number, stdout: string, stderr: string, attempts: number }>}
245
248
  */
246
- export async function gitFetchWithRetry(cwd, ...args) {
249
+ async function gitWithContentionRetry(cwd, argvPrefix, args) {
247
250
  const backoff = [250, 500, 1000];
248
251
  let attempt = 0;
249
252
  let last;
250
253
  for (;;) {
251
254
  attempt++;
252
- last = gitSpawn(cwd, 'fetch', ...args);
255
+ last = gitSpawn(cwd, ...argvPrefix, ...args);
253
256
  if (last.status === 0) return { ...last, attempts: attempt };
254
257
  if (!isPackedRefsContention(last.stderr))
255
258
  return { ...last, attempts: attempt };
@@ -260,6 +263,18 @@ export async function gitFetchWithRetry(cwd, ...args) {
260
263
  }
261
264
  }
262
265
 
266
+ /**
267
+ * Run `git fetch …` with the bounded packed-refs-contention retry loop
268
+ * (see `gitWithContentionRetry`).
269
+ *
270
+ * @param {string} cwd
271
+ * @param {...string} args - Arguments after `fetch` (e.g. `'origin'`).
272
+ * @returns {Promise<{ status: number, stdout: string, stderr: string, attempts: number }>}
273
+ */
274
+ export function gitFetchWithRetry(cwd, ...args) {
275
+ return gitWithContentionRetry(cwd, ['fetch'], args);
276
+ }
277
+
263
278
  /**
264
279
  * Run `git pull --rebase …` with the same bounded retry loop as
265
280
  * `gitFetchWithRetry`. Packed-refs contention can occur during pulls
@@ -269,21 +284,8 @@ export async function gitFetchWithRetry(cwd, ...args) {
269
284
  * @param {...string} args - Arguments after `pull --rebase` (e.g. `'origin', 'main'`).
270
285
  * @returns {Promise<{ status: number, stdout: string, stderr: string, attempts: number }>}
271
286
  */
272
- export async function gitPullWithRetry(cwd, ...args) {
273
- const backoff = [250, 500, 1000];
274
- let attempt = 0;
275
- let last;
276
- for (;;) {
277
- attempt++;
278
- last = gitSpawn(cwd, 'pull', '--rebase', ...args);
279
- if (last.status === 0) return { ...last, attempts: attempt };
280
- if (!isPackedRefsContention(last.stderr))
281
- return { ...last, attempts: attempt };
282
- if (attempt > backoff.length) return { ...last, attempts: attempt };
283
- const base = backoff[attempt - 1];
284
- const jitter = Math.floor(Math.random() * base * _jitterFactor);
285
- await _sleep(base + jitter);
286
- }
287
+ export function gitPullWithRetry(cwd, ...args) {
288
+ return gitWithContentionRetry(cwd, ['pull', '--rebase'], args);
287
289
  }
288
290
 
289
291
  /**
@@ -76,7 +76,6 @@ export function isValidTransition(fromState, toState) {
76
76
 
77
77
  export const TYPE_LABELS = {
78
78
  EPIC: 'type::epic',
79
- FEATURE: 'type::feature',
80
79
  STORY: 'type::story',
81
80
  };
82
81
 
@@ -104,7 +103,7 @@ export const CONTEXT_LABELS = {
104
103
  export const CONTEXT_ACCEPTANCE_SPEC = CONTEXT_LABELS.ACCEPTANCE_SPEC;
105
104
 
106
105
  /**
107
- * Acceptance-axis labels for opt-out signalling on Stories and Features that
106
+ * Acceptance-axis labels for opt-out signalling on Stories that
108
107
  * intentionally have no acceptance-spec coverage. Separate namespace from
109
108
  * `context::` because it expresses absence rather than a linked context
110
109
  * ticket.
@@ -120,7 +119,7 @@ export const ACCEPTANCE_NA = ACCEPTANCE_LABELS.N_A;
120
119
  * loop). `meta::framework-gap` is applied to issues that surface a defect or
121
120
  * missing capability in the framework itself; `meta::consumer-improvement`
122
121
  * is applied to issues that surface improvements to a consumer project
123
- * (workflow tweaks, ergonomic asks, doc polish). The `/epic-plan` Phase 0
122
+ * (workflow tweaks, ergonomic asks, doc polish). The `/plan` Phase 0
124
123
  * fetcher (see `lib/feedback-loop/prior-feedback-fetcher.js`) reads open
125
124
  * issues carrying either label and surfaces them to the planner so retro
126
125
  * signals are routed into durable substrates rather than lost in chat.
@@ -133,7 +132,7 @@ export const META_LABELS = {
133
132
  /**
134
133
  * Planning-axis labels (Epic #2880 F7). Currently scoped to the
135
134
  * `planning::healthcheck-waived` operator-applied waiver, which is the
136
- * documented escape hatch for the `/epic-plan` Phase 10 readiness
135
+ * documented escape hatch for the `/plan` Phase 10 readiness
137
136
  * healthcheck (`epic-plan-healthcheck.js`). The persist half of
138
137
  * `epic-plan-decompose.js` refuses to flip an Epic to `agent::ready`
139
138
  * when the healthcheck returned `ok: false` unless this label is
@@ -53,15 +53,10 @@ export const LABEL_TAXONOMY = [
53
53
  color: LABEL_COLORS.TYPE,
54
54
  description: 'Epic-level work item',
55
55
  },
56
- {
57
- name: TYPE_LABELS.FEATURE,
58
- color: LABEL_COLORS.TYPE,
59
- description: 'Feature under an Epic',
60
- },
61
56
  {
62
57
  name: TYPE_LABELS.STORY,
63
58
  color: LABEL_COLORS.TYPE,
64
- description: 'User story under a Feature',
59
+ description: 'User story under an Epic',
65
60
  },
66
61
 
67
62
  // Agent State
@@ -75,7 +70,7 @@ export const LABEL_TAXONOMY = [
75
70
  name: AGENT_LABELS.READY,
76
71
  color: LABEL_COLORS.AGENT,
77
72
  description:
78
- 'Parking state — frozen dispatch manifest exists; awaiting local /epic-deliver',
73
+ 'Parking state — frozen dispatch manifest exists; awaiting local /deliver',
79
74
  },
80
75
  {
81
76
  name: AGENT_LABELS.EXECUTING,
@@ -120,7 +115,7 @@ export const LABEL_TAXONOMY = [
120
115
  description: 'Acceptance Specification (Gherkin scenarios)',
121
116
  },
122
117
 
123
- // Acceptance axis — explicit opt-out signal for Stories/Features that
118
+ // Acceptance axis — explicit opt-out signal for Stories that
124
119
  // intentionally have no acceptance-spec coverage.
125
120
  {
126
121
  name: ACCEPTANCE_LABELS.N_A,
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import escomplex from 'typhonjs-escomplex';
3
- import { transpileIfNeeded } from './maintainability-utils.js';
3
+ import { transpileIfNeeded } from './transpile.js';
4
4
 
5
5
  /**
6
6
  * Calculates the maintainability score of a JavaScript source file or string.