mandrel 1.91.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.
- package/.agents/README.md +5 -5
- package/.agents/docs/SDLC.md +75 -107
- package/.agents/docs/configuration.md +1 -1
- package/.agents/docs/quality-gates.md +8 -7
- package/.agents/docs/workflows.md +4 -10
- package/.agents/instructions.md +2 -2
- package/.agents/schemas/agentrc.schema.json +1 -1
- package/.agents/schemas/lifecycle/README.md +2 -1
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/risk-verdict.schema.json +14 -1
- package/.agents/scripts/README.md +0 -46
- package/.agents/scripts/analyze-execution.js +76 -1
- package/.agents/scripts/check-doc-links.js +3 -0
- package/.agents/scripts/check-workflow-cli-lint.js +208 -0
- package/.agents/scripts/epic-audit-recheck.js +1 -1
- package/.agents/scripts/epic-plan-clarity.js +16 -6
- package/.agents/scripts/epic-plan-decompose.js +37 -59
- package/.agents/scripts/epic-plan-healthcheck.js +39 -6
- package/.agents/scripts/epic-plan-spec.js +41 -168
- package/.agents/scripts/epic-reconcile.js +7 -1
- package/.agents/scripts/lib/Logger.js +4 -4
- package/.agents/scripts/lib/audit-suite/index.js +4 -5
- package/.agents/scripts/lib/audit-suite/runner.js +6 -4
- package/.agents/scripts/lib/audit-suite/selector.js +2 -3
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/command-header.js +20 -0
- package/.agents/scripts/lib/config/github.js +1 -3
- package/.agents/scripts/lib/config-settings-schema.js +4 -3
- package/.agents/scripts/lib/degraded-mode.js +1 -1
- package/.agents/scripts/lib/label-constants.js +18 -3
- package/.agents/scripts/lib/label-taxonomy.js +14 -3
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +7 -6
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +18 -8
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -4
- package/.agents/scripts/lib/orchestration/docs-digest.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +4 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +22 -12
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +6 -1
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +18 -2
- package/.agents/scripts/{lifecycle-diff.js → lib/orchestration/lifecycle/ledger-diff.js} +10 -76
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-context.js +510 -0
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +177 -0
- package/.agents/scripts/lib/orchestration/plan-metrics.js +445 -0
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +359 -0
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +127 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +978 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +191 -0
- package/.agents/scripts/lib/orchestration/plan-reachability.js +160 -0
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +3 -3
- package/.agents/scripts/lib/orchestration/planning-risk.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +11 -2
- package/.agents/scripts/lib/plan-phase-cleanup.js +16 -0
- package/.agents/scripts/lib/presentation/manifest-persistence.js +2 -3
- package/.agents/scripts/lib/templates/spec-author-prompts.js +74 -0
- package/.agents/scripts/plan-context.js +186 -0
- package/.agents/scripts/plan-critics.js +227 -0
- package/.agents/scripts/plan-persist.js +383 -0
- package/.agents/scripts/pr-watch-with-update.js +8 -7
- package/.agents/scripts/run-lint.js +10 -11
- package/.agents/scripts/story-plan.js +19 -7
- package/.agents/scripts/sync-claude-commands.js +10 -5
- package/.agents/skills/core/knowledge-transfer/SKILL.md +11 -7
- package/.agents/workflows/audit-documentation.md +5 -7
- package/.agents/workflows/audit-lighthouse.md +1 -0
- package/.agents/workflows/audit-security.md +1 -0
- package/.agents/workflows/audit-to-stories.md +16 -5
- package/.agents/workflows/git-deliver.md +76 -243
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/code-review.md +7 -6
- package/.agents/workflows/helpers/deliver-epic.md +5 -4
- package/.agents/workflows/helpers/plan-epic-reference.md +109 -96
- package/.agents/workflows/helpers/plan-epic.md +246 -999
- package/.agents/workflows/helpers/plan-story.md +14 -14
- package/.agents/workflows/helpers/scope-triage-gate.md +6 -4
- package/.agents/workflows/helpers/single-story-deliver.md +5 -4
- package/.agents/workflows/helpers/worktree-lifecycle.md +9 -7
- package/.agents/workflows/mandrel-update.md +144 -466
- package/.agents/workflows/plan.md +73 -143
- package/docs/CHANGELOG.md +13 -0
- package/lib/cli/registry.js +21 -3
- package/package.json +1 -1
- package/.agents/schemas/loop-unit.schema.json +0 -70
- package/.agents/scripts/assert-branch.js +0 -81
- package/.agents/scripts/check-loop-units.js +0 -204
- package/.agents/scripts/detect-merges.js +0 -111
- package/.agents/scripts/git-pr-quality-gate.js +0 -205
- package/.agents/scripts/git-rebase-and-resolve.js +0 -234
- package/.agents/scripts/hierarchy-gate.js +0 -192
- package/.agents/scripts/hydrate-context.js +0 -179
- package/.agents/scripts/lib/loop-units/validate-loop-unit.js +0 -197
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +0 -167
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +0 -69
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +0 -86
- package/.agents/scripts/loc-delta.js +0 -205
- package/.agents/scripts/render-manifest.js +0 -143
- package/.agents/scripts/run-audit-suite.js +0 -97
- package/.agents/scripts/select-audits.js +0 -155
- package/.agents/scripts/update-mutation-baseline.js +0 -189
- package/.agents/workflows/explain.md +0 -118
- package/.agents/workflows/git-merge-pr.md +0 -377
- package/.agents/workflows/helpers/epic-plan-decompose.md +0 -22
- package/.agents/workflows/helpers/epic-plan-spec.md +0 -22
- package/.agents/workflows/loops/README.md +0 -65
- package/.agents/workflows/loops/fix-failing-tests.md +0 -74
- package/.agents/workflows/loops/nightly-audit.md +0 -81
- 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
|
|
5
|
-
(
|
|
6
|
-
|
|
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
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
32
|
-
| `/plan <epicId>` | Existing-Epic path — run [`helpers/plan-epic.md`](helpers/plan-epic.md) from
|
|
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
|
|
38
|
-
|
|
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 |
|
|
45
|
-
| `--
|
|
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 —
|
|
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
|
-
| `--
|
|
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
|
|
59
|
-
|
|
60
|
-
|
|
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)
|
|
67
|
-
|
|
68
|
-
**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
81
|
-
|
|
82
|
-
diff
|
|
83
|
-
|
|
84
|
-
Under `--yes` each
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
108
|
-
|
|
109
|
-
gate — it only *forces a proceed where the gate would
|
|
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
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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. **
|
|
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
|
-
|
|
206
|
-
from its entry
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
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,19 @@
|
|
|
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
|
+
|
|
5
18
|
## [1.91.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.90.0...mandrel-v1.91.0) (2026-07-12)
|
|
6
19
|
|
|
7
20
|
|
package/lib/cli/registry.js
CHANGED
|
@@ -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,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
|
-
}
|