mandrel 2.60.0 → 2.62.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 (662) hide show
  1. package/.agents/README.md +10 -10
  2. package/.agents/agents/story-worker.md +1 -1
  3. package/.agents/docs/agentrc-reference.json +4 -82
  4. package/.agents/docs/configuration.md +20 -87
  5. package/.agents/docs/workflows.md +1 -1
  6. package/.agents/instructions.md +1 -3
  7. package/.agents/rules/gherkin-standards.md +4 -0
  8. package/.agents/schemas/agentrc.schema.json +11 -486
  9. package/.agents/schemas/story-deliver-terminal.schema.json +10 -0
  10. package/.agents/schemas/validation-evidence.schema.json +2 -1
  11. package/.agents/scripts/README.md +14 -14
  12. package/.agents/scripts/acceptance-eval.js +32 -164
  13. package/.agents/scripts/agents-bootstrap-github.js +27 -129
  14. package/.agents/scripts/apply-quality-bootstrap.js +4 -40
  15. package/.agents/scripts/audit-baselines.js +6 -27
  16. package/.agents/scripts/audit-labels-bootstrap.js +5 -29
  17. package/.agents/scripts/audit-to-stories.js +91 -361
  18. package/.agents/scripts/boot-sweep.js +19 -72
  19. package/.agents/scripts/bootstrap.js +74 -395
  20. package/.agents/scripts/ceremony-derive.js +9 -45
  21. package/.agents/scripts/check-arch-cycles.js +14 -64
  22. package/.agents/scripts/check-baselines.js +8 -48
  23. package/.agents/scripts/check-context-budget.js +46 -172
  24. package/.agents/scripts/check-cyclomatic.js +12 -44
  25. package/.agents/scripts/check-dead-exports.js +13 -64
  26. package/.agents/scripts/check-doc-links.js +38 -186
  27. package/.agents/scripts/check-gherkin-corpus.js +24 -127
  28. package/.agents/scripts/check-test-temp-hygiene.js +28 -169
  29. package/.agents/scripts/coverage-capture.js +32 -99
  30. package/.agents/scripts/deliver-light.js +14 -101
  31. package/.agents/scripts/deliver-recover.js +6 -42
  32. package/.agents/scripts/deliver-run.js +36 -133
  33. package/.agents/scripts/diagnose-friction.js +24 -116
  34. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  35. package/.agents/scripts/evidence-gate.js +23 -85
  36. package/.agents/scripts/file-ci-gap.js +10 -50
  37. package/.agents/scripts/generate-config-docs.js +30 -170
  38. package/.agents/scripts/generate-lens-checklists.js +8 -52
  39. package/.agents/scripts/generate-skills-index.js +14 -112
  40. package/.agents/scripts/generate-workflows-doc.js +10 -70
  41. package/.agents/scripts/git-cleanup.js +2 -34
  42. package/.agents/scripts/lib/Graph.js +24 -81
  43. package/.agents/scripts/lib/ITicketingProvider.js +47 -143
  44. package/.agents/scripts/lib/Logger.js +14 -76
  45. package/.agents/scripts/lib/audit-baselines/engine.js +11 -31
  46. package/.agents/scripts/lib/audit-baselines/gate-surface.js +3 -17
  47. package/.agents/scripts/lib/audit-baselines/headroom.js +4 -20
  48. package/.agents/scripts/lib/audit-baselines/hotspots.js +3 -15
  49. package/.agents/scripts/lib/audit-baselines/kinds.js +23 -89
  50. package/.agents/scripts/lib/audit-baselines/outliers.js +6 -23
  51. package/.agents/scripts/lib/audit-baselines/read.js +3 -16
  52. package/.agents/scripts/lib/audit-baselines/staleness.js +7 -26
  53. package/.agents/scripts/lib/audit-baselines/surface-entry.js +7 -28
  54. package/.agents/scripts/lib/audit-baselines/trend.js +7 -20
  55. package/.agents/scripts/lib/audit-baselines/weights.js +9 -37
  56. package/.agents/scripts/lib/audit-suite/audit-rules-reader.js +3 -18
  57. package/.agents/scripts/lib/audit-suite/checklist-threading.js +26 -124
  58. package/.agents/scripts/lib/audit-suite/dispatch-checklist.js +14 -45
  59. package/.agents/scripts/lib/audit-suite/findings.js +12 -52
  60. package/.agents/scripts/lib/audit-suite/frontmatter.js +5 -29
  61. package/.agents/scripts/lib/audit-suite/index.js +1 -15
  62. package/.agents/scripts/lib/audit-suite/lens-checklist.js +10 -45
  63. package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +11 -76
  64. package/.agents/scripts/lib/audit-suite/runner.js +13 -43
  65. package/.agents/scripts/lib/audit-suite/selector.js +49 -349
  66. package/.agents/scripts/lib/audit-suite/substitutions.js +12 -40
  67. package/.agents/scripts/lib/audit-suite/workflow-loader.js +3 -15
  68. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +11 -73
  69. package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +8 -37
  70. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +25 -143
  71. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +14 -80
  72. package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +4 -18
  73. package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +7 -40
  74. package/.agents/scripts/lib/audit-to-stories/group-findings.js +9 -39
  75. package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +10 -66
  76. package/.agents/scripts/lib/audit-to-stories/issue-index.js +6 -30
  77. package/.agents/scripts/lib/audit-to-stories/issues-file.js +6 -36
  78. package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +17 -69
  79. package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +17 -85
  80. package/.agents/scripts/lib/audit-to-stories/ledger-record.js +10 -48
  81. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +38 -146
  82. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +7 -42
  83. package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +10 -57
  84. package/.agents/scripts/lib/baseline-loader.js +12 -51
  85. package/.agents/scripts/lib/baseline-schema-registry.js +5 -22
  86. package/.agents/scripts/lib/baselines/component-matcher.js +3 -12
  87. package/.agents/scripts/lib/baselines/components.js +10 -61
  88. package/.agents/scripts/lib/baselines/coverage-updater-cli.js +7 -36
  89. package/.agents/scripts/lib/baselines/crap-preview-incremental.js +5 -21
  90. package/.agents/scripts/lib/baselines/crap-preview-scan.js +19 -45
  91. package/.agents/scripts/lib/baselines/crap-updater-cli.js +16 -65
  92. package/.agents/scripts/lib/baselines/diff-scope-cli.js +5 -33
  93. package/.agents/scripts/lib/baselines/duplication-scanner.js +11 -60
  94. package/.agents/scripts/lib/baselines/env-overrides.js +12 -67
  95. package/.agents/scripts/lib/baselines/envelope.js +13 -129
  96. package/.agents/scripts/lib/baselines/exit-codes.js +6 -49
  97. package/.agents/scripts/lib/baselines/git-base.js +23 -142
  98. package/.agents/scripts/lib/baselines/kernel.js +11 -111
  99. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +9 -47
  100. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +8 -51
  101. package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +7 -62
  102. package/.agents/scripts/lib/baselines/kinds/bundle-size.js +8 -29
  103. package/.agents/scripts/lib/baselines/kinds/coverage.js +3 -15
  104. package/.agents/scripts/lib/baselines/kinds/crap.js +94 -415
  105. package/.agents/scripts/lib/baselines/kinds/duplication.js +4 -30
  106. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +7 -53
  107. package/.agents/scripts/lib/baselines/kinds/maintainability.js +15 -80
  108. package/.agents/scripts/lib/baselines/kinds/mutation.js +12 -72
  109. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +4 -11
  110. package/.agents/scripts/lib/baselines/merge-envelopes.js +30 -146
  111. package/.agents/scripts/lib/baselines/path-canon.js +19 -128
  112. package/.agents/scripts/lib/baselines/preview-gates.js +8 -37
  113. package/.agents/scripts/lib/baselines/reader.js +14 -107
  114. package/.agents/scripts/lib/baselines/refresh-service.js +35 -252
  115. package/.agents/scripts/lib/baselines/scope.js +11 -135
  116. package/.agents/scripts/lib/baselines/writer.js +16 -149
  117. package/.agents/scripts/lib/bdd-runner-detect.js +18 -113
  118. package/.agents/scripts/lib/bdd-scenario-budget.js +9 -36
  119. package/.agents/scripts/lib/bdd-scenario-scanner.js +11 -73
  120. package/.agents/scripts/lib/bdd-step-index.js +26 -100
  121. package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +23 -103
  122. package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -5
  123. package/.agents/scripts/lib/bootstrap/commit-push.js +12 -50
  124. package/.agents/scripts/lib/bootstrap/gh-preflight.js +14 -101
  125. package/.agents/scripts/lib/bootstrap/hitl-confirm.js +5 -30
  126. package/.agents/scripts/lib/bootstrap/install-ledger.js +17 -73
  127. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +17 -121
  128. package/.agents/scripts/lib/bootstrap/manifest.js +15 -86
  129. package/.agents/scripts/lib/bootstrap/merge-methods.js +4 -33
  130. package/.agents/scripts/lib/bootstrap/preflight.js +12 -61
  131. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +34 -235
  132. package/.agents/scripts/lib/bootstrap/prompt.js +21 -129
  133. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +27 -141
  134. package/.agents/scripts/lib/bootstrap/summary.js +1 -8
  135. package/.agents/scripts/lib/bootstrap/workflow-audit.js +11 -84
  136. package/.agents/scripts/lib/branch-name-guard.js +4 -23
  137. package/.agents/scripts/lib/changed-files.js +31 -143
  138. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -24
  139. package/.agents/scripts/lib/checks/index.js +18 -101
  140. package/.agents/scripts/lib/checks/loop-health.js +11 -53
  141. package/.agents/scripts/lib/checks/state.js +16 -91
  142. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +10 -61
  143. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +14 -83
  144. package/.agents/scripts/lib/child-exec.js +39 -108
  145. package/.agents/scripts/lib/cli/standard-args.js +9 -116
  146. package/.agents/scripts/lib/cli-args.js +31 -146
  147. package/.agents/scripts/lib/cli-usage.js +5 -27
  148. package/.agents/scripts/lib/cli-utils.js +2 -12
  149. package/.agents/scripts/lib/close-validation/commands.js +13 -76
  150. package/.agents/scripts/lib/close-validation/gates.js +92 -331
  151. package/.agents/scripts/lib/close-validation/process.js +121 -151
  152. package/.agents/scripts/lib/close-validation/projections/advisories.js +5 -31
  153. package/.agents/scripts/lib/close-validation/projections/crap.js +19 -67
  154. package/.agents/scripts/lib/close-validation/projections/head-sha.js +1 -17
  155. package/.agents/scripts/lib/close-validation/projections/inputs.js +4 -34
  156. package/.agents/scripts/lib/close-validation/projections/maintainability.js +11 -58
  157. package/.agents/scripts/lib/close-validation/runner.js +53 -110
  158. package/.agents/scripts/lib/command-header.js +6 -28
  159. package/.agents/scripts/lib/config/acceptance-eval.js +4 -30
  160. package/.agents/scripts/lib/config/baselines.js +3 -14
  161. package/.agents/scripts/lib/config/ci.js +8 -38
  162. package/.agents/scripts/lib/config/commands.js +1 -15
  163. package/.agents/scripts/lib/config/defaults.js +4 -33
  164. package/.agents/scripts/lib/config/delivery-routing.js +6 -35
  165. package/.agents/scripts/lib/config/explain.js +9 -80
  166. package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
  167. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +2 -30
  168. package/.agents/scripts/lib/config/gates/crap.schema.js +1 -34
  169. package/.agents/scripts/lib/config/gates/duplication.schema.js +1 -8
  170. package/.agents/scripts/lib/config/gates/index.js +2 -17
  171. package/.agents/scripts/lib/config/gates/maintainability.schema.js +1 -20
  172. package/.agents/scripts/lib/config/gates/mutation.schema.js +1 -7
  173. package/.agents/scripts/lib/config/gates/shared.js +4 -64
  174. package/.agents/scripts/lib/config/github.js +4 -22
  175. package/.agents/scripts/lib/config/limits.js +4 -57
  176. package/.agents/scripts/lib/config/paths.js +2 -21
  177. package/.agents/scripts/lib/config/qa.js +4 -38
  178. package/.agents/scripts/lib/config/quality.js +76 -432
  179. package/.agents/scripts/lib/config/runners.js +12 -56
  180. package/.agents/scripts/lib/config/runtime.js +13 -57
  181. package/.agents/scripts/lib/config/shared.js +2 -16
  182. package/.agents/scripts/lib/config/sync-agentrc.js +9 -50
  183. package/.agents/scripts/lib/config/temp-paths.js +40 -280
  184. package/.agents/scripts/lib/config/validate-orchestration.js +5 -17
  185. package/.agents/scripts/lib/config/worktree-isolation.js +9 -28
  186. package/.agents/scripts/lib/config-resolver.js +10 -52
  187. package/.agents/scripts/lib/config-schema-shared.js +1 -4
  188. package/.agents/scripts/lib/config-settings-schema-delivery.js +12 -294
  189. package/.agents/scripts/lib/config-settings-schema-quality.js +7 -219
  190. package/.agents/scripts/lib/config-settings-schema.js +37 -274
  191. package/.agents/scripts/lib/coverage-baseline.js +20 -110
  192. package/.agents/scripts/lib/coverage-capture-fullscope.js +12 -37
  193. package/.agents/scripts/lib/coverage-capture-incremental.js +12 -43
  194. package/.agents/scripts/lib/coverage-capture-usage.js +4 -24
  195. package/.agents/scripts/lib/coverage-capture.js +99 -224
  196. package/.agents/scripts/lib/coverage-utils.js +14 -79
  197. package/.agents/scripts/lib/cpu-pool.js +17 -123
  198. package/.agents/scripts/lib/crap-baseline-join.js +16 -88
  199. package/.agents/scripts/lib/crap-coordinates.js +6 -25
  200. package/.agents/scripts/lib/crap-engine.js +40 -153
  201. package/.agents/scripts/lib/crap-method-identity.js +16 -78
  202. package/.agents/scripts/lib/crap-utils.js +31 -155
  203. package/.agents/scripts/lib/cyclomatic-ceiling.js +14 -87
  204. package/.agents/scripts/lib/cyclomatic-scope.js +10 -50
  205. package/.agents/scripts/lib/dead-exports-knip.js +18 -56
  206. package/.agents/scripts/lib/dead-exports-mode.js +5 -24
  207. package/.agents/scripts/lib/degraded-mode.js +3 -26
  208. package/.agents/scripts/lib/dependency-parser.js +8 -40
  209. package/.agents/scripts/lib/dependency-version.js +13 -35
  210. package/.agents/scripts/lib/detect-package-manager.js +6 -36
  211. package/.agents/scripts/lib/doc-tiers.js +22 -126
  212. package/.agents/scripts/lib/duplicate-search.js +8 -72
  213. package/.agents/scripts/lib/env-loader.js +5 -27
  214. package/.agents/scripts/lib/error-redactor.js +6 -31
  215. package/.agents/scripts/lib/errors/index.js +2 -22
  216. package/.agents/scripts/lib/escomplex-ast-compat.js +22 -163
  217. package/.agents/scripts/lib/escomplex-kernel.js +21 -123
  218. package/.agents/scripts/lib/feedback-loop/graduator-core.js +93 -419
  219. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
  220. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -151
  221. package/.agents/scripts/lib/findings/audit-ledger.js +23 -97
  222. package/.agents/scripts/lib/findings/classify-finding.js +14 -69
  223. package/.agents/scripts/lib/findings/promote-finding.js +14 -112
  224. package/.agents/scripts/lib/findings/provenance-field.js +11 -52
  225. package/.agents/scripts/lib/findings/route-finding.js +37 -255
  226. package/.agents/scripts/lib/findings/semantic-issue-search.js +14 -59
  227. package/.agents/scripts/lib/findings/severity.js +18 -104
  228. package/.agents/scripts/lib/format-generated-json.js +12 -44
  229. package/.agents/scripts/lib/full-suite-lock.js +160 -429
  230. package/.agents/scripts/lib/full-suite-queue.js +213 -0
  231. package/.agents/scripts/lib/gates/baseline-store.js +5 -9
  232. package/.agents/scripts/lib/gates/friction.js +1 -3
  233. package/.agents/scripts/lib/generated/agentrc-validator.js +2 -2
  234. package/.agents/scripts/lib/gh-exec.js +31 -259
  235. package/.agents/scripts/lib/git/cached-fetch.js +9 -52
  236. package/.agents/scripts/lib/git/sync-from-base.js +15 -107
  237. package/.agents/scripts/lib/git-branch-cleanup.js +14 -61
  238. package/.agents/scripts/lib/git-branch-lifecycle.js +16 -87
  239. package/.agents/scripts/lib/git-utils.js +37 -161
  240. package/.agents/scripts/lib/github/framework-repo.js +8 -71
  241. package/.agents/scripts/lib/github-url.js +2 -17
  242. package/.agents/scripts/lib/import-graph.js +11 -38
  243. package/.agents/scripts/lib/install-cmd-parser.js +4 -13
  244. package/.agents/scripts/lib/json-utils.js +3 -18
  245. package/.agents/scripts/lib/label-constants.js +16 -111
  246. package/.agents/scripts/lib/label-taxonomy.js +4 -30
  247. package/.agents/scripts/lib/maintainability-engine.js +12 -80
  248. package/.agents/scripts/lib/maintainability-unscorable.js +5 -25
  249. package/.agents/scripts/lib/maintainability-utils.js +16 -92
  250. package/.agents/scripts/lib/mandrel-catalog.js +12 -70
  251. package/.agents/scripts/lib/notifications/notifier.js +13 -38
  252. package/.agents/scripts/lib/npm-scripts.js +4 -23
  253. package/.agents/scripts/lib/observability/metrics-ledger.js +16 -76
  254. package/.agents/scripts/lib/observability/runtime-friction.js +44 -286
  255. package/.agents/scripts/lib/observability/signal-validator.js +5 -37
  256. package/.agents/scripts/lib/observability/signals-writer.js +11 -121
  257. package/.agents/scripts/lib/observability/source-classifier.js +21 -203
  258. package/.agents/scripts/lib/observability/terse-result.js +14 -47
  259. package/.agents/scripts/lib/onboard/init-tail.js +13 -74
  260. package/.agents/scripts/lib/onboard/scaffold-docs.js +11 -34
  261. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +25 -139
  262. package/.agents/scripts/lib/orchestration/auto-merge-cwd.js +11 -66
  263. package/.agents/scripts/lib/orchestration/behind-recovery.js +10 -55
  264. package/.agents/scripts/lib/orchestration/ceremony-routing.js +5 -75
  265. package/.agents/scripts/lib/orchestration/change-set.js +7 -36
  266. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +9 -46
  267. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +11 -44
  268. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +5 -24
  269. package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +3 -12
  270. package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +5 -38
  271. package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +3 -8
  272. package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +28 -136
  273. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +3 -13
  274. package/.agents/scripts/lib/orchestration/check-state.js +92 -0
  275. package/.agents/scripts/lib/orchestration/ci-gap-intake.js +37 -144
  276. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +42 -189
  277. package/.agents/scripts/lib/orchestration/code-review.js +24 -128
  278. package/.agents/scripts/lib/orchestration/column-sync.js +23 -111
  279. package/.agents/scripts/lib/orchestration/complexity-gate.js +42 -214
  280. package/.agents/scripts/lib/orchestration/deliver-recover.js +36 -168
  281. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +4 -35
  282. package/.agents/scripts/lib/orchestration/dependency-candidates.js +9 -41
  283. package/.agents/scripts/lib/orchestration/diff-magnitude.js +25 -115
  284. package/.agents/scripts/lib/orchestration/doc-reader.js +2 -6
  285. package/.agents/scripts/lib/orchestration/docs-digest.js +13 -51
  286. package/.agents/scripts/lib/orchestration/epic-candidates.js +13 -53
  287. package/.agents/scripts/lib/orchestration/epic-checklist.js +9 -33
  288. package/.agents/scripts/lib/orchestration/epic-container.js +41 -167
  289. package/.agents/scripts/lib/orchestration/epic-expansion.js +10 -40
  290. package/.agents/scripts/lib/orchestration/epic-rollup.js +55 -207
  291. package/.agents/scripts/lib/orchestration/file-assumption-enum.js +2 -22
  292. package/.agents/scripts/lib/orchestration/file-assumptions.js +45 -253
  293. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-detect.js +9 -46
  294. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +7 -57
  295. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +21 -126
  296. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +2 -6
  297. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +1 -4
  298. package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +1 -5
  299. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +9 -34
  300. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +41 -190
  301. package/.agents/scripts/lib/orchestration/git-cleanup/phases/merged-tip.js +9 -45
  302. package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +1 -4
  303. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +19 -85
  304. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +3 -17
  305. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +1 -5
  306. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +16 -100
  307. package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +1 -4
  308. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +16 -67
  309. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +9 -33
  310. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +8 -29
  311. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +15 -68
  312. package/.agents/scripts/lib/orchestration/light-backstop.js +11 -49
  313. package/.agents/scripts/lib/orchestration/light-escalation.js +21 -89
  314. package/.agents/scripts/lib/orchestration/light-suitability.js +26 -231
  315. package/.agents/scripts/lib/orchestration/merge-block-class.js +39 -216
  316. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -377
  317. package/.agents/scripts/lib/orchestration/merge-queue.js +158 -0
  318. package/.agents/scripts/lib/orchestration/pinned-identifier-lint.js +17 -57
  319. package/.agents/scripts/lib/orchestration/plan-context.js +52 -303
  320. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +22 -79
  321. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +5 -31
  322. package/.agents/scripts/lib/orchestration/plan-metrics.js +29 -108
  323. package/.agents/scripts/lib/orchestration/plan-navigation.js +8 -27
  324. package/.agents/scripts/lib/orchestration/plan-persist/acceptance-handle-repair.js +12 -49
  325. package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +18 -73
  326. package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +14 -78
  327. package/.agents/scripts/lib/orchestration/plan-persist/cross-plan-links.js +6 -30
  328. package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +14 -65
  329. package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +21 -76
  330. package/.agents/scripts/lib/orchestration/plan-persist/external-deps.js +6 -43
  331. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +14 -68
  332. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +9 -41
  333. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +49 -248
  334. package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
  335. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +85 -368
  336. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +8 -38
  337. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +30 -190
  338. package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +9 -49
  339. package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +7 -28
  340. package/.agents/scripts/lib/orchestration/plan-reachability.js +13 -42
  341. package/.agents/scripts/lib/orchestration/plan-run-labels/reap.js +16 -91
  342. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +16 -54
  343. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +10 -52
  344. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +14 -109
  345. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +11 -83
  346. package/.agents/scripts/lib/orchestration/pr-watch.js +48 -306
  347. package/.agents/scripts/lib/orchestration/project-meta-cache.js +15 -80
  348. package/.agents/scripts/lib/orchestration/project-meta-resolver.js +10 -51
  349. package/.agents/scripts/lib/orchestration/reassert-status-column.js +12 -76
  350. package/.agents/scripts/lib/orchestration/remote-verifier.js +12 -36
  351. package/.agents/scripts/lib/orchestration/resolve-stories.js +33 -164
  352. package/.agents/scripts/lib/orchestration/retro-proposals.js +66 -361
  353. package/.agents/scripts/lib/orchestration/review-base-ref.js +9 -43
  354. package/.agents/scripts/lib/orchestration/review-depth.js +12 -94
  355. package/.agents/scripts/lib/orchestration/review-providers/codex.js +10 -111
  356. package/.agents/scripts/lib/orchestration/review-providers/degraded-gates.js +13 -71
  357. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +7 -51
  358. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +8 -53
  359. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -214
  360. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +7 -52
  361. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +4 -33
  362. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +11 -71
  363. package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +19 -131
  364. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +6 -89
  365. package/.agents/scripts/lib/orchestration/review-providers/types.js +26 -56
  366. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +3 -46
  367. package/.agents/scripts/lib/orchestration/run-epilogue.js +40 -199
  368. package/.agents/scripts/lib/orchestration/run-scoped-config.js +13 -81
  369. package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +7 -34
  370. package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +16 -89
  371. package/.agents/scripts/lib/orchestration/single-story-close/gate-log.js +18 -109
  372. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +190 -280
  373. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +18 -80
  374. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +103 -97
  375. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -64
  376. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +183 -485
  377. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +30 -134
  378. package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +9 -50
  379. package/.agents/scripts/lib/orchestration/single-story-close/phases/lock-wait-pending.js +49 -0
  380. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +13 -86
  381. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +20 -95
  382. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +46 -203
  383. package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +8 -50
  384. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +12 -83
  385. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -39
  386. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -6
  387. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-outcome.js +4 -28
  388. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +6 -51
  389. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +6 -34
  390. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +23 -121
  391. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +110 -290
  392. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +15 -69
  393. package/.agents/scripts/lib/orchestration/story-body-gate.js +4 -23
  394. package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +50 -179
  395. package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +11 -41
  396. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +23 -311
  397. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +22 -147
  398. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +16 -67
  399. package/.agents/scripts/lib/orchestration/story-deliver-terminal-schema.js +12 -68
  400. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +42 -201
  401. package/.agents/scripts/lib/orchestration/story-follow-ups.js +43 -245
  402. package/.agents/scripts/lib/orchestration/story-init-envelope.js +4 -23
  403. package/.agents/scripts/lib/orchestration/story-init-remote.js +2 -6
  404. package/.agents/scripts/lib/orchestration/story-reachability.js +3 -23
  405. package/.agents/scripts/lib/orchestration/task-body-validator.js +12 -126
  406. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -131
  407. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +15 -149
  408. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +5 -33
  409. package/.agents/scripts/lib/orchestration/ticket-validator.js +39 -181
  410. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
  411. package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -250
  412. package/.agents/scripts/lib/orchestration/ticketing/state.js +14 -68
  413. package/.agents/scripts/lib/orchestration/ticketing/transition.js +46 -263
  414. package/.agents/scripts/lib/orchestration/ticketing.js +2 -26
  415. package/.agents/scripts/lib/orchestration/verify-credit.js +18 -79
  416. package/.agents/scripts/lib/orchestration/worktree-dirty.js +5 -30
  417. package/.agents/scripts/lib/path-security.js +3 -6
  418. package/.agents/scripts/lib/plan-phase-cleanup.js +9 -49
  419. package/.agents/scripts/lib/preflight-runner.js +13 -69
  420. package/.agents/scripts/lib/process-group.js +143 -0
  421. package/.agents/scripts/lib/project-root.js +2 -8
  422. package/.agents/scripts/lib/provider-factory.js +2 -25
  423. package/.agents/scripts/lib/qa/console-allowlist.js +10 -59
  424. package/.agents/scripts/lib/qa/qa-session.js +15 -69
  425. package/.agents/scripts/lib/qa/redact-evidence.js +18 -129
  426. package/.agents/scripts/lib/qa/resolve-qa-contract.js +26 -135
  427. package/.agents/scripts/lib/qa/resolve-selection.js +16 -84
  428. package/.agents/scripts/lib/reserved-test-ids.js +9 -47
  429. package/.agents/scripts/lib/runtime-deps/dep-resolution.js +13 -53
  430. package/.agents/scripts/lib/runtime-deps/ensure-installed.js +10 -44
  431. package/.agents/scripts/lib/runtime-deps/manifest.js +10 -30
  432. package/.agents/scripts/lib/runtime-deps/parser-major.js +15 -51
  433. package/.agents/scripts/lib/runtime-deps/preflight.js +4 -22
  434. package/.agents/scripts/lib/runtime-deps/scan-imports.js +11 -55
  435. package/.agents/scripts/lib/signals/detectors/common.js +12 -38
  436. package/.agents/scripts/lib/signals/index.js +2 -19
  437. package/.agents/scripts/lib/signals/schema.js +14 -109
  438. package/.agents/scripts/lib/signals/write.js +2 -11
  439. package/.agents/scripts/lib/single-story/confirm-merge.js +31 -72
  440. package/.agents/scripts/lib/single-story/story-merged-notify.js +8 -49
  441. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +4 -26
  442. package/.agents/scripts/lib/single-story-sweep/protection.js +13 -100
  443. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +60 -258
  444. package/.agents/scripts/lib/single-story-sweep.js +14 -97
  445. package/.agents/scripts/lib/skills/parse-skill.js +13 -59
  446. package/.agents/scripts/lib/skills/skills-index.js +8 -29
  447. package/.agents/scripts/lib/skills/walk-skill-files.js +17 -78
  448. package/.agents/scripts/lib/source-extensions.js +11 -42
  449. package/.agents/scripts/lib/source-text/strip-js-comments.js +8 -41
  450. package/.agents/scripts/lib/stdio-flush.js +9 -36
  451. package/.agents/scripts/lib/story-adjacency.js +8 -40
  452. package/.agents/scripts/lib/story-body/body-format-lints.js +22 -83
  453. package/.agents/scripts/lib/story-body/footer-block.js +11 -44
  454. package/.agents/scripts/lib/story-body/story-body.js +83 -358
  455. package/.agents/scripts/lib/temp-retention.js +42 -143
  456. package/.agents/scripts/lib/templates/decomposer-prompts.js +11 -65
  457. package/.agents/scripts/lib/test-env.js +12 -65
  458. package/.agents/scripts/lib/test-run-credit.js +10 -65
  459. package/.agents/scripts/lib/test-runner-contract.js +19 -74
  460. package/.agents/scripts/lib/test-temp.js +38 -184
  461. package/.agents/scripts/lib/test-tiers.js +12 -96
  462. package/.agents/scripts/lib/ticket-body-sections.js +21 -96
  463. package/.agents/scripts/lib/transpile.js +15 -74
  464. package/.agents/scripts/lib/util/concurrent-map.js +6 -25
  465. package/.agents/scripts/lib/util/parse-id-list.js +8 -34
  466. package/.agents/scripts/lib/util/poll-loop.js +8 -28
  467. package/.agents/scripts/lib/util/with-timeout.js +2 -11
  468. package/.agents/scripts/lib/validation-evidence.js +21 -96
  469. package/.agents/scripts/lib/wave-runner/footprint.js +17 -70
  470. package/.agents/scripts/lib/wave-runner/live-probe.js +53 -221
  471. package/.agents/scripts/lib/wave-runner/ready-set.js +72 -301
  472. package/.agents/scripts/lib/workers/crap-worker.js +11 -64
  473. package/.agents/scripts/lib/workers/maintainability-report-worker.js +8 -45
  474. package/.agents/scripts/lib/workers/maintainability-worker.js +5 -21
  475. package/.agents/scripts/lib/workers/serve-worker-messages.js +2 -18
  476. package/.agents/scripts/lib/workflow-closure.js +22 -105
  477. package/.agents/scripts/lib/workspace-provisioner.js +16 -54
  478. package/.agents/scripts/lib/worktree/git-hooks.js +15 -60
  479. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +20 -60
  480. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +10 -62
  481. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +22 -86
  482. package/.agents/scripts/lib/worktree/lifecycle/reap.js +24 -126
  483. package/.agents/scripts/lib/worktree/lifecycle-manager.js +2 -20
  484. package/.agents/scripts/lib/worktree/node-modules-strategy.js +46 -180
  485. package/.agents/scripts/lib/worktree-manager.js +15 -40
  486. package/.agents/scripts/lint-issue-body.js +12 -68
  487. package/.agents/scripts/mandrel-update-preflight.js +10 -70
  488. package/.agents/scripts/merge-baseline.js +30 -104
  489. package/.agents/scripts/nav-registry-diff.js +22 -108
  490. package/.agents/scripts/notify.js +12 -69
  491. package/.agents/scripts/plan-context.js +27 -111
  492. package/.agents/scripts/plan-critics.js +12 -66
  493. package/.agents/scripts/plan-persist.js +31 -148
  494. package/.agents/scripts/plan-run-epilogue.js +11 -37
  495. package/.agents/scripts/pr-watch-with-update.js +76 -267
  496. package/.agents/scripts/providers/github/auth.js +3 -8
  497. package/.agents/scripts/providers/github/blocked-by-add.js +14 -61
  498. package/.agents/scripts/providers/github/board-add.js +5 -21
  499. package/.agents/scripts/providers/github/branch-protection.js +11 -43
  500. package/.agents/scripts/providers/github/cache.js +2 -14
  501. package/.agents/scripts/providers/github/comments.js +7 -40
  502. package/.agents/scripts/providers/github/compose.js +3 -19
  503. package/.agents/scripts/providers/github/errors.js +22 -125
  504. package/.agents/scripts/providers/github/issues.js +31 -123
  505. package/.agents/scripts/providers/github/labels.js +24 -124
  506. package/.agents/scripts/providers/github/mappers.js +5 -27
  507. package/.agents/scripts/providers/github/merge-methods.js +3 -22
  508. package/.agents/scripts/providers/github/project-board.js +2 -17
  509. package/.agents/scripts/providers/github/projects-v2-graphql.js +3 -6
  510. package/.agents/scripts/providers/github/request-helpers.js +5 -32
  511. package/.agents/scripts/providers/github/search-budget.js +9 -39
  512. package/.agents/scripts/providers/github/search-query.js +5 -26
  513. package/.agents/scripts/providers/github/sub-issue-add.js +15 -65
  514. package/.agents/scripts/providers/github/sub-issues.js +7 -36
  515. package/.agents/scripts/providers/github/tickets.js +32 -134
  516. package/.agents/scripts/providers/github.js +18 -59
  517. package/.agents/scripts/prune-plan-run-labels.js +12 -46
  518. package/.agents/scripts/quality-preview.js +35 -220
  519. package/.agents/scripts/resolve-doc-tiers.js +3 -26
  520. package/.agents/scripts/resolve-stories.js +16 -67
  521. package/.agents/scripts/resync-status-column.js +6 -25
  522. package/.agents/scripts/run-tests.js +29 -104
  523. package/.agents/scripts/single-story-close.js +23 -135
  524. package/.agents/scripts/single-story-confirm-merge.js +32 -139
  525. package/.agents/scripts/single-story-init.js +47 -209
  526. package/.agents/scripts/stories-wave-tick.js +102 -472
  527. package/.agents/scripts/sync-agentrc.js +3 -21
  528. package/.agents/scripts/sync-claude-agents.js +9 -55
  529. package/.agents/scripts/sync-claude-commands.js +20 -110
  530. package/.agents/scripts/test-wrapper.js +6 -43
  531. package/.agents/scripts/update-coverage-baseline.js +5 -22
  532. package/.agents/scripts/update-crap-baseline.js +7 -30
  533. package/.agents/scripts/update-duplication-baseline.js +16 -92
  534. package/.agents/scripts/update-maintainability-baseline.js +8 -62
  535. package/.agents/scripts/update-ticket-state.js +1 -7
  536. package/.agents/scripts/validate-skills.js +6 -26
  537. package/.agents/templates/agent-protocol.md +2 -2
  538. package/.agents/templates/docs/audit-sweep-runbook.md +3 -4
  539. package/.agents/workflows/audit-accessibility.md +4 -7
  540. package/.agents/workflows/audit-adrs.md +3 -6
  541. package/.agents/workflows/audit-architecture.md +3 -11
  542. package/.agents/workflows/audit-baselines.md +6 -9
  543. package/.agents/workflows/audit-clean-code.md +8 -12
  544. package/.agents/workflows/audit-data-model.md +3 -7
  545. package/.agents/workflows/audit-dependencies.md +4 -7
  546. package/.agents/workflows/audit-devops.md +4 -8
  547. package/.agents/workflows/audit-documentation.md +1 -4
  548. package/.agents/workflows/audit-mobile.md +4 -7
  549. package/.agents/workflows/audit-navigability.md +3 -6
  550. package/.agents/workflows/audit-performance.md +2 -4
  551. package/.agents/workflows/audit-privacy.md +4 -7
  552. package/.agents/workflows/audit-quality.md +4 -7
  553. package/.agents/workflows/audit-security.md +4 -7
  554. package/.agents/workflows/audit-seo.md +4 -7
  555. package/.agents/workflows/audit-sre.md +4 -7
  556. package/.agents/workflows/audit-to-stories.md +17 -30
  557. package/.agents/workflows/audit-ux-ui.md +4 -7
  558. package/.agents/workflows/helpers/audit-lens-core.md +1 -1
  559. package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
  560. package/.agents/workflows/helpers/deliver-digest.md +6 -4
  561. package/.agents/workflows/helpers/deliver-reference.md +114 -188
  562. package/.agents/workflows/helpers/deliver-story-reference.md +189 -533
  563. package/.agents/workflows/helpers/plan-reference.md +86 -139
  564. package/.agents/workflows/helpers/qa-core.md +12 -0
  565. package/.agents/workflows/memory-consolidate.md +1 -1
  566. package/.agents/workflows/qa-assist.md +27 -71
  567. package/.agents/workflows/qa-explore.md +19 -59
  568. package/.agents/workflows/qa-run.md +11 -33
  569. package/README.md +1 -1
  570. package/bin/mandrel.js +5 -46
  571. package/bin/postinstall.js +18 -107
  572. package/docs/CHANGELOG.md +32 -0
  573. package/lib/cli/doctor.js +11 -71
  574. package/lib/cli/init.js +26 -165
  575. package/lib/cli/migrate.js +5 -64
  576. package/lib/cli/registry.js +84 -467
  577. package/lib/cli/sync-agents.js +4 -64
  578. package/lib/cli/sync-commands.js +11 -64
  579. package/lib/cli/sync.js +34 -213
  580. package/lib/cli/uninstall.js +40 -219
  581. package/lib/cli/update.js +105 -661
  582. package/lib/cli/version-check.js +8 -72
  583. package/lib/cli/version-helpers.js +14 -87
  584. package/lib/migrations/helpers/retire-agentrc-key.js +11 -52
  585. package/lib/migrations/index.js +15 -92
  586. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +2 -15
  587. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +1 -13
  588. package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +2 -15
  589. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +9 -38
  590. package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +1 -13
  591. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +2 -27
  592. package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +2 -23
  593. package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +2 -27
  594. package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +2 -21
  595. package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
  596. package/package.json +16 -14
  597. package/.agents/docs/SDLC.md +0 -590
  598. package/.agents/docs/quality-gates.md +0 -1183
  599. package/.agents/rules/known-tooling-behavior.md +0 -200
  600. package/.agents/rules/orchestration-error-handling.md +0 -61
  601. package/.agents/rules/test-seams.md +0 -59
  602. package/.agents/schemas/baselines/lighthouse.schema.json +0 -59
  603. package/.agents/schemas/baselines/lint.schema.json +0 -47
  604. package/.agents/scripts/check-action-pinning.js +0 -260
  605. package/.agents/scripts/check-audit-attribution.js +0 -302
  606. package/.agents/scripts/check-baseline-drift.js +0 -211
  607. package/.agents/scripts/check-baseline-scope.js +0 -362
  608. package/.agents/scripts/check-generated-validator.js +0 -202
  609. package/.agents/scripts/check-knip-entries.js +0 -159
  610. package/.agents/scripts/check-lifecycle-lint.js +0 -294
  611. package/.agents/scripts/check-pinned-override-notes.js +0 -102
  612. package/.agents/scripts/check-schema-references.js +0 -368
  613. package/.agents/scripts/check-test-portability.js +0 -512
  614. package/.agents/scripts/check-workflow-citations.js +0 -218
  615. package/.agents/scripts/check-workflow-cli-lint.js +0 -299
  616. package/.agents/scripts/check-workflow-timeouts.js +0 -291
  617. package/.agents/scripts/install-matrix-assert.js +0 -326
  618. package/.agents/scripts/lib/audit-advisories.js +0 -195
  619. package/.agents/scripts/lib/audit-attribution.js +0 -134
  620. package/.agents/scripts/lib/baselines/drift-detector.js +0 -351
  621. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +0 -87
  622. package/.agents/scripts/lib/baselines/kinds/lint.js +0 -184
  623. package/.agents/scripts/lib/baselines/orphan-pruner.js +0 -233
  624. package/.agents/scripts/lib/baselines/scope-assert.js +0 -223
  625. package/.agents/scripts/lib/baselines/scope-inventory.js +0 -314
  626. package/.agents/scripts/lib/c8-cli-path.js +0 -21
  627. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +0 -51
  628. package/.agents/scripts/lib/config/gates/lint.schema.js +0 -18
  629. package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +0 -70
  630. package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +0 -284
  631. package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +0 -80
  632. package/.agents/scripts/lib/dynamic-workflow/degraded-coverage.js +0 -81
  633. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +0 -87
  634. package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +0 -74
  635. package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +0 -90
  636. package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +0 -43
  637. package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +0 -83
  638. package/.agents/scripts/lib/fs-walk.js +0 -52
  639. package/.agents/scripts/lib/knip-config-resolver.js +0 -181
  640. package/.agents/scripts/lib/knip-entry-sync.js +0 -452
  641. package/.agents/scripts/lib/pinned-override-notes.js +0 -88
  642. package/.agents/scripts/lib/pinned-override-resolve.js +0 -212
  643. package/.agents/scripts/lib/test-isolate/cli-options.js +0 -93
  644. package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +0 -52
  645. package/.agents/scripts/lib/test-isolate/list-files.js +0 -90
  646. package/.agents/scripts/lib/test-isolate/parse-tap.js +0 -75
  647. package/.agents/scripts/lib/test-isolate/progress-log.js +0 -45
  648. package/.agents/scripts/lib/test-isolate/render-report.js +0 -97
  649. package/.agents/scripts/lib/test-isolate/run-isolate.js +0 -87
  650. package/.agents/scripts/lib/test-isolate/runner.js +0 -483
  651. package/.agents/scripts/lib/test-profile/parse-tap.js +0 -136
  652. package/.agents/scripts/lib/test-profile/render-report.js +0 -45
  653. package/.agents/scripts/lint-label-vocabulary.js +0 -214
  654. package/.agents/scripts/post-structured-comment.js +0 -127
  655. package/.agents/scripts/provision-git-hooks.js +0 -85
  656. package/.agents/scripts/prune-baseline-orphans.js +0 -181
  657. package/.agents/scripts/run-coverage.js +0 -197
  658. package/.agents/scripts/run-lint.js +0 -133
  659. package/.agents/scripts/run-test-profile.js +0 -129
  660. package/.agents/scripts/run-verify.js +0 -125
  661. package/.agents/scripts/test-isolate.js +0 -55
  662. package/.agents/scripts/update-dead-exports-baseline.js +0 -321
