mandrel 1.90.0 → 1.92.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 (115) hide show
  1. package/.agents/README.md +5 -5
  2. package/.agents/docs/SDLC.md +75 -107
  3. package/.agents/docs/configuration.md +1 -1
  4. package/.agents/docs/quality-gates.md +8 -7
  5. package/.agents/docs/workflows.md +4 -10
  6. package/.agents/instructions.md +2 -2
  7. package/.agents/schemas/agentrc.schema.json +1 -1
  8. package/.agents/schemas/lifecycle/README.md +2 -1
  9. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  10. package/.agents/schemas/risk-verdict.schema.json +14 -1
  11. package/.agents/scripts/README.md +0 -46
  12. package/.agents/scripts/analyze-execution.js +76 -1
  13. package/.agents/scripts/check-doc-links.js +3 -0
  14. package/.agents/scripts/check-workflow-cli-lint.js +208 -0
  15. package/.agents/scripts/epic-audit-recheck.js +1 -1
  16. package/.agents/scripts/epic-deliver-preflight.js +37 -1
  17. package/.agents/scripts/epic-plan-clarity.js +16 -6
  18. package/.agents/scripts/epic-plan-decompose.js +37 -59
  19. package/.agents/scripts/epic-plan-healthcheck.js +39 -6
  20. package/.agents/scripts/epic-plan-spec.js +41 -168
  21. package/.agents/scripts/epic-reconcile.js +7 -1
  22. package/.agents/scripts/lib/Logger.js +4 -4
  23. package/.agents/scripts/lib/audit-suite/index.js +4 -5
  24. package/.agents/scripts/lib/audit-suite/runner.js +6 -4
  25. package/.agents/scripts/lib/audit-suite/selector.js +2 -3
  26. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  27. package/.agents/scripts/lib/command-header.js +20 -0
  28. package/.agents/scripts/lib/config/github.js +1 -3
  29. package/.agents/scripts/lib/config-settings-schema.js +4 -3
  30. package/.agents/scripts/lib/degraded-mode.js +1 -1
  31. package/.agents/scripts/lib/label-constants.js +18 -3
  32. package/.agents/scripts/lib/label-taxonomy.js +14 -3
  33. package/.agents/scripts/lib/mutation/baseline-snapshot.js +7 -6
  34. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +18 -8
  35. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -4
  36. package/.agents/scripts/lib/orchestration/docs-digest.js +2 -2
  37. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +4 -0
  38. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +22 -12
  39. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +6 -1
  40. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +18 -2
  41. package/.agents/scripts/{lifecycle-diff.js → lib/orchestration/lifecycle/ledger-diff.js} +10 -76
  42. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +1 -1
  43. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +32 -0
  44. package/.agents/scripts/lib/orchestration/plan-context.js +510 -0
  45. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +177 -0
  46. package/.agents/scripts/lib/orchestration/plan-metrics.js +445 -0
  47. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +359 -0
  48. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +127 -0
  49. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +978 -0
  50. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +191 -0
  51. package/.agents/scripts/lib/orchestration/plan-reachability.js +160 -0
  52. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +3 -3
  53. package/.agents/scripts/lib/orchestration/planning-risk.js +1 -1
  54. package/.agents/scripts/lib/orchestration/remote-verifier.js +165 -0
  55. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +10 -0
  56. package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
  57. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +1 -1
  58. package/.agents/scripts/lib/orchestration/ticketing/reads.js +11 -2
  59. package/.agents/scripts/lib/plan-phase-cleanup.js +16 -0
  60. package/.agents/scripts/lib/presentation/manifest-persistence.js +2 -3
  61. package/.agents/scripts/lib/templates/spec-author-prompts.js +74 -0
  62. package/.agents/scripts/plan-context.js +186 -0
  63. package/.agents/scripts/plan-critics.js +227 -0
  64. package/.agents/scripts/plan-persist.js +383 -0
  65. package/.agents/scripts/pr-watch-with-update.js +8 -7
  66. package/.agents/scripts/run-lint.js +10 -11
  67. package/.agents/scripts/single-story-init.js +22 -0
  68. package/.agents/scripts/story-plan.js +19 -7
  69. package/.agents/scripts/sync-claude-commands.js +10 -5
  70. package/.agents/skills/core/knowledge-transfer/SKILL.md +11 -7
  71. package/.agents/workflows/audit-documentation.md +5 -7
  72. package/.agents/workflows/audit-lighthouse.md +1 -0
  73. package/.agents/workflows/audit-security.md +1 -0
  74. package/.agents/workflows/audit-to-stories.md +16 -5
  75. package/.agents/workflows/deliver.md +8 -0
  76. package/.agents/workflows/git-deliver.md +76 -243
  77. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  78. package/.agents/workflows/helpers/code-review.md +7 -6
  79. package/.agents/workflows/helpers/deliver-epic.md +16 -4
  80. package/.agents/workflows/helpers/plan-epic-reference.md +109 -96
  81. package/.agents/workflows/helpers/plan-epic.md +246 -999
  82. package/.agents/workflows/helpers/plan-story.md +14 -14
  83. package/.agents/workflows/helpers/scope-triage-gate.md +6 -4
  84. package/.agents/workflows/helpers/single-story-deliver.md +13 -4
  85. package/.agents/workflows/helpers/worktree-lifecycle.md +9 -7
  86. package/.agents/workflows/mandrel-update.md +144 -466
  87. package/.agents/workflows/plan.md +73 -143
  88. package/docs/CHANGELOG.md +20 -0
  89. package/lib/cli/registry.js +21 -3
  90. package/package.json +1 -1
  91. package/.agents/schemas/loop-unit.schema.json +0 -70
  92. package/.agents/scripts/assert-branch.js +0 -81
  93. package/.agents/scripts/check-loop-units.js +0 -204
  94. package/.agents/scripts/detect-merges.js +0 -111
  95. package/.agents/scripts/git-pr-quality-gate.js +0 -205
  96. package/.agents/scripts/git-rebase-and-resolve.js +0 -234
  97. package/.agents/scripts/hierarchy-gate.js +0 -192
  98. package/.agents/scripts/hydrate-context.js +0 -179
  99. package/.agents/scripts/lib/loop-units/validate-loop-unit.js +0 -197
  100. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +0 -167
  101. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +0 -69
  102. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +0 -86
  103. package/.agents/scripts/loc-delta.js +0 -205
  104. package/.agents/scripts/render-manifest.js +0 -143
  105. package/.agents/scripts/run-audit-suite.js +0 -97
  106. package/.agents/scripts/select-audits.js +0 -155
  107. package/.agents/scripts/update-mutation-baseline.js +0 -189
  108. package/.agents/workflows/explain.md +0 -118
  109. package/.agents/workflows/git-merge-pr.md +0 -377
  110. package/.agents/workflows/helpers/epic-plan-decompose.md +0 -22
  111. package/.agents/workflows/helpers/epic-plan-spec.md +0 -22
  112. package/.agents/workflows/loops/README.md +0 -65
  113. package/.agents/workflows/loops/fix-failing-tests.md +0 -74
  114. package/.agents/workflows/loops/nightly-audit.md +0 -81
  115. package/.agents/workflows/loops/watch-ci.md +0 -68
