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
@@ -29,11 +29,11 @@
29
29
  * 3 below does the real enforcement against ground truth.
30
30
  * 2. **Over-scope stops, never silently proceeds ({@link
31
31
  * resolveLightGateOutcome}).** An over-ceiling prompt does **not**
32
- * hard-fail — it STOPS and asks the operator to escalate to `/plan` or
32
+ * hard-fail — it STOPS and asks the operator to escalate to `/mandrel-plan` or
33
33
  * proceed light. Both answers are executable: `proceed-light` is recorded
34
34
  * through {@link resolveOperatorOverride}, which waives a *size
35
35
  * prediction* only, never a risk rule, and only with a human present.
36
- * Under `--yes` (unattended) it fails closed to recommending `/plan`.
36
+ * Under `--yes` (unattended) it fails closed to recommending `/mandrel-plan`.
37
37
  * 3. **Diff-derived backstop ({@link checkLightDiffBackstop}).** After
38
38
  * implementation the **actual** change set is re-checked with
39
39
  * {@link module:lib/orchestration/review-depth.deriveChangeLevel} plus the
@@ -41,7 +41,7 @@
41
41
  * the diff is the real scope signal — and an over-ceiling diff is blocked
42
42
  * rather than landed silently. Story #4856 moved this from a `maxFiles: 4`
43
43
  * cardinality ceiling to changed lines over implementation files, and made
44
- * a block **recycle** its receipt Story through `/plan` tickets mode
44
+ * a block **recycle** its receipt Story through `/mandrel-plan` tickets mode
45
45
  * instead of orphaning it.
46
46
  * 4. **Minimal receipt Story ({@link buildReceiptStoryTicket}).** A
47
47
  * `type::story` ticket is authored inline so `refs #`, history, telemetry,
@@ -131,7 +131,7 @@ function deriveUnwaivableRisk(decision) {
131
131
  `class(es) ${classes.join(', ')} — this is risk, not size, so no ` +
132
132
  `re-slicing, shrinking, or operator override satisfies it and the ` +
133
133
  `diff backstop would refuse the same footprint after the work is ` +
134
- `finished; take this to /plan now`,
134
+ `finished; take this to /mandrel-plan now`,
135
135
  };
136
136
  }
137
137
  if (shape?.migrationSpan === true) {
@@ -142,7 +142,7 @@ function deriveUnwaivableRisk(decision) {
142
142
  reason:
143
143
  `un-waivable: the predicted footprint pairs a migration with its ` +
144
144
  `consumers — this is risk, not size, so no re-slicing or operator ` +
145
- `override satisfies it; take this to /plan now`,
145
+ `override satisfies it; take this to /mandrel-plan now`,
146
146
  };
147
147
  }
148
148
  return { present: false, code: null, classes: [], reason: null };
@@ -237,7 +237,7 @@ export function resolveLedgeredVerdict({ route, reason } = {}) {
237
237
  route: 'full',
238
238
  reason: recordedReason || null,
239
239
  recorded: recordedReason !== '',
240
- note: 'model verdict is not lite — standard /plan route',
240
+ note: 'model verdict is not lite — standard /mandrel-plan route',
241
241
  };
242
242
  }
