mandrel 1.93.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/acceptance-critic.md +129 -0
  3. package/.agents/agents/story-worker.md +161 -0
  4. package/.agents/docs/SDLC.md +489 -1285
  5. package/.agents/docs/agentrc-reference.json +177 -67
  6. package/.agents/docs/configuration.md +108 -136
  7. package/.agents/docs/execution-reference.md +44 -22
  8. package/.agents/docs/quality-gates.md +13 -19
  9. package/.agents/docs/workflows.md +3 -3
  10. package/.agents/instructions.md +107 -108
  11. package/.agents/rules/ci-remediation.md +8 -12
  12. package/.agents/rules/git-conventions-reference.md +224 -0
  13. package/.agents/rules/git-conventions.md +42 -223
  14. package/.agents/rules/security-baseline.md +5 -0
  15. package/.agents/rules/testing-standards.md +106 -13
  16. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  17. package/.agents/schemas/agentrc.schema.json +71 -201
  18. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  20. package/.agents/schemas/risk-verdict.schema.json +0 -13
  21. package/.agents/scripts/acceptance-eval.js +62 -18
  22. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  23. package/.agents/scripts/analyze-execution.js +1 -1
  24. package/.agents/scripts/audit-to-stories.js +7 -7
  25. package/.agents/scripts/boot-sweep.js +1 -1
  26. package/.agents/scripts/check-context-budget.js +62 -5
  27. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  28. package/.agents/scripts/check-prepush-recovery.js +1 -1
  29. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  30. package/.agents/scripts/diagnose-friction.js +0 -6
  31. package/.agents/scripts/lib/Logger.js +6 -10
  32. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  33. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  34. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  35. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  36. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  37. package/.agents/scripts/lib/baselines/reader.js +1 -6
  38. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  39. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  40. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  41. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  42. package/.agents/scripts/lib/checks/index.js +2 -1
  43. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  44. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  45. package/.agents/scripts/lib/cli-args.js +16 -7
  46. package/.agents/scripts/lib/close-validation/gates.js +160 -22
  47. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  48. package/.agents/scripts/lib/config/ci.js +6 -31
  49. package/.agents/scripts/lib/config/delivery-routing.js +103 -0
  50. package/.agents/scripts/lib/config/explain.js +57 -36
  51. package/.agents/scripts/lib/config/limits.js +17 -58
  52. package/.agents/scripts/lib/config/paths.js +0 -2
  53. package/.agents/scripts/lib/config/quality.js +1 -1
  54. package/.agents/scripts/lib/config/runners.js +17 -50
  55. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  56. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  57. package/.agents/scripts/lib/config-resolver.js +3 -8
  58. package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
  59. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -14
  60. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  61. package/.agents/scripts/lib/dependency-parser.js +3 -2
  62. package/.agents/scripts/lib/doc-tiers.js +39 -4
  63. package/.agents/scripts/lib/duplicate-search.js +211 -41
  64. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  65. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  66. package/.agents/scripts/lib/framework-version.js +2 -3
  67. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  68. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  69. package/.agents/scripts/lib/git-utils.js +32 -6
  70. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  71. package/.agents/scripts/lib/label-constants.js +10 -23
  72. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  73. package/.agents/scripts/lib/observability/active-story-env.js +112 -3
  74. package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
  75. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  76. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  77. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  78. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  79. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  80. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  81. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
  82. package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
  83. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  84. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  85. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  86. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  87. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  88. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  89. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  90. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  91. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  92. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  93. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  94. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  95. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  96. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  97. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  98. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  99. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  100. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  102. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  103. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  104. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  105. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  106. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  107. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  108. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +14 -14
  109. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  110. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  111. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  112. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  113. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  114. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  115. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  116. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  117. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  118. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  119. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  120. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  121. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  122. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  123. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  124. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  131. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  132. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  134. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  135. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  136. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  137. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  138. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  139. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  140. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  141. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  142. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  143. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  144. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  145. package/.agents/scripts/lib/orchestration/ticket-validator.js +3 -8
  146. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  147. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  148. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  149. package/.agents/scripts/lib/planning-corpus.js +16 -11
  150. package/.agents/scripts/lib/preflight-runner.js +2 -2
  151. package/.agents/scripts/lib/provider-factory.js +1 -1
  152. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  153. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  154. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  155. package/.agents/scripts/lib/story-adjacency.js +11 -14
  156. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  157. package/.agents/scripts/lib/story-plan.js +2 -4
  158. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  159. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  160. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  161. package/.agents/scripts/lib/validation-evidence.js +1 -1
  162. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  163. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  164. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  165. package/.agents/scripts/lint-issue-body.js +71 -40
  166. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  167. package/.agents/scripts/notify.js +4 -3
  168. package/.agents/scripts/plan-context.js +64 -74
  169. package/.agents/scripts/plan-persist.js +121 -280
  170. package/.agents/scripts/plan-run-epilogue.js +97 -0
  171. package/.agents/scripts/post-structured-comment.js +38 -0
  172. package/.agents/scripts/providers/github/issues.js +17 -33
  173. package/.agents/scripts/providers/github/mappers.js +0 -12
  174. package/.agents/scripts/providers/github/tickets.js +2 -5
  175. package/.agents/scripts/resolve-plan-run.js +117 -0
  176. package/.agents/scripts/signals-view.js +24 -19
  177. package/.agents/scripts/single-story-close.js +11 -14
  178. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  179. package/.agents/scripts/single-story-init.js +29 -20
  180. package/.agents/scripts/stories-wave-tick.js +6 -6
  181. package/.agents/scripts/story-plan.js +26 -47
  182. package/.agents/scripts/sync-claude-agents.js +165 -0
  183. package/.agents/scripts/update-ticket-state.js +37 -15
  184. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  185. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  186. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  187. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  188. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  189. package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
  190. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  191. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  192. package/.agents/skills/skills.index.json +8 -418
  193. package/.agents/starter-agentrc.json +0 -5
  194. package/.agents/templates/agent-protocol.md +9 -10
  195. package/.agents/workflows/audit-architecture.md +3 -3
  196. package/.agents/workflows/audit-clean-code.md +3 -3
  197. package/.agents/workflows/audit-dependencies.md +3 -3
  198. package/.agents/workflows/audit-devops.md +3 -3
  199. package/.agents/workflows/audit-documentation.md +5 -5
  200. package/.agents/workflows/audit-lighthouse.md +3 -3
  201. package/.agents/workflows/audit-navigability.md +3 -2
  202. package/.agents/workflows/audit-performance.md +3 -3
  203. package/.agents/workflows/audit-privacy.md +3 -3
  204. package/.agents/workflows/audit-quality.md +3 -3
  205. package/.agents/workflows/audit-security.md +3 -3
  206. package/.agents/workflows/audit-seo.md +3 -3
  207. package/.agents/workflows/audit-sre.md +3 -3
  208. package/.agents/workflows/audit-to-stories.md +20 -20
  209. package/.agents/workflows/audit-ux-ui.md +3 -3
  210. package/.agents/workflows/deliver.md +122 -131
  211. package/.agents/workflows/git-cleanup.md +3 -4
  212. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  213. package/.agents/workflows/helpers/acceptance-self-eval.md +52 -40
  214. package/.agents/workflows/helpers/code-review.md +70 -193
  215. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  216. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  217. package/.agents/workflows/helpers/diagnose.md +10 -10
  218. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  219. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  220. package/.agents/workflows/helpers/signals.md +16 -16
  221. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  222. package/.agents/workflows/mandrel-update.md +3 -2
  223. package/.agents/workflows/plan.md +112 -145
  224. package/.agents/workflows/qa-assist.md +24 -30
  225. package/.agents/workflows/qa-explore.md +29 -38
  226. package/.agents/workflows/qa-run.md +2 -2
  227. package/README.md +9 -8
  228. package/docs/CHANGELOG.md +46 -0
  229. package/lib/cli/registry.js +95 -0
  230. package/lib/migrations/index.js +6 -5
  231. package/package.json +5 -3
  232. package/.agents/personas/architect.md +0 -113
  233. package/.agents/personas/devops-engineer.md +0 -38
  234. package/.agents/personas/engineer-mobile.md +0 -120
  235. package/.agents/personas/engineer-web.md +0 -111
  236. package/.agents/personas/engineer.md +0 -119
  237. package/.agents/personas/product.md +0 -94
  238. package/.agents/personas/project-manager.md +0 -114
  239. package/.agents/personas/qa-engineer.md +0 -95
  240. package/.agents/personas/refactorer.md +0 -113
  241. package/.agents/personas/security-engineer.md +0 -112
  242. package/.agents/personas/sre.md +0 -86
  243. package/.agents/personas/technical-writer.md +0 -101
  244. package/.agents/personas/ux-designer.md +0 -95
  245. package/.agents/schemas/dispatch-manifest.json +0 -232
  246. package/.agents/schemas/epic-spec.schema.json +0 -153
  247. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  248. package/.agents/scripts/dispatcher.js +0 -295
  249. package/.agents/scripts/epic-audit-prepare.js +0 -497
  250. package/.agents/scripts/epic-audit-recheck.js +0 -274
  251. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  252. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  253. package/.agents/scripts/epic-deliver-prepare.js +0 -590
  254. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  255. package/.agents/scripts/epic-plan-clarity.js +0 -211
  256. package/.agents/scripts/epic-plan-decompose.js +0 -54
  257. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  258. package/.agents/scripts/epic-plan-spec.js +0 -64
  259. package/.agents/scripts/epic-reconcile.js +0 -625
  260. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  261. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  262. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  263. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  264. package/.agents/scripts/lib/config/preflight.js +0 -58
  265. package/.agents/scripts/lib/config/retro.js +0 -77
  266. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  267. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  268. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  269. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -660
  270. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  271. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  272. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  273. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  274. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  275. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  276. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  277. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  278. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  279. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  280. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  281. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  282. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  283. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  284. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  285. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  286. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  287. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  288. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -388
  289. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  290. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  291. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  292. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  293. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  294. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  295. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  296. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  297. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  298. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  299. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  300. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  301. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  302. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  303. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  304. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  305. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  306. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  307. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  308. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  309. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  310. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  311. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  312. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  313. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
  314. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  315. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  316. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  317. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -278
  318. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  319. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  320. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  321. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  322. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  323. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  324. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  325. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  326. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  327. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  328. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  329. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  330. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  331. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  332. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  333. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  334. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  335. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  336. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  337. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  338. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  339. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  340. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  341. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  342. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  343. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  344. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  345. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  346. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  347. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  348. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  349. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  350. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  351. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  352. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  353. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  354. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  355. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  356. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  357. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  358. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  359. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  360. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  361. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -301
  362. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  363. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  364. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  365. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  366. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  367. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  368. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  369. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  370. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  371. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  372. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  373. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  374. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  375. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  376. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  377. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  378. package/.agents/scripts/lib/spec/index.js +0 -36
  379. package/.agents/scripts/lib/spec/loader.js +0 -425
  380. package/.agents/scripts/lib/spec/state.js +0 -208
  381. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  382. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  383. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  384. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  385. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  386. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  387. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  388. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  389. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  390. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  391. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  392. package/.agents/scripts/lifecycle-emit.js +0 -510
  393. package/.agents/scripts/plan-critics.js +0 -199
  394. package/.agents/scripts/retro-run.js +0 -218
  395. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  396. package/.agents/scripts/story-close.js +0 -294
  397. package/.agents/scripts/story-init.js +0 -599
  398. package/.agents/scripts/story-phase.js +0 -369
  399. package/.agents/scripts/wave-tick.js +0 -335
  400. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  401. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  402. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  403. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  404. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  405. package/.agents/skills/core/context-engineering/examples.md +0 -58
  406. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  407. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  408. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  409. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  410. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  411. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  412. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  413. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -413
  414. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  415. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  416. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  417. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  418. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  419. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  420. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  421. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  422. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  423. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  424. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  425. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  426. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  427. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  428. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  429. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  430. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  431. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  432. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  433. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  434. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  435. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  436. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  437. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  438. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  439. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  440. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  441. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  442. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  443. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  444. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  445. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  446. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  447. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  448. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  449. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  450. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  451. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  452. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
  453. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
  454. package/.agents/workflows/helpers/deliver-epic.md +0 -955
  455. package/.agents/workflows/helpers/deliver-stories.md +0 -440
  456. package/.agents/workflows/helpers/epic-audit.md +0 -189
  457. package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
  458. package/.agents/workflows/helpers/epic-testing.md +0 -125
  459. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  460. package/.agents/workflows/helpers/plan-epic.md +0 -351
  461. package/.agents/workflows/helpers/plan-story.md +0 -251
  462. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  463. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -1,54 +0,0 @@
