mandrel 2.36.0 → 2.38.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 (225) hide show
  1. package/.agents/README.md +16 -16
  2. package/.agents/agents/plan-critic.md +1 -1
  3. package/.agents/docs/SDLC.md +40 -40
  4. package/.agents/docs/configuration.md +41 -16
  5. package/.agents/docs/execution-reference.md +3 -3
  6. package/.agents/docs/quality-gates.md +2 -2
  7. package/.agents/docs/workflows.md +6 -6
  8. package/.agents/instructions.md +5 -5
  9. package/.agents/rules/changelog-style.md +3 -3
  10. package/.agents/rules/ci-remediation.md +1 -1
  11. package/.agents/rules/git-conventions-reference.md +4 -4
  12. package/.agents/rules/testing-standards.md +56 -0
  13. package/.agents/schemas/agentrc.schema.json +9 -9
  14. package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
  15. package/.agents/schemas/validation-evidence.schema.json +1 -1
  16. package/.agents/scripts/acceptance-eval.js +1 -1
  17. package/.agents/scripts/apply-quality-bootstrap.js +1 -1
  18. package/.agents/scripts/audit-to-stories.js +2 -2
  19. package/.agents/scripts/boot-sweep.js +7 -1
  20. package/.agents/scripts/check-context-budget.js +195 -103
  21. package/.agents/scripts/check-cyclomatic.js +112 -42
  22. package/.agents/scripts/check-doc-links.js +32 -4
  23. package/.agents/scripts/check-generated-validator.js +202 -0
  24. package/.agents/scripts/check-lifecycle-lint.js +2 -68
  25. package/.agents/scripts/check-schema-references.js +2 -28
  26. package/.agents/scripts/check-test-temp-hygiene.js +1 -1
  27. package/.agents/scripts/check-workflow-cli-lint.js +1 -1
  28. package/.agents/scripts/deliver-light.js +10 -10
  29. package/.agents/scripts/deliver-recover.js +1 -1
  30. package/.agents/scripts/drain-pending-cleanup.js +19 -2
  31. package/.agents/scripts/evidence-gate.js +1 -1
  32. package/.agents/scripts/generate-workflows-doc.js +1 -1
  33. package/.agents/scripts/lib/audit-suite/selector.js +1 -1
  34. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +1 -1
  35. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +1 -1
  36. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +5 -5
  37. package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +1 -1
  38. package/.agents/scripts/lib/baselines/components.js +32 -2
  39. package/.agents/scripts/lib/baselines/env-overrides.js +1 -1
  40. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  41. package/.agents/scripts/lib/bdd-scenario-budget.js +1 -1
  42. package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
  43. package/.agents/scripts/lib/bdd-step-index.js +1 -1
  44. package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -2
  45. package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
  46. package/.agents/scripts/lib/bootstrap/install-ledger.js +1 -1
  47. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +1 -1
  48. package/.agents/scripts/lib/bootstrap/prompt.js +1 -1
  49. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +2 -2
  50. package/.agents/scripts/lib/checks/core-bare-clean.js +1 -1
  51. package/.agents/scripts/lib/checks/loop-health.js +1 -1
  52. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +1 -1
  53. package/.agents/scripts/lib/cli-args.js +1 -3
  54. package/.agents/scripts/lib/close-validation/gates.js +1 -1
  55. package/.agents/scripts/lib/close-validation/process.js +1 -1
  56. package/.agents/scripts/lib/command-header.js +1 -1
  57. package/.agents/scripts/lib/config/delivery-routing.js +1 -1
  58. package/.agents/scripts/lib/config/explain.js +1 -1
  59. package/.agents/scripts/lib/config/runners.js +2 -2
  60. package/.agents/scripts/lib/config/runtime.js +1 -1
  61. package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
  62. package/.agents/scripts/lib/config/temp-paths.js +2 -2
  63. package/.agents/scripts/lib/config-settings-schema-delivery.js +3 -3
  64. package/.agents/scripts/lib/config-settings-schema-quality.js +6 -6
  65. package/.agents/scripts/lib/config-settings-schema.js +49 -9
  66. package/.agents/scripts/lib/coverage-baseline.js +2 -2
  67. package/.agents/scripts/lib/cpu-pool.js +90 -10
  68. package/.agents/scripts/lib/crap-utils.js +6 -2
  69. package/.agents/scripts/lib/cyclomatic-ceiling.js +28 -6
  70. package/.agents/scripts/lib/cyclomatic-scope.js +144 -0
  71. package/.agents/scripts/lib/dependency-version.js +86 -0
  72. package/.agents/scripts/lib/duplicate-search.js +2 -2
  73. package/.agents/scripts/lib/errors/index.js +1 -1
  74. package/.agents/scripts/lib/feedback-loop/graduator-core.js +2 -2
  75. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +5 -5
  76. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +2 -2
  77. package/.agents/scripts/lib/findings/classify-finding.js +1 -1
  78. package/.agents/scripts/lib/findings/promote-finding.js +12 -12
  79. package/.agents/scripts/lib/findings/route-finding.js +2 -2
  80. package/.agents/scripts/lib/generated/agentrc-validator.js +16 -0
  81. package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
  82. package/.agents/scripts/lib/install-cmd-parser.js +1 -1
  83. package/.agents/scripts/lib/knip-entry-sync.js +1 -57
  84. package/.agents/scripts/lib/label-constants.js +2 -2
  85. package/.agents/scripts/lib/label-taxonomy.js +1 -1
  86. package/.agents/scripts/lib/maintainability-utils.js +108 -10
  87. package/.agents/scripts/lib/observability/source-classifier.js +1 -0
  88. package/.agents/scripts/lib/onboard/init-tail.js +4 -4
  89. package/.agents/scripts/lib/onboard/scaffold-docs.js +2 -2
  90. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
  91. package/.agents/scripts/lib/orchestration/code-review.js +4 -4
  92. package/.agents/scripts/lib/orchestration/complexity-gate.js +5 -5
  93. package/.agents/scripts/lib/orchestration/deliver-recover.js +3 -3
  94. package/.agents/scripts/lib/orchestration/docs-digest.js +3 -3
  95. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +2 -2
  96. package/.agents/scripts/lib/orchestration/light-backstop.js +1 -1
  97. package/.agents/scripts/lib/orchestration/light-escalation.js +6 -6
  98. package/.agents/scripts/lib/orchestration/light-suitability.js +19 -19
  99. package/.agents/scripts/lib/orchestration/plan-context.js +4 -4
  100. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +2 -2
  101. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +2 -2
  102. package/.agents/scripts/lib/orchestration/plan-metrics.js +1 -1
  103. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +1 -1
  104. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +3 -3
  105. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +6 -6
  106. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +86 -25
  107. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +3 -3
  108. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +6 -6
  109. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +7 -7
  110. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -1
  111. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +3 -3
  112. package/.agents/scripts/lib/orchestration/pr-watch.js +1 -30
  113. package/.agents/scripts/lib/orchestration/reassert-status-column.js +2 -2
  114. package/.agents/scripts/lib/orchestration/remote-verifier.js +2 -2
  115. package/.agents/scripts/lib/orchestration/resolve-stories.js +4 -4
  116. package/.agents/scripts/lib/orchestration/review-providers/codex.js +3 -3
  117. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +1 -1
  118. package/.agents/scripts/lib/orchestration/review-providers/native.js +9 -2
  119. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +2 -2
  120. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -3
  121. package/.agents/scripts/lib/orchestration/run-epilogue.js +2 -2
  122. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +2 -2
  123. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +1 -1
  124. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +5 -1
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +52 -1
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -1
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +3 -3
  128. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -2
  129. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  130. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +2 -5
  131. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +14 -12
  132. package/.agents/scripts/lib/orchestration/story-follow-ups.js +1 -1
  133. package/.agents/scripts/lib/orchestration/ticket-lease.js +34 -9
  134. package/.agents/scripts/lib/orchestration/ticket-validator.js +1 -1
  135. package/.agents/scripts/lib/orchestration/ticketing/reads.js +6 -4
  136. package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
  137. package/.agents/scripts/lib/preflight-runner.js +3 -3
  138. package/.agents/scripts/lib/qa/qa-session.js +1 -1
  139. package/.agents/scripts/lib/runtime-deps/manifest.js +1 -7
  140. package/.agents/scripts/lib/runtime-deps/scan-imports.js +3 -58
  141. package/.agents/scripts/lib/signals/schema.js +1 -1
  142. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +284 -25
  143. package/.agents/scripts/lib/skills/walk-skill-files.js +1 -1
  144. package/.agents/scripts/lib/source-text/strip-js-comments.js +115 -0
  145. package/.agents/scripts/lib/story-adjacency.js +2 -2
  146. package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
  147. package/.agents/scripts/lib/test-isolate/runner.js +3 -3
  148. package/.agents/scripts/lib/test-runner-contract.js +14 -6
  149. package/.agents/scripts/lib/test-tiers.js +135 -18
  150. package/.agents/scripts/lib/ticket-body-sections.js +1 -1
  151. package/.agents/scripts/lib/transpile.js +16 -3
  152. package/.agents/scripts/lib/util/concurrent-map.js +1 -1
  153. package/.agents/scripts/lib/util/parse-id-list.js +1 -1
  154. package/.agents/scripts/lib/wave-runner/live-probe.js +4 -4
  155. package/.agents/scripts/lib/wave-runner/ready-set.js +1 -1
  156. package/.agents/scripts/lib/workflow-closure.js +1 -1
  157. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +170 -32
  158. package/.agents/scripts/lib/worktree/node-modules-strategy.js +2 -5
  159. package/.agents/scripts/lint-issue-body.js +3 -3
  160. package/.agents/scripts/plan-context.js +4 -4
  161. package/.agents/scripts/plan-critics.js +2 -2
  162. package/.agents/scripts/plan-persist.js +2 -2
  163. package/.agents/scripts/plan-run-epilogue.js +2 -2
  164. package/.agents/scripts/pr-watch-with-update.js +305 -137
  165. package/.agents/scripts/providers/github/board-add.js +1 -1
  166. package/.agents/scripts/providers/github/errors.js +1 -1
  167. package/.agents/scripts/providers/github/tickets.js +114 -21
  168. package/.agents/scripts/quality-preview.js +162 -70
  169. package/.agents/scripts/resolve-stories.js +3 -3
  170. package/.agents/scripts/resync-status-column.js +1 -1
  171. package/.agents/scripts/run-lint.js +1 -1
  172. package/.agents/scripts/run-test-profile.js +8 -5
  173. package/.agents/scripts/run-tests.js +79 -14
  174. package/.agents/scripts/single-story-close.js +2 -4
  175. package/.agents/scripts/single-story-init.js +10 -6
  176. package/.agents/scripts/stories-wave-tick.js +8 -8
  177. package/.agents/skills/core/idea-refinement/SKILL.md +5 -5
  178. package/.agents/skills/core/scope-triage/SKILL.md +3 -3
  179. package/.agents/skills/skills.index.json +1 -1
  180. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -1
  181. package/.agents/workflows/audit-data-model.md +1 -1
  182. package/.agents/workflows/audit-documentation.md +2 -2
  183. package/.agents/workflows/audit-to-stories.md +11 -11
  184. package/.agents/workflows/git-deliver.md +4 -4
  185. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  186. package/.agents/workflows/helpers/audit-lens-core.md +1 -1
  187. package/.agents/workflows/helpers/code-review.md +4 -4
  188. package/.agents/workflows/helpers/deliver-light.md +26 -26
  189. package/.agents/workflows/helpers/deliver-reference.md +15 -7
  190. package/.agents/workflows/helpers/deliver-story-reference.md +9 -4
  191. package/.agents/workflows/helpers/deliver-story.md +6 -7
  192. package/.agents/workflows/helpers/diagnose.md +2 -2
  193. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  194. package/.agents/workflows/helpers/plan-reference.md +9 -9
  195. package/.agents/workflows/helpers/qa-core.md +8 -8
  196. package/.agents/workflows/helpers/worktree-lifecycle.md +6 -6
  197. package/.agents/workflows/{deliver.md → mandrel-deliver.md} +15 -19
  198. package/.agents/workflows/{plan.md → mandrel-plan.md} +9 -9
  199. package/.agents/workflows/memory-consolidate.md +4 -4
  200. package/.agents/workflows/prototype.md +3 -3
  201. package/.agents/workflows/qa-assist.md +12 -12
  202. package/.agents/workflows/qa-explore.md +6 -6
  203. package/.agents/workflows/qa-run.md +1 -1
  204. package/README.md +7 -7
  205. package/docs/CHANGELOG.md +43 -0
  206. package/lib/cli/doctor.js +1 -1
  207. package/lib/cli/init.js +1 -1
  208. package/lib/cli/migrate.js +100 -94
  209. package/lib/migrations/helpers/retire-agentrc-key.js +200 -0
  210. package/lib/migrations/index.js +32 -33
  211. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +21 -80
  212. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +15 -87
  213. package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +13 -71
  214. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +1 -1
  215. package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +13 -101
  216. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +13 -100
  217. package/package.json +7 -3
  218. package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +0 -32
  219. package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +0 -37
  220. package/.agents/scripts/lib/cli/parse-numeric.js +0 -60
  221. package/.agents/scripts/lib/close-validation/telemetry.js +0 -79
  222. package/.agents/scripts/lib/orchestration/label-transitions.js +0 -44
  223. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +0 -147
  224. package/.agents/scripts/lib/orchestration/phase-runner.js +0 -88
  225. package/.agents/scripts/lib/orchestration/recut.js +0 -56