package/lib/cli/update.js CHANGED
@@ -1,164 +1,12 @@
1
1
  // lib/cli/update.js
2
2
  /**
3
- * `mandrel update` subcommand — the auto-update orchestrator (f-update-command,
4
- * Story #3503, Epic #3437 — Auto-Update & Version Lifecycle).
5
- *
6
- * Advances `mandrel` to the newest published version, re-materializes
7
- * `.agents/`, runs applicable version-keyed migrations, surfaces the target
8
- * changelog, and verifies the result via the doctor registry. Major crossings
9
- * are applied like any other bump (hard-cutover doctrine —
10
- * `.agents/rules/git-conventions.md` § Contract Cutovers).
11
- *
12
- * ## Ordered cycle (happy path)
13
- *
14
- * 1. resolve target version (newest published) and the current version
15
- * 2. drift-aware short-circuit — version check gates on installed version AND
16
- * materialized `.agents/` state (Story #4065). When already on newest AND
17
- * no drift: nothing to do (true no-op). When already on newest BUT drift
18
- * detected: skip npm-update/migrations, run sync + sync-commands to heal.
19
- * 3. install — bump the dependency (manifest + lockfile rewritten on
20
- * disk; whether the package manager also stages them is reported, never
21
- * assumed — see § No git mutation).
22
- * The package manager is auto-detected from the lockfile in the project
23
- * root: `pnpm-lock.yaml` ⇒ `pnpm add -D …` (with `-w` at a
24
- * `pnpm-workspace.yaml` root), `yarn.lock` ⇒ `yarn add -D …`, otherwise
25
- * `npm install …`. An explicit `--install-cmd "<pm> <args>"` overrides
26
- * detection; a `{target}` placeholder in the override is substituted with
27
- * the resolved version so an override can still consume the auto-probed
28
- * newest. The registry probe in step 1 always stays on `npm view` (a
29
- * PM-agnostic registry query).
30
- * 4. runSync — re-materialize ./.agents/ **from the newly-installed
31
- * binary** so the materialized payload is always the target version's.
32
- * 5. runMigrations — apply version-keyed steps for the crossed range,
33
- * **from the newly-installed binary**.
34
- * 6. doctor — run the check registry **from the newly-installed
35
- * binary** so `agents-drift` is never a false-green against stale payload.
36
- * 7. surface the changelog for the target version
37
- *
38
- * ## Re-exec of post-install phases (Story #4034)
39
- *
40
- * Steps 4–6 execute as **child processes spawned from the newly-installed
41
- * bin script** (`node <cwd>/node_modules/mandrel/bin/mandrel.js`; Story #4613
42
- * resolves the script layout-agnostically rather than via the `.bin` shim)
43
- * rather than in the running
44
- * process. Node cannot hot-swap a `require`d module mid-process, so without
45
- * re-exec, the still-running old binary's `runSync`/`runMigrations`/`runDoctor`
46
- * code would materialise the old payload even though the package on disk has
47
- * already been updated. This produced the silent stale-`.agents/`
48
- * materialization and `doctor` false-green observed in the v1.58.0 → v1.59.0
49
- * consumer upgrade.
50
- *
51
- * The orchestration (progress messages, step tracking, changelog surface, exit
52
- * code) stays in the parent process; only the version-sensitive phases run from
53
- * the new bin. The `spawnPhase` seam makes the child-process boundary fully
54
- * injectable so tests can verify the re-exec path without a real npm install.
55
- * It is the **only** post-install execution path — tests stub the spawn
56
- * boundary rather than swapping in an in-process implementation (No-Shim:
57
- * `.agents/rules/git-conventions.md` § Contract Cutovers).
58
- *
59
- * ## No git mutation
60
- *
61
- * The dependency bump rewrites `package.json` / the lockfile in the working
62
- * tree but the orchestrator performs **no** `git add` / `git commit`. This
63
- * module **reads git state to report it; never mutates it** (Story #5339).
64
- *
65
- * The final success line used to assert "the lockfile bump is staged for
66
- * review" unconditionally. That is only ever true where `npm install` happens
67
- * to stage; the `pnpm add` / `yarn add` seams stage nothing, so on a pnpm
68
- * consumer the operator was told to review an empty index while the manifest,
69
- * the lockfile and every re-materialized `.agents/` path sat unstaged — an
70
- * invitation to commit a half-upgrade. The orchestrator now probes the real
71
- * index through the injectable `gitStatus` seam — two read-only calls, a
72
- * pathspec-anchored `git status --porcelain` plus `git ls-files .agents` — and
73
- * prints one of three truthful lines: staged, not staged (with the exact
74
- * `git add` command), or a neutral "review the working tree" line when git is
75
- * unavailable or the probe fails. A failed probe never fails the update: the
76
- * run still exits 0.
77
- *
78
- * **Staged means both halves** (Story #5364): the index differs from HEAD
79
- * *and* the worktree agrees with the index. Reading only the index reported a
80
- * manifest pair the operator staged *before* running the command as staged
81
- * afterwards, even though the install had since rewritten both files on disk —
82
- * the exact false claim this report exists to remove. The drift-heal path
83
- * reports the re-materialized payload the same way.
84
- *
85
- * ## `--dry-run`
86
- *
87
- * Prints the resolved target version and the ordered step plan, then returns
88
- * without invoking any effectful seam (no npm update, no sync, no migrations,
89
- * no doctor) and writing nothing.
90
- *
91
- * ## Changelog surface
92
- *
93
- * `defaultSurfaceChangelog` prints the `docs/CHANGELOG.md` section(s) for the
94
- * applied range `(current, target]`. It resolves the file against the target
95
- * version's install directory (the freshly bumped `node_modules/mandrel/`),
96
- * where the changelog is now included in the published tarball
97
- * (`docs/CHANGELOG.md` in the `files` allowlist — Story #4035).
98
- *
99
- * When the packaged file is absent (e.g. an older installed version predating
100
- * Story #4035), the seam attempts a one-shot HTTP GET of the raw file from
101
- * GitHub via the injectable `fetchChangelog` seam. If that fetch also fails,
102
- * the seam degrades gracefully — never throwing — and emits an actionable
103
- * message directing the operator to the GitHub Releases page.
104
- *
105
- * ## Injectable seams (used by lib/cli/__tests__/update*.test.js)
106
- *
107
- * - `argv` — subcommand args (after `mandrel update`)
108
- * - `currentVersion` — the installed `mandrel` version string
109
- * - `resolveTargetVersion`— async, returns the newest published version
110
- * - `checkDrift` — sync or async, returns `true` when `.agents/`
111
- * differs from the installed payload. Used by the
112
- * drift-aware no-op short-circuit (Story #4065).
113
- * Defaults to `() => !runAgentsDrift().ok`, which
114
- * reuses the same `agents-drift` doctor signal.
115
- * - `npmUpdate` — async, performs the dependency bump (no git);
116
- * receives `(target, { installCmd })`
117
- * - `spawnPhase` — async, spawns a post-install phase from the new
118
- * binary; receives `(phase, args, { binPath, cwd })`
119
- * and returns `{ ok, stdout, stderr }`. This is the
120
- * sole post-install execution path. See § Re-exec of
121
- * post-install phases.
122
- * - `surfaceChangelog` — emits the target changelog section
123
- * - `gitStatus` — read-only git probe backing the staging report;
124
- * receives `({ cwd, lockfile })` and returns
125
- * `{ ok, stagedManifest, stagedLockfile,
126
- * stagedPayload, tracksAgents }` (Story #5339,
127
- * #5364). Never mutates the repository.
128
- * - `detectLockfile` — resolves the lockfile name the staging report
129
- * names — and the probe's pathspec uses — from the
130
- * uncoerced package-manager probe, so a bun
131
- * consumer is named its real lockfile; receives
132
- * `(cwd)`
133
- * - `write` / `writeErr` — stdout / stderr sinks
134
- * - `exit` — process.exit replacement
135
- * - `cwd` — process.cwd() replacement (used to resolve the
136
- * new binary path the post-install phases spawn from)
137
- *
138
- * Security (security-baseline § 5 — Data Leakage & Logging): logs only version
139
- * strings and step names. No tokens, credentials, or env
140
- * values are read or logged; no shell-string interpolation occurs here (the
141
- * npm bump is delegated to the injected `npmUpdate` seam, which owns transport).
142
- *
143
- * ## Windows spawn (CVE-2024-27980)
144
- *
145
- * Both child-process boundaries — the `npm view` registry probe and the
146
- * install — route through helpers that pass `shell: process.platform ===
147
- * 'win32'`. On Windows `npm`/`pnpm`/`yarn` resolve to `.cmd` shims, and
148
- * Node 18.20+/20.12+/22+/24 refuses to spawn `.cmd`/`.bat` with `shell:false`
149
- * (the CVE-2024-27980 hardening), throwing `spawnSync npm ENOENT`. The win32
150
- * shell flag is the documented fix. It is injection-safe because every argv
151
- * here is a **fixed vector**: the probe argv is the constant package name, and
152
- * the install argv is a tokenized list whose only variable segment is a
153
- * resolved semver string — see `lib/install-cmd-parser.js` for the shared
154
- * tokenize-and-spawn rationale this module reuses (no duplicated workaround).
155
- *
156
- * The `spawnPhase` default (Story #4034) does **not** use the win32 shell flag:
157
- * it spawns `process.execPath` (node) against the resolved `bin/mandrel.js`
158
- * script (Story #4613), so it never touches a `.cmd` shim and needs no
159
- * shell on any platform. The per-phase argv vector is a constant fixed list
160
- * (e.g. `['sync']`, `['migrate', '--from', v, '--to', v]`, `['doctor']`) with
161
- * no operator-supplied data.
3
+ * `mandrel update`: install the newest published version, then re-materialize
4
+ * `.agents/`, run migrations and doctor from the NEW binary (Node cannot
5
+ * hot-swap loaded modules, so in-process phases would materialize the old
6
+ * payload), and surface the changelog. Already-newest with `.agents/` drift
7
+ * runs only the sync phases. Never mutates git: it reports the real index state.
8
+ * Windows: `npm`/`pnpm`/`yarn` are `.cmd` shims that need `shell: true` under
9
+ * CVE-2024-27980; every argv here is a fixed vector, so that stays safe.
162
10
  */