@@ -1,21 +1,21 @@
1
1
  ---
2
2
  description:
3
3
  Unified planning entry point. Routes a seed idea (via scope triage) or an
4
- existing Epic ID to the right planning path — the full Epic pipeline
5
- (sectioned Epic body: Tech Spec + Acceptance Table, then decomposition)
6
- or the standalone-Story authoring path — and absorbs every planning flag.
4
+ existing Epic ID to the right planning path — the 3-step Epic path
5
+ (interrogate author persist) or the standalone-Story authoring path —
6
+ and absorbs every planning flag.
7
7
  ---
8
8
 
9
9
  # /plan [Epic ID] | --idea "<seed>" | --from-notes <path>
10
10
 
11
11
  ## Role
12
12
 
13
- Router. `/plan` owns argument parsing and path selection only — all phase
13
+ Router. `/plan` owns argument parsing and path selection only — all step
14
14
  content lives in the two path helpers:
15
15
 
16
- - [`helpers/plan-epic.md`](helpers/plan-epic.md) — the full Epic planning
17
- pipeline (Tech Spec + Acceptance Table folded into the Epic body, work
18
- breakdown, healthcheck, handoff).
16
+ - [`helpers/plan-epic.md`](helpers/plan-epic.md) — the 3-step Epic planning
17
+ path (interrogate → author → persist; Tech Spec + Acceptance Table folded
18
+ into the Epic body, then the Story backlog or a single-delivery marker).
19
19
  - [`helpers/plan-story.md`](helpers/plan-story.md) — the standalone-Story
20
20
  authoring path (context envelope → host-LLM draft → HITL → issue create).
21
21
 
@@ -28,25 +28,27 @@ path; no new classification machinery exists.
28
28
 
29
29
  | Invocation | Behavior |
30
30
  | --- | --- |
31
- | `/plan --idea "<seed>"` | Ideation → **scope triage**. Verdict `epic` → run [`helpers/plan-epic.md`](helpers/plan-epic.md) from Phase 1 (Idea Refinement). Verdict `story` → run [`helpers/plan-story.md`](helpers/plan-story.md) Phases 1–3. Verdict `borderline` → present both options and let the operator choose. |
32
- | `/plan <epicId>` | Existing-Epic path — run [`helpers/plan-epic.md`](helpers/plan-epic.md) from Phase 5. When the helper's story-sized advisory fires (the Epic is really one Story), convert **internally** by switching to [`helpers/plan-story.md`](helpers/plan-story.md) — do not re-triage and do not hop commands. |
31
+ | `/plan --idea "<seed>"` | Ideation → **scope triage**. Verdict `epic` → run [`helpers/plan-epic.md`](helpers/plan-epic.md) from its ideation entry. Verdict `story` → run [`helpers/plan-story.md`](helpers/plan-story.md) Phases 1–3. Verdict `borderline` → present both options and let the operator choose. |
32
+ | `/plan <epicId>` | Existing-Epic path — run [`helpers/plan-epic.md`](helpers/plan-epic.md) from its existing-Epic entry. When the helper's story-sized advisory fires (the Epic is really one Story), convert **internally** by switching to [`helpers/plan-story.md`](helpers/plan-story.md) — do not re-triage and do not hop commands. |
33
33
  | `/plan --from-notes <path>` | Internal handoff target (e.g. from `/audit-to-stories`). The notes file already encodes the path decision; do **not** re-run scope triage. Route per the notes' declared shape. |