@@ -2,7 +2,7 @@
2
2
  * phases/authoring-context.js — emit-context phase.
3
3
  *
4
4
  * Builds the authoring context the host LLM (or the
5
- * `/plan` author step) needs to write the Tech Spec.
5
+ * `/mandrel-plan` author step) needs to write the Tech Spec.
6
6
  * Returns a plain JSON-serialisable object; never hits the network beyond
7
7
  * the provider call needed to load the Epic.
8
8
  */
@@ -31,12 +31,12 @@ import { buildMemoryPoolAdvisory } from './memory-pool-advisory.js';
31
31
  * pull-on-demand, mirroring the Story #4324 delivery-children cutover).
32
32
  *
33
33
  * Ensures a docs digest exists at the **same** per-Epic temp path the
34
- * `/deliver` story sub-agents already consume
34
+ * `/mandrel-deliver` story sub-agents already consume
35
35
  * (`<tempRoot>/epic-<seedIssueId>/docs-digest.md`) via the shared
36
36
  * `ensureDocsDigest` export in `docs-digest.js` — one generator, one file,
37
37
  * reused across the planning and delivery surfaces for the same Epic. The
38
38
  * envelope carries only the digest path, not embedded doc content; the
39
- * planner (host LLM driving the `/plan` author step) reads the digest
39
+ * planner (host LLM driving the `/mandrel-plan` author step) reads the digest
40
40
  * and pulls a full file/section on demand when it bears on the decision.