1
- /**
2
- * epic-merge-lock-stale — refuse-and-print warning check.
3
- *
4
- * Detects orphan `epic-<id>.merge.lock` files left behind by a crashed
5
- * or hung `withEpicMergeLock()` holder. The live serialization path is
6
- * owned by `lib/epic-merge-lock.js` itself; this check intentionally
7
- * does NOT serialize against it. Instead, at preflight time it probes
8
- * the lock file for each known epic and surfaces a warning when:
9
- *
10
- * - the lock file exists, AND
11
- * - the recorded holder PID is not alive (per `process.kill(pid, 0)`).
12
- *
13
- * Severity is `warning` rather than `blocker` because the live lock
14
- * implementation already steals stale locks on its own (per
15
- * `tryStealStale()` in epic-merge-lock.js). Surfacing the residue
16
- * earlier just helps operators diagnose hung CI runs before they
17
- * snowball into wave-aggregation timeouts. The fix is a literal
18
- * `rm <lockPath>` — refuse-and-print so the operator runs it
19
- * deliberately.
20
- *
21
- * Reads `state.fs.epicMergeLocks`, a map of `epicId → { exists, pid,
22
- * holderAlive, path, ... }` populated by state.js.
23
- */
24
- export default {
25
- id: 'epic-merge-lock-stale',
26
- severity: 'warning',
27
- scope: ['story-close', 'retro'],
28
- autoCorrect: 'refuse-and-print',
29
- detect(state) {
30
- const locks = state?.fs?.epicMergeLocks ?? {};
31
- for (const [epicId, info] of Object.entries(locks)) {
32
- if (!info?.exists) continue;
33
- if (info.holderAlive) continue;
34
- // Lock exists and the holder PID is not alive — orphan.
35
- return {
36
- id: 'epic-merge-lock-stale',
37
- severity: 'warning',
38
- scope: state?.scope ?? 'story-close',
39
- summary: `Orphan epic merge lock for epic/${epicId} (holder PID ${info.pid ?? 'unknown'} not alive)`,
40
- detail: [
41
- `lock path: ${info.path}`,
42
- `holder pid: ${info.pid ?? 'unknown'}`,
43
- info.acquiredAt
44
- ? `acquired at: ${new Date(info.acquiredAt).toISOString()}`
45
- : 'acquired at: unknown',
46
- 'The live lock will steal this on next contention, but the residue can be cleared manually.',
47
- ].join('\n'),
48
- fixCommand: `rm "${info.path}"`,
49
- autoCorrectable: false,
50
- };
51
- }
52
- return null;
53
- },
54
- };
@@ -1,49 +0,0 @@
1
- /**
2
- * stale-origin-epic — refuse-and-print blocker check.
3
- *
4
- * Detects the failure mode where a manual merge into `epic/<id>` landed
5
- * locally but the operator forgot to `git push` the epic branch before
6
- * re-running `story-close.js`. The close script's rebase then fights a
7
- * stale `origin/epic/<id>` and re-introduces a different conflict on
8
- * every retry.
9
- *
10
- * The check surfaces this at story-close preflight (and at retro time
11
- * read-only) so the operator is told to fetch + push the epic branch
12
- * before the close re-runs. It is `refuse-and-print` because the fix
13
- * is `git fetch origin` followed by `git push origin epic/<id>`; both
14
- * touch remote state and therefore violate the autoCorrect rules.
15
- *
16
- * Detection reads `state.git.epicBranchSync` (assembled in state.js).
17
- * Each entry reports `{ local, remote, ahead }` for an `epic/<id>` ref;
18
- * we surface the first ref whose local SHA differs from its remote SHA.
19
- * If the remote ref is missing entirely we return null (this is a
20
- * pre-push epic, not a stale one — story-close.js handles that path).
21
- */
22
- export default {
23
- id: 'stale-origin-epic',
24
- severity: 'blocker',
25
- scope: ['story-close', 'retro'],
26
- autoCorrect: 'refuse-and-print',
27
- detect(state) {
28
- const sync = state?.git?.epicBranchSync ?? {};
29
- const entries = Object.entries(sync);
30
- for (const [branch, info] of entries) {
31
- if (!info?.local || !info.remote) continue;
32
- if (!info.ahead) continue;
33
- return {
34
- id: 'stale-origin-epic',
35
- severity: 'blocker',
36
- scope: state?.scope ?? 'story-close',
37
- summary: `Local ${branch} differs from origin/${branch}; close will rebase against a stale base.`,
38
- detail: [
39
- `local ${branch} = ${info.local}`,
40
- `remote origin/${branch} = ${info.remote}`,
41
- 'Push the epic branch (or fetch origin) before re-running story-close.',
42
- ].join('\n'),
43
- fixCommand: `git fetch origin && git push origin ${branch}`,
44
- autoCorrectable: false,
45
- };
46
- }
47
- return null;
48
- },
49
- };
@@ -1,40 +0,0 @@
1
- /**
2
- * `delivery.lifecycle` accessor + framework defaults.
3
- *
4
- * `TimeoutWatchdog` returns null (no budget) for any timeout key absent from
5
- * the operator config, meaning phases can hang indefinitely on projects that
6
- * omit the block. These defaults ensure every Epic run has a watchdog out of
7
- * the box; projects override individual keys by supplying their own values,
8
- * which are merged over the defaults.
9
- */
10
-
11
- export const LIFECYCLE_DEFAULTS = Object.freeze({
12
- timeouts: Object.freeze({
13
- 'acceptance.reconcile': 600,
14
- 'epic.finalize': 600,
15
- 'epic.watch': 1800,
16
- }),
17
- heartbeatWarnSeconds: 60,
18
- });
19
-
20
- /**
21
- * Read the merged `delivery.lifecycle` block, applying framework defaults
22
- * for any field the operator omitted. Accepts the full resolved config or
23
- * a bare `{ delivery: { lifecycle: ... } }` / `{ lifecycle: ... }` shape.
24
- *
25
- * User-supplied timeout keys win over defaults; unknown keys are preserved
26
- * so projects can define custom phase budgets.
27
- *
28
- * @param {object | null | undefined} config
29
- * @returns {{ timeouts: Record<string, number>, heartbeatWarnSeconds: number }}
30
- */
31
- export function getLifecycle(config) {
32
- const lc = config?.delivery?.lifecycle ?? config?.lifecycle ?? config ?? {};
33
- return {
34
- timeouts: { ...LIFECYCLE_DEFAULTS.timeouts, ...(lc.timeouts ?? {}) },
35
- heartbeatWarnSeconds:
36
- Number.isInteger(lc.heartbeatWarnSeconds) && lc.heartbeatWarnSeconds >= 1
37
- ? lc.heartbeatWarnSeconds
38
- : LIFECYCLE_DEFAULTS.heartbeatWarnSeconds,
39
- };
40
- }
@@ -1,58 +0,0 @@
1
- /**
2
- * `delivery.preflight` accessor + framework defaults — Story #2899
3
- * (Epic #2880, F13).
4
- *
5
- * Thresholds consumed by `.agents/scripts/epic-deliver-preflight.js`.
6
- * The defaults are intentionally generous so that a brand-new Epic on a
7
- * zero-config project does not trip a breach. Operators tighten thresholds
8
- * per project in `.agentrc.json`. A `null` floor means "no cap" — used by
9
- * the preflight runner to skip the corresponding breach check.
10
- */
11
-
12
- export const PREFLIGHT_DEFAULTS = Object.freeze({
13
- maxStories: null,
14
- maxWaves: null,
15
- maxInstallCostSeconds: null,
16
- maxGithubApiRequests: null,
17
- maxClaudeQuotaTokens: null,
18
- });
19
-
20
- /**
21
- * Read the merged `delivery.preflight` block, applying framework defaults
22
- * for any field the operator omitted. Accepts the full resolved config,
23
- * the bare delivery bag, or the bare preflight bag.
24
- *
25
- * @param {object | null | undefined} config
26
- * @returns {{
27
- * maxStories: number | null,
28
- * maxWaves: number | null,
29
- * maxInstallCostSeconds: number | null,
30
- * maxGithubApiRequests: number | null,
31
- * maxClaudeQuotaTokens: number | null,
32
- * }}
33
- */
34
- export function getPreflight(config) {
35
- const pf = config?.delivery?.preflight ?? config?.preflight ?? config ?? {};
36
- return {
37
- maxStories: normaliseFloor(pf.maxStories, PREFLIGHT_DEFAULTS.maxStories),
38
- maxWaves: normaliseFloor(pf.maxWaves, PREFLIGHT_DEFAULTS.maxWaves),
39
- maxInstallCostSeconds: normaliseFloor(
40
- pf.maxInstallCostSeconds,
41
- PREFLIGHT_DEFAULTS.maxInstallCostSeconds,
42
- ),
43
- maxGithubApiRequests: normaliseFloor(
44
- pf.maxGithubApiRequests,
45
- PREFLIGHT_DEFAULTS.maxGithubApiRequests,
46
- ),
47
- maxClaudeQuotaTokens: normaliseFloor(
48
- pf.maxClaudeQuotaTokens,
49
- PREFLIGHT_DEFAULTS.maxClaudeQuotaTokens,
50
- ),
51
- };
52
- }
53
-
54
- function normaliseFloor(value, fallback) {
55
- if (value === null) return null;
56
- if (Number.isInteger(value) && value >= 1) return value;
57
- return fallback;
58
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * Retro accessor (Story #3042 / Task #3043 — Epic #3019).
3
- *
4
- * Resolves `.agentrc.json → delivery.retro` into the canonical shape the
5
- * retro-runner consumes. Currently exposes only `perfThresholds`, the
6
- * operator-tunable gates for the perf-signals classifier in
7
- * `lib/orchestration/retro-perf-heuristics.js`.
8
- *
9
- * Defaults mirror `DEFAULT_RETRO_PERF_THRESHOLDS` in the heuristics module
10
- * (single source of behavioural truth; this resolver is a thin merge layer).
11
- */
12
-
13
- /**
14
- * Default perf-threshold trio applied when `.agentrc.json` omits
15
- * `delivery.retro.perfThresholds` (or any of its sub-keys). Frozen so
16
- * downstream callers cannot accidentally mutate the resolver's defaults
17
- * across processes.
18
- *
19
- * Keep in lockstep with `DEFAULT_RETRO_PERF_THRESHOLDS` in
20
- * `lib/orchestration/retro-perf-heuristics.js` and the schema mirror in
21
- * `agentrc.schema.json → $defs.retro.properties.perfThresholds`.
22
- */
23
- export const DEFAULT_RETRO = Object.freeze({
24
- perfThresholds: Object.freeze({
25
- utilisation: 0.6,
26
- bootstrapShare: 0.4,
27
- capBindingRunLength: 2,
28
- }),
29
- });
30
-
31
- /**
32
- * Read the merged retro block. Returns the canonical shape:
33
- *
34
- * {
35
- * perfThresholds: {
36
- * utilisation: number,
37
- * bootstrapShare: number,
38
- * capBindingRunLength: number,
39
- * },
40
- * }
41
- *
42
- * Each sub-key falls back to its documented default when the resolved
43
- * `.agentrc.json` omits it. Out-of-range values fall back to defaults too —
44
- * the AJV schema rejects them before they reach this accessor, but the
45
- * resolver stays defensive so unit-test fixtures and degraded configs
46
- * never produce nonsensical thresholds.
47
- *
48
- * @param {object | null | undefined} config
49
- * @returns {{ perfThresholds: { utilisation: number, bootstrapShare: number, capBindingRunLength: number } }}
50
- */
51
- export function getRetro(config) {
52
- const user = config?.delivery?.retro?.perfThresholds ?? {};
53
- const defaults = DEFAULT_RETRO.perfThresholds;
54
- return {
55
- perfThresholds: {
56
- utilisation: resolveUnit(user.utilisation, defaults.utilisation),
57
- bootstrapShare: resolveUnit(user.bootstrapShare, defaults.bootstrapShare),
58
- capBindingRunLength: resolvePositiveInt(
59
- user.capBindingRunLength,
60
- defaults.capBindingRunLength,
61
- ),
62
- },
63
- };
64
- }
65
-
66
- function resolveUnit(value, fallback) {
67
- if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;
68
- if (value < 0 || value > 1) return fallback;
69
- return value;
70
- }
71
-
72
- function resolvePositiveInt(value, fallback) {
73
- if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) {
74
- return fallback;
75
- }
76
- return value;
77
- }
@@ -1,322 +0,0 @@
1
- /**
2
- * lib/epic-merge-lock.js — Filesystem mutex for Epic-branch merges.
3
- *
4
- * Parallel-wave story closures can race on the Epic branch: two
5
- * `story-close.js` invocations both `git checkout <epic>`, both
6
- * `git pull --rebase`, and both attempt to merge — the second push
7
- * often ends up rejected or, worse, races past the first and produces
8
- * an incorrect history.
9
- *
10
- * This module provides a best-effort cooperative lock keyed per Epic.
11
- * The lock file lives at `<repoRoot>/.git/epic-<epicId>.merge.lock`
12
- * (inside `.git/` so it never lands in a commit). Acquisition uses
13
- * `fs.openSync(..., 'wx')` for atomicity; on contention we poll every
14
- * 250ms until `timeoutMs` elapses.
15
- *
16
- * Stale-lock stealing:
17
- * - If the PID recorded in the lock is not running (per
18
- * `process.kill(pid, 0)`), or
19
- * - if the lock file is older than `timeoutMs * 2`,
20
- * the lock is stolen (unlinked) and re-acquired.
21
- *
22
- * Test seams (mirroring `single-story-sweep/sweep-lock.js`):
23
- * - `nowFn` — `() => number` (ms epoch); replaces `Date.now`.
24
- * - `fsImpl` — Node `fs` shim; replaces the imported `fs`.
25
- * - `killFn` — `(pid, signal) => void`; replaces `process.kill`,
26
- * so a test can make `pidDead` deterministic without
27
- * fabricating a real dead PID.
28
- * - `sleepFn` — `(ms) => Promise<void>`; replaces the real
29
- * `setTimeout`-backed sleep, so the poll loop can spin
30
- * with no wall-clock waits.
31
- * All four default to the real implementations, so production callers
32
- * (`acquire(epicId, { repoRoot, timeoutMs })`) see no behavior change.
33
- */
34
-
35
- import { execFileSync } from 'node:child_process';
36
- import fs from 'node:fs';
37
- import path from 'node:path';
38
-
39
- const POLL_INTERVAL_MS = 250;
40
-
41
- /**
42
- * Resolve the *common* gitdir for a given working directory.
43
- *
44
- * In a linked worktree (`git worktree add ...`), `<repoRoot>/.git` is a
45
- * one-line gitlink **file**, not a directory. `path.join(repoRoot, '.git')`
46
- * therefore points at the gitlink file and any `mkdir`/`openSync` against
47
- * it fails with `EEXIST: file already exists`.
48
- *
49
- * Resolution order:
50
- * 1. If `<repoRoot>/.git` is already a directory, return it. Covers the
51
- * main-checkout case and the test fixtures, which create a bare
52
- * `.git/` under a temp root — no need to spawn git for those.
53
- * 2. Otherwise (gitlink file, or `.git` absent), shell out to
54
- * `git rev-parse --git-common-dir`. In a worktree this returns the
55
- * parent repo's `.git/`, so lock files placed there are shared
56
- * across every worktree racing on the same Epic — which is the
57
- * correct semantics for an epic-merge mutex.
58
- * 3. If neither succeeds, fall back to `<repoRoot>/.git`. Lock
59
- * acquisition will then surface the underlying error to the
60
- * operator with the literal path that failed.
61
- */
62
- export function resolveGitCommonDir(repoRoot, fsImpl = fs) {
63
- const local = path.join(repoRoot, '.git');
64
- try {
65
- if (fsImpl.statSync(local).isDirectory()) return local;
66
- } catch {
67
- // .git does not exist — fall through to git rev-parse.
68
- }
69
- try {
70
- const out = execFileSync('git', ['rev-parse', '--git-common-dir'], {
71
- cwd: repoRoot,
72
- encoding: 'utf8',
73
- stdio: ['ignore', 'pipe', 'ignore'],
74
- }).trim();
75
- if (out) return path.isAbsolute(out) ? out : path.resolve(repoRoot, out);
76
- } catch {
77
- // not a git repo, or git is unavailable — fall through.
78
- }
79
- return local;
80
- }
81
-
82
- function lockPathFor(epicId, repoRoot, fsImpl = fs) {
83
- return path.join(
84
- resolveGitCommonDir(repoRoot, fsImpl),
85
- `epic-${epicId}.merge.lock`,
86
- );
87
- }
88
-
89
- function isProcessRunning(pid, killFn = process.kill) {
90
- if (!Number.isFinite(pid) || pid <= 0) return false;
91
- try {
92
- // Signal 0 does not deliver a signal; it just checks existence.
93
- killFn(pid, 0);
94
- return true;
95
- } catch (err) {
96
- // ESRCH = no such process. EPERM = exists but we can't signal — still alive.
97
- return err.code === 'EPERM';
98
- }
99
- }
100
-
101
- function readLockMeta(filePath, fsImpl = fs) {
102
- try {
103
- const raw = fsImpl.readFileSync(filePath, 'utf8');
104
- const parsed = JSON.parse(raw);
105
- return {
106
- pid: Number(parsed.pid),
107
- acquiredAt: Number(parsed.acquiredAt),
108
- };
109
- } catch {
110
- return null;
111
- }
112
- }
113
-
114
- function tryStealStale(filePath, timeoutMs, seams) {
115
- const { fsImpl, nowFn, killFn } = seams;
116
- let stats;
117
- try {
118
- stats = fsImpl.statSync(filePath);
119
- } catch {
120
- return false;
121
- }
122
-
123
- const meta = readLockMeta(filePath, fsImpl);
124
- // Corrupted lock file (null meta): we can't verify the writer's PID and
125
- // the age comparison is unsafe on Windows where NTFS mtime vs Date.now()
126
- // can disagree by hundreds of milliseconds, falsely flipping `ancient`
127
- // true at short timeouts. Treat the file as held; the caller times out.
128
- // A truly stuck corrupted lock has to be cleared manually — that's the
129
- // safer failure mode than wrongly stealing a lock another process owns.
130
- if (!meta) return false;
131
-
132
- const ageMs = nowFn() - stats.mtimeMs;
133
- const pidDead = !isProcessRunning(meta.pid, killFn);
134
- const ancient = ageMs > timeoutMs * 2;
135
-
136
- if (pidDead || ancient) {
137
- try {
138
- fsImpl.unlinkSync(filePath);
139
- return true;
140
- } catch {
141
- return false;
142
- }
143
- }
144
- return false;
145
- }
146
-
147
- function defaultSleep(ms) {
148
- return new Promise((resolve) => setTimeout(resolve, ms));
149
- }
150
-
151
- // Inner polling loop. Acquires the lock file with `wx` (atomic
152
- // create-or-error); on EEXIST it tries to steal a stale lock, then
153
- // either times out or sleeps and retries. Kept separate from
154
- // `acquireEpicMergeLock` so the public function's cyclomatic complexity
155
- // stays flat under CRAP.
156
- async function pollForLock(epicId, filePath, timeoutMs, seams) {
157
- const { fsImpl, nowFn, sleepFn } = seams;
158
- const started = nowFn();
159
- while (true) {
160
- try {
161
- const fd = fsImpl.openSync(filePath, 'wx');
162
- const acquiredAt = nowFn();
163
- fsImpl.writeSync(
164
- fd,
165
- JSON.stringify({ pid: process.pid, acquiredAt }, null, 2),
166
- );
167
- fsImpl.closeSync(fd);
168
- return { epicId, filePath, acquiredAt };
169
- } catch (err) {
170
- if (err.code !== 'EEXIST') throw err;
171
- if (tryStealStale(filePath, timeoutMs, seams)) continue;
172
- if (nowFn() - started >= timeoutMs) {
173
- const meta = readLockMeta(filePath, fsImpl);
174
- const detail = meta
175
- ? ` (held by pid ${meta.pid} since ${new Date(meta.acquiredAt).toISOString()})`
176
- : '';
177
- throw new Error(
178
- `acquireEpicMergeLock timed out after ${timeoutMs}ms for epic ${epicId}${detail}`,
179
- );
180
- }
181
- await sleepFn(POLL_INTERVAL_MS);
182
- }
183
- }
184
- }
185
-
186
- /**
187
- * Acquire an exclusive Epic merge lock.
188
- *
189
- * @param {number|string} epicId
190
- * @param {object} opts
191
- * @param {string} opts.repoRoot Repo working dir; the lock lands
192
- * in its common `.git/`.
193
- * @param {number} [opts.timeoutMs=60000] Poll-until deadline.
194
- * @param {() => number} [opts.nowFn] Clock seam (ms epoch).
195
- * @param {object} [opts.fsImpl] Node `fs` shim.
196
- * @param {(pid:number, signal:number)=>void} [opts.killFn]
197
- * `process.kill` shim for the
198
- * pid-liveness probe.
199
- * @param {(ms:number)=>Promise<void>} [opts.sleepFn]
200
- * Poll-interval sleep shim.
201
- * @returns {Promise<{ epicId: number|string, filePath: string, acquiredAt: number }>}
202
- * @throws {Error} on timeout.
203
- */
204
- export async function acquireEpicMergeLock(
205
- epicId,
206
- {
207
- repoRoot,
208
- timeoutMs = 60_000,
209
- nowFn = Date.now,
210
- fsImpl = fs,
211
- killFn = process.kill.bind(process),
212
- sleepFn = defaultSleep,
213
- } = {},
214
- ) {
215
- if (!repoRoot) throw new Error('acquireEpicMergeLock: repoRoot is required');
216
-
217
- const filePath = lockPathFor(epicId, repoRoot, fsImpl);
218
- // Ensure the .git directory exists (it will, in a real repo, but the
219
- // tests use a temp dir and need us to be forgiving).
220
- fsImpl.mkdirSync(path.dirname(filePath), { recursive: true });
221
-
222
- const seams = { fsImpl, nowFn, killFn, sleepFn };
223
- return pollForLock(epicId, filePath, timeoutMs, seams);
224
- }
225
-
226
- /**
227
- * Release a previously-acquired Epic merge lock.
228
- *
229
- * @param {{ filePath: string }} handle
230
- * @param {object} [fsImpl] Node `fs` shim for tests.
231
- */
232
- export function releaseEpicMergeLock(handle, fsImpl = fs) {
233
- if (!handle?.filePath) return;
234
- try {
235
- fsImpl.unlinkSync(handle.filePath);
236
- } catch (err) {
237
- if (err.code !== 'ENOENT') throw err;
238
- }
239
- }
240
-
241
- const LOCK_FILE_PREFIX = 'epic-';
242
- const LOCK_FILE_SUFFIX = '.merge.lock';
243
-
244
- // Extract the epic id from a lock filename, or `null` when `entry` isn't a
245
- // `epic-*.merge.lock` file. Split out of `findForeignActiveEpicLock` so
246
- // that function stays a flat filter+map instead of a nested-conditional
247
- // loop body (Story #4460).
248
- function parseLockFileEpicId(entry) {
249
- if (
250
- !entry.startsWith(LOCK_FILE_PREFIX) ||
251
- !entry.endsWith(LOCK_FILE_SUFFIX)
252
- ) {
253
- return null;
254
- }
255
- return entry.slice(
256
- LOCK_FILE_PREFIX.length,
257
- entry.length - LOCK_FILE_SUFFIX.length,
258
- );
259
- }
260
-
261
- // Read `<dir>/<entry>`'s lock meta and report it only when the recorded
262
- // pid is still alive — a foreign lock whose pid is dead (or whose meta is
263
- // unreadable/corrupt) is stale debris, not an active holder, mirroring the
264
- // pid-liveness half of `tryStealStale`'s heuristic.
265
- function readLiveLock(dir, entry, otherEpicId, fsImpl, killFn) {
266
- const filePath = path.join(dir, entry);
267
- const meta = readLockMeta(filePath, fsImpl);
268
- if (!meta || !isProcessRunning(meta.pid, killFn)) return null;
269
- return {
270
- epicId: otherEpicId,
271
- filePath,
272
- pid: meta.pid,
273
- acquiredAt: meta.acquiredAt,
274
- };
275
- }
276
-
277
- /**
278
- * Scan the shared common `.git/` dir for a *different* epic's live
279
- * merge lock (Story #4460 — cross-epic shared-checkout guard).
280
- *
281
- * `acquireEpicMergeLock` only ever contends against locks for the *same*
282
- * `epicId` (its own lock filename). This helper is the cross-epic
283
- * counterpart: it lists every `epic-*.merge.lock` file in the common
284
- * gitdir, skips the caller's own `epicId` namespace, and returns the
285
- * first foreign lock whose recorded `pid` is still alive.
286
- *
287
- * @param {number|string} epicId Caller's own epic id (excluded from the scan).
288
- * @param {{
289
- * repoRoot: string,
290
- * fsImpl?: object,
291
- * killFn?: (pid:number, signal:number)=>void,
292
- * }} opts
293
- * @returns {{ epicId: string, filePath: string, pid: number, acquiredAt: number }|null}
294
- */
295
- export function findForeignActiveEpicLock(
296
- epicId,
297
- { repoRoot, fsImpl = fs, killFn = process.kill.bind(process) } = {},
298
- ) {
299
- if (!repoRoot) {
300
- throw new Error('findForeignActiveEpicLock: repoRoot is required');
301
- }
302
- const dir = resolveGitCommonDir(repoRoot, fsImpl);
303
- let entries;
304
- try {
305
- entries = fsImpl.readdirSync(dir);
306
- } catch {
307
- return null;
308
- }
309
-
310
- const foreignEntries = entries
311
- .map((entry) => ({ entry, otherEpicId: parseLockFileEpicId(entry) }))
312
- .filter(
313
- ({ otherEpicId }) =>
314
- otherEpicId !== null && String(otherEpicId) !== String(epicId),
315
- );
316
-
317
- for (const { entry, otherEpicId } of foreignEntries) {
318
- const live = readLiveLock(dir, entry, otherEpicId, fsImpl, killFn);
319
- if (live) return live;
320
- }
321
- return null;
322
- }