163
11
 
164
12
  import { spawnSync } from 'node:child_process';
@@ -177,38 +25,18 @@ import {
177
25
  resolveConsumerPinVersion,
178
26
  } from './version-helpers.js';
179
27
 
180
- /** The published package whose newest version `mandrel update` advances to. */
181
28
  const PACKAGE_NAME = 'mandrel';
182
29
 
183
- /**
184
- * GitHub raw-file base URL for fetching `docs/CHANGELOG.md` when the packaged
185
- * file is absent (Story #4035 — GitHub fallback). Resolves to the tagged
186
- * release, e.g. `.../mandrel-v1.59.0/docs/CHANGELOG.md`.
187
- */
188
30
  const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/dsj1984/mandrel/';
189
31
 
190
- /**
191
- * Human-readable GitHub Releases page — surfaced in the actionable fallback
192
- * message when neither the packaged file nor the GitHub fetch succeeds.
193
- */
194
32
  const GITHUB_RELEASES_URL = 'https://github.com/dsj1984/mandrel/releases';
195
33
 
196
- /** Default freshness-cache filename — mirrors version-check.js. */
197
34
  const DEFAULT_CACHE_FILENAME = 'version-check.json';
198
35
 
199
36
  /**
200
- * Resolve the installed `mandrel` version from this package's own
201
- * `package.json`. The module lives at `<root>/lib/cli/update.js`, so the
202
- * manifest is two directories up.
203
- *
204
- * Pre-Story-#4525 this was the update decision's `current` — the exact
205
- * self-referential confusion #4525 filed: "the version of the mandrel that
206
- * is executing" is tautologically `>= target` whenever the installed
207
- * package is newest, which made the `npm-update` step unreachable whenever
208
- * a consumer's declared pin had fallen behind what happened to be resolved
209
- * in `node_modules`. It survives here only as the last-resort fallback
210
- * inside {@link resolveCurrentVersionForUpdate}, for the case where even
211
- * `node_modules` resolution from the consumer root fails.
37
+ * The version of the EXECUTING package — last-resort fallback only: as the
38
+ * update decision's `current` it is tautologically newest and would hide a
39
+ * lagging consumer pin.
212
40
  *
213
41
  * @param {typeof nodeFs} [fs]
214
42
  * @returns {string}
@@ -221,27 +49,14 @@ function defaultCurrentVersion(fs = nodeFs) {
221
49
  }
222
50
 
223
51
  /**
224
- * Resolve the "current" version for the `mandrel update` decision
225
- * (Story #4525 / #4530): the consumer's declared `mandrel` dependency pin
226
- * when it resolves to a plain semver, falling back — in order — to the
227
- * version actually resolvable in the consumer's `node_modules` (anchored at
228
- * `consumerRoot` via the same resolution `mandrel sync` uses, unlike the
229
- * pre-#4525 self-referential `defaultCurrentVersion`), and finally to
230
- * `defaultCurrentVersion` itself when neither resolves (a corrupted or
231
- * highly unusual install — keeps `mandrel update` from throwing outright
232
- * rather than silently misreporting "already current").
233
- *
234
- * The declared pin is preferred because it is exactly what the `npm-update`
235
- * step moves: a consumer whose `package.json` pin lags an inflated
236
- * `node_modules` resolution (e.g. an out-of-band symlink or manual
237
- * `npm install mandrel@latest --no-save`) must still see `planUpdate`
238
- * choose `updated`, not `resynced` — the bug #4525 reported.
52
+ * The update decision's `current`: the consumer's declared pin (it is what
53
+ * `npm-update` moves, so a pin lagging an inflated `node_modules` still
54
+ * updates), else the consumer's resolved `node_modules` version, else the
55
+ * executing package's own.
239
56
  *
240
57
  * @param {string} consumerRoot
241
58
  * @param {typeof nodeFs} [fs]
242
- * @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts] - test
243
- * seam for the `node_modules` resolution tier; defaults to the real
244
- * `defaultResolvePackageRoot` from `sync.js`.
59
+ * @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
245
60
  * @returns {string}
246
61
  */
