mandrel 1.82.0 → 1.83.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 +41 -0
- package/.agents/docs/SDLC.md +4 -2
- package/.agents/docs/agentrc-reference.json +10 -2
- package/.agents/docs/execution-reference.md +52 -0
- package/.agents/instructions.md +76 -38
- package/.agents/schemas/agentrc.schema.json +31 -3
- package/.agents/schemas/qa-ledger.schema.json +2 -2
- package/.agents/scripts/epic-deliver-prepare.js +41 -1
- package/.agents/scripts/lib/config/explain.js +4 -1
- package/.agents/scripts/lib/config-settings-schema.js +25 -1
- package/.agents/scripts/lib/epic-body-sections.js +88 -0
- package/.agents/scripts/lib/findings/promote-finding.js +3 -3
- package/.agents/scripts/lib/findings/severity.js +5 -6
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +65 -2
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +96 -11
- package/.agents/scripts/lib/orchestration/doc-reader.js +29 -0
- package/.agents/scripts/lib/orchestration/docs-digest.js +134 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +15 -1
- package/.agents/scripts/lib/qa/console-allowlist.js +5 -4
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +144 -8
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +7 -5
- package/.agents/skills/core/epic-plan-consolidate/examples.md +51 -0
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +4 -22
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +47 -0
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +9 -8
- package/.agents/skills/core/epic-plan-premortem/examples.md +53 -0
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +21 -81
- package/.agents/skills/core/epic-plan-spec-author/examples.md +91 -0
- package/.agents/skills/skills.index.json +3 -3
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +52 -38
- package/.agents/workflows/helpers/deliver-epic-reference.md +514 -0
- package/.agents/workflows/helpers/deliver-epic.md +164 -469
- package/.agents/workflows/helpers/epic-deliver-story.md +35 -11
- package/.agents/workflows/helpers/plan-epic-reference.md +136 -0
- package/.agents/workflows/helpers/plan-epic.md +56 -186
- package/.agents/workflows/helpers/plan-story.md +31 -61
- package/.agents/workflows/helpers/qa-run-scenario.md +194 -0
- package/.agents/workflows/helpers/scope-triage-gate.md +97 -0
- package/.agents/workflows/helpers/single-story-deliver-reference.md +423 -0
- package/.agents/workflows/helpers/single-story-deliver.md +128 -392
- package/.agents/workflows/qa-explore.md +63 -32
- package/.agents/workflows/qa-run.md +293 -130
- package/docs/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/.agents/schemas/qa-finding.schema.json +0 -133
|
@@ -133,11 +133,15 @@ in-process (retrying the install command when
|
|
|
133
133
|
`dependenciesInstalled === 'false'`, default `npm ci`) and rendered the
|
|
134
134
|
initial snapshot (`phase: "init"`). There is no separate command to run.
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
Step 0's init run already upserted the initial `story-run-progress`
|
|
137
|
+
snapshot (`phase: "init"`) as a structured comment on the Story — that
|
|
138
|
+
comment, refreshed by `story-phase.js` at each transition, is the
|
|
139
|
+
authoritative Story-level rollup the parent `/deliver` aggregator reads.
|
|
140
|
+
You do **not** relay `prepare.renderedBody` verbatim to chat. Instead,
|
|
141
|
+
relay **one line per phase transition** (e.g. `Story #<id>: init →
|
|
142
|
+
implementing`), and do the same after every transition in Step 1 / Step 3.
|
|
143
|
+
The snapshot CLI carries the full body; the chat line is a terse progress
|
|
144
|
+
delta, not a body dump.
|
|
141
145
|
|
|
142
146
|
---
|
|
143
147
|
|
|
@@ -162,6 +166,20 @@ Run a single Story-implementation phase against the inline `acceptance[]`
|
|
|
162
166
|
`context.verify`). Treat the acceptance items as the contract and
|
|
163
167
|
the verify items as the canonical at-keyboard checks.
|
|
164
168
|
|
|
169
|
+
**Docs context — read the digest, not the full set.** Do **not**
|
|
170
|
+
re-read every file in `project.docsContextFiles`. The parent prompt
|
|
171
|
+
passes a `docsDigestPath` (the per-Epic docs digest at
|
|
172
|
+
`temp/epic-<epicId>/docs-digest.md`, written by
|
|
173
|
+
`epic-deliver-prepare.js`). Read that digest — a compact per-file
|
|
174
|
+
outline (path, size, heading outline with line numbers, first
|
|
175
|
+
paragraph under each `##`) — to decide which docs bear on this Story,
|
|
176
|
+
then **pull the full file on demand** (jump to the section at the line
|
|
177
|
+
number the digest names) only when relevant. When `docsDigestPath` is
|
|
178
|
+
null (the project configured no `docsContextFiles`), there is no
|
|
179
|
+
digest and no per-Story docs mandate — read a full doc only if the
|
|
180
|
+
Story's own context points you at one. See
|
|
181
|
+
[`.agents/instructions.md` § 3](../../instructions.md).
|
|
182
|
+
|
|
165
183
|
3. Implement the work as one or more commits on `story-<storyId>`.
|
|
166
184
|
Author commits directly with the project's editor / `git commit`,
|
|
167
185
|
following
|
|
@@ -240,10 +258,14 @@ partially-implemented Story picks up from whatever commits are already
|
|
|
240
258
|
on `story-<storyId>`; the agent inspects `git log` to decide what work
|
|
241
259
|
remains.
|
|
242
260
|
|
|
243
|
-
After each `story-phase.js` call,
|
|
244
|
-
`
|
|
245
|
-
|
|
246
|
-
the
|
|
261
|
+
After each `story-phase.js` call, relay **one line naming the phase
|
|
262
|
+
transition** (e.g. `Story #<id>: implementing → closing`) as the Story's
|
|
263
|
+
progress update — not the envelope's `renderedBody` verbatim. The
|
|
264
|
+
`story-phase.js` CLI has already upserted the full body into the
|
|
265
|
+
`story-run-progress` snapshot; that comment is the authoritative rollup
|
|
266
|
+
the parent `/deliver` aggregator reads. Skip chat relay entirely when
|
|
267
|
+
running in a non-interactive sub-agent context where the parent will
|
|
268
|
+
aggregate.
|
|
247
269
|
|
|
248
270
|
> Rebase pauses on conflicts → follow
|
|
249
271
|
> [`_merge-conflict-template.md`](_merge-conflict-template.md).
|
|
@@ -338,8 +360,10 @@ regardless of the reap status.
|
|
|
338
360
|
`story-phase.js` (typically the `phase: 'done'` snapshot at close,
|
|
339
361
|
or the `phase: 'blocked'` snapshot on a blocker). The parent
|
|
340
362
|
`/deliver` may inline a digest of this in its wave-level Notable
|
|
341
|
-
section. When run interactively (no parent), omit it — the
|
|
342
|
-
|
|
363
|
+
section. When run interactively (no parent), omit it — the authoritative
|
|
364
|
+
body lives in the `story-run-progress` snapshot the phase CLI upserted,
|
|
365
|
+
and the chat already carries the per-transition progress lines from
|
|
366
|
+
Step 1 / Step 3.
|
|
343
367
|
|
|
344
368
|
---
|
|
345
369
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
Reference companion to plan-epic.md — the recovery procedures, --resume
|
|
4
|
+
mechanics, troubleshooting, and background rationale blocks moved out of the
|
|
5
|
+
runtime core so every /plan run ingests only the phase flow. Read on demand
|
|
6
|
+
from the trigger-point pointers in plan-epic.md.
|
|
7
|
+
caller: plan-epic.md
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# helpers/plan-epic-reference — Epic-planning reference & recovery
|
|
11
|
+
|
|
12
|
+
> **Not a slash command.** This file lives in `helpers/` and is a
|
|
13
|
+
> path-included reference module (not projected into the plugin command
|
|
14
|
+
> tree). [`plan-epic.md`](plan-epic.md) is the runtime core — the phase
|
|
15
|
+
> flow, commands, and gate contracts. This file holds the secondary
|
|
16
|
+
> material a run needs only when it hits an edge (a recovery path, a
|
|
17
|
+
> troubleshooting symptom) or wants the design rationale behind a phase.
|
|
18
|
+
> Each section below is reached from a one-line pointer at its trigger
|
|
19
|
+
> point in the core.
|
|
20
|
+
|
|
21
|
+
## Phase 7 — background rationale
|
|
22
|
+
|
|
23
|
+
The Phase 7 (Tech Spec & Acceptance Spec) core carries only the commands and
|
|
24
|
+
the gate contract. The design rationale for the phase's guards and managed
|
|
25
|
+
sections lives here.
|
|
26
|
+
|
|
27
|
+
### Epic-lease preflight (workflow guard)
|
|
28
|
+
|
|
29
|
+
Before any Phase 7 mutation, `epic-plan-spec.js` acquires the Epic-lease via
|
|
30
|
+
the assignee-as-lease primitive (`lib/orchestration/ticket-lease.js`, wired
|
|
31
|
+
through `lib/orchestration/epic-plan-lease-guard.js`). The lease rides the
|
|
32
|
+
Epic's single assignee: the operator (`github.operatorHandle` in
|
|
33
|
+
`.agentrc.json`) claims the Epic for the duration of the plan. The guard
|
|
34
|
+
**fails closed**: `/plan` emits no `story.heartbeat` during its run
|
|
35
|
+
(heartbeats are a delivery-time signal), so there is no live-heartbeat source
|
|
36
|
+
to judge a concurrent plan's liveness from. Any **foreign assignee** is
|
|
37
|
+
therefore treated as a live claim — the persist half **exits non-zero and
|
|
38
|
+
names the current owner**, so two `/plan` runs cannot drive the same Epic
|
|
39
|
+
concurrently. Pass **`--steal`** to forcibly transfer a foreign claim once you
|
|
40
|
+
have confirmed the other run is dead. An **unassigned** Epic, or one **already
|
|
41
|
+
held by this operator**, is taken (or re-affirmed) silently. The lease is
|
|
42
|
+
**released after Phase 8** (decompose) completes.
|
|
43
|
+
|
|
44
|
+
### Idempotent managed sections
|
|
45
|
+
|
|
46
|
+
The persist half is section-scoped and keyed on the Epic body: a re-run that
|
|
47
|
+
finds the requested sections already present
|
|
48
|
+
(`<!-- mandrel:tech-spec:start/end -->` /
|
|
49
|
+
`<!-- mandrel:acceptance-table:start/end -->`) short-circuits as
|
|
50
|
+
`already-planned` instead of duplicating content. Pass `--force` to overwrite
|
|
51
|
+
the managed sections in place (same Epic issue, refreshed section bodies).
|
|
52
|
+
|
|
53
|
+
### One planning document
|
|
54
|
+
|
|
55
|
+
A `/plan` Epic run creates exactly **one** issue — the Epic. The planning
|
|
56
|
+
artifacts land as marker-delimited managed sections of the Epic body: the Tech
|
|
57
|
+
Spec (opening with `## Delivery Slicing`) inside
|
|
58
|
+
`<!-- mandrel:tech-spec:start/end -->`, and the Acceptance Spec's AC-ID table
|
|
59
|
+
(headed `## Acceptance Table`) inside
|
|
60
|
+
`<!-- mandrel:acceptance-table:start/end -->`. The PRD artifact class was
|
|
61
|
+
retired (Story #4314) — its one novel section, **User Stories**, lives inline
|
|
62
|
+
in the Epic body as a `## User Stories` section — and Story #4324 retired the
|
|
63
|
+
`context::tech-spec` / `context::acceptance-spec` ticket classes the same way.
|
|
64
|
+
The `## Acceptance Table` section captures the stable-ID acceptance criteria
|
|
65
|
+
table (`| AC ID | Outcome | Feature File | Scenario | Disposition |`) that
|
|
66
|
+
drives close-time reconciliation during `/deliver` Phase 6. Operators may opt
|
|
67
|
+
out for refactor-only or docs-only Epics by applying the `acceptance::n-a`
|
|
68
|
+
label to the Epic ticket — when present, the `epic-plan-spec-author` skill
|
|
69
|
+
skips the Acceptance Table output and the runtime gates (start gate, finalize
|
|
70
|
+
reconciler) honour the waiver — the section need not be authored when the
|
|
71
|
+
waiver is set. See [SDLC § Acceptance Table — the second folded planning
|
|
72
|
+
section](../../docs/SDLC.md#acceptance-table--the-second-folded-planning-section)
|
|
73
|
+
for the full lifecycle.
|
|
74
|
+
|
|
75
|
+
### Parallel-safe file naming (per-Epic tree)
|
|
76
|
+
|
|
77
|
+
Multiple Epics may be planned or decomposed concurrently. Every temp file
|
|
78
|
+
written in the workflow lives under the per-Epic tree
|
|
79
|
+
(`temp/epic-[Epic_ID]/<artifact>`) — e.g.
|
|
80
|
+
`temp/epic-[Epic_ID]/planner-context.json`,
|
|
81
|
+
`temp/epic-[Epic_ID]/techspec.md`,
|
|
82
|
+
`temp/epic-[Epic_ID]/decomposer-context.json`,
|
|
83
|
+
`temp/epic-[Epic_ID]/tickets.json`. The directory namespace is the isolation
|
|
84
|
+
boundary; basenames inside it are stable. Do **not** reuse bare flat names
|
|
85
|
+
like `temp/techspec.md` or the legacy `temp/<artifact>-epic-<id>.<ext>` shape
|
|
86
|
+
— both have been retired.
|
|
87
|
+
|
|
88
|
+
**Durability.** The per-Epic tree is durable across runs: only the wrapper
|
|
89
|
+
scripts perform intra-phase cleanup of files they wrote in the same invocation
|
|
90
|
+
(see [`lib/plan-phase-cleanup.js`](../../scripts/lib/plan-phase-cleanup.js)).
|
|
91
|
+
Nothing else garbage-collects the tree, so cross-Epic artifacts — retros, perf
|
|
92
|
+
reports, signals, manifests — accumulate until an operator explicitly removes
|
|
93
|
+
them.
|
|
94
|
+
|
|
95
|
+
## Phase 8 — `--resume` recovery (secondary rate limit)
|
|
96
|
+
|
|
97
|
+
The Phase 8 (Work Breakdown Decomposition) core carries the normal-path and
|
|
98
|
+
`--force` persist commands. The `--resume` recovery path — reached when a
|
|
99
|
+
large decomposition aborts mid-persist — lives here.
|
|
100
|
+
|
|
101
|
+
**Secondary rate limit on large Epics.** For backlogs over ~60 tickets,
|
|
102
|
+
GitHub's secondary rate limit (HTTP 403, body contains "secondary rate limit")
|
|
103
|
+
can trip mid-decomposition after ~80 issue creations. The http-client retries
|
|
104
|
+
automatically with a 30–120s backoff and the decomposer drops `concurrencyCap`
|
|
105
|
+
to 1 for the rest of the run on the first observation. If the run still aborts
|
|
106
|
+
(network drop, exhausted retries, etc.), resume from the partial backlog with:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
node .agents/scripts/epic-plan-decompose.js --epic [Epic_ID] \
|
|
110
|
+
--tickets temp/epic-[Epic_ID]/tickets.json --resume
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`--resume` is idempotent: the reconciler recovers the slug→issue map from
|
|
114
|
+
`temp/epic-[Epic_ID]/[Epic_ID].state.json`, and when that file is missing or
|
|
115
|
+
incomplete it **reseeds the map from live GitHub state** by matching each spec
|
|
116
|
+
slug against the open children of the Epic by title. Slugs that resolve to an
|
|
117
|
+
existing open child diff as Updates/no-ops; only the genuinely-missing children
|
|
118
|
+
are created — the existing tree is never duplicated. To force-throttle from the
|
|
119
|
+
first call on a known-large Epic, set `(framework constant: decomposer
|
|
120
|
+
concurrency): 1` in `.agentrc.json`.
|
|
121
|
+
|
|
122
|
+
## Troubleshooting
|
|
123
|
+
|
|
124
|
+
- If `epic-plan-spec.js --emit-context` fails, confirm the Epic exists and
|
|
125
|
+
has a body with enough initial context.
|
|
126
|
+
- If `epic-plan-decompose.js` rejects the tickets file, re-read the
|
|
127
|
+
validator's error message — the most common causes are a ticket whose
|
|
128
|
+
`type` is not `story`, a Story missing its inline `acceptance[]` /
|
|
129
|
+
`verify[]` contract, or a dependency cycle in the Story `depends_on`
|
|
130
|
+
graph.
|
|
131
|
+
- If decomposition persisted the tickets but the Epic is not on `agent::ready`,
|
|
132
|
+
you likely called `runDecomposePhase` from `epic-plan-decompose.js`
|
|
133
|
+
directly without completing the persist flow — only the CLI surface
|
|
134
|
+
(`node epic-plan-decompose.js --tickets ...`) drives the full
|
|
135
|
+
reconciler pipeline and flips the lifecycle label. Apply `agent::ready`
|
|
136
|
+
by hand and re-run via the CLI next time.
|
|
@@ -92,54 +92,25 @@ Phase 5 (Re-Plan Detection).
|
|
|
92
92
|
|
|
93
93
|
## Phase 1.5: Scope Triage (ideation path only)
|
|
94
94
|
|
|
95
|
-
This phase runs
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
2. **Fold the verdict into the existing Phase 1 HITL stop** — do **not** add a
|
|
115
|
-
second stop:
|
|
116
|
-
- **`epic` verdict** → no extra prompt. The Phase 1 confirmation proceeds
|
|
117
|
-
as a plain one-pager confirm and the run continues to Phase 2.
|
|
118
|
-
- **`story` or `borderline` verdict** → the Phase 1 confirmation prompt
|
|
119
|
-
presents a **three-way operator choice**:
|
|
120
|
-
- **Recommended: single Story** (with the triage rationale) — persist the
|
|
121
|
-
one-pager to a notes file and hand off to
|
|
122
|
-
`/plan --from-notes <path>`, identifying the invocation as a
|
|
123
|
-
scope-triage handoff so `/plan` skips its own gate. Then **exit
|
|
124
|
-
`/plan`**.
|
|
125
|
-
- **Plan as Epic anyway** — ignore the recommendation and continue to
|
|
126
|
-
Phase 2 with the one-pager.
|
|
127
|
-
- **Abort** — stop planning entirely.
|
|
128
|
-
|
|
129
|
-
**Never auto-route.** The verdict is advisory; the operator always decides.
|
|
130
|
-
Being wrong in the `epic` direction is cheap (Phase 8.3 consolidation and
|
|
131
|
-
the sizing validator catch an over-planned Story later); the gate exists to
|
|
132
|
-
avoid the ceremony tax of pushing a story-sized scope through the full Epic
|
|
133
|
-
pipeline.
|
|
134
|
-
|
|
135
|
-
> **`--yes` (headless) exception.** "Never auto-route" is the interactive
|
|
136
|
-
> contract. Under `--yes` the operator has *pre-authorized* the
|
|
137
|
-
> recommendation: the three-way choice resolves to its **Recommended**
|
|
138
|
-
> branch deterministically — `single Story` hands off to
|
|
139
|
-
> `/plan --from-notes <path>` (carrying `--yes` so the receiving story
|
|
140
|
-
> path also auto-proceeds), and an `epic` verdict simply continues to
|
|
141
|
-
> Phase 2. No operator wait. This is the only sanctioned auto-route, and it
|
|
142
|
-
> exists solely to make `/plan` driveable headlessly.
|
|
95
|
+
This phase runs the shared scope-triage gate over the Phase 1 one-pager. The
|
|
96
|
+
gate mechanics — verdict meanings, the three-way operator choice, the `--yes`
|
|
97
|
+
resolution, and the no-re-triage rule — live in the single-homed fragment
|
|
98
|
+
[`scope-triage-gate.md`](scope-triage-gate.md); read it and follow it. This
|
|
99
|
+
phase supplies only its path-specific firing conditions:
|
|
100
|
+
|
|
101
|
+
- **When it runs**: only on the ideation path, immediately after Phase 1
|
|
102
|
+
produces the sharpened one-pager. The rubric is applied to the **Phase 1
|
|
103
|
+
one-pager**, and the verdict folds into the **existing Phase 1 HITL stop**
|
|
104
|
+
(do **not** add a second stop).
|
|
105
|
+
- **When it is skipped**: entirely, when an Epic ID argument was supplied (the
|
|
106
|
+
existing-Epic path jumps straight to Phase 5); and when `/plan` was entered
|
|
107
|
+
via a scope-triage handoff from [`/plan`](plan-story.md) (the no-re-triage
|
|
108
|
+
rule in the fragment).
|
|
109
|
+
- **Recommended branch on a `story` / `borderline` verdict**: persist the
|
|
110
|
+
one-pager to a notes file and hand off to `/plan --from-notes <path>`,
|
|
111
|
+
identifying the invocation as a scope-triage handoff so `/plan` skips its
|
|
112
|
+
own gate, then **exit `/plan`**. The alternative branches are **plan as Epic
|
|
113
|
+
anyway** (continue to Phase 2) and **abort**.
|
|
143
114
|
|
|
144
115
|
## Phase 2: Cross-Epic Duplicate Search
|
|
145
116
|
|
|
@@ -278,36 +249,21 @@ triage on the one-pager before the Epic existed.
|
|
|
278
249
|
|
|
279
250
|
### Triage
|
|
280
251
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
run continues straight to Phase 6. Being wrong in the `epic` direction is
|
|
297
|
-
cheap (Phase 8.3 consolidation and the sizing validator catch an
|
|
298
|
-
over-planned Story later), so an `epic` verdict never costs the operator a
|
|
299
|
-
stop.
|
|
300
|
-
|
|
301
|
-
3. **`story` or `borderline` verdict** → **STOP** and present a **three-way
|
|
302
|
-
operator choice**. Never auto-route; the verdict is advisory and the
|
|
303
|
-
operator always decides.
|
|
304
|
-
|
|
305
|
-
- **Recommended: convert to a standalone Story** (with the triage
|
|
306
|
-
rationale) — run the conversion path below.
|
|
307
|
-
- **Proceed as Epic anyway** — ignore the recommendation and continue to
|
|
308
|
-
Phase 6 with the Epic unchanged.
|
|
309
|
-
- **Abort** — stop planning entirely. The Epic is left exactly as it was;
|
|
310
|
-
no labels move and nothing is closed.
|
|
252
|
+
Run the shared scope-triage gate over the **Epic body** — the gate mechanics
|
|
253
|
+
(verdict meanings, the three-way operator choice, the `--yes` resolution, and
|
|
254
|
+
the no-re-triage rule) live in [`scope-triage-gate.md`](scope-triage-gate.md);
|
|
255
|
+
read it and follow it. The scope-triage skill is artifact-agnostic, so it
|
|
256
|
+
reads the same against an Epic body as against a one-pager or a Story draft.
|
|
257
|
+
The path-specific bindings for this advisory:
|
|
258
|
+
|
|
259
|
+
- **`epic` verdict** → proceed silently to Phase 6. No extra prompt, no HITL
|
|
260
|
+
stop.
|
|
261
|
+
- **`story` / `borderline` verdict** → **STOP** and present the three-way
|
|
262
|
+
choice, whose **Recommended** branch here is **convert to a standalone
|
|
263
|
+
Story** via the conversion path below. The alternative branches are
|
|
264
|
+
**proceed as Epic anyway** (continue to Phase 6 with the Epic unchanged) and
|
|
265
|
+
**abort** (the Epic is left exactly as it was; no labels move and nothing is
|
|
266
|
+
closed).
|
|
311
267
|
|
|
312
268
|
### Conversion path (close-and-recreate)
|
|
313
269
|
|
|
@@ -453,78 +409,15 @@ for the scoring logic.
|
|
|
453
409
|
|
|
454
410
|
## Phase 7: Epic Planning (Tech Spec & Acceptance Spec)
|
|
455
411
|
|
|
456
|
-
> **
|
|
457
|
-
>
|
|
458
|
-
>
|
|
459
|
-
> `
|
|
460
|
-
>
|
|
461
|
-
>
|
|
462
|
-
>
|
|
463
|
-
>
|
|
464
|
-
>
|
|
465
|
-
> treated as a live claim — the persist half **exits non-zero and names the
|
|
466
|
-
> current owner**, so two `/plan` runs cannot drive the same Epic
|
|
467
|
-
> concurrently. Pass **`--steal`** to forcibly transfer a foreign claim once
|
|
468
|
-
> you have confirmed the other run is dead. An **unassigned** Epic, or one
|
|
469
|
-
> **already held by this operator**, is taken (or re-affirmed) silently. The
|
|
470
|
-
> lease is **released after Phase 8** (decompose) completes; see the Phase 8
|
|
471
|
-
> note.
|
|
472
|
-
|
|
473
|
-
<!-- separator: adjacent blockquotes -->
|
|
474
|
-
|
|
475
|
-
> **Idempotent managed sections.** The persist half is section-scoped and
|
|
476
|
-
> keyed on the Epic body: a re-run that finds the requested sections already
|
|
477
|
-
> present (`<!-- mandrel:tech-spec:start/end -->` /
|
|
478
|
-
> `<!-- mandrel:acceptance-table:start/end -->`) short-circuits as
|
|
479
|
-
> `already-planned` instead of duplicating content. Pass `--force` to
|
|
480
|
-
> overwrite the managed sections in place (same Epic issue, refreshed
|
|
481
|
-
> section bodies).
|
|
482
|
-
|
|
483
|
-
<!-- separator: adjacent blockquotes -->
|
|
484
|
-
|
|
485
|
-
> **One planning document.** A `/plan` Epic run creates exactly **one**
|
|
486
|
-
> issue — the Epic. The planning artifacts land as marker-delimited
|
|
487
|
-
> managed sections of the Epic body: the Tech Spec (opening with
|
|
488
|
-
> `## Delivery Slicing`) inside `<!-- mandrel:tech-spec:start/end -->`,
|
|
489
|
-
> and the Acceptance Spec's AC-ID table (headed `## Acceptance Table`)
|
|
490
|
-
> inside `<!-- mandrel:acceptance-table:start/end -->`. The PRD artifact
|
|
491
|
-
> class was retired (Story #4314) — its one novel section, **User
|
|
492
|
-
> Stories**, lives inline in the Epic body as a `## User Stories`
|
|
493
|
-
> section — and Story #4324 retired the `context::tech-spec` /
|
|
494
|
-
> `context::acceptance-spec` ticket classes the same way. The
|
|
495
|
-
> `## Acceptance Table` section captures the stable-ID acceptance
|
|
496
|
-
> criteria table (`| AC ID | Outcome | Feature File | Scenario |
|
|
497
|
-
> Disposition |`) that drives close-time reconciliation during
|
|
498
|
-
> `/deliver` Phase 6. Operators may opt out for refactor-only or
|
|
499
|
-
> docs-only Epics by applying the `acceptance::n-a` label to the Epic
|
|
500
|
-
> ticket — when present, the `epic-plan-spec-author` skill skips the
|
|
501
|
-
> Acceptance Table output and the runtime gates (start gate, finalize
|
|
502
|
-
> reconciler) honour the waiver — the section need not be authored when
|
|
503
|
-
> the waiver is set. See [SDLC § Acceptance Table — the second folded
|
|
504
|
-
> planning section](../../docs/SDLC.md#acceptance-table--the-second-folded-planning-section)
|
|
505
|
-
> for the full lifecycle.
|
|
506
|
-
|
|
507
|
-
<!-- separator: adjacent blockquotes -->
|
|
508
|
-
|
|
509
|
-
> **Parallel-safe file naming (per-Epic tree).** Multiple Epics may be
|
|
510
|
-
> planned or decomposed concurrently. Every temp file written in this
|
|
511
|
-
> workflow lives under the per-Epic tree
|
|
512
|
-
> (`temp/epic-[Epic_ID]/<artifact>`) — e.g.
|
|
513
|
-
> `temp/epic-[Epic_ID]/planner-context.json`,
|
|
514
|
-
> `temp/epic-[Epic_ID]/techspec.md`,
|
|
515
|
-
> `temp/epic-[Epic_ID]/decomposer-context.json`,
|
|
516
|
-
> `temp/epic-[Epic_ID]/tickets.json`. The directory namespace is the
|
|
517
|
-
> isolation boundary; basenames inside it are stable. Do **not** reuse
|
|
518
|
-
> bare flat names like `temp/techspec.md` or the legacy
|
|
519
|
-
> `temp/<artifact>-epic-<id>.<ext>` shape — both have been retired.
|
|
520
|
-
>
|
|
521
|
-
> **Durability.** The per-Epic tree is durable across runs: only the
|
|
522
|
-
> wrapper scripts perform intra-phase cleanup of files they wrote in
|
|
523
|
-
> the same invocation (see
|
|
524
|
-
> [`lib/plan-phase-cleanup.js`](../../scripts/lib/plan-phase-cleanup.js)).
|
|
525
|
-
> Nothing else garbage-collects the tree, so cross-Epic artifacts —
|
|
526
|
-
> retros, perf reports, signals, manifests — accumulate until an
|
|
527
|
-
> operator explicitly removes them.
|
|
412
|
+
> **Phase-7 guards & managed-section rationale (reference).** This phase runs
|
|
413
|
+
> behind an **Epic-lease preflight** (`--steal` transfers a confirmed-dead
|
|
414
|
+
> claim; the lease releases after Phase 8), writes **idempotent managed
|
|
415
|
+
> sections** (`--force` overwrites them in place), lands **one planning
|
|
416
|
+
> document** (Tech Spec + Acceptance Table folded into the Epic body;
|
|
417
|
+
> `acceptance::n-a` waives the table), and namespaces every temp file under
|
|
418
|
+
> the durable **per-Epic tree** (`temp/epic-[Epic_ID]/…`). The full rationale
|
|
419
|
+
> for each guard is in
|
|
420
|
+
> [`plan-epic-reference.md` § Phase 7 — background rationale](plan-epic-reference.md#phase-7--background-rationale).
|
|
528
421
|
|
|
529
422
|
1. **Gather Authoring Context**: Run the spec-phase CLI in context-emission
|
|
530
423
|
mode to fetch the Epic body, scraped project docs, and the recommended
|
|
@@ -883,6 +776,11 @@ node .agents/scripts/epic-plan-spec-validate.js \
|
|
|
883
776
|
--tickets temp/epic-[Epic_ID]/tickets.json --force
|
|
884
777
|
```
|
|
885
778
|
|
|
779
|
+
> On a large Epic (~60+ tickets) the persist can trip GitHub's secondary
|
|
780
|
+
> rate limit mid-run. Resume from the partial backlog with `--resume` — the
|
|
781
|
+
> full recovery contract is in
|
|
782
|
+
> [`plan-epic-reference.md` § Phase 8 — `--resume` recovery (secondary rate limit)](plan-epic-reference.md#phase-8----resume-recovery-secondary-rate-limit).
|
|
783
|
+
|
|
886
784
|
7. **Cross-Validation**:
|
|
887
785
|
- Hierarchy completeness, dependency-DAG acyclicity, and `risk::high`
|
|
888
786
|
labelling are deterministic invariants enforced by
|
|
@@ -1075,40 +973,12 @@ condition.
|
|
|
1075
973
|
node .agents/scripts/notify.js [Epic_ID] "Planning complete, review tickets. Backlog decomposition complete. Epic is ready for /deliver." --action
|
|
1076
974
|
```
|
|
1077
975
|
|
|
1078
|
-
## Troubleshooting
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
-
|
|
1083
|
-
|
|
1084
|
-
`type` is not `story`, a Story missing its inline `acceptance[]` /
|
|
1085
|
-
`verify[]` contract, or a dependency cycle in the Story `depends_on`
|
|
1086
|
-
graph.
|
|
1087
|
-
- If decomposition persisted the tickets but the Epic is not on `agent::ready`,
|
|
1088
|
-
you likely called `runDecomposePhase` from `epic-plan-decompose.js`
|
|
1089
|
-
directly without completing the persist flow — only the CLI surface
|
|
1090
|
-
(`node epic-plan-decompose.js --tickets ...`) drives the full
|
|
1091
|
-
reconciler pipeline and flips the lifecycle label. Apply `agent::ready`
|
|
1092
|
-
by hand and re-run via the CLI next time.
|
|
1093
|
-
- **Secondary rate limit on large Epics**: For backlogs over ~60 tickets,
|
|
1094
|
-
GitHub's secondary rate limit (HTTP 403, body contains "secondary rate
|
|
1095
|
-
limit") can trip mid-decomposition after ~80 issue creations. The
|
|
1096
|
-
http-client retries automatically with a 30–120s backoff and the
|
|
1097
|
-
decomposer drops `concurrencyCap` to 1 for the rest of the run on the
|
|
1098
|
-
first observation. If the run still aborts (network drop, exhausted
|
|
1099
|
-
retries, etc.), resume from the partial backlog with:
|
|
1100
|
-
|
|
1101
|
-
```bash
|
|
1102
|
-
node .agents/scripts/epic-plan-decompose.js --epic [Epic_ID] \
|
|
1103
|
-
--tickets temp/epic-[Epic_ID]/tickets.json --resume
|
|
1104
|
-
```
|
|
976
|
+
## Troubleshooting & recovery
|
|
977
|
+
|
|
978
|
+
Troubleshooting symptoms (emit-context failures, tickets-file rejections, a
|
|
979
|
+
decomposition that persisted but left the Epic off `agent::ready`) and the
|
|
980
|
+
large-Epic **secondary-rate-limit `--resume`** recovery path live in the
|
|
981
|
+
reference companion — they are consulted only when a run hits the edge:
|
|
1105
982
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
missing or incomplete it **reseeds the map from live GitHub state** by
|
|
1109
|
-
matching each spec slug against the open children of the Epic by title.
|
|
1110
|
-
Slugs that resolve to an existing open child diff as Updates/no-ops;
|
|
1111
|
-
only the genuinely-missing children are created — the existing tree is
|
|
1112
|
-
never duplicated. To force-throttle from the first call on a known-large
|
|
1113
|
-
Epic, set `(framework constant: decomposer concurrency): 1` in
|
|
1114
|
-
`.agentrc.json`.
|
|
983
|
+
- [`plan-epic-reference.md` § Troubleshooting](plan-epic-reference.md#troubleshooting)
|
|
984
|
+
- [`plan-epic-reference.md` § Phase 8 — `--resume` recovery (secondary rate limit)](plan-epic-reference.md#phase-8----resume-recovery-secondary-rate-limit)
|
|
@@ -125,32 +125,31 @@ Write the draft to `temp/single-story-draft.md`.
|
|
|
125
125
|
|
|
126
126
|
### Scope-triage escalation gate (symmetric counterpart to `/plan` Phase 1.5)
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
an
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
add a second stop.
|
|
128
|
+
This gate runs the shared scope-triage gate over the **drafted Story body** to
|
|
129
|
+
catch an Epic-sized scope before it is persisted as a standalone Story — the
|
|
130
|
+
outbound mirror of `/plan` Phase 1.5's inbound downgrade gate. The gate
|
|
131
|
+
mechanics (verdict meanings, the three-way operator choice, the `--yes`
|
|
132
|
+
resolution, and the no-re-triage rule) live in the single-homed fragment
|
|
133
|
+
[`scope-triage-gate.md`](scope-triage-gate.md); read it and follow it. This
|
|
134
|
+
phase supplies only its path-specific firing conditions:
|
|
135
|
+
|
|
136
|
+
- **When it runs**: only **after the draft body exists** — the seed alone is
|
|
137
|
+
not an honest basis for a sizing judgment. The rubric is applied to the
|
|
138
|
+
**drafted Story body**, and the verdict folds into the **existing**
|
|
139
|
+
draft-confirmation HITL stop below (it does **not** add a second stop). (The
|
|
140
|
+
`refine` heuristic in `story-plan.js` is unchanged — it is a deterministic
|
|
141
|
+
seed-length proxy, not a scope-size judgment.)
|
|
142
|
+
- **When it is skipped**: entirely, when `/plan` was entered via a scope-triage
|
|
143
|
+
handoff — from `/plan` Phase 1.5 (the inbound route above) or the `/plan`
|
|
144
|
+
Phase 5.5 existing-Epic conversion path (the no-re-triage rule in the
|
|
145
|
+
fragment).
|
|
146
|
+
- **Recommended branch on an `epic` verdict**: escalate to `/plan --idea` —
|
|
147
|
+
persist the notes/draft to a notes file and hand off to `/plan --idea` (or
|
|
148
|
+
`--from-notes <path>`), identifying the invocation as a scope-triage handoff
|
|
149
|
+
so `/plan` skips its own Phase 1.5 gate, then **abandon the draft and exit
|
|
150
|
+
`/plan`** (no standalone Story is created). The alternative branches are
|
|
151
|
+
**persist as a standalone Story anyway** (proceed to Phase 3 with the draft
|
|
152
|
+
unchanged) and **abort**.
|
|
154
153
|
|
|
155
154
|
### HITL — operator confirms the draft (verdict folded in)
|
|
156
155
|
|
|
@@ -158,41 +157,12 @@ Display the draft to the operator and **STOP**. Do not call the persist phase
|
|
|
158
157
|
until the operator explicitly confirms the draft. This mirrors the HITL gate
|
|
159
158
|
`/plan` Phase 3 enforces before opening the Epic Issue. This is the
|
|
160
159
|
story-path face of `/plan`'s **gate #1** (the ideation one-pager /
|
|
161
|
-
scope-triage confirm). The scope-triage verdict folds into this same stop
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
> abandoning the draft — rather than prompting the three-way choice. Display
|
|
168
|
-
> the draft and the verdict line for the record, then proceed without
|
|
169
|
-
> waiting. See
|
|
170
|
-
> [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
171
|
-
|
|
172
|
-
- **`story` verdict (or gate skipped via handoff)** → no extra prompt. The
|
|
173
|
-
operator confirms the draft as usual and the run proceeds to Phase 3
|
|
174
|
-
(persist).
|
|
175
|
-
- **`epic` verdict** (multiple independent capabilities, a plausible
|
|
176
|
-
sizing-ceiling breach, or a real dependency structure) → the confirmation
|
|
177
|
-
prompt presents a **three-way operator choice**:
|
|
178
|
-
- **Recommended: escalate to `/plan --idea`** (with the triage
|
|
179
|
-
rationale) — persist the notes/draft to a notes file and hand off to
|
|
180
|
-
`/plan --idea` (or `--from-notes <path>`), identifying the invocation
|
|
181
|
-
as a **scope-triage handoff** so `/plan` skips its own Phase 1.5 gate
|
|
182
|
-
(the skill's no-re-triage rule). Then **abandon the draft and exit
|
|
183
|
-
`/plan`** — no standalone Story is created.
|
|
184
|
-
- **Persist as a standalone Story anyway** — ignore the recommendation and
|
|
185
|
-
proceed to Phase 3 with the draft unchanged. Being wrong in the `epic`
|
|
186
|
-
direction is cheap to tolerate: if the operator persists an oversized Story,
|
|
187
|
-
the sizing validator and delivery reality push back later. The gate's value
|
|
188
|
-
is surfacing the recommendation while the scope is still free to change.
|
|
189
|
-
- **Abort** — stop planning entirely. No Issue is created.
|
|
190
|
-
|
|
191
|
-
**Never auto-route.** The verdict is advisory; the operator always decides,
|
|
192
|
-
and no `agent::*` / label transition happens on either side of the choice.
|
|
193
|
-
(**`--yes` exception:** headless mode pre-authorizes the **Recommended**
|
|
194
|
-
branch deterministically — see the `--yes` note above — the only sanctioned
|
|
195
|
-
auto-route, present solely to make `/plan` driveable without an operator.)
|
|
160
|
+
scope-triage confirm). The scope-triage verdict folds into this same stop, per
|
|
161
|
+
the branch bindings above; on a **`story` verdict (or gate skipped via
|
|
162
|
+
handoff)** there is no extra prompt and the operator confirms the draft as
|
|
163
|
+
usual. The `--yes` headless resolution of this stop is the fragment's — see
|
|
164
|
+
[`scope-triage-gate.md` § `--yes` (headless) resolution](scope-triage-gate.md#--yes-headless-resolution)
|
|
165
|
+
and [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
196
166
|
|
|
197
167
|
## Phase 3 — Persist (`gh issue create`)
|
|
198
168
|
|