34
34
 
35
35
  ## Flags
36
36
 
37
- `/plan` absorbs every flag the two retired planning commands accepted and
38
- forwards them to the active path helper:
37
+ `/plan` absorbs every planning flag and forwards it to the active path
38
+ helper:
39
39
 
40
40
  | Flag | Path | Meaning |
41
41
  | --- | --- | --- |
42
42
  | `--idea "<seed>"` | both | Seed text; triggers scope triage. |
43
43
  | `--from-notes <path>` | both | Pre-triaged handoff notes; skips triage. |
44
- | `--force` | Epic | Close + recreate an existing ticket tree on re-plan. |
45
- | `--force-review` | Epic | Force the operator review gate even when risk routing would skip it. |
44
+ | `--force` | Epic | Re-plan: overwrite managed sections in place and close + recreate the ticket tree. |
45
+ | `--amend` | Epic | Change-request delta persist: tickets carry `op: add\|modify\|keep\|close`; close ops require `--explicit-delete` after the dry-run diff. |
46
+ | `--force-review` | Epic | Force the gate #2 operator review even when risk routing would skip it. |
46
47
  | `--allow-over-budget` | Epic | Permit a decomposition that exceeds the framework `maxTickets` reviewability budget. |
47
- | `--yes` | both | **Non-interactive / headless mode.** Deterministically auto-proceeds the two `/plan` HITL STOP gates — the ideation one-pager / scope-triage confirm (gate #1) and the Phase-7 Epic operator review gate (gate #2) — without waiting for operator input. Parallel to [`/deliver --yes`](deliver.md). Composes with `--allow-over-budget` and with the risk-routed Phase-7 skip (it forces a proceed where those do not apply). Default (flag absent) behavior is unchanged: both gates still STOP for interactive use. |
48
+ | `--yes` | both | **Non-interactive / headless mode.** Deterministically auto-proceeds the two `/plan` HITL STOP gates — gate #1 (exit of interrogate) and gate #2 (risk-routed pre-persist review) — without waiting for operator input. Parallel to [`/deliver --yes`](deliver.md). Composes with `--allow-over-budget` and with the risk-routed gate #2 skip (it forces a proceed where those do not apply). Default (flag absent) behavior is unchanged: both gates still STOP for interactive use. |
48
49
  | `--steal` | Epic | Forcibly transfer a foreign Epic-lease. |
49
- | `--dry-run` | both | Author + validate without GitHub writes. |
50
+ | `--resume` | Epic | Continue a partial persist (rate-limit / crash recovery). |
51
+ | `--dry-run` | Story | Author + validate without GitHub writes (`story-plan.js --dry-run`). |
50
52
  | `--body <path>` | Story | Pre-authored Story body file; validate (and create, unless `--dry-run`) without re-authoring. |
51
53
  | `--persona <name>` | Story | Override the persona label on the drafted Story. |
52
54
  | `--refine` / `--no-refine` | Story | Toggle the draft refinement loop. |
@@ -55,48 +57,48 @@ forwards them to the active path helper:
55
57
  the story path (or vice versa) is reported once
56
58
  (`[plan] --force has no effect on the story path`) and ignored — never an
57
59
  error. The historical bidirectional escalation between the two planning
58
- commands (story-sized Epic ↘ Story; epic-sized Story draft ↗ Epic) is now
59
- an **internal branch switch** inside this router: same skills, same
60
- helpers, no command hop and no operator re-entry.
60
+ commands (story-sized Epic ↘ Story; epic-sized Story draft ↗ Epic) is an
61
+ **internal branch switch** inside this router: same skills, same helpers, no
62
+ command hop and no operator re-entry.
61
63
 
62
64
  ### Headless / non-interactive mode (`--yes`)
63
65
 
64
66
  `--yes` is the headless escape hatch for an unattended driver (CI, a
65
67
  benchmark harness, or any `claude -p` run with no human at the keyboard). It
66
- is the `/plan`-side parallel of [`/deliver --yes`](deliver.md), which
67
- suppresses that command's segment-plan confirmation. `/plan` has exactly
68
- **two** HITL STOP gates, and `--yes` deterministically auto-proceeds **both**
69
- without waiting for operator input:
70
-
71
- 1. **Gate #1 the ideation one-pager / scope-triage / clarity confirm.**
72
- This single conceptual gate has three faces depending on the entry form,
73
- and `--yes` auto-proceeds **all three**:
74
- - On the `--idea` Epic path,
75
- [`helpers/plan-epic.md`](helpers/plan-epic.md) Phase 1 (folding in the
76
- Phase 1.5 scope-triage verdict) STOPs to confirm the sharpened one-pager.
68
+ is the `/plan`-side parallel of [`/deliver --yes`](deliver.md). `/plan` has
69
+ exactly **two** HITL STOP gates, and `--yes` deterministically auto-proceeds
70
+ **both** without waiting for operator input:
71
+
72
+ 1. **Gate #1 — the exit of the interrogate step.** One conceptual gate with
73
+ a face per entry form, and `--yes` auto-proceeds all of them:
74
+ - On the `--idea` Epic path, the interrogate step of
75
+ [`helpers/plan-epic.md`](helpers/plan-epic.md) STOPs to confirm the
76
+ sharpened one-pager, folding in the scope-triage verdict and the
77
+ duplicate-candidate review.
77
78
  - On the `--idea` Story path,
78
79
  [`helpers/plan-story.md`](helpers/plan-story.md) Phase 2 STOPs to
79
80
  confirm the drafted Story body.
80
- - On the existing-Epic (`/plan <epicId>`) path, the Phase 6 Epic Clarity
81
- Gate's **needs-refinement** branch STOPs to confirm the refined-body
82
- diff before persisting it.
83
-
84
- Under `--yes` each auto-proceeds: the one-pager / draft / refined-body
85
- confirm resolves as **approved**, and a `story` / `borderline` triage
86
- verdict resolves to its **recommended** branch (the one the gate prose
87
- marks "Recommended") rather than prompting the three-way choice. The
88
- verdict / clarity scoring is still recorded in chat (one line); only the
89
- *wait* is suppressed. The clarity-gate *scoring* itself (deterministic
90
- section-presence in Phase 6 step 1) still runs `--yes` suppresses only
91
- the operator confirm of the proposed refinement.
92
- 2. **Gate #2 — the Phase-7 Epic operator review gate.** When risk routing
93
- forces a review (`planningRisk.requiresReview === true`, or the operator
94
- also passed `--force-review`),
95
- [`helpers/plan-epic.md`](helpers/plan-epic.md) Phase 7 STOPs for operator
96
- approval of the updated Epic body (its Tech Spec sections and
97
- `## Acceptance Table`) before decomposition.
98
- Under `--yes` this review auto-proceeds straight to Phase 8 exactly as
99
- on the low-risk auto-proceed path.
81
+ - On the existing-Epic (`/plan <epicId>`) path, the interrogate step
82
+ STOPs to confirm the folded re-plan / clarity-refinement / advisory
83
+ outcomes (the Clarity Gate's refined-body diff among them).
84
+
85
+ Under `--yes` each resolves as **approved**, and a `story` /
86
+ `borderline` triage verdict resolves to its **Recommended** branch rather
87
+ than prompting the three-way choice. **The interrogation itself runs
88
+ exactly one bounded pass**: no operator questions are asked — facts come
89
+ from the codebase, and every unresolved unknown lands in the one-pager's
90
+ **Key Assumptions** section instead of a question, so a headless driver
91
+ can never hang inside a free-form interrogation. The verdict / clarity
92
+ scoring is still recorded in chat (one line); only the *wait* is
93
+ suppressed — the deterministic clarity *scoring* inside the
94
+ `plan-context.js` envelope always runs.
95
+ 2. **Gate #2 — the risk-routed pre-persist review.** When the authored risk
96
+ verdict routes to review (high risk, or the operator also passed
97
+ `--force-review`), [`helpers/plan-epic.md`](helpers/plan-epic.md) step 3
98
+ STOPs for operator approval of the assembled plan (spec, tickets, risk,
99
+ and `deliveryShape` in one view) before the persist CLI runs. Under
100
+ `--yes` this review auto-proceeds straight to the persist call, exactly
101
+ as on the low-risk auto-proceed branch.
100
102
 
101
103
  **Composition.** `--yes` is orthogonal to the other planning flags and
102
104
  composes cleanly:
@@ -104,111 +106,36 @@ composes cleanly:
104
106
  - With **`--allow-over-budget`**: `--yes` suppresses the gate *waits* while
105
107
  `--allow-over-budget` still governs the `maxTickets` over-budget persist —
106
108
  passing `--yes` alone does **not** waive the budget gate.
107
- - With the **risk-routed Phase-7 skip**: when risk routing already skips the
108
- Phase-7 review (low-risk, no `--force-review`), `--yes` is a no-op for that
109
- gate — it only *forces a proceed where the gate would otherwise STOP*, it
110
- never *adds* a stop or relaxes any non-HITL validator.
109
+ - With the **risk-routed gate #2 skip**: when risk routing already
110
+ auto-proceeds the review (low-risk, no `--force-review`), `--yes` is a
111
+ no-op for that gate — it only *forces a proceed where the gate would
112
+ otherwise STOP*, it never *adds* a stop or relaxes any non-HITL validator.
111
113
 
112
114
  **`--yes` suppresses only the HITL operator *waits* above.** It does **not**
113
- relax any deterministic gate — the Phase 6 Epic Clarity Gate **scoring**
114
- (section-presence), the Phase 7.5 Tech Spec Section Gate, the file-assumption
115
- / DAG validators, the Phase 10 readiness healthcheck, and the `agent::blocked`
116
- runtime pause all behave exactly as without the flag. A `--yes` run that hits
117
- one of those still fails closed; the flag is an operator-input suppressor, not
118
- a validation override. The other operator-input *waits* in the pipeline also
119
- auto-proceed under `--yes` the non-blocking Phase 2 duplicate-search pause
120
- (distinct-Epic confirmed), the Phase 6 clarity refinement-diff confirm
121
- (refined body approved), and the advisory Phase 8.3/8.4/8.5
122
- consolidation/critic diffs (consolidated/critic output applied) — since they
123
- are operator-input waits, not validators. The two *named* HITL STOP gates the
124
- Story tracks (gate #1, gate #2) are the load-bearing pair; these additional
125
- waits are auto-proceeded for the same headless reason.
126
-
127
- ## Boot sweep
128
-
129
- Before anything else — ahead of the first-run preflight — run the **protected
130
- boot sweep** so `/plan` opens against a tidy local checkout instead of one
131
- still carrying the merged refs of the last delivered Epic or Story:
132
-
133
- ```bash
134
- node .agents/scripts/boot-sweep.js \
135
- --include 'story-*' --include 'epic/*' \
136
- --include 'feat/*' --include 'fix/*' --include 'chore/*'
137
- ```
138
-
139
- This is the **safe subset** of the `/git-cleanup` phases: it fast-forwards the
140
- base branch (`main`), prunes stale remote-tracking refs, and reaps every local
141
- branch whose PR is **merged** and whose HEAD matches the merged `headRefOid`.
142
- It **never** touches the stash stack, and its `evaluateProtection` partition
143
- skips (never reaps) any candidate with unpushed work, a dirty worktree, or a
144
- still-open parent Story ticket — so it is safe to run unattended at the top of
145
- a planning session. The sweep is **silent on a no-op**: with nothing to reap
146
- and `main` already current it prints a single summary line
147
- (`[boot-sweep] reaped 0 local + 0 remote; protected 0.`) and moves on. Its exit
148
- code is always `0` — a failed sweep is swallowed and reported in the summary,
149
- never allowed to fail the planning run.
150
-
151
- ## First-run preflight
152
-
153
- Before routing to a path helper, run a **first-run preflight** to catch
154
- common day-0 issues that would silently degrade every downstream task.
155
-
156
- ### When the preflight fires
157
-
158
- The preflight runs when **any** of these is true:
159
-
160
- 1. One or more `project.docsContextFiles` entries are absent under the
161
- configured `docsRoot`.
162
- 2. One or more present `docsContextFiles` still carry the
163
- `<!-- MANDREL:STUB -->` marker (i.e. they are un-edited scaffolded stubs).
164
- 3. The last `mandrel doctor` verdict cached in `temp/doctor-result.json`
165
- records `"verdict": "unready"`. An **absent** cache file is no signal —
166
- doctor may simply never have run; only an explicit recorded unready
167
- verdict fires this signal.
168
-
169
- ### Preflight procedure
170
-
171
- 1. **Detect the condition.** Check the three signals above. When none is
172
- true, skip the preflight entirely — no operator interaction, no delay.
173
- 2. **Offer to flesh out docs.** Summarize the found condition to the
174
- operator (e.g. "3 docsContextFiles are missing" or "architecture.md
175
- still carries the stub marker") and ask:
176
- > *Do you want to flesh out these docs from the codebase before planning?
177
- > [y/N]*
178
- 3. **On acceptance.** Walk through each affected file, read relevant
179
- codebase artifacts (source files, README, existing docs), and write real
180
- content to replace the stub. Then re-run `mandrel doctor` to confirm
181
- readiness. If doctor passes, proceed to routing.
182
- 4. **On decline.** Log one line:
183
- > *[plan] Proceeding with degraded doc context — planning quality may be
184
- > reduced.*
185
- Then continue to the normal routing procedure below.
186
-
187
- The preflight is **never a hard stop** — declining continues planning with a
188
- noted degradation. It only fires when there is a genuine signal (missing or
189
- stubbed docs, or an unready doctor verdict).
115
+ relax any deterministic gate — the clarity scoring, the Tech Spec section
116
+ gate, the ticket validator / file-assumption / DAG / budget gates, the
117
+ inline readiness healthcheck, and the `agent::blocked` runtime pause all
118
+ behave exactly as without the flag; it is an operator-input suppressor, not
119
+ a validation override. A `--yes` run that hits one of those still fails
120
+ closed. The smaller operator-input waits folded into the two gates — the
121
+ duplicate-candidate review, the clarity refinement-diff confirm, and the
122
+ advisory critic diffs — auto-proceed for the same headless reason.
190
123
 
191
124
  ## Procedure
192
125
 
193
126
  1. **Parse args.** Exactly one of `<epicId>`, `--idea`, `--from-notes`, or
194
127
  `--body` must be present; anything else is a usage error naming the four
195
128
  forms. A `--body` invocation routes to the story path (no triage).
196
- 2. **Boot sweep.** Run the protected boot sweep above
197
- (`node .agents/scripts/boot-sweep.js …`) to fast-forward `main`, prune
198
- stale remotes, and reap merged-PR branches. Silent on a no-op; never fails
199
- the run.
200
- 3. **First-run preflight.** Run the preflight above. Skip when all signals
201
- are clear (healthy project).
202
- 4. **Triage (idea path only).** Run the
129
+ 2. **Triage (idea path only).** Run the
203
130
  [`core/scope-triage`](../skills/core/scope-triage/SKILL.md) skill on the
204
131
  seed. Record the verdict in chat (one line).
205
- 5. **Delegate.** Read the selected path helper **in full** and execute it
206
- from its entry phase, forwarding the absorbed flags (including `--yes`).
207
- The helper's phase numbering, HITL gates, and scripts are unchanged — this
208
- router adds no phase content. When `--yes` is present, the two HITL STOP
209
- gates auto-proceed per [Headless / non-interactive mode](#headless--non-interactive-mode---yes)
132
+ 3. **Delegate.** Read the selected path helper **in full** and execute it
133
+ from its entry, forwarding the absorbed flags (including `--yes`). The
134
+ helper's steps, HITL gates, and scripts are the procedure — this router
135
+ adds no step content. When `--yes` is present, the two HITL STOP gates
136
+ auto-proceed per [Headless / non-interactive mode](#headless--non-interactive-mode---yes)
210
137
  above; every deterministic gate still runs.
211
- 6. **Internal returns.** When a path helper would historically have handed
138
+ 4. **Internal returns.** When a path helper would historically have handed
212
139
  off to the other planning command, switch helpers in-place and continue;
213
140
  surface the switch to the operator as a one-line note.
214
141
 
@@ -219,6 +146,9 @@ stubbed docs, or an unready doctor verdict).
219
146
  planned Epic, `/deliver <storyId>` for a standalone Story.
220
147
  - The router never calls planning scripts directly; the path helpers own
221
148
  every script invocation.
149
+ - Checkout hygiene (branch sweep) and day-0 doc readiness are owned by
150
+ [`/git-cleanup`](git-cleanup.md) and `mandrel doctor` respectively —
151
+ `/plan` runs no boot sweep and no first-run preflight.
222
152
 
223
153
  ## See also
224
154
 
package/docs/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.92.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.91.0...mandrel-v1.92.0) (2026-07-12)
6
+
7
+
8
+ ### Added
9
+
10
+ * **plan:** collapse the plan workflow prose to interrogate → author → persist (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4492](https://github.com/dsj1984/mandrel/issues/4492)) ([b7cb068](https://github.com/dsj1984/mandrel/commit/b7cb068fed8c599ce1a42e51b3ff3f7658cb08b1))
11
+ * **plan:** delivery-shape modes — single persist, amend delta (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4491](https://github.com/dsj1984/mandrel/issues/4491)) ([63e97c5](https://github.com/dsj1984/mandrel/commit/63e97c5d4546f1f1245e4d6b33428338d50bcded))
12
+ * **plan:** plan-context emit cli — single envelope for the 3-step collapse (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4489](https://github.com/dsj1984/mandrel/issues/4489)) ([9255067](https://github.com/dsj1984/mandrel/commit/92550676af49ec33ec8ba9f9cc48785254bd53af))
13
+ * **plan:** plan-metrics ledger — measure turns-per-plan before the collapse (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4487](https://github.com/dsj1984/mandrel/issues/4487)) ([d64b3aa](https://github.com/dsj1984/mandrel/commit/d64b3aa275f31c00760cc2ba6637674f1945d408))
14
+ * **plan:** plan-persist cli — single github-write surface for the 3-step collapse (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4490](https://github.com/dsj1984/mandrel/issues/4490)) ([b01b9bf](https://github.com/dsj1984/mandrel/commit/b01b9bf080718f237f394818e0978d6f87f58e49))
15
+ * **plan:** retire the 12-phase delegates — plan pipeline is context → author → persist (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4494](https://github.com/dsj1984/mandrel/issues/4494)) ([98e80f6](https://github.com/dsj1984/mandrel/commit/98e80f6550e6874c5793cfe7535a8d93bb8e7e72))
16
+ * **plan:** risk/size-conditional critics — consolidation, reachability, pre-mortem (refs [#4474](https://github.com/dsj1984/mandrel/issues/4474)) ([#4493](https://github.com/dsj1984/mandrel/issues/4493)) ([a264cb3](https://github.com/dsj1984/mandrel/commit/a264cb3ab0d21167ef444b9f8af8141baeef7b21))
17
+
18
+ ## [1.91.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.90.0...mandrel-v1.91.0) (2026-07-12)
19
+
20
+
21
+ ### Fixed
22
+
23
+ * **deliver:** verify the remote at entry and assert the delivery branch on origin (refs [#4483](https://github.com/dsj1984/mandrel/issues/4483)) ([#4484](https://github.com/dsj1984/mandrel/issues/4484)) ([a99ca2f](https://github.com/dsj1984/mandrel/commit/a99ca2fc4c74f7c397041dc4c703d845fae640c2))
24
+
5
25
  ## [1.90.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.89.0...mandrel-v1.90.0) (2026-07-12)
6
26
 
7
27
 
@@ -25,12 +25,12 @@ import fs from 'node:fs';
25
25
  import { createRequire } from 'node:module';
26
26
  import path from 'node:path';
27
27
  import { fileURLToPath } from 'node:url';
28
-
29
28
  import {
30
29
  REQUIRED_NODE_CEILING_MAJOR,
31
30
  REQUIRED_NODE_FLOOR,
32
31
  satisfiesNodeEngine,
33
32
  } from '../../.agents/scripts/lib/bootstrap/project-bootstrap.js';
33
+ import { isCommandExcluded } from '../../.agents/scripts/lib/command-header.js';
34
34
  import {
35
35
  defaultResolvePackageRoot,
36
36
  listFiles as listPayloadFiles,
@@ -238,11 +238,14 @@ function runGhAuth({ runner = spawn, env = process.env } = {}) {
238
238
  * Injectable seams (used by tests so no real filesystem is touched):
239
239
  * - `cwd()` replaces `process.cwd` so tests can pin the consumer root.
240
240
  * - `readDir` replaces `fs.readdirSync`.
241
+ * - `readFile` replaces `fs.readFileSync` (frontmatter `command: false`
242
+ * projection opt-out, #4482 — an unreadable source is treated as
243
+ * projected, matching the sync script's fail-loud read).
241
244
  *
242
- * @param {{ projectRoot?: string, cwd?: () => string, readDir?: (dir: string) => string[] }} [opts]
245
+ * @param {{ projectRoot?: string, cwd?: () => string, readDir?: (dir: string) => string[], readFile?: (file: string) => string | null }} [opts]
243
246
  * @returns {{ ok: boolean, detail: string, remedy?: string }}
244
247
  */
245
- function runCommandsInSync({ projectRoot, cwd, readDir } = {}) {
248
+ function runCommandsInSync({ projectRoot, cwd, readDir, readFile } = {}) {
246
249
  const getCwd = cwd ?? (() => process.cwd());
247
250
  const root = projectRoot ?? getCwd();
248
251
  const listDir =
@@ -254,14 +257,29 @@ function runCommandsInSync({ projectRoot, cwd, readDir } = {}) {
254
257
  return [];
255
258
  }
256
259
  });
260
+ const readSource =
261
+ readFile ??
262
+ ((file) => {
263
+ try {
264
+ return fs.readFileSync(file, 'utf8');
265
+ } catch {
266
+ return null;
267
+ }
268
+ });
257
269
 
258
270
  const srcDir = path.join(root, '.agents', 'workflows');
259
271
  const destDir = path.join(root, '.claude', 'commands');
260
272
 
261
273
  // Only top-level .md files are synced (helpers/ subdirectory excluded by
262
274
  // the sync script — they are path-included modules, not slash commands).
275
+ // Workflows whose frontmatter carries `command: false` (#4482) opt out of
276
+ // projection and must not count toward the expected command set.
263
277
  const sources = listDir(srcDir)
264
278
  .filter((f) => !f.startsWith('.'))
279
+ .filter((f) => {
280
+ const content = readSource(path.join(srcDir, f));
281
+ return content == null || !isCommandExcluded(content);
282
+ })
265
283
  .sort();
266
284
  const dests = listDir(destDir)
267
285
  .filter((f) => !f.startsWith('.'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mandrel",
3
- "version": "1.90.0",
3
+ "version": "1.92.0",
4
4
  "description": "Claude Code-first opinionated workflow framework: instructions, personas, skills, and SDLC workflows that govern AI coding assistants.",
5
5
  "files": [
6
6
  ".agents/",
@@ -1,70 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/loop-unit.schema.json",
4
- "version": "1.0.0",
5
- "title": "Loop Unit (recurring-work definition)",
6
- "description": "Schema for the YAML frontmatter of a loop unit — a markdown file under .agents/workflows/loops/ that defines a recurring/iterative unit of work with a checkable definition of done. The loop block carries the cadence (how the loop is paced), the goal (what the loop is trying to achieve), an optional verify command/array (the at-keyboard checks proving a round is complete — required for self-paced cadence, optional for interval/cron), a maxRounds backstop, and an onExhaust policy describing what happens when the round cap is hit without the goal being met.",
7
- "type": "object",
8
- "additionalProperties": true,
9
- "required": ["loop"],
10
- "properties": {
11
- "$schema": {
12
- "type": "string",
13
- "description": "Optional reference to this schema file so YAML editors with $schema-aware autocomplete can resolve it from the unit file itself."
14
- },
15
- "description": {
16
- "type": "string",
17
- "description": "Optional human-readable summary of what this loop unit does."
18
- },
19
- "loop": {
20
- "type": "object",
21
- "additionalProperties": false,
22
- "required": ["cadence", "goal"],
23
- "description": "The loop definition block. Carries the cadence, goal, conditional verify, round cap, and exhaustion policy.",
24
- "properties": {
25
- "cadence": {
26
- "type": "string",
27
- "enum": ["self-paced", "interval", "cron"],
28
- "description": "How the loop is paced. 'self-paced' lets the agent decide when to run the next round and therefore MUST carry a verify[] so each round has a checkable definition of done. 'interval' and 'cron' are externally scheduled, so verify is optional."
29
- },
30
- "goal": {
31
- "type": "string",
32
- "minLength": 1,
33
- "description": "Required. What the loop is trying to achieve — the standing objective each round works toward."
34
- },
35
- "verify": {
36
- "description": "Command(s) that prove a round is complete. A single command string or an array of command strings. Required when cadence is 'self-paced'; optional for 'interval' / 'cron'.",
37
- "oneOf": [
38
- { "type": "string", "minLength": 1 },
39
- {
40
- "type": "array",
41
- "items": { "type": "string", "minLength": 1 },
42
- "minItems": 1
43
- }
44
- ]
45
- },
46
- "maxRounds": {
47
- "type": "integer",
48
- "minimum": 1,
49
- "description": "Optional positive-integer backstop on the number of loop rounds before the onExhaust policy fires."
50
- },
51
- "onExhaust": {
52
- "type": "string",
53
- "enum": ["block", "report", "hand-back"],
54
- "description": "What happens when maxRounds is hit without the goal met. 'block' transitions to a HITL gate; 'report' emits a summary and stops; 'hand-back' returns control to the caller."
55
- }
56
- },
57
- "allOf": [
58
- {
59
- "if": {
60
- "properties": { "cadence": { "const": "self-paced" } },
61
- "required": ["cadence"]
62
- },
63
- "then": {
64
- "required": ["verify"]
65
- }
66
- }
67
- ]
68
- }
69
- }
70
- }
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /* node:coverage ignore file */
4
-
5
- /**
6
- * assert-branch.js — Pre-commit Branch Guard
7
- *
8
- * Exits non-zero if the current git branch does not match `--expected`.
9
- * Intended to be invoked immediately before `git add`/`git commit` in any
10
- * workflow where multiple agents share a working directory. Prevents the
11
- * parallel-story contention bug where one agent's `git add` sweeps another
12
- * agent's WIP after a concurrent `git checkout`.
13
- *
14
- * Usage:
15
- * node .agents/scripts/assert-branch.js --expected <branch-name>
16
- *
17
- * Exit codes:
18
- * 0 — Current branch matches expected.
19
- * 1 — Mismatch (stderr explains) or invocation error.
20
- */
21
-
22
- import { fileURLToPath } from 'node:url';
23
- import { gitSpawn } from './lib/git-utils.js';
24
- import { Logger } from './lib/Logger.js';
25
- import { PROJECT_ROOT } from './lib/project-root.js';
26
- export function assertBranch(expected, { cwd = PROJECT_ROOT } = {}) {
27
- if (!expected || typeof expected !== 'string') {
28
- return { ok: false, reason: 'missing --expected <branch>' };
29
- }
30
- const result = gitSpawn(cwd, 'branch', '--show-current');
31
- if (result.status !== 0) {
32
- return {
33
- ok: false,
34
- reason: `git branch --show-current failed: ${result.stderr}`,
35
- };
36
- }
37
- const actual = result.stdout;
38
- if (actual !== expected) {
39
- return {
40
- ok: false,
41
- reason: `branch mismatch — expected "${expected}", on "${actual}". Another agent may have switched the working directory. STOP: do not commit.`,
42
- actual,
43
- expected,
44
- };
45
- }
46
- return { ok: true, actual, expected };
47
- }
48
-
49
- const FLAG_SPEC = {
50
- expected: { type: 'string' },
51
- cwd: { type: 'string' },
52
- };
53
-
54
- function parseArgs(argv) {
55
- const out = { expected: null, cwd: null };
56
- for (const [name, def] of Object.entries(FLAG_SPEC)) {
57
- const idx = argv.indexOf(`--${name}`);
58
- if (idx >= 0 && idx + 1 < argv.length && def.type === 'string') {
59
- out[name] = argv[idx + 1];
60
- }
61
- }
62
- return out;
63
- }
64
-
65
- // cli-opt-out: synchronous CLI with bespoke main-guard; runAsCli's async-main pattern doesn't fit.
66
- const isMain = process.argv[1] === fileURLToPath(import.meta.url);
67
- if (isMain) {
68
- const { expected, cwd: flagCwd } = parseArgs(process.argv.slice(2));
69
- // Worktree-aware: hooks invoked inside a per-story worktree must guard the
70
- // worktree's HEAD, not the main checkout. Resolution precedence:
71
- // --cwd <path> > AGENT_WORKTREE_ROOT env > PROJECT_ROOT (main checkout)
72
- // Flag wins so operators can override even when the env var leaked from a
73
- // parent shell.
74
- const cwd = flagCwd || process.env.AGENT_WORKTREE_ROOT || PROJECT_ROOT;
75
- const result = assertBranch(expected, { cwd });
76
- if (!result.ok) {
77
- Logger.error(`[assert-branch] ${result.reason}`);
78
- process.exit(1);
79
- }
80
- Logger.info(`[assert-branch] ✅ on ${result.actual}`);
81
- }