247
62
  export function resolveCurrentVersionForUpdate(
@@ -262,42 +77,15 @@ export function resolveCurrentVersionForUpdate(
262
77
  }
263
78
  }
264
79
 
265
- /**
266
- * Resolve the project root — the directory two levels up from this module
267
- * (`<root>/lib/cli/update.js`). Mirrors `lib/cli/registry.js#resolveProjectRoot`.
268
- *
269
- * @returns {string}
270
- */
80
+ /** @returns {string} */
271
81
  function resolveProjectRoot() {
272
82
  const here = path.dirname(fileURLToPath(import.meta.url));
273
83
  return path.resolve(here, '..', '..');
274
84
  }
275
85
 
276
86
  /**
277
- * Default `resolveTargetVersion` seam: determine the newest published
278
- * `mandrel` version via the daily freshness cache (`version-check.js`).
279
- *
280
- * When `bypassCache` is `true` (the default for an explicit `mandrel update`
281
- * call — Story #4046 A1b), `isStale` is asked to skip its freshness read via
282
- * `forceRefresh`, so any existing cache is ignored and exactly one network
283
- * probe is issued. The cache is still written so the post-update
284
- * `version-current` advisory has a fresh baseline.
285
- *
286
- * The bypass deliberately does **not** shift `now`: the same clock is stamped
287
- * into the refreshed `checkedAt`, so a shifted clock would persist a
288
- * ~48h-future timestamp and defeat the 24h window for every later passive
289
- * check until real time caught up (Story #4878).
290
- *
291
- * When `bypassCache` is `false` (passive staleness checks only), the normal
292
- * 24h-cache semantics apply: a fresh cache returns the cached version with
293
- * zero network I/O.
294
- *
295
- * The network probe shells `npm view` through `spawnSync` with a fixed argument
296
- * vector (no shell-string interpolation; the package name is a constant). On
297
- * Windows the spawn sets `shell: true` so the `npm.cmd` shim resolves under the
298
- * CVE-2024-27980 hardening (mirrors `lib/install-cmd-parser.js`); the fixed
299
- * argv carries no injection risk even with the shell flag set
300
- * (security-baseline § Output & Rendering).
87
+ * Newest published version via the daily freshness cache. `bypassCache` forces
88
+ * one probe but still writes the cache.
301
89
  *
302
90
  * @param {{
303
91
  * cachePath?: string,
@@ -317,9 +105,8 @@ async function defaultResolveTargetVersion({
317
105
  bypassCache = false,
318
106
  log = () => {},
319
107
  } = {}) {
320
- // Bypass the cache by telling isStale to skip its freshness read — never by
321
- // shifting `now`, which is also the clock persisted as `checkedAt`
322
- // (Story #4046 A1b forcing the probe; Story #4878 keeping the stamp honest).
108
+ // Bypass via forceRefresh, never by shifting `now`: `now` is persisted as
109
+ // `checkedAt`, and a shifted stamp would defeat the 24h window.
323
110
  const result = await isStale({
324
111
  cachePath,
325
112
  now,
@@ -332,15 +119,10 @@ async function defaultResolveTargetVersion({
332
119
  }
333
120
 
334
121
  /**
335
- * Default network `runner` for the freshness probe: shells
336
- * `npm view mandrel version` synchronously and returns the trimmed
337
- * stdout. Fixed argv (the package name is a constant), and `shell:true` only on
338
- * Windows so the `npm.cmd` shim resolves under CVE-2024-27980 — the fixed
339
- * vector keeps it injection-safe with or without the shell flag.
122
+ * `npm view mandrel version`, trimmed.
340
123
  *
341
- * @param {{ spawnSync?: typeof spawnSync }} [deps] — test seam for the spawn
342
- * boundary; defaults to the real `node:child_process` spawnSync.
343
- * @returns {string} The newest published version string.
124
+ * @param {{ spawnSync?: typeof spawnSync }} [deps]
125
+ * @returns {string}
344
126
  */