243
243
  if (recordedReason === '') {
@@ -261,7 +261,7 @@ export function resolveLedgeredVerdict({ route, reason } = {}) {
261
261
  * light path. The deterministic effort/risk derivation and the ledgered model
262
262
  * verdict must **both** agree on `lite`; anything else — clearly-epic work, a
263
263
  * sensitive-path footprint, an unledgered verdict — resolves to `full` (the
264
- * conservative default that routes the operator to `/plan`).
264
+ * conservative default that routes the operator to `/mandrel-plan`).
265
265
  *
266
266
  * The predicted axes are declared by the caller: `predictedKinds` (the distinct
267
267
  * kinds of change; absent, each entry's `assumption` is its kind, so N
@@ -394,7 +394,7 @@ export function resolveOperatorOverride({
394
394
  }
395
395
  if (yes === true) {
396
396
  return refuse(
397
- 'operator override refused — it is attended-only, and --yes means nobody is at the keyboard; over-scope still fails closed to /plan',
397
+ 'operator override refused — it is attended-only, and --yes means nobody is at the keyboard; over-scope still fails closed to /mandrel-plan',
398
398
  );
399
399
  }
400
400
 
@@ -412,7 +412,7 @@ export function resolveOperatorOverride({
412
412
  ? ` (${unwaivable.classes.join(', ')})`
413
413
  : ''
414
414
  }; waiving the size prediction cannot make this land light, and the ` +
415
- `diff backstop would refuse the finished work. Escalate to /plan.`,
415
+ `diff backstop would refuse the finished work. Escalate to /mandrel-plan.`,
416
416
  );
417
417
  }
418
418
  if (!OVERRIDABLE_SHAPE_CODES.includes(code)) {
@@ -441,7 +441,7 @@ export function resolveOperatorOverride({
441
441
  * Resolve what the light gate does with a suitability decision (Story #4740
442
442
  * AC-3). Over-scope never hard-fails: it STOPS and asks the operator to choose,
443
443
  * unless the run is unattended (`--yes`), where it fails closed to recommending
444
- * `/plan` rather than silently proceeding light.
444
+ * `/mandrel-plan` rather than silently proceeding light.
445
445
  *
446
446
  * - suitable → `proceed-light`
447
447
  * - over-scope + attended (`yes:false`) → `ask-operator` (escalate | proceed)
@@ -498,7 +498,7 @@ export function resolveLightGateOutcome({
498
498
  action: 'escalate-plan',
499
499
  reasons: [
500
500
  ...reasons,
501
- '--yes on over-scope fails closed to /plan (never silently proceeds light)',
501
+ '--yes on over-scope fails closed to /mandrel-plan (never silently proceeds light)',
502
502
  ],
503
503
  };
504
504
  }
@@ -519,7 +519,7 @@ export function resolveLightGateOutcome({
519
519
  options: ['escalate-plan', 'proceed-light'],
520
520
  reasons: [
521
521
  ...reasons,
522
- 'predicted scope exceeds the light ceilings — STOP and ask the operator to escalate to /plan or proceed light',
522
+ 'predicted scope exceeds the light ceilings — STOP and ask the operator to escalate to /mandrel-plan or proceed light',
523
523
  ],
524
524
  };
525
525
  }
@@ -587,7 +587,7 @@ export function checkLightDiffBackstop({
587
587
  magnitude: null,
588
588
  ceilings: resolved,
589
589
  reasons: [
590
- 'actual change set is unknown or empty — cannot verify the diff is light; escalate to /plan',
590
+ 'actual change set is unknown or empty — cannot verify the diff is light; escalate to /mandrel-plan',
591
591
  ],
592
592
  };
593
593
  }
@@ -646,12 +646,12 @@ function normalizeMagnitude(magnitude) {
646
646
  function describeSensitivity({ level, classes }) {
647
647
  if (classes.length > 0) {
648
648
  return [
649
- `diff intersects sensitive-path class(es) ${classes.join(', ')} — escalate to /plan (do not land light)`,
649
+ `diff intersects sensitive-path class(es) ${classes.join(', ')} — escalate to /mandrel-plan (do not land light)`,
650
650
  ];
651
651
  }
652
652
  if (level !== 'low') {
653
653
  return [
654
- 'sensitive-path classification unavailable — cannot verify the diff is non-sensitive; escalate to /plan',
654
+ 'sensitive-path classification unavailable — cannot verify the diff is non-sensitive; escalate to /mandrel-plan',
655
655
  ];
656
656
  }
657
657
  return [];
@@ -667,18 +667,18 @@ function describeSensitivity({ level, classes }) {
667
667
  function describeMagnitude(measured, ceilings) {
668
668
  if (measured === null) {
669
669
  return [
670
- 'change magnitude could not be measured (unreadable or unparseable numstat) — cannot verify the diff is light; escalate to /plan',
670
+ 'change magnitude could not be measured (unreadable or unparseable numstat) — cannot verify the diff is light; escalate to /mandrel-plan',
671
671
  ];
672
672
  }
673
673
  const reasons = [];
674
674
  if (measured.implLines > ceilings.maxImplLines) {
675
675
  reasons.push(
676
- `diff changes ${measured.implLines} implementation line(s) (> maxImplLines ${ceilings.maxImplLines}) — escalate to /plan (do not land light)`,
676
+ `diff changes ${measured.implLines} implementation line(s) (> maxImplLines ${ceilings.maxImplLines}) — escalate to /mandrel-plan (do not land light)`,
677
677
  );
678
678
  }
679
679
  if (measured.implFiles > ceilings.maxImplFiles) {
680
680
  reasons.push(
681
- `diff spans ${measured.implFiles} implementation file(s) (> maxImplFiles ${ceilings.maxImplFiles}) — escalate to /plan (do not land light)`,
681
+ `diff spans ${measured.implFiles} implementation file(s) (> maxImplFiles ${ceilings.maxImplFiles}) — escalate to /mandrel-plan (do not land light)`,
682
682
  );
683
683
  }
684
684
  return reasons;
@@ -832,7 +832,7 @@ export function buildReceiptStoryTicket({
832
832
  goal: `${text}${amendNote}`,
833
833
  spec:
834
834
  `Delivered via /deliver-light as a validated single-session change — ` +
835
- `the /plan session is removed for genuinely small work while every ` +
835
+ `the /mandrel-plan session is removed for genuinely small work while every ` +
836
836
  `single-story-close gate runs byte-identical.${amendNote}` +
837
837
  `${overrideNote} ` +
838
838
  `Operator prompt: ${text}`,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * plan-context.js — single planner-context envelope build for `/plan`.
2
+ * plan-context.js — single planner-context envelope build for `/mandrel-plan`.
3
3
  *
4
4
  * Folds the authoring-context builders plus the cross-Story dup search into
5
5
  * ONE JSON envelope, so the authoring middle reads a single file instead of
@@ -481,7 +481,7 @@ export function buildDeliverLightSuggestion(complexitySignals) {
481
481
  reasons: suggested
482
482
  ? [
483
483
  'seed carries no risk signal (no risk-heuristic hits, no ' +
484
- 'sensitive-path classes) — the operator may prefer /deliver for ' +
484
+ 'sensitive-path classes) — the operator may prefer /mandrel-deliver for ' +
485
485
  "this scope; the light path's own gate and diff backstop decide size",
486
486
  ]
487
487
  : reasons,
@@ -597,7 +597,7 @@ function uiSurfaceReason({
597
597
  * derived from the consumer's own checkout, so a frontend-less project — this
598
598
  * repository included — resolves falsey and the offer never fires.
599
599
  *
600
- * The signal carries **no routing authority** (`automatic: false`): `/plan`
600
+ * The signal carries **no routing authority** (`automatic: false`): `/mandrel-plan`
601
601
  * may say that a plan touches UI and that `/prototype` exists, and must never
602
602
  * invoke it. Pure over its inputs and total — a malformed signal bag or an
603
603
  * unreadable manifest degrades, never throws.
@@ -911,7 +911,7 @@ async function gatherEnvelopeInputs({
911
911
  (gather) => gather(),
912
912
  // The per-mode envelope gathers (Story #4952): the duplicate search, the
913
913
  // authoring-context fold and the docs digest have no data dependency on
914
- // one another, so their serialization was incidental and `/plan` paid it
914
+ // one another, so their serialization was incidental and `/mandrel-plan` paid it
915
915
  // with the operator waiting at Gate #1.
916
916
  { concurrency: FANOUT_CONCURRENCY },
917
917
  );
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * plan-critic-conditions.js — size/heuristic-conditional dispatch decisions for
3
- * the /plan author-step critics (Epic #4474 PR6, design §4).
3
+ * the /mandrel-plan author-step critics (Epic #4474 PR6, design §4).
4
4
  *
5
5
  * The collapsed plan flow keeps the consolidation and pre-mortem critics as
6
6
  * fresh-context sub-agent dispatches, but makes each dispatch
@@ -44,7 +44,7 @@
44
44
  * (`appendCriticSkip`) by the caller so under-firing is auditable.
45
45
  *
46
46
  * Pure, synchronous, no I/O. The single caller is `plan-critics-evaluate.js`,
47
- * driven by the `plan-critics.js` CLI that `/plan` runs between Author and
47
+ * driven by the `plan-critics.js` CLI that `/mandrel-plan` runs between Author and
48
48
  * Persist (Story #4592); the CLI owns reading the authored artifacts and the
49
49
  * resolved config.
50
50
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * plan-critics-evaluate.js — shared critic-dispatch evaluation for the
3
- * collapsed /plan flow (#4496 fix 6).
3
+ * collapsed /mandrel-plan flow (#4496 fix 6).
4
4
  *
5
- * One consumer: the `plan-critics.js` CLI, which `/plan` runs between its
5
+ * One consumer: the `plan-critics.js` CLI, which `/mandrel-plan` runs between its
6
6
  * Author and Persist steps. The CLI loads the draft artifacts, calls this
7
7
  * module, prints the verdict as JSON, and records every skip on the
8
8
  * plan-metrics ledger; the workflow dispatches a fresh-context critic
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * plan-metrics.js — append-only plan-invocation ledger (Epic #4474, PR1).
3
3
  *
4
- * The `/plan` collapse (#4474) is measured, not asserted: before any pipeline
4
+ * The `/mandrel-plan` collapse (#4474) is measured, not asserted: before any pipeline
5
5
  * phase is removed, every plan CLI invocation stamps an entry/exit record so
6
6
  * the current 12-phase baseline is captured on disk. Each record is one
7
7
  * newline-terminated JSON line appended to
@@ -1,5 +1,5 @@
1
1
  /**
2
- * persist-helpers.js — pure helper surface for the flat Story `/plan` persist.
2
+ * persist-helpers.js — pure helper surface for the flat Story `/mandrel-plan` persist.
3
3
  *
4
4
  * Exports:
5
5
  * - `resolveBaseBranchRef(config)` — the one place the persist gates learn
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * plan-context-source.js — locate and read the `plan-context.js` envelope so
3
- * persist can derive the `/plan --tickets` source ids from the run that
3
+ * persist can derive the `/mandrel-plan --tickets` source ids from the run that
4
4
  * actually fetched them (Story #4554).
5
5
  *
6
6
  * This lives beside the persist ops rather than inside `plan-persist.js` so
@@ -17,7 +17,7 @@ import path from 'node:path';
17
17
  import { Logger } from '../../Logger.js';
18
18
 
19
19
  /**
20
- * Filename the `/plan` interrogate step writes its envelope to inside
20
+ * Filename the `/mandrel-plan` interrogate step writes its envelope to inside
21
21
  * `--plan-dir` (`plan-context.js --out <plan-dir>/plan-context.json`).
22
22
  */
23
23
  export const PLAN_CONTEXT_FILENAME = 'plan-context.json';
@@ -81,7 +81,7 @@ export async function loadPlanContextEnvelope(planContext) {
81
81
  if (!planContext) {
82
82
  Logger.warn(
83
83
  '[plan-persist] no --plan-dir or --plan-context given, so no ' +
84
- 'plan-context envelope was read. If this was a `/plan --tickets` ' +
84
+ 'plan-context envelope was read. If this was a `/mandrel-plan --tickets` ' +
85
85
  'run, its source tickets can only come from --source-tickets and ' +
86
86
  `will NOT be closed otherwise. ${CAPTURE_HINT}`,
87
87
  );
@@ -1,5 +1,5 @@
1
1
  /**
2
- * run-plan-persist.js — flat Story persist for the v2 `/plan` collapse
2
+ * run-plan-persist.js — flat Story persist for the v2 `/mandrel-plan` collapse
3
3
  * (Stage 3 — `docs/roadmap.md`).
4
4
  *
5
5
  * Ordered, fail-closed pipeline:
@@ -293,7 +293,7 @@ async function renderRunScopedPlanMetricsLine({
293
293
  * from the seed.
294
294
  *
295
295
  * The resolved route decides whether the created Stories carry the
296
- * {@link LITE_ROUTE_LABEL} **hint** (never the control signal — `/deliver`
296
+ * {@link LITE_ROUTE_LABEL} **hint** (never the control signal — `/mandrel-deliver`
297
297
  * re-derives the route from the Story body's shape) and the `route` block
298
298
  * ledgered on their `story-plan-state` checkpoint, including the authored
299
299
  * verdict, its recorded reason, and the per-Story shape evidence. A refused
@@ -531,7 +531,7 @@ async function enforceReachability(reachability, config) {
531
531
  * concurrency, but the `await` on that whole fan-out is what keeps the
532
532
  * Story #4541 invariant intact: *every* checkpoint is on its ticket before the
533
533
  * first `agent::ready` flip is issued, so `ready` still means "fully
534
- * persisted" and a `/deliver` that picks a Story up cannot read a null
534
+ * persisted" and a `/mandrel-deliver` that picks a Story up cannot read a null
535
535
  * checkpoint. Concurrency inside the phase is safe; overlapping the phases is
536
536
  * the race this ordering exists to close.
537
537
  *
@@ -611,7 +611,7 @@ function logEffectiveRoute(route, isLiteRoute) {
611
611
  Logger.info(
612
612
  `[plan-persist] ceremony-lite route upheld by the shape backstop: ` +
613
613
  `created Stories carry the ${LITE_ROUTE_LABEL} hint ` +
614
- `(recorded reason: ${route.authored.reason}). /deliver re-derives ` +
614
+ `(recorded reason: ${route.authored.reason}). /mandrel-deliver re-derives ` +
615
615
  'the route from each Story body — the label is never the control signal.',
616
616
  );
617
617
  return;
@@ -644,10 +644,10 @@ function logPersistEpilogue({ created, primary, planRunLabel }) {
644
644
  `; primary #${primary.id} is agent::ready.`,
645
645
  );
646
646
  Logger.info(
647
- `[plan-persist] Deliver with: /deliver ${created.map((s2) => s2.id).join(' ')}`,
647
+ `[plan-persist] Deliver with: /mandrel-deliver ${created.map((s2) => s2.id).join(' ')}`,
648
648
  );
649
649
  // Metadata only — a GitHub filter for the cohort this run authored, never
650
- // a delivery-resolution input (/deliver stays ids-only, Story #4540).
650
+ // a delivery-resolution input (/mandrel-deliver stays ids-only, Story #4540).
651
651
  Logger.info(
652
652
  `[plan-persist] Cohort grouping label: ${planRunLabel} — filter with ` +
653
653
  `label:${planRunLabel}`,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * story-ops.js — flat Story creation for v2 plan-persist (Stage 3).
3
3
  *
4
- * Under the Story collapse (`docs/roadmap.md` § Stage 3), `/plan` persists
4
+ * Under the Story collapse (`docs/roadmap.md` § Stage 3), `/mandrel-plan` persists
5
5
  * zero-or-more Story issues directly — no Epic parent, no reconciler tree,
6
6
  * no `deliveryShape` mode matrix. Default is **one Story**; N>1 is gated by
7
7
  * the Stage-1 split-policy validator (`assertAcceptancePartition`).
@@ -44,7 +44,7 @@ import {
44
44
  *
45
45
  * Reintroduced (Story #4692) after Story #4540 retired it: #4540 was right
46
46
  * that batch identity is the wrong axis for *ordering delivery across runs*
47
- * (`/deliver` takes ids and resolves the graph from live state — that stays),
47
+ * (`/mandrel-deliver` takes ids and resolves the graph from live state — that stays),
48
48
  * but it is the correct axis for *grouping the Stories one plan run created*
49
49
  * so a cohort is filterable and traceable in the GitHub UI. The label is
50
50
  * metadata only; nothing in persist or delivery reads it as a
@@ -57,7 +57,7 @@ const PLAN_RUN_LABEL_COLOR = '#C5DEF5';
57
57
 
58
58
  /**
59
59
  * Stable color for the `route::lite` ceremony-route hint (Story #4707;
60
- * hint-only since Story #4722 — `/deliver` re-derives the route from the
60
+ * hint-only since Story #4722 — `/mandrel-deliver` re-derives the route from the
61
61
  * Story body's shape).
62
62
  */
63
63
  const LITE_ROUTE_LABEL_COLOR = '#D4C5F9';
@@ -197,6 +197,26 @@ function planFingerprintMarker(fingerprint) {
197
197
  return `<!-- ${PLAN_FINGERPRINT_MARKER_PREFIX} ${fingerprint} -->`;
198
198
  }
199
199
 
200
+ /** The one parser for the marker {@link planFingerprintMarker} renders. */
201
+ const PLAN_FINGERPRINT_MARKER_RE = new RegExp(
202
+ `<!--\\s*${PLAN_FINGERPRINT_MARKER_PREFIX}\\s*([0-9a-f]+)\\s*-->`,
203
+ );
204
+
205
+ /**
206
+ * Recover the plan fingerprint an issue body was stamped with, or `null` when
207
+ * the body carries no marker. Module-private: both readers of the marker (the
208
+ * resume index and the create-retry adoption probe) go through it so they can
209
+ * never drift into recognising different Stories as "already created".
210
+ *
211
+ * @param {unknown} body
212
+ * @returns {string|null}
213
+ */
214
+ function extractPlanFingerprint(body) {
215
+ if (typeof body !== 'string') return null;
216
+ const match = body.match(PLAN_FINGERPRINT_MARKER_RE);
217
+ return match ? match[1] : null;
218
+ }
219
+
200
220
  /**
201
221
  * Labels the authoring pass is never allowed to set. The `agent::*` axis is
202
222
  * the runtime's lifecycle state (persist owns the terminal `agent::ready`
@@ -455,7 +475,7 @@ function assembleOnePlanStory(ticket, opts) {
455
475
  const serialized = serializeStoryBody({ ...folded, depends_on });
456
476
  // Carry audit dedup provenance into the persisted body (Story #4877). The
457
477
  // audit sweep's Single-plan path stamps the `audit-fingerprints` /
458
- // `audit-semantic-keys` footers into the seed it hands `/plan`; without this
478
+ // `audit-semantic-keys` footers into the seed it hands `/mandrel-plan`; without this
459
479
  // the persisted Story carries no provenance and the next sweep re-files work
460
480
  // it already planned. Mechanical on purpose — the authoring agent is not
461
481
  // asked to notice HTML comments in a one-pager. A non-audit seed carries no
@@ -509,7 +529,7 @@ function assertSharedSpecAllowed(tickets, sharedSpec) {
509
529
  * @param {object} [opts]
510
530
  * @param {string|null} [opts.sharedSpec]
511
531
  * @param {string[]} [opts.planAcceptance]
512
- * @param {number[]} [opts.sourceTicketIds] Ids passed to `/plan --tickets`.
532
+ * @param {number[]} [opts.sourceTicketIds] Ids passed to `/mandrel-plan --tickets`.
513
533
  * @returns {{ stories: Array<{ slug: string, title: string, body: string, acceptance: string[], depends_on: string[], supersedes: Array<{ id: number, note: string|null }> }> }}
514
534
  */
515
535
  export function assemblePlanStories(tickets, opts = {}) {
@@ -617,14 +637,9 @@ async function indexExistingStories(provider) {
617
637
  if (title !== '') {
618
638
  idsByTitle.set(title, [...(idsByTitle.get(title) ?? []), id]);
619
639
  }
620
- const body = typeof issue?.body === 'string' ? issue.body : '';
621
- const match = body.match(
622
- new RegExp(
623
- `<!--\\s*${PLAN_FINGERPRINT_MARKER_PREFIX}\\s*([0-9a-f]+)\\s*-->`,
624
- ),
625
- );
626
- if (!match) continue;
627
- byFingerprint.set(match[1], {
640
+ const fingerprint = extractPlanFingerprint(issue?.body);
641
+ if (!fingerprint) continue;
642
+ byFingerprint.set(fingerprint, {
628
643
  id,
629
644
  title,
630
645
  url: issue.html_url ?? issue.url ?? undefined,
@@ -633,6 +648,42 @@ async function indexExistingStories(provider) {
633
648
  return { byFingerprint, idsByTitle };
634
649
  }
635
650
 
651
+ /**
652
+ * Re-run the resume lookup for a single fingerprint and return the **raw**
653
+ * issue, or `null`.
654
+ *
655
+ * This is the probe `createIssue` calls before any retry POST (Story #5112).
656
+ * A create whose response was lost has already filed the issue; retrying
657
+ * blind duplicates it. Because the body posted carries the fingerprint
658
+ * marker, the same content-keyed lookup the resume path uses answers "did
659
+ * attempt 1 land?" authoritatively — from the server's state, not from a
660
+ * client-side guess about where the connection broke.
661
+ *
662
+ * Best-effort like {@link indexExistingStories}: a provider without the
663
+ * listing surface, or a listing that throws, yields `null` and the retry
664
+ * proceeds exactly as it did before.
665
+ *
666
+ * @param {{ provider: object, fingerprint: string }} args
667
+ * @returns {Promise<object|null>}
668
+ */
669
+ async function findOpenStoryByPlanFingerprint({ provider, fingerprint }) {
670
+ if (typeof provider?.listIssuesByLabel !== 'function') return null;
671
+ if (typeof fingerprint !== 'string' || fingerprint.length === 0) return null;
672
+ let issues;
673
+ try {
674
+ issues = await provider.listIssuesByLabel({
675
+ state: 'open',
676
+ labels: TYPE_LABELS.STORY,
677
+ });
678
+ } catch {
679
+ return null;
680
+ }
681
+ for (const issue of Array.isArray(issues) ? issues : []) {
682
+ if (extractPlanFingerprint(issue?.body) === fingerprint) return issue;
683
+ }
684
+ return null;
685
+ }
686
+
636
687
  /**
637
688
  * Warn when a Story with this title is already open but did **not** match the
638
689
  * fingerprint — i.e. its authored content differs from what this run is about
@@ -705,14 +756,14 @@ function renderStoryBodyForCreate(story, idBySlug) {
705
756
  *
706
757
  * Ordering is authored as slugs and, until now, survived persist only as
707
758
  * `blocked by #N` prose in the body footer. That footer stays — it is what
708
- * `/deliver`'s resolver falls back on — but a native edge is the durable,
759
+ * `/mandrel-deliver`'s resolver falls back on — but a native edge is the durable,
709
760
  * machine-readable form: visible in the GitHub UI, readable without parsing
710
761
  * markdown, and settable by an operator later for cross-run order.
711
762
  *
712
763
  * **Non-fatal by design, and deliberately asymmetric with the read path.** A
713
764
  * missing native edge is cosmetic here: `renderStoryBodyForCreate` has already
714
765
  * written the footer, so ordering is not lost when the dependencies API says
715
- * no. `/deliver`'s *read* of these edges is a real dispatch gate, which is why
766
+ * no. `/mandrel-deliver`'s *read* of these edges is a real dispatch gate, which is why
716
767
  * that side fails loud. Persist reports the failure and completes.
717
768
  *
718
769
  * Two shape hazards this crossing has to get right, both silent if missed:
@@ -788,7 +839,7 @@ async function mirrorNativeDependencyEdges({ provider, stories, idBySlug }) {
788
839
  *
789
840
  * **Non-fatal by design**, matching the native-blocked_by mirroring posture:
790
841
  * neither label is load-bearing for correctness (grouping is cosmetic, and
791
- * the route label is a human-visible hint only — `/deliver` re-derives the
842
+ * the route label is a human-visible hint only — `/mandrel-deliver` re-derives the
792
843
  * route from the Story body's shape, Story #4722), so it is never a reason
793
844
  * to fail persist. On an ensure
794
845
  * failure (throw, or the label reported `missing` by the post-loop
@@ -869,7 +920,7 @@ async function ensurePersistLabel({
869
920
  * create — no relabel call is needed on the resume path, and the cohort is
870
921
  * never split across two labels. The label is ensured to exist before the
871
922
  * first POST, **non-fatally**: grouping is cosmetic and never fails the run
872
- * (see `ensureCohortLabel`). `/deliver` never reads it — delivery stays
923
+ * (see `ensureCohortLabel`). `/mandrel-deliver` never reads it — delivery stays
873
924
  * ids-only over live state (Story #4540's actual point).
874
925
  *
875
926
  * **The create loop stays serial and dependency-ordered** — deliberately, and
@@ -889,7 +940,7 @@ async function ensurePersistLabel({
889
940
  * effective complexity route to `lite` (the planner's recorded verdict,
890
941
  * upheld by the shape backstop), it passes the label via `opts.routeLabel`
891
942
  * and every created Story carries it — a **human-visible hint only**, never
892
- * the control signal: `/deliver` re-derives the route from each Story body's
943
+ * the control signal: `/mandrel-deliver` re-derives the route from each Story body's
893
944
  * own shape (`resolveStoryDispatchMode`), so a lost or failed label write
894
945
  * cannot misroute delivery. A full-routed plan passes nothing and its
895
946
  * Stories carry **no** route label. Like the cohort label, the ensure is
@@ -949,8 +1000,8 @@ export async function createStoryIssues({ provider, stories, opts = {} }) {
949
1000
  label: cohortLabel,
950
1001
  color: PLAN_RUN_LABEL_COLOR,
951
1002
  description:
952
- 'Groups the Stories one /plan persist run authored (metadata ' +
953
- 'only — /deliver stays ids-only).',
1003
+ 'Groups the Stories one /mandrel-plan persist run authored (metadata ' +
1004
+ 'only — /mandrel-deliver stays ids-only).',
954
1005
  role: 'cohort',
955
1006
  });
956
1007
  const applyRouteLabel =
@@ -960,7 +1011,7 @@ export async function createStoryIssues({ provider, stories, opts = {} }) {
960
1011
  label: routeLabel,
961
1012
  color: LITE_ROUTE_LABEL_COLOR,
962
1013
  description:
963
- 'Ceremony-lite hint (human-visible only): /deliver re-derives the ' +
1014
+ 'Ceremony-lite hint (human-visible only): /mandrel-deliver re-derives the ' +
964
1015
  'route from the Story body shape; every close gate runs unchanged.',
965
1016
  role: 'route-marker',
966
1017
  }));
@@ -1000,6 +1051,14 @@ export async function createStoryIssues({ provider, stories, opts = {} }) {
1000
1051
  ...(applyCohortLabel ? [cohortLabel] : []),
1001
1052
  ...(applyRouteLabel ? [routeLabel] : []),
1002
1053
  ],
1054
+ // Story #5112 — hand the provider the same content-keyed lookup this
1055
+ // loop's resume path uses, so a retry after a lost response adopts the
1056
+ // issue attempt 1 already filed instead of creating a twin.
1057
+ findExisting: () =>
1058
+ findOpenStoryByPlanFingerprint({
1059
+ provider,
1060
+ fingerprint: story.fingerprint,
1061
+ }),
1003
1062
  });
1004
1063
  const id = result?.id ?? result?.number;
1005
1064
  if (!Number.isInteger(id)) {
@@ -1012,7 +1071,9 @@ export async function createStoryIssues({ provider, stories, opts = {} }) {
1012
1071
  id,
1013
1072
  title: story.title,
1014
1073
  url: result.url,
1015
- adopted: false,
1074
+ // True when the provider's retry probe adopted an issue a lost-response
1075
+ // first attempt had already filed — pre-existing either way.
1076
+ adopted: result.adopted === true,
1016
1077
  });
1017
1078
  idBySlug.set(story.slug, id);
1018
1079
  }
@@ -1040,9 +1101,9 @@ export async function createStoryIssues({ provider, stories, opts = {} }) {
1040
1101
  *
1041
1102
  * This is what makes `agent::ready` *mean* "fully persisted": by the time it
1042
1103
  * lands, the Story's `story-plan-state` checkpoint is already on the ticket, so
1043
- * a `/deliver` that picks it up cannot read a null checkpoint.
1104
+ * a `/mandrel-deliver` that picks it up cannot read a null checkpoint.
1044
1105
  *
1045
- * Fails closed: an un-flipped Story is invisible to `/deliver`, which is the
1106
+ * Fails closed: an un-flipped Story is invisible to `/mandrel-deliver`, which is the
1046
1107
  * safe direction — the operator is told exactly which ids need the label.
1047
1108
  *
1048
1109
  * **Collect failures, never fast-fail** (preserved verbatim under the
@@ -1098,7 +1159,7 @@ export async function markStoriesReady({ provider, created }) {
1098
1159
  `[plan-persist] ${failed.length} Story(ies) were created with their ` +
1099
1160
  'checkpoints but could not be flipped to agent::ready:\n' +
1100
1161
  `${failed.map((f) => ` - ${f}`).join('\n')}\n` +
1101
- 'They are invisible to /deliver until the label lands. Re-run persist ' +
1162
+ 'They are invisible to /mandrel-deliver until the label lands. Re-run persist ' +
1102
1163
  '(it resumes rather than duplicating) or add the label by hand.',
1103
1164
  );
1104
1165
  }
@@ -163,8 +163,8 @@ export function buildPlanSummaryCommentBody({
163
163
  // must name real ids, not a batch token that no longer exists.
164
164
  const deliverCommand =
165
165
  Array.isArray(stories) && stories.length > 0
166
- ? `/deliver ${stories.map((s) => s.id).join(' ')}`
167
- : '/deliver <storyId> [<storyId> ...]';
166
+ ? `/mandrel-deliver ${stories.map((s) => s.id).join(' ')}`
167
+ : '/mandrel-deliver <storyId> [<storyId> ...]';
168
168
 
169
169
  return [
170
170
  `### 📋 Plan Summary — Story #${epicId} is \`agent::ready\``,
@@ -182,6 +182,6 @@ export function buildPlanSummaryCommentBody({
182
182
  ...renderWaveTableLines(waveTable),
183
183
  ...renderSharedEditorLines(conflictFindings),
184
184
  '',
185
- `_Deliver with \`${deliverCommand}\` — \`/deliver\` resolves the dependency graph from live state, so edges may point at Stories from earlier plan runs._`,
185
+ `_Deliver with \`${deliverCommand}\` — \`/mandrel-deliver\` resolves the dependency graph from live state, so edges may point at Stories from earlier plan runs._`,
186
186
  ].join('\n');
187
187
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
- * supersede-ops.js — close the `/plan --tickets` source issues that the
2
+ * supersede-ops.js — close the `/mandrel-plan --tickets` source issues that the
3
3
  * authored Stories supersede (Story #4535).
4
4
  *
5
5
  * `plan-context.js` fetches the source issues, emits `sourceTickets[]` on the
6
- * `/plan` envelope, and (with `--out`) writes that envelope to disk.
6
+ * `/mandrel-plan` envelope, and (with `--out`) writes that envelope to disk.
7
7
  * `resolveSourceTicketIds` below reads the id set back off it, so the normal
8
- * `/plan --tickets` path needs no flag; an explicit `--source-tickets` still
8
+ * `/mandrel-plan --tickets` path needs no flag; an explicit `--source-tickets` still
9
9
  * wins when passed, as the override for hand-driven runs (Story #4554).
10
10
  * Before that thread existed the ids reached
11
11
  * this module *solely* via the hand-passed flag, so a forgotten flag left
@@ -187,7 +187,7 @@ function sameIdSet(a, b) {
187
187
  * loudly (the operator is overriding what the run actually fetched)
188
188
  * but honoured.
189
189
  * 2. otherwise the envelope's `sourceTickets[]` — so the common
190
- * `/plan --tickets` path needs no flag at all.
190
+ * `/mandrel-plan --tickets` path needs no flag at all.
191
191
  * 3. otherwise empty, reported as `origin: 'none'`.
192
192
  *
193
193
  * `origin` is surfaced on the persist envelope so a run that superseded
@@ -240,7 +240,7 @@ function describeStoryIds(entries) {
240
240
  * live against an inconsistent tracker.
241
241
  *
242
242
  * @param {Array<{ slug: string, supersedes: Array<{ id: number }> }>} stories
243
- * @param {number[]} sourceTicketIds Ids passed to `/plan --tickets`.
243
+ * @param {number[]} sourceTicketIds Ids passed to `/mandrel-plan --tickets`.
244
244
  */
245
245
  export function assertSupersedePartition(stories, sourceTicketIds = []) {
246
246
  const list = Array.isArray(stories) ? stories : [];
@@ -313,7 +313,7 @@ export function buildSupersedeCommentBody({
313
313
  const lines = [
314
314
  `**Superseded by #${story.id}** — *${story.title}* (${labels.join(', ')}).`,
315
315
  '',
316
- `Planned via \`/plan --tickets ${sourceTicketIds.join(',')}\`.`,
316
+ `Planned via \`/mandrel-plan --tickets ${sourceTicketIds.join(',')}\`.`,
317
317
  ];
318
318
  if (note) {
319
319
  lines.push('', note);