41
41
  *
42
42
  * Returns `null` — a silent no-op — when `project.docsContextFiles` is not
@@ -80,7 +80,7 @@ async function buildPlanningDocsContext({ seedIssueId, settings, cwd }) {
80
80
  * Story #4977 — the scan itself stays uncapped (a faithful `.feature`
81
81
  * index), but the envelope-bound result is truncated to
82
82
  * `BDD_SCENARIOS_BYTE_BUDGET` via `capBddScenarios` so a mature Gherkin
83
- * corpus cannot alone consume the `/plan` context-envelope ceiling. Callers
83
+ * corpus cannot alone consume the `/mandrel-plan` context-envelope ceiling. Callers
84
84
  * needing the raw count read `totalScenarios` vs `includedScenarios`.
85
85
  *
86
86
  * @returns {ReturnType<typeof capBddScenarios>}
@@ -97,7 +97,7 @@ function scanBddScenariosBestEffort() {
97
97
 
98
98
  /**
99
99
  * Build the authoring context the host LLM (or the
100
- * `/plan` author step) needs to write the Tech Spec.
100
+ * `/mandrel-plan` author step) needs to write the Tech Spec.
101
101
  *
102
102
  * `docsContext` is digest-first (Story #4433): a pointer at the per-Epic
103
103
  * docs digest rather than embedded doc content, `null` when
@@ -133,7 +133,7 @@ export async function buildAuthoringContext(
133
133
  const githubCfg = opts.github ?? null;
134
134
 
135
135
  // Story #4952 — these five gathers share no data, so they run under bounded
136
- // concurrency instead of five sequential awaits on the interactive `/plan`
136
+ // concurrency instead of five sequential awaits on the interactive `/mandrel-plan`
137
137
  // path. `concurrentMap` preserves input order, so the destructuring is
138
138
  // positional and the produced context is identical to the serial build:
139
139
  //
@@ -149,7 +149,7 @@ export async function buildAuthoringContext(
149
149
  // memory corpus is delivery retrospectives whose subject IS a delivered
150
150
  // Story — and its directory (`~/.claude/projects/<repo>/memory/`) never
151
151
  // resolved, because harness project dirs are cwd-slugs. This renders no
152
- // per-entry verdict at all: it stats and counts, and the `/plan` spine
152
+ // per-entry verdict at all: it stats and counts, and the `/mandrel-plan` spine
153
153
  // surfaces `recommend` at Gate #1. Filesystem-only and total;
154
154
  // 5. Story #2554 — open meta feedback issues, so retro signals are routed
155
155
  // into durable substrates rather than lost in chat. Best-effort:
@@ -1,5 +1,5 @@
1
1
  /**
2
- * decomposer-context.js — system-prompt builder for the `/plan` authoring
2
+ * decomposer-context.js — system-prompt builder for the `/mandrel-plan` authoring
3
3
  * pass (Story #2466; v2 keeps the prompt helper, drops the Epic-fetch
4
4
  * decomposition envelope).
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * memory-pool-advisory.js — the `/plan` Phase 0 memory-hygiene advisory.
2
+ * memory-pool-advisory.js — the `/mandrel-plan` Phase 0 memory-hygiene advisory.
3
3
  *
4
4
  * Replaces the retired memory-freshness pre-flight (Story #2557 / #4414) in
5
5
  * the same slot, fixing both of that design's defects:
@@ -10,7 +10,7 @@
10
10
  * replaced by `-` — so the old path never resolved in any consumer and
11
11
  * the scan was a silent no-op everywhere.
12
12
  * 2. **A named consumer.** The retired scanner emitted a per-entry staleness
13
- * verdict nothing read. This emits one advisory the `/plan` spine
13
+ * verdict nothing read. This emits one advisory the `/mandrel-plan` spine
14
14
  * surfaces at Gate #1, recommending `/memory-consolidate`.
15
15
  *
16
16
  * It also drops the semantic that made the old scanner unfixable: it renders
@@ -131,7 +131,7 @@ function countEntries({ poolDir, fsImpl }) {
131
131
  * Build the `memoryPoolAdvisory` envelope field.
132
132
  *
133
133
  * Advisory only — it carries **no routing authority**, mirroring
134
- * `deliverLightSuggestion`. The `/plan` spine surfaces `recommend` at Gate #1;
134
+ * `deliverLightSuggestion`. The `/mandrel-plan` spine surfaces `recommend` at Gate #1;
135
135
  * nothing auto-runs, and nothing here mutates the operator's memory store.
136
136
  *
137
137
  * @param {object} [opts]
@@ -50,35 +50,6 @@ import { applyBehindUpdate } from './behind-recovery.js';
50
50
  * values collapse to `'skipped'` so any future GitHub state we haven't
51
51
  * enumerated still maps into the vocabulary.
52
52
  */
53
- /**
54
- * The raw check-state tokens `normalizeCheckState` recognizes (lowercased).
55
- * A token absent from this set is one we have NOT enumerated — the watch
56
- * path collapses it to `'skipped'` (validate-anything), but a fail-closed
57
- * consumer (the auto-merge arming probe) must treat it as unknown-therefore-
58
- * blocking rather than trust the `'skipped'` collapse. Exported so that
59
- * stricter consumer lives here as the single vocabulary owner.
60
- */
61
- export const RECOGNIZED_CHECK_STATES = Object.freeze(
62
- new Set([
63
- '',
64
- 'pending',
65
- 'queued',
66
- 'in_progress',
67
- 'requested',
68
- 'waiting',
69
- 'success',
70
- 'completed',
71
- 'failure',
72
- 'startup_failure',
73
- 'neutral',
74
- 'cancelled',
75
- 'timed_out',
76
- 'action_required',
77
- 'stale',
78
- 'skipped',
79
- ]),
80
- );
81
-
82
53
  export function normalizeCheckState(raw) {
83
54
  const v = String(raw ?? '')
84
55
  .trim()
@@ -325,7 +296,7 @@ export function allTerminal(outcomes) {
325
296
  * red check): a `'still-running'` map means "re-arm the watch / hand off
326
297
  * to `/loop`," never "the change is broken."
327
298
  */
328
- export const STILL_RUNNING = 'still-running';
299
+ const STILL_RUNNING = 'still-running';
329
300
 
330
301
  /**
331
302
  * Promote any leftover `'pending'` outcomes to the schema-valid
@@ -38,14 +38,14 @@ import { ColumnSync, columnForLabels } from './column-sync.js';
38
38
  * long enough to outlast the typical GitHub built-in workflow fire
39
39
  * timing without ballooning the close-path wall clock.
40
40
  */
41
- export const DEFAULT_POLL_ATTEMPTS = 4;
41
+ const DEFAULT_POLL_ATTEMPTS = 4;
42
42
 
43
43
  /**
44
44
  * Default delay between drift checks (ms). Five seconds is a sweet
45
45
  * spot: short enough that the operator doesn't perceive the close
46
46
  * path as hung, long enough that one re-fire usually wins the race.
47
47
  */
48
- export const DEFAULT_POLL_DELAY_MS = 5000;
48
+ const DEFAULT_POLL_DELAY_MS = 5000;
49
49
 
50
50
  /**
51
51
  * Production sleep function. Tests inject `sleepFn: () => Promise.resolve()`
@@ -3,11 +3,11 @@
3
3
  * remote-verifier.js — deterministic "is there a live, pushable remote?"
4
4
  * evidence for the delivery entry seams. Issue #4483.
5
5
  *
6
- * `/deliver` could silently shortcut the entire orchestration — building
6
+ * `/mandrel-deliver` could silently shortcut the entire orchestration — building
7
7
  * the delivery inline and committing to local `main` without pushing —
8
8
  * when the driving agent *perceived* the environment had no live GitHub
9
9
  * remote. The judgment was vibes, not fact. This module gives the entry
10
- * seams (`single-story-init.js` for v2 `/deliver`) a verified
10
+ * seams (`single-story-init.js` for v2 `/mandrel-deliver`) a verified
11
11
  * probe result to record in their envelopes so the workflow can branch on
12
12
  * `remoteVerified: true|false` deterministically: use the remote, or
13
13
  * transition to `agent::blocked` quoting the probe output — never a
@@ -2,7 +2,7 @@
2
2
  * lib/orchestration/resolve-stories.js — resolve a set of Story ids into the
3
3
  * `{ stories, dag, done }` envelope the delivery scheduler consumes.
4
4
  *
5
- * This is the ONE resolution step for `/deliver`. It generalizes the
5
+ * This is the ONE resolution step for `/mandrel-deliver`. It generalizes the
6
6
  * envelope shaping proven by the retired `resolve-plan-run.js` and fixes the
7
7
  * two defects that shipped with it:
8
8
  *
@@ -77,7 +77,7 @@ export function toStoryRecord(issue, requestedId) {
77
77
  if (!labels.includes(TYPE_LABELS.STORY)) {
78
78
  throw new Error(
79
79
  `[resolve-stories] Issue #${id} is not a Story (labels: ${labels.join(', ') || 'none'}). ` +
80
- `/deliver accepts ${TYPE_LABELS.STORY} tickets only — close it or re-plan it as a v2 Story.`,
80
+ `/mandrel-deliver accepts ${TYPE_LABELS.STORY} tickets only — close it or re-plan it as a v2 Story.`,
81
81
  );
82
82
  }
83
83
  const body = String(issue?.body ?? '');
@@ -351,7 +351,7 @@ export function buildStoriesEnvelope({
351
351
  return {
352
352
  kind: 'stories',
353
353
  // `dispatchMode` (Story #4722, #4736, #4829): the resolver reports the
354
- // per-Story execution mode so `/deliver` reads one field — `inline` (run
354
+ // per-Story execution mode so `/mandrel-deliver` reads one field — `inline` (run
355
355
  // deliver-story in the router's own session: no story-worker /
356
356
  // acceptance-critic sub-agent boots) or `subagent` (the conservative
357
357
  // default). Model-side fan-out only; close gates are untouched.
@@ -385,7 +385,7 @@ export function buildStoriesEnvelope({
385
385
  /**
386
386
  * Parse and validate the `--ids` list, expanding any `A-B` dash range.
387
387
  *
388
- * A contiguous span is how an operator names a plan run — `/deliver 4922 -
388
+ * A contiguous span is how an operator names a plan run — `/mandrel-deliver 4922 -
389
389
  * 4926` — so the range is expanded here rather than transcribed by the host.
390
390
  * `stories-wave-tick.js --stories` reads through this same function, which is
391
391
  * what keeps the sequencing set identical to the resolved one.
@@ -57,7 +57,7 @@ export const CODEX_REMEDIATIONS = Object.freeze({
57
57
  *
58
58
  * Exported so tests can extend the list without monkey-patching `os`.
59
59
  */
60
- export const DEFAULT_PLUGIN_MARKERS = Object.freeze([
60
+ const DEFAULT_PLUGIN_MARKERS = Object.freeze([
61
61
  path.join(os.homedir(), '.claude', 'plugins', 'codex-plugin-cc'),
62
62
  path.join(os.homedir(), '.claude', 'plugins', 'openai', 'codex-plugin-cc'),
63
63
  ]);
@@ -194,7 +194,7 @@ export function parseCodexFindings(rawStdout) {
194
194
  * @param {{ baseRef: string, headRef: string, depth?: import('./types.js').ReviewDepth }} args
195
195
  * @returns {string}
196
196
  */
197
- export function buildCodexReviewPrompt({ baseRef, headRef, depth }) {
197
+ function buildCodexReviewPrompt({ baseRef, headRef, depth }) {
198
198
  return (
199
199
  `/codex:review --base ${baseRef} --head ${headRef} --wait ` +
200
200
  `${renderDepthDirective(depth)}`
@@ -214,7 +214,7 @@ export function buildCodexReviewPrompt({ baseRef, headRef, depth }) {
214
214
  * @param {{ baseRef: string, headRef: string, depth?: import('./types.js').ReviewDepth }} args
215
215
  * @returns {{ status: number, stdout: string, stderr: string }}
216
216
  */
217
- export function defaultInvokeCodexReview({ baseRef, headRef, depth }) {
217
+ function defaultInvokeCodexReview({ baseRef, headRef, depth }) {
218
218
  const cliArgs = [
219
219
  '--print',
220
220
  buildCodexReviewPrompt({ baseRef, headRef, depth }),
@@ -102,7 +102,7 @@ export function renderFinding(finding) {
102
102
  * @param {ReadonlyArray<string>} messages
103
103
  * @returns {string[]} lines to append (empty when no messages)
104
104
  */
105
- export function renderManualPromptsSection(messages) {
105
+ function renderManualPromptsSection(messages) {
106
106
  const filtered = Array.isArray(messages)
107
107
  ? messages.filter((m) => typeof m === 'string' && m.trim().length > 0)
108
108
  : [];
@@ -267,8 +267,14 @@ function isJsMaintainabilityFile(relPath) {
267
267
  * `reportFn` forces the serial path (the injected scorer cannot cross the
268
268
  * worker boundary).
269
269
  *
270
+ * `serialThreshold` overrides the pool-vs-serial cutover for one call. It is
271
+ * the seam the parity test uses to drive the pooled branch: Story #5109
272
+ * retuned `POOL_SERIAL_THRESHOLD` from 8 to 256 against measured data, and a
273
+ * fixture sized to clear the old number would otherwise have gone quietly
274
+ * serial while still claiming to cover the pool.
275
+ *
270
276
  * @param {string[]} changedFiles
271
- * @param {{ reportFn?: Function, classifier?: Function, runOnPoolFn?: typeof runOnPool, headRef?: string|null, gitSpawnFn?: typeof gitSpawn, readHeadSourceFn?: typeof readHeadSource }} [deps]
277
+ * @param {{ reportFn?: Function, classifier?: Function, runOnPoolFn?: typeof runOnPool, headRef?: string|null, gitSpawnFn?: typeof gitSpawn, readHeadSourceFn?: typeof readHeadSource, serialThreshold?: number }} [deps]
272
278
  * @returns {Promise<{ totalFiles: number, jsFiles: number, maintainability: object[], criticalFindings: Finding[], mediumFindings: Finding[] }>}
273
279
  */
274
280
  export async function analyzeChangedFiles(
@@ -280,6 +286,7 @@ export async function analyzeChangedFiles(
280
286
  headRef = null,
281
287
  gitSpawnFn = gitSpawn,
282
288
  readHeadSourceFn = readHeadSource,
289
+ serialThreshold = SERIAL_THRESHOLD,
283
290
  } = {},
284
291
  ) {
285
292
  const results = {
@@ -308,7 +315,7 @@ export async function analyzeChangedFiles(
308
315
  const customReportFn = reportFn != null;
309
316
 
310
317
  // Serial path: small batches, or whenever a caller injects its own scorer.
311
- if (jsFiles.length < SERIAL_THRESHOLD || customReportFn) {
318
+ if (jsFiles.length < serialThreshold || customReportFn) {
312
319
  for (let i = 0; i < jsFiles.length; i += 1) {
313
320
  const relPath = jsFiles[i];
314
321
  const source = sources[i];
@@ -28,7 +28,7 @@
28
28
  *
29
29
  * @type {Readonly<Record<ReviewDepth, string>>}
30
30
  */
31
- export const DEPTH_DIRECTIVES = Object.freeze({
31
+ const DEPTH_DIRECTIVES = Object.freeze({
32
32
  light:
33
33
  'Review depth: LIGHT. Run a single pass focused on spec adherence over the ' +
34
34
  'changed surface — confirm the change matches its stated intent. Reduce the ' +
@@ -54,7 +54,7 @@ export const DEPTH_DIRECTIVES = Object.freeze({
54
54
  * @param {unknown} depth
55
55
  * @returns {ReviewDepth}
56
56
  */
57
- export function normalizeDepth(depth) {
57
+ function normalizeDepth(depth) {
58
58
  return depth === 'light' || depth === 'deep' ? depth : 'standard';
59
59
  }
60
60
 
@@ -59,7 +59,7 @@ export const SECURITY_REVIEW_REMEDIATIONS = Object.freeze({
59
59
  * @param {{ spawnFn?: typeof spawnSync }} [opts]
60
60
  * @returns {boolean}
61
61
  */
62
- export function defaultProbeClaudeCli(opts = {}) {
62
+ function defaultProbeClaudeCli(opts = {}) {
63
63
  const spawnFn = opts.spawnFn ?? spawnSync;
64
64
  try {
65
65
  const result = spawnFn('claude', ['--version'], {
@@ -162,7 +162,7 @@ export function parseSecurityReviewFindings(rawStdout) {
162
162
  * (Story #3937) so a high-risk Epic instructs the model toward a deeper
163
163
  * second-pass review while a low-risk one keeps it light.
164
164
  */
165
- export const SECURITY_REVIEW_INVOKE_PROMPT =
165
+ const SECURITY_REVIEW_INVOKE_PROMPT =
166
166
  'Run /security-review against the diff `{baseRef}`...`{headRef}` ' +
167
167
  'for {scopeLabel} #{ticketId}. {depthDirective} After the review, emit ' +
168
168
  'ONLY a JSON array of findings on stdout with this exact shape:\n\n' +
@@ -208,7 +208,7 @@ export function buildSecurityReviewPrompt(input) {
208
208
  * @param {ReviewInput} input
209
209
  * @returns {{ status: number, stdout: string, stderr: string }}
210
210
  */
211
- export function defaultInvokeSecurityReview(input) {
211
+ function defaultInvokeSecurityReview(input) {
212
212
  const prompt = buildSecurityReviewPrompt(input);
213
213
  const result = spawnSync('claude', ['--print', prompt], {
214
214
  encoding: 'utf-8',
@@ -1,5 +1,5 @@
1
1
  /**
2
- * run-epilogue.js — real per-run closeout for `/deliver --run`.
2
+ * run-epilogue.js — real per-run closeout for `/mandrel-deliver --run`.
3
3
  *
4
4
  * After the last Story in a multi-Story plan-run lands, this module:
5
5
  * 1. Selects the cross-Story audit lens roster over the combined landed
@@ -100,7 +100,7 @@ export function planRunEpilogue({ planRunId, stories } = {}) {
100
100
  };
101
101
  }
102
102
 
103
- // Positional `/deliver 101 102` has no plan-run label. Synthesize a
103
+ // Positional `/mandrel-deliver 101 102` has no plan-run label. Synthesize a
104
104
  // stable adhoc id from the sorted Story set so the epilogue still
105
105
  // anchors comments / audit roster without requiring `--run`.
106
106
  const effectiveRunId =
@@ -83,7 +83,7 @@ export async function runBaseSyncPhase({
83
83
  : syncResult.stderr
84
84
  ? `: ${syncResult.stderr.slice(0, 200)}`
85
85
  : '') +
86
- `. Story transitioned to ${AGENT_LABELS.BLOCKED}; resolve in ${syncCwd} and re-run \`/deliver ${storyId}\`.`,
86
+ `. Story transitioned to ${AGENT_LABELS.BLOCKED}; resolve in ${syncCwd} and re-run \`/mandrel-deliver ${storyId}\`.`,
87
87
  );
88
88
  }
89
89
  progress('SYNC', `✅ Synced from origin/${baseBranch} (${syncResult.kind}).`);
@@ -182,7 +182,7 @@ export function buildSyncFailureCommentBody({
182
182
  const lines = [
183
183
  `### ${heading}`,
184
184
  '',
185
- '`/deliver` close-validation passed, but the pre-push',
185
+ '`/mandrel-deliver` close-validation passed, but the pre-push',
186
186
  `sync against \`origin/${baseBranch}\` could not complete. The Story has`,
187
187
  `been transitioned to \`agent::blocked\`. To resume:`,
188
188
  '',
@@ -343,7 +343,7 @@ function scanForBreaking(text, readHeaderBang) {
343
343
  * writing the commit that does the breaking.
344
344
  * - **The Story body** — the same footer, written as its own line anywhere
345
345
  * in the Story issue (the `## Spec` block is the natural home). This is
346
- * the declarative path: `/plan` can state the break up front and close
346
+ * the declarative path: `/mandrel-plan` can state the break up front and close
347
347
  * propagates it even when no individual commit remembered the footer.
348
348
  * Only the footer form counts; prose describing a break does not, because
349
349
  * a keyword-free sentence is exactly what release-please cannot parse.
@@ -191,7 +191,11 @@ function synthesizeSubject({ description, storyId, commitMessages }) {
191
191
  * @returns {string}
192
192
  */
193
193
  function buildPrBody({ storyId, breakingNotes }) {
194
- const lines = [`Closes #${storyId}`, '', '_Auto-opened by `/deliver`._'];
194
+ const lines = [
195
+ `Closes #${storyId}`,
196
+ '',
197
+ '_Auto-opened by `/mandrel-deliver`._',
198
+ ];
195
199
  if (breakingNotes.length > 0) {
196
200
  lines.push('', `BREAKING CHANGE: ${breakingNotes.join(' ')}`);
197
201
  }
@@ -87,6 +87,50 @@ export function resolveWaitForMerge({
87
87
  };
88
88
  }
89
89
 
90
+ /**
91
+ * Flags this CLI advertised in its `--help` descriptor and never implemented
92
+ * (Story #5100). `--dry-run` was transcribed into the usage block by the
93
+ * #4750 self-describing sweep and read by nothing in this pipeline —
94
+ * `git log -S dryRun` over it is empty in every revision — so an operator who
95
+ * trusted it got a real base-sync merge, an `agent::blocked` Story, and a
96
+ * terminal envelope that then captured `deliver-recover`'s routing.
97
+ * `--no-evidence` names a working flag on the gate wrappers
98
+ * (`evidence-gate.js`), but the slot this CLI parsed had no reader at all.
99
+ *
100
+ * Deleting them from the descriptor is NOT enough. `parseSprintArgs` runs
101
+ * `parseArgs` with `strict: false`, so an unknown flag is silently ignored and
102
+ * the close proceeds for real — the identical injury, now with no help text to
103
+ * explain it. Fail closed instead, exactly as `parseMergeWatchMode` does for an
104
+ * unrecognized mode: throw during option parsing, before the first phase, so
105
+ * the CLI boundary reports `failed` at phase `init` and nothing is mutated.
106
+ */
107
+ const RETIRED_FLAGS = Object.freeze({
108
+ '--dry-run':
109
+ 'this pipeline has never had a dry-run mode; it was advertised in error.',
110
+ '--no-evidence':
111
+ 'per-close evidence control was never wired here; the working flag of that name belongs to the gate wrappers.',
112
+ });
113
+
114
+ /**
115
+ * Reject a retired flag before any phase runs.
116
+ *
117
+ * Matches `--flag` and `--flag=value`; a bare `--` terminator or a positional
118
+ * that merely contains the text is not a flag and does not trip it.
119
+ *
120
+ * @param {string[]} argv argv tail (`process.argv.slice(2)`)
121
+ * @throws {Error} naming the flag, why it is gone, and that nothing was mutated
122
+ */
123
+ function assertNoRetiredFlags(argv) {
124
+ for (const [flag, why] of Object.entries(RETIRED_FLAGS)) {
125
+ const present = argv.some((a) => a === flag || a.startsWith(`${flag}=`));
126
+ if (!present) continue;
127
+ throw new Error(
128
+ `${flag} was retired: ${why} Nothing was mutated — no branch, label, ` +
129
+ `comment, or PR was touched. Re-run without it to close for real.`,
130
+ );
131
+ }
132
+ }
133
+
90
134
  /**
91
135
  * Parse and resolve all CLI / injection options for `runSingleStoryClose`.
92
136
  *
@@ -116,7 +160,14 @@ export function parseCloseOptions({
116
160
  // which is precisely what `resolveFlag` already does below, preferring the
117
161
  // param over the parsed slot. One expression per flag now serves both
118
162
  // callers, so a new flag is added in one place instead of two that can drift.
119
- const parsed = storyIdParam === undefined ? parseSprintArgs() : {};
163
+ // The guard runs on the argv door only: an injecting caller supplies
164
+ // `storyIdParam` and never reads argv, so the host process's flags (a test
165
+ // runner's, say) are none of its business.
166
+ let parsed = {};
167
+ if (storyIdParam === undefined) {
168
+ assertNoRetiredFlags(process.argv.slice(2));
169
+ parsed = parseSprintArgs();
170
+ }
120
171
  // Preserve undefined so resolveWaitForMerge can apply the closeAndLand
121
172
  // config default when neither flag was supplied.
122
173
  const waitForMergeExplicit = resolveFlag(
@@ -19,7 +19,7 @@ export async function handleCriticalReviewBlock({
19
19
  '### Code review blocked delivery',
20
20
  '',
21
21
  `The Story-scope review reported **${criticalCount} critical blocker(s)** on ${prUrl}.`,
22
- 'Remediate the posted findings, then re-run `/deliver`.',
22
+ 'Remediate the posted findings, then re-run `/mandrel-deliver`.',
23
23
  '',
24
24
  'If you have reviewed a finding and judged it wrong, re-run close with',
25
25
  '`--override-review-block "<reason>"` rather than merging by hand — see',
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * phases/wrong-tree-guard.js — detect worktree/main-checkout edit divergence.
3
3
  *
4
- * Story #3364 — `/single-story-deliver` (and `/deliver`) materializes a
4
+ * Story #3364 — `/single-story-deliver` (and `/mandrel-deliver`) materializes a
5
5
  * per-Story worktree and instructs the agent to `cd` into it before editing.
6
6
  * On Windows that guidance is silently insufficient: `cd <workCwd>` steers the
7
7
  * Bash tool's working directory, but the path-based Edit/Write tools operate on
@@ -232,7 +232,7 @@ export function formatWrongTreeFinding({ storyId, strayFiles, worktreePath }) {
232
232
  `**Recovery:** relocate these edits into the worktree (re-apply them under ` +
233
233
  `\`${worktreePath}\`), restore the main checkout ` +
234
234
  `(\`git -C <main-repo> checkout -- <files>\`), then re-run ` +
235
- `\`/deliver ${storyId}\`.`
235
+ `\`/mandrel-deliver ${storyId}\`.`
236
236
  );
237
237
  }
238
238
 
@@ -307,7 +307,7 @@ async function abortWrongTree({
307
307
  `diff-path set while the worktree (${worktreePath}) is the active work ` +
308
308
  `tree. Close aborted to avoid an empty-diff PR. Stray files: ` +
309
309
  `${strayFiles.join(', ')}. Relocate the edits into the worktree, ` +
310
- `restore the main checkout, then re-run /deliver ${storyId}.`,
310
+ `restore the main checkout, then re-run /mandrel-deliver ${storyId}.`,
311
311
  );
312
312
  }
313
313
 
@@ -84,7 +84,7 @@ async function emitTerminal({ terminal, result, config }) {
84
84
  * issue as `completed` (the `Closes #<id>` footer firing on merge — the work
85
85
  * IS on the base branch) or as `not_planned` (superseded by a re-plan,
86
86
  * abandoned — nothing ever merged). Reporting the second as `landed` told
87
- * `/deliver` that work had reached `main` when no PR ever merged, which would
87
+ * `/mandrel-deliver` that work had reached `main` when no PR ever merged, which would
88
88
  * also satisfy any dependent Story waiting on it. Fail loudly instead: being
89
89
  * handed an abandoned Story is an input error only the operator can resolve.
90
90
  *
@@ -283,7 +283,7 @@ async function openAndReviewPr({
283
283
  });
284
284
  throw new Error(
285
285
  `[single-story-close] Story-scope review reported ${criticalCount} critical blocker(s) on PR ${prUrl}. ` +
286
- 'Auto-merge was not enabled. Remediate the findings posted to the PR and re-run `/deliver`. ' +
286
+ 'Auto-merge was not enabled. Remediate the findings posted to the PR and re-run `/mandrel-deliver`. ' +
287
287
  'If you have reviewed a finding and judged it wrong, re-run with ' +
288
288
  '`--override-review-block "<reason>"` rather than merging by hand.',
289
289
  );
@@ -105,7 +105,7 @@ export async function acquireStoryLease({
105
105
  steal,
106
106
  renderRefusal: (result) =>
107
107
  `single-story lease: Story #${storyId} is currently held by @${result.owner}. ` +
108
- 'Another /deliver run owns this Story. Coordinate with that ' +
108
+ 'Another /mandrel-deliver run owns this Story. Coordinate with that ' +
109
109
  'operator, or re-run with --steal to forcibly transfer the claim once you ' +
110
110
  'have confirmed the other run is dead. (A foreign assignee always ' +
111
111
  'blocks unless stolen.)',
@@ -63,7 +63,7 @@ export const FORMAT_AUTOFIX_TIMEOUT_EXIT_CODE = 124;
63
63
  * @param {(args: string[], opts: object) => string} git
64
64
  * @returns {string[]}
65
65
  */
66
- export function listDirtyPaths(cwd, git) {
66
+ function listDirtyPaths(cwd, git) {
67
67
  const out = git(['status', '--porcelain'], {
68
68
  cwd,
69
69
  encoding: 'utf8',
@@ -91,10 +91,7 @@ export function listDirtyPaths(cwd, git) {
91
91
  * }} [opts]
92
92
  * @returns {{ writeCmdString: string, writeCmd: string, writeArgs: string[] }}
93
93
  */
94
- export function resolveFormatterCmd({
95
- commands,
96
- dropTrailingDot = false,
97
- } = {}) {
94
+ function resolveFormatterCmd({ commands, dropTrailingDot = false } = {}) {
98
95
  // `resolveFormatWriteCommand` reads `config.project.commands`; wrap the
99
96
  // caller-supplied `commands` map into that canonical shape.
100
97
  const writeCmdString = resolveFormatWriteCommand({ project: { commands } });
@@ -47,7 +47,7 @@ export const TERMINAL_ENVELOPE_KIND = 'story-deliver-terminal';
47
47
  * `pending` is **3**, not 0 and not 1. Zero would tell a headless caller the
48
48
  * Story landed when it did not; one would conflate a resumable slow-CI wait
49
49
  * with a hard block and send the operator to diagnose branch protection that
50
- * is working fine. A distinct code is what lets `/deliver` resume without
50
+ * is working fine. A distinct code is what lets `/mandrel-deliver` resume without
51
51
  * classifying.
52
52
  */
53
53
  export const TERMINAL_EXIT_CODES = Object.freeze({
@@ -61,7 +61,7 @@ export const TERMINAL_EXIT_CODES = Object.freeze({
61
61
  * terminal, not a new one, so giving it a fresh code would fork a vocabulary
62
62
  * callers already branch on. It stays distinct from `landed` (nothing was
63
63
  * delivered) and from `blocked`/`failed` (nothing is wrong — the work simply
64
- * belongs to `/plan`).
64
+ * belongs to `/mandrel-plan`).
65
65
  */
66
66
  escalated: 2,
67
67
  });
@@ -74,12 +74,12 @@ export const TERMINAL_STATUSES = Object.freeze([
74
74
  'escalated',
75
75
  ]);
76
76
 
77
- /** Cap on the prompt echoed into an escalation's `/plan` next command. */
77
+ /** Cap on the prompt echoed into an escalation's `/mandrel-plan` next command. */
78
78
  const PLAN_PROMPT_MAX = 200;
79
79
 
80
80
  /**
81
81
  * Render an operator prompt safe to sit inside the double quotes of the
82
- * `/plan "<prompt>"` next command: collapse newlines (a next command is one
82
+ * `/mandrel-plan "<prompt>"` next command: collapse newlines (a next command is one
83
83
  * line by contract), escape backslashes and double quotes so the quoting
84
84
  * cannot be broken out of, and cap the length so a long prompt does not turn
85
85
  * the envelope into a transcript. Total — a non-string yields the empty
@@ -133,7 +133,7 @@ export const NEXT_COMMANDS = Object.freeze({
133
133
  recover: (storyId) =>
134
134
  `node .agents/scripts/deliver-recover.js --story ${storyId}`,
135
135
  /**
136
- * Hand over-scope work to `/plan` — the next command of an `escalated`
136
+ * Hand over-scope work to `/mandrel-plan` — the next command of an `escalated`
137
137
  * terminal (Story #4746).
138
138
  *
139
139
  * The only entry here that is a slash command rather than a script, and
@@ -143,7 +143,7 @@ export const NEXT_COMMANDS = Object.freeze({
143
143
  * the workflow's escalation section for why running it in the escalating
144
144
  * session is forbidden.
145
145
  */
146
- escalateToPlan: (prompt) => `/plan "${quoteForPlan(prompt)}"`,
146
+ escalateToPlan: (prompt) => `/mandrel-plan "${quoteForPlan(prompt)}"`,
147
147
  });
148
148
 
149
149
  /**
@@ -260,15 +260,15 @@ export function buildTerminalEnvelope({
260
260
  * `/deliver-light`'s suitability gate already decided correctly when it
261
261
  * overrode a `lite` self-verdict on shape; what it lacked was an outcome a
262
262
  * session could not walk past. A mandrel-bench 2.13.0 light-arm run did
263
- * exactly that — it read the gate's `escalate-plan`, then invoked `/plan`
263
+ * exactly that — it read the gate's `escalate-plan`, then invoked `/mandrel-plan`
264
264
  * in the same session and delivered. The continuation was not harmless:
265
265
  * planning inside a session already framed as small work authored ONE Story
266
- * against the scenario's 3-5 contract, where a fresh `/plan` session on the
266
+ * against the scenario's 3-5 contract, where a fresh `/mandrel-plan` session on the
267
267
  * identical seed authored four. Escalation silently produced the very
268
268
  * under-decomposition the guard exists to prevent.
269
269
  *
270
270
  * So the outcome is a validated envelope with its own exit code, naming the
271
- * `/plan` command that owns the work, and asserting per artifact that nothing
271
+ * `/mandrel-plan` command that owns the work, and asserting per artifact that nothing
272
272
  * was started. Every guarantee here is enforced by the schema rather than by
273
273
  * prose: `storyId` must be null, `escalation.created.*` are pinned `false`.
274
274
  *
@@ -288,10 +288,10 @@ export function buildEscalationTerminal({
288
288
  }) {
289
289
  const quoted = quoteForPlan(prompt);
290
290
  if (quoted === '') {
291
- // An escalation whose next command is `/plan ""` hands the operator
291
+ // An escalation whose next command is `/mandrel-plan ""` hands the operator
292
292
  // nothing — the same walk-past-able non-outcome in envelope clothing.
293
293
  throw new TypeError(
294
- 'buildEscalationTerminal: a non-empty prompt is required — the escalated terminal exists to name the /plan invocation that owns the work',
294
+ 'buildEscalationTerminal: a non-empty prompt is required — the escalated terminal exists to name the /mandrel-plan invocation that owns the work',
295
295
  );
296
296
  }
297
297
  const recorded = (Array.isArray(reasons) ? reasons : []).filter(
@@ -305,7 +305,9 @@ export function buildEscalationTerminal({
305
305
  reasons:
306
306
  recorded.length > 0
307
307
  ? recorded
308
- : ['predicted scope exceeds the light ceilings — escalate to /plan'],
308
+ : [
309
+ 'predicted scope exceeds the light ceilings — escalate to /mandrel-plan',
310
+ ],
309
311
  // Not computed from anything: the escalation path returns before the
310
312
  // receipt/init call sites, so these are the assertion that it did.
311
313
  created: { receiptStory: false, storyBranch: false, worktree: false },
@@ -24,7 +24,7 @@ import {
24
24
  } from './retro-proposals.js';
25
25
  import { upsertStructuredComment } from './ticketing.js';
26
26
 
27
- export const FOLLOW_UPS_COMMENT_TYPE = 'follow-ups';
27
+ const FOLLOW_UPS_COMMENT_TYPE = 'follow-ups';
28
28
 
29
29
  /** Milliseconds in one day — the unit `frictionWindowDays` is expressed in. */
30
30
  const MS_PER_DAY = 24 * 60 * 60 * 1000;