345
127
  export function defaultVersionRunner({ spawnSync: spawn = spawnSync } = {}) {
346
128
  const r = spawn('npm', ['view', PACKAGE_NAME, 'version'], {
@@ -368,9 +150,7 @@ export function defaultVersionRunner({ spawnSync: spawn = spawnSync } = {}) {
368
150
  }
369
151
 
370
152
  /**
371
- * Map a detected package manager to the command that re-runs a full install.
372
- * Surfaced in the repair hint when an install fails so the operator can restore
373
- * `node_modules` to a consistent state (Story #3575 AC-4).
153
+ * The full-install command named in the failed-install repair hint.
374
154
  *
375
155
  * @param {'pnpm' | 'yarn' | 'npm'} packageManager
376
156
  * @returns {string}
@@ -382,45 +162,26 @@ function repairInstallCommand(packageManager) {
382
162
  }
383
163
 
384
164
  /**
385
- * Detect the project's package manager by probing for a lockfile in `cwd`.
386
- * Precedence mirrors the ecosystem norm: a `pnpm-lock.yaml` wins over a
387
- * `yarn.lock`, which wins over the npm default. `workspaceRoot` is true only
388
- * for pnpm when a `pnpm-workspace.yaml` sits alongside the lockfile — the
389
- * signal that `pnpm add` must carry `-w` to target the workspace-root manifest.
165
+ * The lockfile-detected package manager, so the bump runs under the tool that
166
+ * owns the tree (npm in a pnpm workspace fails and can corrupt `node_modules`).
167
+ * `workspaceRoot` means `pnpm add` needs `-w`. `bun` coerces to `npm`: the
168
+ * install builder has no bun command.
390
169
  *
391
- * Running the wrong package manager (e.g. `npm install` in a pnpm workspace) is
392
- * the root cause this resolves (Story #3575): npm chokes on the pnpm-managed
393
- * tree, exits non-zero, and can flip `node_modules` to a stale store entry.
394
- * Detecting the lockfile keeps the bump on the operator's real package manager
395
- * so the change lands in the matching lockfile.
396
- *
397
- * Delegates to the shared `detectPackageManagerWithWorkspace` helper
398
- * (Story #4048 B3 — one implementation per concept). The `fs` seam is adapted
399
- * to the shared module's `exists` contract; the shared module's `bun` return
400
- * value coerces to `npm` here because `bun add` is not yet a first-class update
401
- * path for this orchestrator.
402
- *
403
- * @param {string} [cwd] - Project root to probe (default `process.cwd()`).
170
+ * @param {string} [cwd]
404
171
  * @param {typeof nodeFs} [fs]
405
172
  * @returns {{ packageManager: 'pnpm' | 'yarn' | 'npm', workspaceRoot: boolean }}
406
173
  */
407
174
  export function detectPackageManager(cwd = process.cwd(), fs = nodeFs) {
408
175
  const result = probePackageManager(cwd, fs);
409
- // Coerce `bun` → `npm` because this orchestrator's install-command builder
410
- // only handles pnpm / yarn / npm today.
411
176
  const packageManager =
412
177
  result.packageManager === 'bun' ? 'npm' : result.packageManager;
413
178
  return { packageManager, workspaceRoot: result.workspaceRoot };
414
179
  }
415
180
 
416
181
  /**
417
- * The raw, **uncoerced** package-manager probe. `detectPackageManager` above
418
- * flattens `bun` to `npm` for the install-command builder's benefit; the
419
- * staging report must not inherit that flattening, or a bun consumer is told
420
- * to stage a `package-lock.json` its tree does not have (Story #5364). Reading
421
- * the probe before the coercion is the whole fix.
182
+ * The uncoerced probe; the staging report must name bun's real lockfile.
422
183
  *
423
- * @param {string} cwd - Project root to probe.
184
+ * @param {string} cwd
424
185
  * @param {typeof nodeFs} [fs]
425
186
  * @returns {{ packageManager: 'pnpm'|'yarn'|'bun'|'npm', workspaceRoot: boolean }}
426
187
  */
@@ -436,24 +197,11 @@ function probePackageManager(cwd, fs = nodeFs) {
436
197
  }
437
198
 
438
199
  /**
439
- * Resolve the install command string `defaultNpmUpdate` runs.
440
- *
441
- * With no override the command is built from the detected package manager:
442
- * - `pnpm` ⇒ `pnpm add -D mandrel@<target>` (plus ` -w` at a pnpm
443
- * workspace root)
444
- * - `yarn` ⇒ `yarn add -D mandrel@<target>`
445
- * - `npm` ⇒ `npm install mandrel@<target>` (the unchanged default)
200
+ * The install command. An `--install-cmd` override is used verbatim except
201
+ * that `{target}` is replaced with the resolved semver.
446
202
  *
447
- * An explicit `--install-cmd` override is used verbatim, except that a
448
- * `{target}` placeholder is substituted with the resolved semver so an override
449
- * can still consume the auto-probed newest version (Story #3575 AC-3).
450
- *
451
- * This function is pure: package-manager detection happens in
452
- * `detectPackageManager` (the only filesystem seam) and is passed in as
453
- * `detected`, keeping the command-string assembly trivially unit-testable.
454
- *
455
- * @param {string} target - The resolved semver to install.
456
- * @param {string} [override] - Operator-supplied `--install-cmd` value.
203
+ * @param {string} target
204
+ * @param {string} [override]
457
205
  * @param {{
458
206
  * packageManager?: 'pnpm' | 'yarn' | 'npm',
459
207
  * workspaceRoot?: boolean,
@@ -480,11 +228,6 @@ export function resolveInstallCmd(
480
228
  return `npm install ${PACKAGE_NAME}@${target}`;
481
229
  }
482
230
 
483
- /**
484
- * The lockfile each supported package manager writes. The staging report names
485
- * the detected one so the `git add` hint is copy-pasteable in the consumer's
486
- * real tree rather than always saying `package-lock.json` (Story #5339).
487
- */
488
231
  const LOCKFILE_BY_PACKAGE_MANAGER = {
489
232
  pnpm: 'pnpm-lock.yaml',
490
233
  yarn: 'yarn.lock',
@@ -492,23 +235,12 @@ const LOCKFILE_BY_PACKAGE_MANAGER = {
492
235
  npm: 'package-lock.json',
493
236
  };
494
237
 
495
- /** The manifest the dependency bump rewrites alongside the lockfile. */
496
238
  const MANIFEST_FILENAME = 'package.json';
497
239
 
498
- /** The materialized payload directory the sync phase rewrites. */
499
240
  const AGENTS_DIR = '.agents';
500
241
 
501
242
  /**
502
- * Default `detectLockfile` seam: resolve the lockfile name for the staging
503
- * report from the **uncoerced** package-manager probe (`pnpm-lock.yaml` /
504
- * `yarn.lock` / `bun.lockb` / `package-lock.json`). Reading
505
- * `probePackageManager` rather than `detectPackageManager` is deliberate: the
506
- * latter flattens `bun` to `npm` so the install-command builder has a command
507
- * to emit, and inheriting that here would name a lockfile the consumer's tree
508
- * does not carry (Story #5364). A directory with no recognizable toolchain
509
- * still resolves to `npm`, so the report always names a concrete file.
510
- *
511
- * @param {string} cwd - Consumer project root.
243
+ * @param {string} cwd
512
244
  * @param {typeof nodeFs} [fs]
513
245
  * @returns {string}
514
246
  */
@@ -517,11 +249,9 @@ export function defaultDetectLockfile(cwd, fs = nodeFs) {
517
249
  return LOCKFILE_BY_PACKAGE_MANAGER[packageManager] ?? 'package-lock.json';
518
250
  }
519
251
 
520
- /** The line printed when the probe cannot report the index at all. */
521
252
  const NEUTRAL_STAGING_LINE =
522
253
  'Review the working tree and commit the bump (git not available to report staging state).';
523
254
 
524
- /** What the probe reports when it cannot read the index at all. */
525
255
  const DEGRADED_GIT_STATE = Object.freeze({
526
256
  ok: false,
527
257
  stagedManifest: false,
@@ -530,24 +260,14 @@ const DEGRADED_GIT_STATE = Object.freeze({
530
260
  tracksAgents: false,
531
261
  });
532
262
 
533
- /** True when a `spawnSync` result is a clean, non-throwing exit. */
534
263
  const spawnOk = (result) =>
535
264
  Boolean(result) && !result.error && result.status === 0;
536
265
 
537
266
  /**
538
- * The path one `git status --porcelain` v1 record reports as **staged**, or
539
- * `null` when that record is not staged (or carries no path).
540
- *
541
- * Staged means both halves of the contract (Story #5364): the index differs
542
- * from HEAD *and* the worktree agrees with the index. Porcelain v1 states that
543
- * as `XY<space>path` — `X` the index column, `Y` the worktree column — so a
544
- * path the operator staged before the install and the install then rewrote on
545
- * disk reports `MM` and is correctly NOT staged. `?` in the index column is an
546
- * untracked file, which is never staged.
547
- *
548
- * A rename carries `orig -> new`; the post-rename path is the one the operator
549
- * stages. Porcelain quotes paths containing unusual bytes, so the quoting is
550
- * stripped.
267
+ * The path a porcelain v1 record reports as staged, else `null`. Staged means
268
+ * index differs from HEAD AND the worktree agrees (`Y` blank), so a pair staged
269
+ * before the install and rewritten by it (`MM`) is not staged. Renames yield
270
+ * the new path; porcelain quoting is stripped.
551
271
  *
552
272
  * @param {string} record
553
273
  * @returns {string | null}
@@ -566,17 +286,10 @@ function stagedPathFromPorcelain(record) {
566
286
  }
567
287
 
568
288
  /**
569
- * Which slot of the probe state one staged path fills, or `null` for a path
570
- * that fills none.
571
- *
572
- * The payload test is anchored on a path separator, so a sibling like
573
- * `my.agents/` never matches, and it runs first: a `package.json` *inside*
574
- * `.agents/` is payload, never the consumer's root manifest. The manifest and
575
- * lockfile tests tolerate one leading prefix because git reports porcelain
576
- * paths relative to the repository root, which need not be the probe root —
577
- * safe only because the caller's pathspec already anchored the query there, so
578
- * a staged `packages/app/package.json` never reaches this function at all
579
- * (Story #5364).
289
+ * The probe-state slot a staged path fills. Payload is tested first (a
290
+ * `package.json` inside `.agents/` is payload). The manifest/lockfile tests
291
+ * accept a leading prefix because porcelain paths are repo-root-relative;
292
+ * that is safe only because the caller's pathspec anchors the query at `cwd`.
580
293
  *
581
294
  * @param {string} path
582
295
  * @param {string} lockfile
@@ -591,26 +304,9 @@ function stagedSlotFor(path, lockfile) {
591
304
  }
592
305
 
593
306
  /**
594
- * Default `gitStatus` seam: probe the consumer repository's real index state.
595
- *
596
- * **Two** read-only plumbing calls, no mutation of any kind (§ No git
597
- * mutation):
598
- * - `git status --porcelain -- package.json <lockfile> .agents` — one read
599
- * that answers every branch the report chooses between. The pathspecs are
600
- * resolved relative to `cwd`, which anchors the query at the probe root:
601
- * a nested workspace manifest is simply not in the output. Both status
602
- * columns come back, so "staged" can mean index-differs-**and**-worktree-
603
- * clean rather than the index alone.
604
- * - `git ls-files .agents` — whether the consumer *tracks* the materialized
605
- * payload, which decides whether the `git add` hint names it.
606
- *
607
- * Any failure — git absent, not a repository, a non-zero exit, a throw from the
608
- * spawn boundary — degrades to `{ ok: false }` so the caller prints the neutral
609
- * line and the update still exits 0. The probe never throws.
610
- *
611
- * Security (security-baseline § Output & Rendering): every argv segment here is
612
- * a fixed vector or a lockfile name drawn from `LOCKFILE_BY_PACKAGE_MANAGER`,
613
- * never operator input, and no shell is used.
307
+ * Read-only index probe: a `cwd`-anchored `git status --porcelain` over the
308
+ * manifest, lockfile and `.agents`, plus `git ls-files .agents` (is the
309
+ * payload tracked?). Never throws; any failure degrades to `{ ok: false }`.
614
310
  *
615
311
  * @param {{
616
312
  * cwd?: string,
@@ -659,25 +355,10 @@ export function defaultGitStatus({
659
355
  }
660
356
 
661
357
  /**
662
- * Render the one truthful staging line a successful run closes with
663
- * (Story #5339, corrected by Story #5364).
664
- *
665
- * `scope: 'bump'` (the full upgrade) reports the manifest + lockfile pair:
666
- *
667
- * - **staged** — both are staged, so "staged for review" is a fact.
668
- * - **not staged** — anything else: the line says so and prints the exact
669
- * `git add` command, naming `.agents/` too when the consumer tracks that
670
- * tree (the sync re-materialized it, and an operator who stages only the
671
- * manifest pair silently drops that diff).
672
- *
673
- * `scope: 'payload'` (the drift heal, which bumps no dependency) reports the
674
- * re-materialized `.agents/` tree instead, and returns `''` when the consumer
675
- * does not track it — there is then nothing to stage and nothing to say.
676
- *
677
- * Either scope degrades to a line that claims nothing about the index when the
678
- * probe could not report.
679
- *
680
- * Pure: takes the probe result and the resolved lockfile name, touches no fs.
358
+ * The staging line a successful run closes with. `bump` reports the manifest +
359
+ * lockfile pair, and a not-staged hint also names a tracked `.agents/` (else
360
+ * that diff is silently dropped). `payload` (drift heal) reports only
361
+ * `.agents/`, or `''` when it is untracked.
681
362
  *
682
363
  * @param {{
683
364
  * ok?: boolean,
@@ -685,11 +366,10 @@ export function defaultGitStatus({
685
366
  * stagedLockfile?: boolean,
686
367
  * stagedPayload?: boolean,
687
368
  * tracksAgents?: boolean,
688
- * }} gitState - The `gitStatus` seam's return value.
689
- * @param {string} lockfile - The detected lockfile name.
369
+ * }} gitState
370
+ * @param {string} lockfile
690
371
  * @param {{ scope?: 'bump' | 'payload' }} [opts]
691
- * @returns {string} A single sentence-run with no trailing newline, or `''`
692
- * when there is nothing to report.
372
+ * @returns {string}
693
373
  */
694
374
  export function formatStagingReport(
695
375
  gitState,
@@ -727,13 +407,8 @@ export function formatStagingReport(
727
407
  }
728
408
 
729
409
  /**
730
- * Resolve the staging line for a success surface, absorbing any failure in
731
- * either seam. Reporting the index is a courtesy on top of completed work: a
732
- * probe that throws (git missing, a seam raising) must degrade to the neutral
733
- * line, never turn a successful run into a crash.
734
- *
735
- * The lockfile is resolved first because the probe's pathspec names it — that
736
- * is what anchors the query at the probe root.
410
+ * The staging line, degrading any seam failure to the neutral line so a
411
+ * courtesy report never crashes a completed update.
737
412
  *
738
413
  * @param {{
739
414
  * gitStatus: (opts: { cwd: string, lockfile: string }) => object,
@@ -762,27 +437,12 @@ function resolveStagingReport({
762
437
  }
763
438
 
764
439
  /**
765
- * Default `npmUpdate` seam: install the resolved target version. The install
766
- * rewrites `package.json` / the lockfile on disk for the operator to review;
767
- * this performs no git mutation. Whether the package manager also staged them
768
- * is reported by the staging probe, never assumed (Story #5339).
769
- *
770
- * The package manager is auto-detected from `cwd`'s lockfile (Story #3575) so
771
- * the bump lands in the operator's real lockfile rather than running
772
- * `npm install` against a pnpm/yarn-managed tree. The install routes through
773
- * the shared `runInstallCommand` helper from `lib/install-cmd-parser.js`, which
774
- * tokenizes the command and spawns with `shell: process.platform === 'win32'`
775
- * so the Windows `.cmd` shim resolves under CVE-2024-27980 — the win32 shell
776
- * handling and tokenization are reused, not re-implemented here. The resolved
777
- * argv is a fixed vector; an `--install-cmd` override is tokenized and escaped
778
- * per-arg by the parser even when the win32 shell flag is required.
779
- *
780
- * On any install failure the thrown error names the detected package manager's
781
- * own `install` command so the operator can restore `node_modules` to a
782
- * consistent state — `mandrel update` never silently leaves a half-mutated
783
- * tree (Story #3575 AC-4).
440
+ * Install `target` with the detected package manager (no git mutation). The
441
+ * shared `runInstallCommand` tokenizes and escapes per-arg under the win32
442
+ * shell. A failure names the repair install so `node_modules` is never left
443
+ * silently half-mutated.
784
444
  *
785
- * @param {string} target - The version to install.
445
+ * @param {string} target
786
446
  * @param {{
787
447
  * installCmd?: string,
788
448
  * runInstall?: typeof runInstallCommand,
@@ -822,30 +482,16 @@ export function defaultNpmUpdate(
822
482
  }
823
483
 
824
484
  /**
825
- * Fetch `docs/CHANGELOG.md` for a specific mandrel tag from GitHub's raw
826
- * content endpoint. This is the fallback when the packaged file is absent
827
- * (e.g. an older install predating Story #4035 which added the file to the
828
- * npm `files` allowlist).
829
- *
830
- * Injectable via the `fetchChangelog` seam so tests can verify the fallback
831
- * path without issuing real network calls.
832
- *
833
- * The tag shape follows the `mandrel-vX.Y.Z` namespace (namespaced at
834
- * `mandrel-v1.44.0`; bare `vX.Y.Z` for earlier releases). This function
835
- * tries the namespaced tag first, then the bare-tag form, so it covers both
836
- * tag series without forcing callers to know the boundary.
485
+ * `docs/CHANGELOG.md` from GitHub raw, for installs whose package lacks it.
486
+ * Tags are `mandrel-vX.Y.Z` from 1.44.0 and bare `vX.Y.Z` before, so both
487
+ * are tried.
837
488
  *
838
- * Security (security-baseline § Transport & Headers): the URL is constructed
839
- * from a constant base and a semver string — no user input, no shell
840
- * interpolation. The GET is a read-only fetch with no credentials.
841
- *
842
- * @param {string} version - The target semver string (e.g. `"1.59.0"`).
489
+ * @param {string} version
843
490
  * @param {{
844
491
  * https?: typeof nodeHttps,
845
492
  * }} [deps]
846
- * @returns {Promise<string>} The raw changelog text.
847
- * @throws {Error} When both tag forms return a non-2xx response or the request
848
- * errors out — the caller handles this gracefully.
493
+ * @returns {Promise<string>}
494
+ * @throws {Error} When every tag form is non-2xx or the request errors.
849
495
  */
850
496
  export async function fetchChangelogFromGitHub(
851
497
  version,
@@ -888,25 +534,10 @@ export async function fetchChangelogFromGitHub(
888
534
  }
889
535
 
890
536
  /**
891
- * Default `surfaceChangelog` seam: print the `docs/CHANGELOG.md` section(s)
892
- * covering the applied version range `(current, target]`. The changelog is
893
- * authored by release-please with `## [<version>](…)` section headers; this
894
- * prints every section whose version is newer than `current` and no newer than
895
- * `target`.
896
- *
897
- * Resolution order (Story #4035):
898
- * 1. Read `docs/CHANGELOG.md` from the target version's install directory
899
- * (`node_modules/mandrel/docs/CHANGELOG.md` — now in the published
900
- * tarball since `package.json` lists `docs/CHANGELOG.md` in `files`).
901
- * 2. When the packaged file is absent (older install), fetch it from GitHub
902
- * via the injectable `fetchChangelog` seam.
903
- * 3. When both sources fail, emit an actionable warning with a link to the
904
- * GitHub Releases page — never a bare "not found … skipping".
537
+ * Print the changelog sections in `(current, target]` — packaged file first,
538
+ * then GitHub, then a Releases link. Best-effort: warns, never throws.
905
539
  *
906
- * Degrades gracefully (warns, never throws) — surfacing the changelog is
907
- * best-effort and must never fail an otherwise-successful upgrade.
908
- *
909
- * @param {string} target - The applied target version.
540
+ * @param {string} target
910
541
  * @param {{
911
542
  * current?: string,
912
543
  * changelogPath?: string,
@@ -930,19 +561,16 @@ async function defaultSurfaceChangelog(
930
561
  ) {
931
562
  let raw;
932
563
 
933
- // 1. Try the packaged file (present in installs since Story #4035).
934
564
  try {
935
565
  raw = fs.readFileSync(changelogPath, 'utf8');
936
566
  } catch {
937
- // File absent — fall through to GitHub fetch.
567
+ // Absent: fall through to the GitHub fetch.
938
568
  }
939
569
 
940
- // 2. Packaged file absent: attempt a GitHub fetch for the target tag.
941
570
  if (raw === undefined) {
942
571
  try {
943
572
  raw = await fetchChangelog(target);
944
573
  } catch {
945
- // Both sources unavailable — emit an actionable message and return.
946
574
  writeErr(
947
575
  `mandrel update: changelog not available for v${target} — ` +
948
576
  `view the release notes at ${GITHUB_RELEASES_URL}\n`,
@@ -973,9 +601,8 @@ async function defaultSurfaceChangelog(
973
601
  }
974
602
 
975
603
  /**
976
- * Split a release-please `CHANGELOG.md` into `{ version, body }` sections keyed
977
- * by the `## [<version>]…` headers. Each `body` includes the header line and
978
- * everything up to (but not including) the next version header.
604
+ * Split a release-please changelog on `## [<version>]` headers; each body
605
+ * includes its header line.
979
606
  *
980
607
  * @param {string} raw
981
608
  * @returns {Array<{ version: string, body: string }>}
@@ -1008,32 +635,13 @@ function parseChangelogSections(raw) {
1008
635
  }
1009
636
 
1010
637
  /**
1011
- * Resolve the newly-installed `mandrel` bin **script**
1012
- * (`<packageRoot>/bin/mandrel.js`) from the consumer project root. This is the
1013
- * target for the post-install phase re-exec (Story #4034), spawned via
1014
- * `process.execPath` (node) rather than executed directly — see
1015
- * {@link defaultSpawnPhase}.
1016
- *
1017
- * It deliberately does **not** return the `node_modules/.bin/mandrel` shim.
1018
- * That shim only works because npm chmods the bin target `+x` at install time:
1019
- * `bin/mandrel.js` ships non-executable in the published tarball, and pnpm
1020
- * symlinks `.bin/mandrel` straight at it, so spawning the shim directly fails
1021
- * with `EACCES` under pnpm (Story #4613). Spawning node against the resolved
1022
- * `.js` script removes the dependency on the exec bit, the shebang, and the
1023
- * Windows `.cmd` shim entirely.
638
+ * The consumer's installed `bin/mandrel.js`, NOT the `.bin` shim: the script
639
+ * ships non-executable and pnpm symlinks the shim straight at it, so spawning
640
+ * the shim fails with EACCES. Node runs the script directly instead.
1024
641
  *
1025
- * Resolution reuses the same consumer-anchored resolver
1026
- * (`defaultResolvePackageRoot`) that {@link resolveCurrentVersionForUpdate}
1027
- * uses, so it points at the consumer's install rather than a copy hoisted next
1028
- * to this CLI module. The `mandrel` package directory is version-invariant
1029
- * (`node_modules/mandrel/`), so resolving it before the in-place `npm-update`
1030
- * step still yields the directory whose `bin/mandrel.js` the install overwrites.
1031
- *
1032
- * @param {string} projectRoot - Absolute path to the consumer project.
1033
- * @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts] - test
1034
- * seam for the `node_modules` resolution; defaults to the real
1035
- * `defaultResolvePackageRoot` from `sync.js`.
1036
- * @returns {string} Absolute path to the new bin script.
642
+ * @param {string} projectRoot
643
+ * @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
644
+ * @returns {string}
1037
645
  */
1038
646
  export function resolveNewBinScriptPath(
1039
647
  projectRoot,
@@ -1044,33 +652,19 @@ export function resolveNewBinScriptPath(
1044
652
  }
1045
653
 
1046
654
  /**
1047
- * Default `spawnPhase` seam (Story #4034): spawn a post-install phase from the
1048
- * newly-installed `mandrel` bin script and stream its stdout/stderr through the
1049
- * parent's write sinks. Each phase runs as an isolated child process so the
1050
- * newly-installed module code (not the currently-loaded old module) executes.
1051
- *
1052
- * The child is spawned as `process.execPath <binScript> <phase> …` — node run
1053
- * against the resolved `bin/mandrel.js` (see {@link resolveNewBinScriptPath}).
1054
- * Spawning node against a plain `.js` file removes any dependency on the bin's
1055
- * exec bit, its shebang, or a Windows `.cmd` shim, so **no** `shell` flag is
1056
- * needed on any platform (this is the pnpm/layout-agnostic fix, Story #4613,
1057
- * that retired the former win32-only `shell: true` branch). The argv vector is
1058
- * a fixed constant list per phase — no operator-supplied data enters it
1059
- * (security-baseline § Output & Rendering).
655
+ * Run one post-install phase as `node <binScript> <phase> …` so the new
656
+ * package's code executes; no shell is needed on any platform. Throws only on
657
+ * a spawn error.
1060
658
  *
1061
- * Throws when the child exits non-zero so the orchestrator can surface the
1062
- * failure to the operator.
1063
- *
1064
- * @param {string} phase - The mandrel sub-command to run (e.g. `'sync'`).
1065
- * @param {string[]} args - Additional arguments for the sub-command.
659
+ * @param {string} phase
660
+ * @param {string[]} args
1066
661
  * @param {{
1067
662
  * binPath: string,
1068
663
  * cwd: string,
1069
664
  * write: (s: string) => void,
1070
665
  * writeErr: (s: string) => void,
1071
666
  * spawnFn?: typeof spawnSync,
1072
- * }} opts - `binPath` is the resolved bin **script** path (not the
1073
- * `node_modules/.bin` shim); it becomes node's first argv entry.
667
+ * }} opts
1074
668
  * @returns {{ ok: boolean, stdout: string, stderr: string }}
1075
669
  */
1076
670
  export function defaultSpawnPhase(
@@ -1096,19 +690,7 @@ export function defaultSpawnPhase(
1096
690
  return { ok, stdout, stderr };
1097
691
  }
1098
692
 
1099
- /**
1100
- * The ordered step names the orchestrator drives on an update. Shared
1101
- * by the live path and the `--dry-run` plan printout so the two never drift.
1102
- *
1103
- * Step ordering (Story #4046 A1c; sync-agents added by Story #4528/#4530):
1104
- * 1. npm-update — install the new version
1105
- * 2. runSync — re-materialize .agents/ from the new payload
1106
- * 3. sync-commands — regenerate .claude/commands/ from the new payload
1107
- * 4. sync-agents — regenerate .claude/agents/ from the new payload
1108
- * 5. runMigrations — apply version-keyed migrations
1109
- * 6. doctor — validate the post-upgrade state
1110
- * 7. surface changelog — print the changelog (always last, best-effort)
1111
- */
693
+ /** The `--dry-run` printout of the full-upgrade step order. */
1112
694
  const STEP_PLAN = [
1113
695
  'npm-update',
1114
696
  'runSync',
@@ -1120,19 +702,9 @@ const STEP_PLAN = [
1120
702
  ];
1121
703
 
1122
704
  /**
1123
- * The ordered post-install phase descriptors for a full upgrade. Each entry is
1124
- * a plain value (no I/O) describing one step the executor drives:
1125
- *
1126
- * - `kind: 'npm-update'` — bump the dependency via the `npmUpdate` seam.
1127
- * - `kind: 'spawn'` — spawn `phase`/`args` from the new binary; a
1128
- * non-zero exit is fatal and throws `failMessage`.
1129
- * - `kind: 'doctor'` — spawn `doctor` from the new binary; a non-zero
1130
- * exit is *soft* (maps to `action: 'doctor-failed'`
1131
- * + exit 1), so it carries no `failMessage`.
1132
- *
1133
- * `label` is the name pushed into the run's `stepsRun[]` (the external return
1134
- * contract). `migrate` is the only phase whose argv depends on the version
1135
- * range, so its descriptor is built per-plan in `planUpdate`.
705
+ * Full-upgrade phases. A `spawn` non-zero exit is fatal (throws
706
+ * `failMessage`); a `doctor` failure is soft (`doctor-failed`, exit 1).
707
+ * `label` is what `stepsRun[]` reports.
1136
708
  *
1137
709
  * @param {string} current
1138
710
  * @param {string} target
@@ -1162,10 +734,7 @@ function fullUpgradeSteps(current, target) {
1162
734
  'Run `npm run sync:commands` manually to restore.',
1163
735
  },
1164
736
  {
1165
- // Story #4528/#4530: the CLI update path previously never projected the
1166
- // role-agent tree at all — only the bootstrap path did. Added alongside
1167
- // sync-commands so `.claude/agents/` materializes here too, which is
1168
- // what makes the tightened `agents-in-sync` doctor check satisfiable.
737
+ // Required for the `agents-in-sync` doctor check to be satisfiable.
1169
738
  kind: 'spawn',
1170
739
  phase: 'sync-agents',
1171
740
  args: [],
@@ -1190,10 +759,7 @@ function fullUpgradeSteps(current, target) {
1190
759
  }
1191
760
 
1192
761
  /**
1193
- * The ordered phase descriptors for a drift-heal (version already current, but
1194
- * `.agents/` is stale). No npm-update, no migrations, no doctor — only the
1195
- * three sync phases re-materialize the payload from the already-installed
1196
- * binary.
762
+ * Drift-heal phases: the sync phases only, from the installed binary.
1197
763
  *
1198
764
  * @returns {Array<{ kind: 'spawn', phase: string, args: string[], label: string, failMessage: string }>}
1199
765
  */
@@ -1220,7 +786,6 @@ function driftHealSteps() {
1220
786
  'Run `npm run sync:commands` manually to restore.',
1221
787
  },
1222
788
  {
1223
- // Story #4528/#4530: see the matching entry in fullUpgradeSteps().
1224
789
  kind: 'spawn',
1225
790
  phase: 'sync-agents',
1226
791
  args: [],
@@ -1234,25 +799,7 @@ function driftHealSteps() {
1234
799
  }
1235
800
 
1236
801
  /**
1237
- * Pure decision function for `mandrel update`: given the resolved version
1238
- * inputs and the two flags, decide which of the four actions to take and the
1239
- * ordered phase plan for that action. **No I/O** — no filesystem, child
1240
- * process, network, `write`, or `exit`. This isolates the scheduler-style
1241
- * branch-selection and step-sequencing logic (the surface under review in
1242
- * Story #4182 / audit::architecture) so it can be exercised as a table over
1243
- * plain inputs rather than by running the whole async orchestration with every
1244
- * seam stubbed.
1245
- *
1246
- * The four actions:
1247
- *
1248
- * - `up-to-date` — version is current and no drift. True no-op; `steps: []`.
1249
- * - `dry-run` — `dryRun` is set. `steps: []` (nothing is executed); the
1250
- * `variant` distinguishes the drift-heal preview from the
1251
- * full-upgrade preview so the executor prints the right plan.
1252
- * - `resynced` — version is current but drift detected. Heal via the two
1253
- * sync phases (`driftHealSteps()`).
1254
- * - `updated` — a newer version is available. Full upgrade
1255
- * (`fullUpgradeSteps(current, target)`).
802
+ * Pure (no I/O) choice of action and phase plan.
1256
803
  *
1257
804
  * @param {{ current: string, target: string, dryRun: boolean, hasDrift: boolean }} input
1258
805
  * @returns {{
@@ -1265,7 +812,6 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
1265
812
  const versionCurrent = compareVersions(target, current) <= 0;
1266
813
 
1267
814
  if (versionCurrent) {
1268
- // Version is already newest. The only remaining question is drift.
1269
815
  if (!hasDrift) {
1270
816
  return { action: 'up-to-date', steps: [] };
1271
817
  }
@@ -1275,8 +821,7 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
1275
821
  return { action: 'resynced', steps: driftHealSteps() };
1276
822
  }
1277
823
 
1278
- // A newer version is available — full upgrade (drift is irrelevant here; the
1279
- // post-upgrade doctor phase re-checks materialization).
824
+ // Drift is irrelevant here: the post-upgrade doctor re-checks it.
1280
825
  if (dryRun) {
1281
826
  return { action: 'dry-run', steps: [], variant: 'full-upgrade' };
1282
827
  }
@@ -1284,14 +829,8 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
1284
829
  }
1285
830
 
1286
831
  /**
1287
- * Extract the `--install-cmd "<cmd>"` value from the subcommand argv. Accepts
1288
- * both the space form (`--install-cmd npm install …`, captured as the single
1289
- * following token group) and the `=` form (`--install-cmd="<cmd>"`). Returns
1290
- * `undefined` when the flag is absent so the default package manager is used.
1291
- *
1292
- * The argv tokenizer hands us a pre-split array; with the space form the shell
1293
- * has already collapsed a quoted value into one element, so the immediate next
1294
- * token is the full command string.
832
+ * `--install-cmd <cmd>` or `--install-cmd=<cmd>`; the shell has already joined
833
+ * a quoted value into one token.
1295
834
  *
1296
835
  * @param {string[]} argv
1297
836
  * @returns {string | undefined}
@@ -1310,12 +849,6 @@ function parseInstallCmdFlag(argv) {
1310
849
  }
1311
850
 
1312
851
  /**
1313
- * Resolve the drift signal for the no-op short-circuit. Prefers the injected
1314
- * `checkDrift` seam (unit-test friendly); falls back to the production
1315
- * `runAgentsDrift` helper. Only consulted when the installed version is already
1316
- * the newest (Story #4065) — a real version bump skips drift entirely (the
1317
- * post-upgrade doctor phase re-checks materialization).
1318
- *
1319
852
  * @param {(() => boolean | Promise<boolean>) | undefined} checkDrift
1320
853
  * @returns {Promise<boolean>}
1321
854
  */
@@ -1326,12 +859,8 @@ async function resolveDrift(checkDrift) {
1326
859
  }
1327
860
 
1328
861
  /**
1329
- * Execute the ordered phase plan returned by `planUpdate` for the `updated` /
1330
- * `resynced` actions. This is the thin side-effecting shell: it owns the
1331
- * `npmUpdate` seam call, the `spawnPhase` re-exec boundary, the per-step
1332
- * `stepsRun` accounting, and the soft doctor-fail (`exit(1)` +
1333
- * `action: 'doctor-failed'`). The branch-selection logic that produced `steps`
1334
- * lives in the pure `planUpdate`.
862
+ * Drive `planUpdate`'s steps. A doctor failure still surfaces the changelog,
863
+ * then exits 1.
1335
864
  *
1336
865
  * @param {{
1337
866
  * steps: Array<{ kind: 'npm-update' | 'spawn' | 'doctor', label: string, phase?: string, args?: string[], failMessage?: string }>,
@@ -1364,11 +893,8 @@ async function executePlan({
1364
893
  const stepsRun = [];
1365
894
  let doctorOk = true;
1366
895
 
1367
- // Resolve the new bin script lazily and once, on the first spawn phase.
1368
- // Deferring it past the `npm-update` step means (a) a missing `npmUpdate`
1369
- // seam surfaces its own clear error first, and (b) resolution reflects the
1370
- // just-installed package. The `mandrel` package directory is version-stable,
1371
- // so resolving after the in-place bump yields the same directory either way.
896
+ // Resolved lazily after `npm-update`, so a missing npmUpdate seam reports
897
+ // its own error first.
1372
898
  let binPath;
1373
899
  const binScript = () => {
1374
900
  if (binPath === undefined) binPath = resolveBinScript(projectRoot);
@@ -1377,8 +903,6 @@ async function executePlan({
1377
903
 
1378
904
  for (const step of steps) {
1379
905
  if (step.kind === 'npm-update') {
1380
- // Bump the dependency. The lockfile change is left STAGED on disk; this
1381
- // module never commits.
1382
906
  if (typeof npmUpdate !== 'function') {
1383
907
  throw new Error(
1384
908
  'mandrel update: npmUpdate seam is required to bump the dependency',
@@ -1390,9 +914,6 @@ async function executePlan({
1390
914
  continue;
1391
915
  }
1392
916
 
1393
- // Both 'spawn' and 'doctor' kinds run a post-install phase from the
1394
- // newly-installed binary (the Story #4034 re-exec boundary), so the new
1395
- // package's module code — not the old loaded module — executes.
1396
917
  // eslint-disable-next-line no-await-in-loop
1397
918
  const result = await spawnPhase(step.phase, step.args, {
1398
919
  binPath: binScript(),
@@ -1403,18 +924,12 @@ async function executePlan({
1403
924
  stepsRun.push(step.label);
1404
925
 
1405
926
  if (step.kind === 'doctor') {
1406
- // Doctor failure is SOFT: record it, keep going to surface the changelog,
1407
- // then map to exit(1) + doctor-failed by the caller.
1408
927
  doctorOk = result.ok;
1409
928
  } else if (!result.ok) {
1410
- // sync / sync-commands / migrate failures are FATAL.
1411
929
  throw new Error(step.failMessage);
1412
930
  }
1413
931
  }
1414
932
 
1415
- // Surface the target changelog (best-effort; optional seam). Runs even when
1416
- // doctor failed, so the operator still sees the changelog for the version
1417
- // that landed on disk.
1418
933
  if (typeof surfaceChangelog === 'function') {
1419
934
  await surfaceChangelog(target);
1420
935
  }
@@ -1431,14 +946,6 @@ async function executePlan({
1431
946
  }
1432
947
 
1433
948
  /**
1434
- * Run the `mandrel update` orchestration cycle.
1435
- *
1436
- * The cycle is split into a pure decision (`planUpdate`) and a side-effecting
1437
- * shell (this function + `executePlan`). `runUpdate` resolves the inputs
1438
- * (current / target / drift) through the injectable seams, calls `planUpdate`
1439
- * to select the action and its ordered phase plan, then drives the plan through
1440
- * the `spawnPhase` / `write` / `exit` shell.
1441
- *
1442
949
  * @param {{
1443
950
  * argv?: string[],
1444
951
  * currentVersion?: string | (() => string),
@@ -1495,9 +1002,7 @@ export async function runUpdate({
1495
1002
  }
1496
1003
  const target = String(await resolveTargetVersion());
1497
1004
 
1498
- // Drift only matters when the installed version is already the newest. Probe
1499
- // it solely on that branch so a real version bump never calls the drift seam
1500
- // (Story #4065).
1005
+ // Probe drift only when already newest; a real bump never calls the seam.
1501
1006
  const hasDrift =
1502
1007
  compareVersions(target, current) <= 0
1503
1008
  ? await resolveDrift(checkDrift)
@@ -1505,7 +1010,6 @@ export async function runUpdate({
1505
1010
 
1506
1011
  const plan = planUpdate({ current, target, dryRun, hasDrift });
1507
1012
 
1508
- // --- up-to-date: true no-op ----------------------------------------------
1509
1013
  if (plan.action === 'up-to-date') {
1510
1014
  write(`✅ Already up to date (v${current} is the newest version).\n`);
1511
1015
  return {
@@ -1518,7 +1022,6 @@ export async function runUpdate({
1518
1022
  };
1519
1023
  }
1520
1024
 
1521
- // --- dry-run: print the plan, execute nothing -----------------------------
1522
1025
  if (plan.action === 'dry-run') {
1523
1026
  if (plan.variant === 'drift-heal') {
1524
1027
  write(
@@ -1551,7 +1054,6 @@ export async function runUpdate({
1551
1054
  };
1552
1055
  }
1553
1056
 
1554
- // --- resynced / updated: execute the phase plan ---------------------------
1555
1057
  const projectRoot = cwd();
1556
1058
 
1557
1059
  if (plan.action === 'resynced') {
@@ -1577,10 +1079,7 @@ export async function runUpdate({
1577
1079
  });
1578
1080
 
1579
1081
  if (plan.action === 'resynced') {
1580
- // The heal re-materializes `.agents/` and stops — no dependency is bumped,
1581
- // so the payload IS the diff. A consumer that tracks that tree used to be
1582
- // told nothing at all about it (Story #5364), the same silent-diff hazard
1583
- // the staging report exists to close, one branch earlier.
1082
+ // No dependency is bumped here, so the re-materialized payload IS the diff.
1584
1083
  const payloadLine = resolveStagingReport({
1585
1084
  gitStatus,
1586
1085
  detectLockfile,
@@ -1601,7 +1100,6 @@ export async function runUpdate({
1601
1100
  };
1602
1101
  }
1603
1102
 
1604
- // plan.action === 'updated'
1605
1103
  if (!doctorOk) {
1606
1104
  return {
1607
1105
  ok: false,
@@ -1613,9 +1111,6 @@ export async function runUpdate({
1613
1111
  };
1614
1112
  }
1615
1113
 
1616
- // Report the real index state rather than asserting one (Story #5339). Both
1617
- // seams are read-only and failure-tolerant: a probe that cannot answer yields
1618
- // the neutral line, and the update still reports success with a zero exit.
1619
1114
  write(
1620
1115
  `✅ Updated to v${target}. ${resolveStagingReport({ gitStatus, detectLockfile, projectRoot })}\n`,
1621
1116
  );
@@ -1630,46 +1125,10 @@ export async function runUpdate({
1630
1125
  }
1631
1126
 
1632
1127
  /**
1633
- * Default export consumed by `bin/mandrel.js`.
1634
- *
1635
- * Wires the production-default seams that `runUpdate` leaves injectable:
1636
- * - `resolveTargetVersion` always probes the registry via `isStale` with
1637
- * `bypassCache: true` — the 24h cache is overridden for explicit update
1638
- * calls so the resolved version is always fresh (Story #4046 A1b). The
1639
- * cache is still written so the `version-current` doctor advisory reads
1640
- * a current baseline after the upgrade.
1641
- * - `npmUpdate` runs the install command — auto-detected from the project
1642
- * lockfile (`pnpm`/`yarn`/`npm`), or the `--install-cmd` override —
1643
- * through the shared `runInstallCommand` helper — no git mutation; the
1644
- * resulting index state is reported by the staging probe, not asserted.
1645
- * - `spawnPhase` is wired to `defaultSpawnPhase`, which spawns each
1646
- * post-install phase (sync, sync-commands, migrate, doctor) as
1647
- * `node <packageRoot>/bin/mandrel.js …` (Story #4613 — the resolved bin
1648
- * script, not the `node_modules/.bin` shim). This is the
1649
- * Story #4034 fix: the new bin loads the new package's module code and
1650
- * resolves paths against the new install dir, so these phases can never
1651
- * observe the old payload.
1652
- * - `surfaceChangelog` prints the relevant `docs/CHANGELOG.md` section(s)
1653
- * for the applied range. Reads from the packaged file first; falls back to
1654
- * a GitHub raw-content fetch via the injectable `fetchChangelog` seam when
1655
- * the packaged file is absent; emits an actionable link to the GitHub
1656
- * Releases page when both sources fail (Story #4035).
1657
- *
1658
- * Every seam stays injectable on `runUpdate`; these are merely the
1659
- * no-seam-provided fallbacks, so the existing seam-driven tests stay green.
1660
- * `--dry-run` / `--install-cmd` are parsed from `argv` by
1661
- * `runUpdate` itself.
1128
+ * Entry point for `bin/mandrel.js`: wires the production seams. `deps` exposes
1129
+ * the process boundaries for tests only; it is not public contract.
1662
1130
  *
1663
- * The second `deps` argument exposes the **process boundaries** the production
1664
- * defaults shell out across (`versionRunner` = `npm view`, `runInstall` =
1665
- * the install spawn, `spawnFn` = the phase-spawn boundary) plus `fs` /
1666
- * `cachePath` / `now`, so the entrypoint can be driven end-to-end with the
1667
- * network/npm boundary stubbed and no real I/O.
1668
- * `bin/mandrel.js` calls `run(argv)` with no `deps`, getting the production
1669
- * wiring; tests pass fakes. The `deps` surface is NOT part of the public
1670
- * subcommand contract — `bin/mandrel.js` only ever supplies `argv`.
1671
- *
1672
- * @param {string[]} argv - Subcommand arguments (after `mandrel update`).
1131
+ * @param {string[]} argv
1673
1132
  * @param {{
1674
1133
  * currentVersion?: string,
1675
1134
  * cachePath?: string,
@@ -1717,18 +1176,9 @@ export default async function run(argv = [], deps = {}) {
1717
1176
 
1718
1177
  const cwdFn = typeof cwd === 'function' ? cwd : () => process.cwd();
1719
1178
 
1720
- // Story #4525/#4530: prefer the consumer's declared dependency pin over
1721
- // the pre-#4525 self-referential read — see resolveCurrentVersionForUpdate.
1722
1179
  const current =
1723
1180
  deps.currentVersion ?? resolveCurrentVersionForUpdate(cwdFn(), fs);
1724
1181
 
1725
- // The production spawnPhase: spawn each post-install phase as
1726
- // `node <packageRoot>/bin/mandrel.js …` (the newly-installed bin script,
1727
- // resolved layout-agnostically per Story #4613 — not the node_modules/.bin
1728
- // shim). This is the sole post-install execution path (No-Shim — Story #4182
1729
- // retired the in-process runSync/runMigrations/runDoctor seam set). spawnFn
1730
- // is injectable so tests can stub the spawn boundary without running a real
1731
- // child process.
1732
1182
  const productionSpawnPhase = (phase, args, opts) =>
1733
1183
  defaultSpawnPhase(phase, args, {
1734
1184
  ...opts,
@@ -1738,8 +1188,7 @@ export default async function run(argv = [], deps = {}) {
1738
1188
  await runUpdateImpl({
1739
1189
  argv,
1740
1190
  currentVersion: current,
1741
- // Always bypass the 24h cache on an explicit `mandrel update` so the
1742
- // resolved target is fresh from the registry (Story #4046 A1b).
1191
+ // An explicit update always bypasses the 24h cache.
1743
1192
  resolveTargetVersion: () =>
1744
1193
  defaultResolveTargetVersion({
1745
1194
  cachePath:
@@ -1757,9 +1206,6 @@ export default async function run(argv = [], deps = {}) {
1757
1206
  fs,
1758
1207
  }),
1759
1208
  ...(checkDrift ? { checkDrift } : {}),
1760
- // Story #5339 — the staging probe. Production leaves both undefined so
1761
- // runUpdate applies the real read-only git seam and the lockfile detection
1762
- // that mirrors the install seam's; tests stub them for a fake tree.
1763
1209
  ...(gitStatus ? { gitStatus } : {}),
1764
1210
  ...(detectLockfile
1765
1211
  ? { detectLockfile }
@@ -1778,8 +1224,6 @@ export default async function run(argv = [], deps = {}) {
1778
1224
  writeErr,
1779
1225
  exit,
1780
1226
  cwd: cwdFn,
1781
- // Pass through undefined in production so runUpdate applies its default
1782
- // resolver (resolveNewBinScriptPath); tests inject a stub for a fake root.
1783
1227
  resolveBinScript,
1784
1228
  });
1785
1229
  }