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,1038 +1,285 @@
|
|
|
1
1
|
---
|
|
2
2
|
description:
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Epic planning path invoked by /plan — three steps (interrogate → author →
|
|
4
|
+
persist) that produce the Epic body's Tech Spec + Acceptance Table managed
|
|
5
|
+
sections and the Story backlog (or a single-delivery routing marker).
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# helpers/plan-epic — Epic planning path (invoked by /plan)
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Shape
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Three steps replace the retired 12-phase pipeline (Epic #4474). All GitHub
|
|
13
|
+
reads concentrate in `plan-context.js` (step 1), all GitHub writes in
|
|
14
|
+
`plan-persist.js` (step 3); the authoring middle reads one JSON envelope and
|
|
15
|
+
writes 2–4 files. Exactly two HITL STOP gates: **gate #1** at the exit of
|
|
16
|
+
interrogate, **gate #2** risk-routed before persist. Every deterministic
|
|
17
|
+
validator of the old pipeline survives inside the persist CLI.
|
|
12
18
|
|
|
13
|
-
##
|
|
19
|
+
## Constraints
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
- Do not modify existing issues without explicit permission. Every GitHub
|
|
22
|
+
mutation runs inside `plan-persist.js` — or `epic-plan-clarity.js` for a
|
|
23
|
+
gate-#1-approved body refinement — never via ad-hoc `gh issue edit`.
|
|
24
|
+
- Never bypass a persist rejection by editing GitHub directly: fix the named
|
|
25
|
+
artifact and re-run the CLI (one targeted amend pass, below).
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
is the single authoritative home for Phase 7 (spec) and Phase 8 (decompose) —
|
|
21
|
-
it carries the full step-by-step procedure for both and delegates only the
|
|
22
|
-
authoring middle of each to the
|
|
23
|
-
[`epic-plan-spec-author`](../../skills/core/epic-plan-spec-author/SKILL.md) /
|
|
24
|
-
[`epic-plan-decompose-author`](../../skills/core/epic-plan-decompose-author/SKILL.md)
|
|
25
|
-
skills. It runs both phases sequentially with a human confirmation gate
|
|
26
|
-
between them. The Epic ID is the single positional argument.
|
|
27
|
+
## Step 1 — Interrogate
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
Goal: shared understanding and a confirmed planning basis. Grilling
|
|
30
|
+
discipline: ask questions **one at a time** — asking multiple questions at
|
|
31
|
+
once is bewildering. If a **fact** can be found by exploring the codebase,
|
|
32
|
+
look it up rather than asking; the **decisions** are the operator's — put
|
|
33
|
+
each one to them. Do not proceed to authoring until shared understanding is
|
|
34
|
+
confirmed (gate #1).
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
authored **directly by you, the host LLM** — no external Gemini / Anthropic /
|
|
37
|
-
OpenAI API is called. The Node scripts are deterministic GitHub I/O wrappers
|
|
38
|
-
that (a) emit the authoring context you need and (b) validate and persist the
|
|
39
|
-
artifacts you author.
|
|
36
|
+
### Ideation entry (`--idea "<seed>"` or no argument)
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
See [SDLC § Adaptive planning risk routing](../../docs/SDLC.md#adaptive-planning-risk-routing)
|
|
48
|
-
for the full envelope shape and the planner-selected
|
|
49
|
-
`acceptance::n-a` route.
|
|
50
|
-
- Delegate Phase 7 and Phase 8 authoring to the
|
|
51
|
-
[`epic-plan-spec-author`](../../skills/core/epic-plan-spec-author/SKILL.md)
|
|
52
|
-
and [`epic-plan-decompose-author`](../../skills/core/epic-plan-decompose-author/SKILL.md)
|
|
53
|
-
skills respectively — the deterministic `epic-plan-spec.js` /
|
|
54
|
-
`epic-plan-decompose.js` CLIs (invoked by this file's own Phase 7 / Phase 8
|
|
55
|
-
steps) own the Epic lifecycle label transitions and the `epic-plan-state`
|
|
56
|
-
checkpoint. This wrapper must not apply those labels directly.
|
|
57
|
-
|
|
58
|
-
## Prerequisites
|
|
59
|
-
|
|
60
|
-
1. **GitHub Epic**: An existing GitHub Issue with the `type::epic` label.
|
|
61
|
-
Skipped when entering via Phase 1 / `--idea` (the Epic does not exist
|
|
62
|
-
yet — Phases 1–4 will create it).
|
|
63
|
-
2. **API Keys**: `GITHUB_TOKEN` must be set in the `.env` file.
|
|
64
|
-
|
|
65
|
-
## Phase 1: Idea Refinement (s-plan-ideation entry)
|
|
66
|
-
|
|
67
|
-
This phase runs **only** when no `<epic#>` argument is supplied, or when
|
|
68
|
-
`--idea "<seed>"` is passed. If an Epic ID was provided, skip directly to
|
|
69
|
-
Phase 5 (Re-Plan Detection).
|
|
70
|
-
|
|
71
|
-
1. **Activate the ideation skill**: Read
|
|
72
|
-
`<agentRoot>/skills/core/idea-refinement/SKILL.md` via the `Read`
|
|
73
|
-
tool (resolve `<agentRoot>` from `project.paths.agentRoot` —
|
|
74
|
-
default `.agents`) and execute its procedure with the `--idea` value
|
|
75
|
-
(or a user-supplied seed if no argument was given) as the seed. The
|
|
76
|
-
skill drives its own three-phase divergent → convergent → sharpen
|
|
77
|
-
loop and returns a markdown one-pager with the canonical sections
|
|
78
|
-
(Problem Statement, Recommended Direction, Key Assumptions, MVP
|
|
79
|
-
Scope, Not Doing). This is the canonical pattern for framework
|
|
80
|
-
skills — they are library-style content read on-demand per
|
|
81
|
-
`<agentRoot>/instructions.md` section 1.B, not entries in the
|
|
82
|
-
host's harness-level skill registry.
|
|
83
|
-
|
|
84
|
-
2. **Run Phase 1.5 (Scope Triage) before the HITL stop**: The sharpened
|
|
85
|
-
one-pager feeds the scope-triage gate below, whose verdict folds into
|
|
86
|
-
the **same** Phase 1 HITL confirmation. Do not stop twice.
|
|
87
|
-
|
|
88
|
-
3. **HITL stop — confirm the sharpened one-pager** (**gate #1**): Display
|
|
89
|
-
the one-pager to the operator and **STOP**. Do not proceed to Phase 2
|
|
90
|
-
until the user explicitly confirms the direction. This is the same gate
|
|
91
|
-
the skill's own Phase 3 enforces; surfacing it here makes the wait
|
|
92
|
-
contract visible to `/plan` callers. When the Phase 1.5 verdict is
|
|
93
|
-
`story` or `borderline`, this stop carries the three-way choice the
|
|
94
|
-
triage gate defines (below) instead of a plain confirm.
|
|
95
|
-
|
|
96
|
-
> **`--yes` (headless) auto-proceed.** When `/plan` was invoked with
|
|
97
|
-
> `--yes`, this gate does **not** STOP: the one-pager confirm resolves as
|
|
98
|
-
> **approved** and the run continues to Phase 2. A `story` / `borderline`
|
|
99
|
-
> triage verdict resolves to its **Recommended** branch (below) rather
|
|
100
|
-
> than prompting the three-way choice. Display the one-pager and the
|
|
101
|
-
> verdict line for the record, then proceed without waiting. See
|
|
102
|
-
> [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
103
|
-
|
|
104
|
-
## Phase 1.5: Scope Triage (ideation path only)
|
|
105
|
-
|
|
106
|
-
This phase runs the shared scope-triage gate over the Phase 1 one-pager. The
|
|
107
|
-
gate mechanics — verdict meanings, the three-way operator choice, the `--yes`
|
|
108
|
-
resolution, and the no-re-triage rule — live in the single-homed fragment
|
|
109
|
-
[`scope-triage-gate.md`](scope-triage-gate.md); read it and follow it. This
|
|
110
|
-
phase supplies only its path-specific firing conditions:
|
|
111
|
-
|
|
112
|
-
- **When it runs**: only on the ideation path, immediately after Phase 1
|
|
113
|
-
produces the sharpened one-pager. The rubric is applied to the **Phase 1
|
|
114
|
-
one-pager**, and the verdict folds into the **existing Phase 1 HITL stop**
|
|
115
|
-
(do **not** add a second stop).
|
|
116
|
-
- **When it is skipped**: entirely, when an Epic ID argument was supplied (the
|
|
117
|
-
existing-Epic path jumps straight to Phase 5); and when `/plan` was entered
|
|
118
|
-
via a scope-triage handoff from [`/plan`](plan-story.md) (the no-re-triage
|
|
119
|
-
rule in the fragment).
|
|
120
|
-
- **Recommended branch on a `story` / `borderline` verdict**: persist the
|
|
121
|
-
one-pager to a notes file and hand off to `/plan --from-notes <path>`,
|
|
122
|
-
identifying the invocation as a scope-triage handoff so `/plan` skips its
|
|
123
|
-
own gate, then **exit `/plan`**. The alternative branches are **plan as Epic
|
|
124
|
-
anyway** (continue to Phase 2) and **abort**.
|
|
125
|
-
|
|
126
|
-
## Phase 2: Cross-Epic Duplicate Search
|
|
127
|
-
|
|
128
|
-
Runs immediately after Phase 1 (and only on the s-plan-ideation path).
|
|
129
|
-
Its job is to surface open Epics whose scope already overlaps with the
|
|
130
|
-
sharpened one-pager so the operator can fold the work in rather than
|
|
131
|
-
opening a duplicate.
|
|
132
|
-
|
|
133
|
-
1. **Invoke the duplicate-search module**: Call
|
|
134
|
-
`findSimilarOpenEpics({ onePager, provider })` exported from
|
|
135
|
-
[`.agents/scripts/lib/duplicate-search.js`](../../scripts/lib/duplicate-search.js).
|
|
136
|
-
The `provider` is the resolved ticketing provider
|
|
137
|
-
(`provider-factory.js`), and `onePager` is the markdown returned by
|
|
138
|
-
Phase 1.
|
|
139
|
-
|
|
140
|
-
2. **HITL pause on match**: If the module returns a non-empty ranked
|
|
141
|
-
list, render the candidates (id, title, score, URL) and **STOP**. Do
|
|
142
|
-
not proceed to Phase 3 until the user either (a) confirms the new
|
|
143
|
-
Epic is genuinely distinct or (b) chooses to fold the idea into one of
|
|
144
|
-
the existing Epics, in which case `/plan` exits and the operator
|
|
145
|
-
resumes work on the existing Epic ID.
|
|
146
|
-
|
|
147
|
-
3. **No-match fast path**: If the module returns `[]`, proceed
|
|
148
|
-
immediately to Phase 3 — no operator intervention required.
|
|
149
|
-
|
|
150
|
-
## Phase 3: Render Epic Body from One-Pager
|
|
151
|
-
|
|
152
|
-
Runs after Phase 2 clears (no duplicates, or operator confirmed the
|
|
153
|
-
new Epic is genuinely distinct).
|
|
154
|
-
|
|
155
|
-
1. **Render the body**: Call
|
|
156
|
-
`renderEpicBody({ onePager, template })` exported from
|
|
157
|
-
[`.agents/scripts/lib/epic-plan-ideation.js`](../../scripts/lib/epic-plan-ideation.js).
|
|
158
|
-
The `template` argument is the contents of
|
|
159
|
-
[`.agents/templates/epic-from-idea.md`](../../templates/epic-from-idea.md),
|
|
160
|
-
which carries the five canonical sections (Context, Goal, Non-Goals,
|
|
161
|
-
Scope, Acceptance Criteria). Sections missing from the one-pager are
|
|
162
|
-
rendered as `_(not specified)_` rather than left as raw `{{token}}`
|
|
163
|
-
placeholders.
|
|
164
|
-
|
|
165
|
-
2. **HITL stop — confirm the body**: Display the rendered body to the
|
|
166
|
-
operator and **STOP**. Do not proceed to Phase 4 until the user
|
|
167
|
-
explicitly confirms the body is correct. This is the last chance to
|
|
168
|
-
tweak wording before the GitHub Issue is opened.
|
|
169
|
-
|
|
170
|
-
## Phase 4: Open the GitHub Issue (`type::epic` only)
|
|
171
|
-
|
|
172
|
-
1. **Open the Epic Issue**: Call
|
|
173
|
-
`openEpicFromOnePager({ onePager, template, createIssue })` from the
|
|
174
|
-
same `epic-plan-ideation.js` module. Pass a `createIssue` port that
|
|
175
|
-
delegates to the resolved ticketing provider's `createIssue` method
|
|
176
|
-
(`provider-factory.js`; GitHub: `TicketGateway.createIssue`) so the
|
|
177
|
-
labels and body land via the canonical I/O surface.
|
|
178
|
-
|
|
179
|
-
**Board membership (Story #3822)**: the provider's `createIssue`
|
|
180
|
-
adds the new Epic to the configured Projects V2 board via the
|
|
181
|
-
shared `addIssueToBoard` helper
|
|
182
|
-
([`providers/github/board-add.js`](../../scripts/providers/github/board-add.js))
|
|
183
|
-
— idempotent, non-fatal (warn-on-error), and a no-op when no
|
|
184
|
-
project number is configured — so the Epic lands on the board
|
|
185
|
-
without relying on GitHub's "Auto-add to project" built-in
|
|
186
|
-
workflow (off by default on fresh boards and not enableable via
|
|
187
|
-
API). The created issue's GraphQL `node_id` is surfaced as
|
|
188
|
-
`nodeId` on the returned envelope for follow-up board operations.
|
|
189
|
-
|
|
190
|
-
2. **Label discipline**: The Issue is opened with **only** the
|
|
191
|
-
`type::epic` label. **Do not** add any `state::*` label at creation
|
|
192
|
-
time — the Epic carries only `type::epic` until spec authoring
|
|
193
|
-
advances it to `agent::review-spec` in Phase 7. The
|
|
194
|
-
`openEpicFromOnePager` helper already enforces this; the workflow
|
|
195
|
-
prose codifies the intent so future label-set tweaks don't silently
|
|
196
|
-
widen it.
|
|
197
|
-
|
|
198
|
-
3. **Continue to Phase 5**: The captured Epic ID becomes the new
|
|
199
|
-
`[Epic_ID]` for the rest of the planning pipeline. Re-Plan Detection
|
|
200
|
-
(Phase 5) will short-circuit because the fresh Epic body carries no
|
|
201
|
-
Tech Spec sections yet, so the run flows naturally into Phase 6
|
|
202
|
-
(Epic Clarity Gate) and then Phase 7.
|
|
203
|
-
|
|
204
|
-
## Phase 5: Re-Plan Detection
|
|
205
|
-
|
|
206
|
-
Before generating any artifacts, check whether the Epic has already been
|
|
207
|
-
planned.
|
|
208
|
-
|
|
209
|
-
1. **Fetch Epic**: Read the Epic issue body and check for Tech Spec
|
|
210
|
-
content — the managed `<!-- mandrel:tech-spec:start/end -->` section or
|
|
211
|
-
a bare `## Delivery Slicing` heading (`hasTechSpecContent` in
|
|
212
|
-
[`lib/epic-body-sections.js`](../../scripts/lib/epic-body-sections.js)).
|
|
213
|
-
The Tech Spec sections alone are the already-planned signal. Historical
|
|
214
|
-
Epics may still carry a legacy `## Planning Artifacts` section linking
|
|
215
|
-
retired `context::*` tickets — those links are **ignored** (never
|
|
216
|
-
fetched); only the body sections count.
|
|
217
|
-
2. **If already planned**: Inform the user that this Epic already carries
|
|
218
|
-
planning sections. Ask:
|
|
219
|
-
|
|
220
|
-
> "Epic #[ID] already carries a Tech Spec (`## Delivery Slicing`) with
|
|
221
|
-
> YY decomposed tickets. Do you want to **re-plan**? This will
|
|
222
|
-
> **overwrite the Tech Spec and Acceptance Table sections of the Epic
|
|
223
|
-
> body in place** (comment history preserved) and **close-and-recreate**
|
|
224
|
-
> all child Story tickets."
|
|
225
|
-
|
|
226
|
-
3. **If user confirms re-plan**: Pass `--force` to all subsequent script
|
|
227
|
-
invocations. Under `--force`, the Epic body's managed sections (Tech
|
|
228
|
-
Spec, Acceptance Table) are overwritten in place — the Epic's issue
|
|
229
|
-
number and prior discussion are preserved, and the Epic receives a
|
|
230
|
-
one-line regeneration audit comment. Child Story tickets are still
|
|
231
|
-
closed and recreated, because a re-decomposition can legitimately produce a
|
|
232
|
-
different ticket set.
|
|
233
|
-
4. **If user declines**: Abort gracefully.
|
|
234
|
-
|
|
235
|
-
## Phase 5.5: Story-Sized Advisory (existing-Epic path)
|
|
236
|
-
|
|
237
|
-
An **advisory** scope-triage check that catches a story-sized scope which was
|
|
238
|
-
hand-opened directly as a `type::epic` issue before any ceremony is paid for.
|
|
239
|
-
Phase 6's Epic Clarity Gate scores section *presence*, not scope *size* — a
|
|
240
|
-
perfectly clear but story-sized Epic (typically a thin, freshly opened issue)
|
|
241
|
-
sails straight through to full planning. This advisory is the size check the
|
|
242
|
-
clarity gate does not make.
|
|
243
|
-
|
|
244
|
-
### Firing condition (load-bearing)
|
|
245
|
-
|
|
246
|
-
The advisory runs **only** when **both** hold:
|
|
247
|
-
|
|
248
|
-
1. **Phase 5 found no planning sections** — the Epic body carries no Tech
|
|
249
|
-
Spec content (no managed section, no `## Delivery Slicing` heading —
|
|
250
|
-
i.e. Phase 5 did **not** enter its re-plan branch).
|
|
251
|
-
2. **The Epic has no open Story children** — no open `type::story`
|
|
252
|
-
sub-issues are linked to this Epic.
|
|
253
|
-
|
|
254
|
-
If **either** condition fails, **skip this phase silently** and continue to
|
|
255
|
-
Phase 6. It must **never** fire on the re-plan path: recommending a "downgrade
|
|
256
|
-
to a Story" on an Epic that already carries folded Tech Spec sections and a
|
|
257
|
-
ticket tree is nonsense and would collide with the `--force` re-plan flow. The advisory also
|
|
258
|
-
does not fire on the ideation path — that path already ran the Phase 1.5 scope
|
|
259
|
-
triage on the one-pager before the Epic existed.
|
|
260
|
-
|
|
261
|
-
### Triage
|
|
262
|
-
|
|
263
|
-
Run the shared scope-triage gate over the **Epic body** — the gate mechanics
|
|
264
|
-
(verdict meanings, the three-way operator choice, the `--yes` resolution, and
|
|
265
|
-
the no-re-triage rule) live in [`scope-triage-gate.md`](scope-triage-gate.md);
|
|
266
|
-
read it and follow it. The scope-triage skill is artifact-agnostic, so it
|
|
267
|
-
reads the same against an Epic body as against a one-pager or a Story draft.
|
|
268
|
-
The path-specific bindings for this advisory:
|
|
269
|
-
|
|
270
|
-
- **`epic` verdict** → proceed silently to Phase 6. No extra prompt, no HITL
|
|
271
|
-
stop.
|
|
272
|
-
- **`story` / `borderline` verdict** → **STOP** and present the three-way
|
|
273
|
-
choice, whose **Recommended** branch here is **convert to a standalone
|
|
274
|
-
Story** via the conversion path below. The alternative branches are
|
|
275
|
-
**proceed as Epic anyway** (continue to Phase 6 with the Epic unchanged) and
|
|
276
|
-
**abort** (the Epic is left exactly as it was; no labels move and nothing is
|
|
277
|
-
closed).
|
|
278
|
-
|
|
279
|
-
### Conversion path (close-and-recreate)
|
|
280
|
-
|
|
281
|
-
Conversion is **close-and-recreate**, not in-place relabeling. A `type::epic`
|
|
282
|
-
body does not satisfy `validateStoryBody` (it lacks the required Story
|
|
283
|
-
sections and would have to drop the Epic shape), and editing or relabeling the
|
|
284
|
-
existing issue violates the workflow's "Do not modify existing issues without
|
|
285
|
-
explicit permission" Constraint. So the conversion seeds a fresh Story and
|
|
286
|
-
closes the Epic in its favor, and **every** issue mutation below happens
|
|
287
|
-
**only after the operator explicitly confirms the conversion in-session**:
|
|
288
|
-
|
|
289
|
-
1. **Seed a notes file from the Epic body.** Write the Epic's Context / Goal /
|
|
290
|
-
Scope / Acceptance Criteria into a seed file under
|
|
291
|
-
`temp/epic-[Epic_ID]/scope-triage-seed.md` (the `temp/` tree is gitignored).
|
|
292
|
-
This is the same notes-file shape `/plan --from-notes` consumes.
|
|
293
|
-
|
|
294
|
-
2. **Hand off to `/plan --from-notes`.** Invoke
|
|
295
|
-
[`/plan --from-notes temp/epic-[Epic_ID]/scope-triage-seed.md`](plan-story.md),
|
|
296
|
-
**identifying the invocation as a scope-triage handoff** so `/plan`
|
|
297
|
-
skips its own escalation gate (the skill's no-re-triage rule — a handoff is
|
|
298
|
-
a settled triage decision, and re-running the gate on the receiving side
|
|
299
|
-
would re-litigate it and risk a ping-pong between the two workflows). The
|
|
300
|
-
replacement Story's `## Notes` section links back to the closed Epic
|
|
301
|
-
(`Converted from Epic #[Epic_ID] — scope triaged as a standalone Story`) so
|
|
302
|
-
the audit trail is bidirectional.
|
|
303
|
-
|
|
304
|
-
3. **Close the Epic in favor of the replacement.** Once `/plan` has
|
|
305
|
-
created the replacement Story (capture its number as `#N`), close the Epic
|
|
306
|
-
with a cross-linking comment:
|
|
38
|
+
1. Activate the [`core/idea-refinement`](../../skills/core/idea-refinement/SKILL.md)
|
|
39
|
+
skill with the seed. It returns a one-pager with the canonical sections
|
|
40
|
+
(Problem Statement, Recommended Direction, Key Assumptions, MVP Scope,
|
|
41
|
+
Not Doing). Write it to `temp/plan-ideation/<slug>/one-pager.md` (slug
|
|
42
|
+
from the title; the tree is gitignored).
|
|
43
|
+
2. Emit the authoring envelope:
|
|
307
44
|
|
|
308
45
|
```bash
|
|
309
|
-
|
|
310
|
-
--
|
|
46
|
+
node .agents/scripts/plan-context.js \
|
|
47
|
+
--one-pager temp/plan-ideation/<slug>/one-pager.md \
|
|
48
|
+
> temp/plan-ideation/<slug>/plan-context.json
|
|
311
49
|
```
|
|
312
50
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
(Context, Goal, Non-Goals, Scope, Acceptance Criteria — the template
|
|
333
|
-
also carries a `## User Stories` section, which the clarity rubric does
|
|
334
|
-
not score) and either
|
|
335
|
-
skips fast (when the Epic body is already clear) or drops into a
|
|
336
|
-
refinement loop seeded from the current Epic body. The scorer also
|
|
337
|
-
accepts common heading variants for back-compat (e.g. `## Problem`,
|
|
338
|
-
`## Direction`, `## MVP Scope`, `## Not Doing`, `## Out of Scope`) so
|
|
339
|
-
hand-authored Epics that predate the canonical headings continue to
|
|
340
|
-
pass without rewording.
|
|
341
|
-
|
|
342
|
-
The rubric is deterministic: section-presence against the five
|
|
343
|
-
canonical headings. A `clear` verdict requires **both** ≥ 4 of 5
|
|
344
|
-
sections present **and** the **Acceptance Criteria** section present —
|
|
345
|
-
AC is a required section, not one of the four optional passers, so an
|
|
346
|
-
Epic with no Acceptance Criteria is always `needs-refinement` (it would
|
|
347
|
-
otherwise hard-fail the `/deliver` start gate downstream). See
|
|
348
|
-
[`lib/epic-plan-clarity.js`](../../scripts/lib/epic-plan-clarity.js)
|
|
349
|
-
for the scoring logic.
|
|
350
|
-
|
|
351
|
-
1. **Score the body**: Run the clarity-check CLI in context-emission
|
|
352
|
-
mode.
|
|
51
|
+
The ideation envelope carries `duplicates[]` (cross-Epic dup search),
|
|
52
|
+
`deliveryShapeSignal`, `docsContext`, `codebaseSnapshot`, `ticketSchema`,
|
|
53
|
+
and the rendered `systemPrompts`. The Epic does **not** exist yet —
|
|
54
|
+
creation happens in step 3.
|
|
55
|
+
3. Run the shared scope-triage gate over the one-pager — mechanics in
|
|
56
|
+
[`scope-triage-gate.md`](scope-triage-gate.md). The verdict folds into
|
|
57
|
+
gate #1. The Recommended branch on a `story` / `borderline` verdict:
|
|
58
|
+
persist the one-pager as a notes file and hand off to
|
|
59
|
+
[`plan-story.md`](plan-story.md) via `/plan --from-notes <path>` as a
|
|
60
|
+
scope-triage handoff (no re-triage on the receiving side), then exit this
|
|
61
|
+
path. Skipped entirely when `/plan` was itself entered via a scope-triage
|
|
62
|
+
handoff.
|
|
63
|
+
4. Review `duplicates[]`. A non-empty ranked list folds into gate #1: the
|
|
64
|
+
operator either confirms the new Epic is distinct or folds the idea into
|
|
65
|
+
an existing Epic (in which case `/plan` exits).
|
|
66
|
+
|
|
67
|
+
### Existing-Epic entry (`/plan <epicId>`)
|
|
68
|
+
|
|
69
|
+
1. Emit the authoring envelope:
|
|
353
70
|
|
|
354
71
|
```bash
|
|
355
|
-
node .agents/scripts/
|
|
356
|
-
> temp/epic-[Epic_ID]/
|
|
72
|
+
node .agents/scripts/plan-context.js --epic [Epic_ID] \
|
|
73
|
+
> temp/epic-[Epic_ID]/plan-context.json
|
|
357
74
|
```
|
|
358
75
|
|
|
359
|
-
The envelope carries
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
3. **
|
|
367
|
-
|
|
368
|
-
[`
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
approves, edits, or aborts. Flag the blast radius in the
|
|
384
|
-
confirmation prompt: an approved body change feeds **two**
|
|
385
|
-
downstream artifacts (Tech Spec, Acceptance Spec) — treat
|
|
386
|
-
this gate as a one-shot rewrite, not an iterative draft. The
|
|
387
|
-
Constraint ("Do not modify existing issues without explicit
|
|
388
|
-
permission") is honored — no `gh issue edit` call until the
|
|
389
|
-
operator confirms.
|
|
390
|
-
|
|
391
|
-
> **`--yes` (headless) auto-proceed.** This refinement-diff confirm is the
|
|
392
|
-
> clarity-gate face of `/plan`'s **gate #1** on the existing-Epic
|
|
393
|
-
> (`/plan <epicId>`) path — it is an operator *wait*, not a deterministic
|
|
394
|
-
> validator (the deterministic half is the section-presence *scoring* in
|
|
395
|
-
> step 1, which always runs). When `/plan` was invoked with `--yes`, this
|
|
396
|
-
> confirm does **not** STOP: the sharpened body is auto-**approved** and the
|
|
397
|
-
> run proceeds to step 6 (persist). The blast-radius note is still displayed
|
|
398
|
-
> for the record; only the operator wait is suppressed. This keeps
|
|
399
|
-
> `/plan <epicId> --yes` driveable headlessly even when the Epic body needs
|
|
400
|
-
> refinement (`gh issue edit` still runs only via the step 6 persist call,
|
|
401
|
-
> which the auto-approval authorizes). See
|
|
402
|
-
> [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
403
|
-
|
|
404
|
-
6. **Persist**: On approval, run the persist mode:
|
|
76
|
+
The epic envelope additionally carries `clarity` (section-presence
|
|
77
|
+
rubric), `replan` (already-planned signals), and `planState`.
|
|
78
|
+
2. **Re-plan detection** (`replan.alreadyPlanned === true`): the Epic body
|
|
79
|
+
already carries Tech Spec sections. Fold the decision into gate #1: a
|
|
80
|
+
confirmed re-plan runs step 3 with `--force` (managed sections are
|
|
81
|
+
overwritten in place; child Stories are closed and recreated); a decline
|
|
82
|
+
aborts gracefully.
|
|
83
|
+
3. **Story-sized advisory**: only when the Epic is not already planned
|
|
84
|
+
**and** `replan.openStoryCount` is 0, run the shared scope-triage gate
|
|
85
|
+
over the Epic body ([`scope-triage-gate.md`](scope-triage-gate.md)). On a
|
|
86
|
+
`story` / `borderline` verdict the Recommended branch converts via
|
|
87
|
+
close-and-recreate: seed `temp/epic-[Epic_ID]/scope-triage-seed.md` from
|
|
88
|
+
the Epic body, hand off to `/plan --from-notes <path>` as a scope-triage
|
|
89
|
+
handoff, and — only after the replacement Story `#N` exists — close the
|
|
90
|
+
Epic with a cross-linking comment
|
|
91
|
+
(`gh issue close [Epic_ID] --comment "Closed in favor of #N — scope triaged as a standalone Story."`).
|
|
92
|
+
Both mutations happen only after the operator confirms at gate #1.
|
|
93
|
+
4. **Clarity refinement** (`clarity.verdict === 'needs-refinement'`): the
|
|
94
|
+
deterministic scoring already ran inside the envelope build (a `clear`
|
|
95
|
+
verdict requires ≥ 4 of 5 canonical sections **and** Acceptance
|
|
96
|
+
Criteria). Interrogate the operator on the named gaps
|
|
97
|
+
(`clarity.missingOrPlaceholder`) — one question at a time — and draft the
|
|
98
|
+
refined body to `temp/epic-[Epic_ID]/clarity-update.md`. The refined-body
|
|
99
|
+
diff folds into gate #1; on approval persist it:
|
|
405
100
|
|
|
406
101
|
```bash
|
|
407
102
|
node .agents/scripts/epic-plan-clarity.js --epic [Epic_ID] \
|
|
408
103
|
--updated-body temp/epic-[Epic_ID]/clarity-update.md
|
|
409
104
|
```
|
|
410
105
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
> **
|
|
424
|
-
>
|
|
425
|
-
>
|
|
426
|
-
>
|
|
427
|
-
>
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
2.
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
`
|
|
450
|
-
`
|
|
451
|
-
`
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
> persist call rejects the authored artifacts (missing `## Delivery
|
|
464
|
-
> Slicing`, a schema-invalid risk verdict), re-entering this step applies
|
|
465
|
-
> **targeted edits** to the existing `temp/epic-[Epic_ID]/{techspec.md,
|
|
466
|
-
> risk-verdict.json,acceptance-spec.md}` files — fix only what the
|
|
467
|
-
> rejection named — per the `epic-plan-spec-author` skill's re-emit rule.
|
|
468
|
-
> This refinement is bounded to **one pass**: fix the named gap and
|
|
469
|
-
> re-run step 3 once: do not loop wholesale re-authoring across multiple
|
|
470
|
-
> rejections. The same one-pass shape already governs the Phase 6 Epic
|
|
471
|
-
> Clarity Gate's refinement loop above ("Do not loop — one refinement
|
|
472
|
-
> pass per invocation").
|
|
473
|
-
|
|
474
|
-
3. **Persist to GitHub**: Run the spec-phase CLI's persist half. It
|
|
475
|
-
schema-validates the planner-authored risk verdict (`--risk-verdict`,
|
|
476
|
-
required — a missing or malformed verdict fails closed before any
|
|
477
|
-
GitHub mutation), derives the `planningRisk` envelope from it
|
|
478
|
-
(`deriveRiskEnvelope`), records a `risk-verdict` structured comment
|
|
479
|
-
on the Epic, flips the Epic to `agent::review-spec`, and writes the
|
|
480
|
-
`epic-plan-state` checkpoint (including the `riskVerdict` field).
|
|
481
|
-
The `--acceptance-table` flag persists the AC-ID table as the Epic
|
|
482
|
-
body's `## Acceptance Table` managed section alongside the Tech Spec
|
|
483
|
-
sections; the persist half fails loudly if any file is missing or
|
|
484
|
-
empty. Omit `--acceptance-table` only when the Epic carries the
|
|
485
|
-
`acceptance::n-a` waiver label.
|
|
486
|
-
|
|
487
|
-
```bash
|
|
488
|
-
# Normal planning (both managed sections)
|
|
489
|
-
node .agents/scripts/epic-plan-spec.js --epic [Epic_ID] \
|
|
490
|
-
--tech-spec temp/epic-[Epic_ID]/techspec.md \
|
|
491
|
-
--risk-verdict temp/epic-[Epic_ID]/risk-verdict.json \
|
|
492
|
-
--acceptance-table temp/epic-[Epic_ID]/acceptance-spec.md
|
|
493
|
-
|
|
494
|
-
# Re-planning (--force overwrites the Epic body's managed sections
|
|
495
|
-
# IN PLACE — same Epic issue, refreshed section bodies, one
|
|
496
|
-
# regeneration audit comment on the Epic. Child Story tickets are
|
|
497
|
-
# still close-and-recreate at decomposition time.)
|
|
498
|
-
node .agents/scripts/epic-plan-spec.js --epic [Epic_ID] \
|
|
499
|
-
--tech-spec temp/epic-[Epic_ID]/techspec.md \
|
|
500
|
-
--risk-verdict temp/epic-[Epic_ID]/risk-verdict.json \
|
|
501
|
-
--acceptance-table temp/epic-[Epic_ID]/acceptance-spec.md --force
|
|
502
|
-
|
|
503
|
-
# Waived (acceptance::n-a label on Epic — no acceptance table authored)
|
|
504
|
-
node .agents/scripts/epic-plan-spec.js --epic [Epic_ID] \
|
|
505
|
-
--tech-spec temp/epic-[Epic_ID]/techspec.md \
|
|
506
|
-
--risk-verdict temp/epic-[Epic_ID]/risk-verdict.json
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
4. **Verification and review routing**:
|
|
510
|
-
- Verify that the Technical Specification sections and (when not
|
|
511
|
-
waived) the `## Acceptance Table` section have landed as managed
|
|
512
|
-
sections of the Epic body.
|
|
513
|
-
- Read `planningRisk` from the persist stdout JSON (or the
|
|
514
|
-
`epic-plan-state` checkpoint). Branch on
|
|
515
|
-
`planningRisk.requiresReview` unless the operator passed
|
|
516
|
-
`--force-review`:
|
|
517
|
-
- **High risk** (`requiresReview === true`) or **operator override**
|
|
518
|
-
(`--force-review`) — **gate #2**: **STOP**. Ask the USER to review the
|
|
519
|
-
updated Epic body (its Tech Spec sections and `## Acceptance
|
|
520
|
-
Table`) on GitHub. Approval is the user's verbal OK in this
|
|
521
|
-
session — the sections live on the Epic itself, so there is
|
|
522
|
-
nothing to close at delivery time. Do NOT proceed
|
|
523
|
-
to decomposition until the user confirms the plan is accurate.
|
|
524
|
-
|
|
525
|
-
> **`--yes` (headless) auto-proceed.** When `/plan` was invoked with
|
|
526
|
-
> `--yes`, this review gate does **not** STOP, even when
|
|
527
|
-
> `requiresReview === true` or `--force-review` was also passed: the
|
|
528
|
-
> review resolves as **approved** and the run **continues directly to
|
|
529
|
-
> Phase 8**, exactly as the low-risk auto-proceed branch below; only
|
|
530
|
-
> the operator *wait* is suppressed. This is `/plan`'s **gate #2** —
|
|
531
|
-
> the second and last HITL STOP `--yes` suppresses. `--yes` does
|
|
532
|
-
> **not** alter risk routing or the review criteria themselves; it
|
|
533
|
-
> only forces a proceed where this gate would otherwise STOP. See
|
|
534
|
-
> [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
535
|
-
- **Low risk** (`requiresReview === false` and no `--force-review`):
|
|
536
|
-
Emit the auto-proceed message from the persist stdout
|
|
537
|
-
(`reviewRouting.operatorMessage`) and **continue directly to Phase 8**
|
|
538
|
-
without an extra review stop. The Epic still carries
|
|
539
|
-
`agent::review-spec` until decomposition completes; the routing
|
|
540
|
-
decision is recorded in the `epic-plan-state` checkpoint. (`--yes` is
|
|
541
|
-
a no-op on this branch — there is no STOP to suppress.)
|
|
542
|
-
|
|
543
|
-
5. **Tech Spec freshness check (advisory)**: After the Tech Spec sections
|
|
544
|
-
are persisted, `epic-plan-spec.js` runs
|
|
545
|
-
[`validateSpecFreshness`](../../scripts/lib/orchestration/spec-freshness.js)
|
|
546
|
-
against the authored Tech Spec body, probing every cited path-shape
|
|
547
|
-
reference (backticked paths, `// header` lines in code blocks, and
|
|
548
|
-
inline mentions of paths under `.agents/`, `src/`, `lib/`, `app/`,
|
|
549
|
-
`tests/`, `packages/`, `scripts/`, `docs/`) against the configured
|
|
550
|
-
`baseBranch`. Results land in three buckets:
|
|
551
|
-
- **fresh** — path exists at the base ref (no action).
|
|
552
|
-
- **ambiguous** — path is absent but surrounding prose carries a
|
|
553
|
-
net-new cue (`introduce`, `add`, `create`, `new file`, `to be
|
|
554
|
-
created`, etc.); surfaced for review without alarm.
|
|
555
|
-
- **stale** — path is absent and no net-new cue is nearby; likely a
|
|
556
|
-
reference the Architect inherited from drift-stale docs.
|
|
557
|
-
|
|
558
|
-
When ≥1 stale reference is detected, a `spec-freshness` structured
|
|
559
|
-
comment is upserted on the **Epic** listing each citation
|
|
560
|
-
with its line number. The full report is also written to
|
|
561
|
-
`<tempRoot>/epic-<id>-spec-freshness.json` for downstream tooling.
|
|
562
|
-
The check is **advisory and non-blocking** — Phase 7 completes even
|
|
563
|
-
when stale references are present, so the operator retains final
|
|
564
|
-
judgment on edge cases. If the run summary shows
|
|
565
|
-
`⚠️ Spec freshness: N stale / M ambiguous`, review the Epic's
|
|
566
|
-
`spec-freshness` comment and correct the cited spec sections
|
|
567
|
-
before approving the plan for Phase 8.
|
|
568
|
-
|
|
569
|
-
6. **BDD scenario cross-reference (advisory)**: When the project has
|
|
570
|
-
adopted BDD, `epic-plan-spec.js` populates the planner-context
|
|
571
|
-
envelope with `bddScenarios` — the output of
|
|
572
|
-
[`scanBddScenarios`](../../scripts/lib/bdd-scenario-scanner.js) over
|
|
573
|
-
the canonical feature roots resolved by
|
|
574
|
-
[`resolveFeatureRoots`](../../scripts/lib/bdd-runner-detect.js)
|
|
575
|
-
(`tests/features`, `features`, `test/features`). The Acceptance
|
|
576
|
-
Engineer step in the [`epic-plan-spec-author`](../../skills/core/epic-plan-spec-author/SKILL.md)
|
|
577
|
-
skill scores each planned AC against the scenario index via
|
|
578
|
-
`findBestScenarioMatch`; when an existing scenario covers an AC's
|
|
579
|
-
outcome, the AC's `Scenario` column is annotated with `<file>:L<line>`
|
|
580
|
-
and the `Disposition` becomes `unchanged` / `refined` instead of
|
|
581
|
-
`new`. When `bddScenarios` is empty (no `.feature` files), the skill
|
|
582
|
-
degrades silently and the spec is authored exactly as before. The
|
|
583
|
-
matcher is keyword-based and deterministic so re-runs produce stable
|
|
584
|
-
dispositions.
|
|
585
|
-
|
|
586
|
-
7. **Cleanup**: The wrapper script (`epic-plan-spec.js`) deletes the Phase 7
|
|
587
|
-
temp files automatically on success — no operator action required. The
|
|
588
|
-
cleanup contract lives in
|
|
589
|
-
[`lib/plan-phase-cleanup.js`](../../scripts/lib/plan-phase-cleanup.js).
|
|
590
|
-
|
|
591
|
-
> **Tech Spec section gate (folded into Step 3 persist).** The persist call
|
|
592
|
-
> in Step 3 above validates the authored Tech Spec for the required
|
|
593
|
-
> `## Delivery Slicing` heading (case-insensitive; also accepts the variant
|
|
594
|
-
> `## Delivery slicing` and the shorthand `## Slicing`) via
|
|
595
|
-
> [`lib/orchestration/spec-section-validator.js`](../../scripts/lib/orchestration/spec-section-validator.js#validateSpecSections)
|
|
596
|
-
> **before** acquiring the Epic-lease or making any GitHub mutation — a
|
|
597
|
-
> Tech Spec missing the section fails the persist call closed with a message
|
|
598
|
-
> naming the gap and the two recovery paths (re-author, or add the section by
|
|
599
|
-
> hand). This used to run as a separate, standalone
|
|
600
|
-
> `epic-plan-spec-validate.js` CLI invoked as its own Phase 7.5 workflow
|
|
601
|
-
> step; that ordering ran the check *after* the persist had already written
|
|
602
|
-
> to GitHub and *after* `cleanupPhaseTempFiles` had deleted the temp spec
|
|
603
|
-
> file the check read, so the "blocking gate" could never actually block.
|
|
604
|
-
> Folding the check into the persist path's input validation makes it a
|
|
605
|
-
> real hard gate. **Why this gate exists**: Phase 8.3 (Holistic
|
|
606
|
-
> Consolidation) reconciles the draft ticket array against the Tech Spec's
|
|
607
|
-
> `## Delivery Slicing` section, which the
|
|
608
|
-
> [`epic-plan-decompose-author`](../../skills/core/epic-plan-decompose-author/SKILL.md)
|
|
609
|
-
> skill uses as the capability-boundary anchor (see Phase 8 step 2 below).
|
|
610
|
-
> Without it, the consolidation pass runs against a void and produces
|
|
611
|
-
> groupings that reflect **technical shape** (e.g. cron jobs together)
|
|
612
|
-
> rather than **capability boundaries** — reproduced on Epic #18 in
|
|
613
|
-
> `dsj1984/athportal` (planned with v1.54.0), whose spec carried a detailed
|
|
614
|
-
> `## Core Components` table but no `## Delivery Slicing` section. On a
|
|
615
|
-
> persist failure, re-enter the Step 2 spec-author step and apply a
|
|
616
|
-
> **targeted edit** adding the missing `## Delivery Slicing` section to the
|
|
617
|
-
> existing `techspec.md` (amend, don't regenerate — the one-pass refinement
|
|
618
|
-
> contract above) — or add the section by hand — then re-run Step 3 once.
|
|
619
|
-
|
|
620
|
-
## Phase 8: Work Breakdown Decomposition
|
|
621
|
-
|
|
622
|
-
> **Open-children guard (workflow guard).** Before persisting the breakdown,
|
|
623
|
-
> `epic-plan-decompose.js` refuses to run when the Epic **already has open
|
|
624
|
-
> Story children**, unless `--force` (close + recreate the tree) or
|
|
625
|
-
> `--resume` (continue a partial persist) is set. This stops a re-run from
|
|
626
|
-
> stacking a duplicate Story set on top of an existing one — the
|
|
627
|
-
> phase exits non-zero and lists the open children. Once persist completes,
|
|
628
|
-
> the Epic-lease acquired in Phase 7 is **released** (best-effort; a release
|
|
629
|
-
> failure never fails decompose).
|
|
630
|
-
|
|
631
|
-
<!-- separator: adjacent blockquotes -->
|
|
632
|
-
|
|
633
|
-
> **Hierarchy.** The decomposer emits a flat Story backlog under the Epic.
|
|
634
|
-
> Acceptance criteria and verification steps are inlined on each Story
|
|
635
|
-
> body (`acceptance[]` / `verify[]` fields) and resolved against the
|
|
636
|
-
> Epic body's `## Acceptance Table` section at close time. See
|
|
637
|
-
> [`.agents/instructions.md` § 5.D](../../instructions.md) for the full
|
|
638
|
-
> contract.
|
|
639
|
-
|
|
640
|
-
1. **Gather Decomposition Context**:
|
|
641
|
-
|
|
642
|
-
```bash
|
|
643
|
-
node .agents/scripts/epic-plan-decompose.js --epic [Epic_ID] --emit-context > temp/epic-[Epic_ID]/decomposer-context.json
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
2. **Activate the `epic-plan-decompose-author` skill**: Read
|
|
647
|
-
[`<agentRoot>/skills/core/epic-plan-decompose-author/SKILL.md`](../../skills/core/epic-plan-decompose-author/SKILL.md)
|
|
648
|
-
via the `Read` tool (resolve `<agentRoot>` from
|
|
649
|
-
`project.paths.agentRoot` — default `.agents`) and execute its
|
|
650
|
-
procedure with `[Epic_ID]` as input. The skill reads
|
|
651
|
-
`temp/epic-[Epic_ID]/decomposer-context.json` (`epicBody` — carrying
|
|
652
|
-
the folded Tech Spec sections and Acceptance Table — risk
|
|
653
|
-
heuristics, `maxTickets` cap, `contextMode`), applies its
|
|
654
|
-
embedded decomposer system prompt + ticket schema, and writes the
|
|
655
|
-
ticket array to `temp/epic-[Epic_ID]/tickets.json`. Do **not** inline
|
|
656
|
-
the JSON authoring in the workflow body.
|
|
657
|
-
|
|
658
|
-
The `maxTickets` cap is a **framework constant**
|
|
659
|
-
(`LIMITS_DEFAULTS.maxTickets` in
|
|
660
|
-
`.agents/scripts/lib/config/limits.js`; not operator-configurable) and
|
|
661
|
-
a **reviewability budget**, not a hard authoring ceiling: a draft over
|
|
662
|
-
budget warns at authoring time and is rejected at persist unless rerun
|
|
663
|
-
with `--allow-over-budget` (after confirming the over-budget rationale
|
|
664
|
-
on the Epic). The `epic-plan-decompose.js` script also logs the
|
|
665
|
-
resolved budget to stderr. The decomposer system prompt is
|
|
666
|
-
single-sourced in
|
|
667
|
-
`.agents/scripts/lib/templates/decomposer-prompts.js`
|
|
668
|
-
(`renderDecomposerSystemPrompt`); the `epic-plan-decompose-author`
|
|
669
|
-
skill references that rendered `systemPrompt` rather than carrying
|
|
670
|
-
its own copy of the prompt body.
|
|
671
|
-
|
|
672
|
-
When the Tech Spec carries a `## Delivery Slicing` section (authored by
|
|
673
|
-
`epic-plan-spec-author` in Phase 7), the decompose-author skill authors
|
|
674
|
-
toward the Architect's proposed shippable-Story clusters; it degrades
|
|
675
|
-
gracefully when the section is absent.
|
|
676
|
-
|
|
677
|
-
> **One-pass refinement contract (amend, don't regenerate).** When the
|
|
678
|
-
> deterministic validator (`validateAndNormalizeTickets`) rejects the
|
|
679
|
-
> draft, or the 8.3 / 8.4 / 8.5 critics below flag specific Stories,
|
|
680
|
-
> re-entering this step applies
|
|
681
|
-
> **targeted edits** to the existing `temp/epic-[Epic_ID]/tickets.json` —
|
|
682
|
-
> fix only the rejected or flagged Stories — per the
|
|
683
|
-
> `epic-plan-decompose-author` skill's re-emit rule, rather than
|
|
684
|
-
> re-authoring the whole array from scratch. Each critic's feedback gets
|
|
685
|
-
> **one refinement pass**: apply the targeted edit and re-run the
|
|
686
|
-
> relevant downstream step (validator or persist) once, matching the
|
|
687
|
-
> Phase 7 spec-author contract above; do not loop wholesale re-authoring
|
|
688
|
-
> across repeated rejections.
|
|
689
|
-
|
|
690
|
-
3. **Phase 8.3 — Holistic Consolidation (HITL diff gate, precondition-gated
|
|
691
|
-
dispatch)**: After the draft `temp/epic-[Epic_ID]/tickets.json` exists and
|
|
692
|
-
**before** the persist call below, first consult the deterministic
|
|
693
|
-
dispatch precondition — [`evaluateConsolidationPrecondition`](../../scripts/lib/orchestration/consolidation-precondition.js),
|
|
694
|
-
called with the draft Story array and the Epic body (which carries the
|
|
695
|
-
folded Tech Spec `## Delivery Slicing` section). The precondition compares
|
|
696
|
-
the draft against the Delivery Slicing table **1:1** — same shippable-Story
|
|
697
|
-
count (excluding the wave-0 `bdd-scaffold` Story) and a `depends_on` shape
|
|
698
|
-
that agrees with each slice's `Independent?` answer — and returns
|
|
699
|
-
`{ dispatch, reasons }`. It **fails open** (`dispatch: true`) on every
|
|
700
|
-
ambiguous input: a missing or unparseable Delivery Slicing section, or an
|
|
701
|
-
unparseable `Independent?` cell.
|
|
702
|
-
|
|
703
|
-
- **`dispatch: false`** — the draft already matches the Delivery Slicing
|
|
704
|
-
target, so the consolidation critic has nothing to reconcile. **Skip the
|
|
705
|
-
sub-agent dispatch** and surface the skip as a **one-line progress
|
|
706
|
-
note** naming the precondition's `reasons` — quoting verbatim the
|
|
707
|
-
string `evaluateConsolidationPrecondition` actually emits: "Draft
|
|
708
|
-
matches Delivery Slicing 1:1 in count and dependency shape (N
|
|
709
|
-
slice(s)) — skipping the 8.3 consolidation dispatch." (`N` is the
|
|
710
|
-
interpolated `slicing.length`). Proceed straight to Phase 8.4 with the
|
|
711
|
-
draft `tickets.json` unchanged (no `consolidation-report.md` is
|
|
712
|
-
produced when the dispatch is skipped).
|
|
713
|
-
- **`dispatch: true`** — **dispatch a genuine fresh-context sub-agent**
|
|
714
|
-
(`Agent` tool, `subagent_type: general-purpose`) whose task is to read
|
|
715
|
-
the
|
|
716
|
-
[`epic-plan-consolidate`](../../skills/core/epic-plan-consolidate/SKILL.md)
|
|
717
|
-
skill and execute its procedure with `[Epic_ID]` as input, including the
|
|
718
|
-
precondition's `reasons` in the HITL diff context so the operator sees
|
|
719
|
-
*why* the critic ran. Dispatching the critic as a sub-agent — **not**
|
|
720
|
-
activating the skill inline in your authoring turn — is what makes it a
|
|
721
|
-
**separate critic pass with fresh context**: the sub-agent does not
|
|
722
|
-
inherit the conversation that authored the draft, so it cannot grade its
|
|
723
|
-
own homework (the same nested-dispatch mechanic the acceptance self-eval
|
|
724
|
-
loop uses, now that the sub-agent depth limit is lifted — see
|
|
725
|
-
[`.agents/instructions.md` § 4](../../instructions.md)). The sub-agent
|
|
726
|
-
reads the draft array plus the Epic body (which carries the Tech Spec
|
|
727
|
-
sections), reconciles the draft against the Tech Spec `## Delivery
|
|
728
|
-
Slicing` target, and emits a **consolidated** `tickets.json` plus a
|
|
729
|
-
human-readable `temp/epic-[Epic_ID]/consolidation-report.md`. Its operations are scope-preserving only — **merge sibling Stories and
|
|
730
|
-
rewire `depends_on`** — and it MUST NOT add scope or invent tickets; it
|
|
731
|
-
consolidates fragmented slices by merging them into a cohesive Story,
|
|
732
|
-
never by splitting one. The sub-agent **never writes to GitHub**: it
|
|
733
|
-
emits only the two temp artifacts and returns control to this operator
|
|
734
|
-
session. It runs **before** the deterministic validator
|
|
735
|
-
(`validateAndNormalizeTickets`), so the validator re-checks its output
|
|
736
|
-
and the critic cannot emit an invalid plan. **Show the operator the
|
|
737
|
-
consolidation report (the before/after
|
|
738
|
-
diff + rationale) before persisting.** The HITL diff-confirm and the
|
|
739
|
-
persist call both run here in the **operator session**, never inside
|
|
740
|
-
the sub-agent — consolidation is never auto-applied without review: on
|
|
741
|
-
operator approval, persist the consolidated `tickets.json`; on
|
|
742
|
-
rejection, persist the draft instead.
|
|
743
|
-
|
|
744
|
-
Phase 8.4 (Reachability Completeness Critic), Phase 8.5 (Planning
|
|
745
|
-
Pre-Mortem Critic), and the deterministic ticket validator
|
|
746
|
-
(`validateAndNormalizeTickets`) are
|
|
747
|
-
**unconditional** — the precondition governs only the 8.3 sub-agent
|
|
748
|
-
dispatch, never any other Phase 8 gate. This is a sub-step of Phase 8 — it
|
|
749
|
-
does **not** renumber the top-level lifecycle phases (9–12).
|
|
750
|
-
|
|
751
|
-
4. **Phase 8.4 — Reachability Completeness Critic (HITL diff gate, F6)**:
|
|
752
|
-
After consolidation (8.3) and **before** the persist call below, run the
|
|
753
|
-
completeness critic. This is a **distinct pass** from
|
|
754
|
-
[`epic-plan-consolidate`](../../skills/core/epic-plan-consolidate/SKILL.md)
|
|
755
|
-
and **must NOT relax** that skill's scope-preserving conservation invariant:
|
|
756
|
-
the consolidate critic is merge-and-rewire-only (it MUST NOT add scope or
|
|
757
|
-
invent tickets), whereas this critic is permitted exactly one **additive**
|
|
758
|
-
operation — appending a **single reachability Story** when, and only when, an
|
|
759
|
-
Epic introduces a user-facing surface (a new route/page) that **no Story
|
|
760
|
-
gives a navigation owner** (no nav-registry entry, no menu/link door from an
|
|
761
|
-
authenticated home). The two passes are deliberately separate so the
|
|
762
|
-
conservation invariant stays intact: consolidation never adds, and this
|
|
763
|
-
critic's only addition is the reachability Story.
|
|
764
|
-
|
|
765
|
-
- **Detection.** Scan the consolidated `tickets.json` for Stories that add a
|
|
766
|
-
route-shaped surface (paths under the consumer-configured
|
|
767
|
-
`planning.navigation.routeGlobs`, mirroring the
|
|
768
|
-
`epic-plan-healthcheck.js --paranoid` reachability check, F7). For each
|
|
769
|
-
such surface, confirm some Story's `acceptance[]` / `changes[]` references
|
|
770
|
-
the consumer's nav-registry SSOT (`planning.navigation.navRegistry`). A
|
|
771
|
-
surface with **no** nav owner is an orphan.
|
|
772
|
-
- **Action.** When at least one orphan surface exists, append **one**
|
|
773
|
-
reachability Story whose `acceptance[]` requires the orphaned surface to be
|
|
774
|
-
reachable by navigation from the relevant persona's authenticated home
|
|
775
|
-
(never by deep-link), and wire its `depends_on` to the surface-adding
|
|
776
|
-
Story. Append **at most one** reachability Story per decompose run — batch
|
|
777
|
-
every orphan under it — so the critic never fans out a parallel backlog.
|
|
778
|
-
- **No-op when unconfigured.** With no `planning.navigation` config present,
|
|
779
|
-
the critic degrades to a silent no-op (it cannot identify route-shaped
|
|
780
|
-
surfaces or a nav registry), exactly like the F7 healthcheck flag.
|
|
781
|
-
- **HITL.** The added Story is surfaced in the **same Phase 8 HITL diff** as
|
|
782
|
-
consolidation, **before** any GitHub write — never auto-persisted. On
|
|
783
|
-
operator rejection, drop the added Story and persist without it. The
|
|
784
|
-
deterministic validator (`validateAndNormalizeTickets`) re-checks the
|
|
785
|
-
critic's output, so an invalid addition cannot reach GitHub.
|
|
786
|
-
|
|
787
|
-
5. **Phase 8.5 — Planning Pre-Mortem Critic (code-reading, F9)**: After the
|
|
788
|
-
reachability critic (8.4) and **before** the persist call below, **dispatch a
|
|
789
|
-
genuine fresh-context sub-agent** (`Agent` tool,
|
|
790
|
-
`subagent_type: general-purpose`) whose task is to read the
|
|
791
|
-
[`epic-plan-premortem`](../../skills/core/epic-plan-premortem/SKILL.md)
|
|
792
|
-
skill and execute its procedure with `[Epic_ID]` as input. Like the 8.3
|
|
793
|
-
consolidate pass, dispatching the critic as a sub-agent — **not** activating
|
|
794
|
-
the skill inline in your authoring turn — is what makes it a genuine
|
|
795
|
-
**fresh-context critic** sibling to `epic-plan-consolidate`: the sub-agent
|
|
796
|
-
does not inherit the authoring conversation, so its code-reading review is
|
|
797
|
-
independent of the draft it grades. It reads the drafted `tickets.json`, the
|
|
798
|
-
Epic body (with its folded Tech Spec sections), **and the actual cited code
|
|
799
|
-
surfaces** (the files each Story's
|
|
800
|
-
`changes[]` / `references[]` name), then emits predicted-rework findings —
|
|
801
|
-
unverifiable acceptance criteria, over- or under-specified Stories, and
|
|
802
|
-
semantically-wrong assumptions the structural file-assumption gate (step 7)
|
|
803
|
-
cannot catch — to `temp/epic-[Epic_ID]/premortem-report.md`.
|
|
804
|
-
|
|
805
|
-
Unlike the consolidate critic it is **not** scope-preserving-only: it may
|
|
806
|
-
recommend splitting an under-specified Story or tightening an AC. But it
|
|
807
|
-
**never writes to GitHub** and never persists `tickets.json` — it only emits
|
|
808
|
-
the report and returns control to this operator session. Its findings are
|
|
809
|
-
shown in the **same Phase 8 HITL diff** (which stays in the operator
|
|
810
|
-
session), and on
|
|
811
|
-
operator approval the author re-runs (Step 2) on the findings **before** the
|
|
812
|
-
persist call — applying **targeted edits** to the flagged Stories in the
|
|
813
|
-
existing `tickets.json` (amend, don't regenerate; **one refinement pass**
|
|
814
|
-
per the contract above), not a wholesale re-author. The critic runs
|
|
815
|
-
**before** the deterministic validator (`validateAndNormalizeTickets`),
|
|
816
|
-
so the persist below is the single GitHub write for the whole phase.
|
|
817
|
-
|
|
818
|
-
6. **Persist to GitHub**: Run the decompose CLI's persist half. It
|
|
819
|
-
validates the ticket array (`validateAndNormalizeTickets`), creates
|
|
820
|
-
the Story issues, flips the Epic to `agent::ready`, and
|
|
821
|
-
writes the `epic-plan-state` checkpoint.
|
|
822
|
-
|
|
823
|
-
```bash
|
|
824
|
-
# Normal decomposition
|
|
825
|
-
node .agents/scripts/epic-plan-decompose.js --epic [Epic_ID] \
|
|
826
|
-
--tickets temp/epic-[Epic_ID]/tickets.json
|
|
827
|
-
|
|
828
|
-
# Re-planning (close old tickets first)
|
|
829
|
-
node .agents/scripts/epic-plan-decompose.js --epic [Epic_ID] \
|
|
830
|
-
--tickets temp/epic-[Epic_ID]/tickets.json --force
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
> On a large Epic (~60+ tickets) the persist can trip GitHub's secondary
|
|
834
|
-
> rate limit mid-run. Resume from the partial backlog with `--resume` — the
|
|
835
|
-
> full recovery contract is in
|
|
836
|
-
> [`plan-epic-reference.md` § Phase 8 — `--resume` recovery (secondary rate limit)](plan-epic-reference.md#phase-8----resume-recovery-secondary-rate-limit).
|
|
837
|
-
|
|
838
|
-
7. **Cross-Validation**:
|
|
839
|
-
- Hierarchy completeness, dependency-DAG acyclicity, and `risk::high`
|
|
840
|
-
labelling are deterministic invariants enforced by
|
|
841
|
-
`validateAndNormalizeTickets` in
|
|
842
|
-
[`lib/orchestration/ticket-validator.js`](../../scripts/lib/orchestration/ticket-validator.js);
|
|
843
|
-
its output during decomposition is the canonical proof — no manual
|
|
844
|
-
re-check needed.
|
|
845
|
-
- **File-assumption gate (Story #2636)**: Each Story's `body.changes`
|
|
846
|
-
and `body.references` entries declare an explicit `assumption` ∈
|
|
847
|
-
`creates | refactors-existing | exists | deletes`. The validator
|
|
848
|
-
probes the base branch for every declared path and rejects the
|
|
849
|
-
decompose when the declaration contradicts reality:
|
|
850
|
-
- `creates` + path **exists** at base → error.
|
|
851
|
-
- `refactors-existing` / `exists` / `deletes` + path **absent** at
|
|
852
|
-
base → error.
|
|
853
|
-
Errors are batched per-Story into the validator's `errors` envelope
|
|
854
|
-
so the decompose loop surfaces every mismatch in a single re-prompt
|
|
855
|
-
rather than one at a time.
|
|
856
|
-
- **Scope-overlap check (docs/runbook downstream of config work)**:
|
|
857
|
-
Scan for Stories whose scope is "docs update", "runbook", or
|
|
858
|
-
"README" that land downstream of an earlier "config + runbook"
|
|
859
|
-
Story in the same Epic. If the earlier Story's AC already covers
|
|
860
|
-
the same document, the downstream Story's deliverable is likely
|
|
861
|
-
absorbed. Append a "Scope verification note" to the downstream
|
|
862
|
-
Story body pointing the executor to `git diff main -- <path>`
|
|
863
|
-
against the upstream Story branch so they can confirm whether a
|
|
864
|
-
substantive edit is still required (or only a cross-reference
|
|
865
|
-
remains). The decomposer system prompt emits this flag
|
|
866
|
-
automatically where it can detect the pattern — this checklist
|
|
867
|
-
item is the human/host-LLM backstop.
|
|
868
|
-
- **Action**: Fix any scope-overlap exceptions or validator failures by
|
|
869
|
-
re-running the scripted force path so the change is recorded in tooling
|
|
870
|
-
rather than hand-applied:
|
|
871
|
-
|
|
872
|
-
```bash
|
|
873
|
-
node .agents/scripts/epic-plan-decompose.js \
|
|
874
|
-
--epic [Epic_ID] \
|
|
875
|
-
--tickets temp/epic-[Epic_ID]/tickets.json \
|
|
876
|
-
--force
|
|
877
|
-
```
|
|
878
|
-
|
|
879
|
-
8. **Audit**:
|
|
880
|
-
- Check the Epic's comment thread to ensure the backlog summary was posted.
|
|
881
|
-
- Verify that at least one `type::story` issue was created.
|
|
882
|
-
|
|
883
|
-
9. **Cleanup**: The wrapper script (`epic-plan-decompose.js`) deletes the
|
|
884
|
-
Phase 8 temp files automatically on success — no operator action required.
|
|
885
|
-
The cleanup contract lives in
|
|
886
|
-
[`lib/plan-phase-cleanup.js`](../../scripts/lib/plan-phase-cleanup.js).
|
|
887
|
-
|
|
888
|
-
## Phase 9: Execution Roadmap (Story Dispatch)
|
|
889
|
-
|
|
890
|
-
1. **Generate Roadmap**: Automatically invoke the dispatcher in dry-run mode to
|
|
891
|
-
calculate execution waves and model recommendations:
|
|
892
|
-
|
|
893
|
-
```bash
|
|
894
|
-
node .agents/scripts/dispatcher.js [Epic_ID] --dry-run
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
2. **Verify Output**:
|
|
898
|
-
- Confirm the **Story Dispatch Table** is printed.
|
|
899
|
-
- Check for any stories in **Wave 0** — these are ready for immediate
|
|
900
|
-
execution.
|
|
901
|
-
|
|
902
|
-
> **Manifest persistence (v5.9.0):** the dispatcher also posts the manifest
|
|
903
|
-
> as a `dispatch-manifest` structured comment on the Epic (idempotent —
|
|
904
|
-
> re-runs replace the prior comment). That comment is the source of truth for
|
|
905
|
-
> the Wave Completeness Gate in `/deliver` Step 0.5 and for any external
|
|
906
|
-
> wave-tracking tooling.
|
|
907
|
-
|
|
908
|
-
3. **Handoff**: Provide the user with the recommended next step:
|
|
909
|
-
|
|
910
|
-
> "Planning is complete. Run `/deliver #[Epic ID]` to start the wave
|
|
911
|
-
> loop, or pick a single Story from Wave 0 and run `/deliver #[Story
|
|
912
|
-
> ID]` to drive it directly."
|
|
913
|
-
|
|
914
|
-
## Phase 10: Readiness Health Check
|
|
915
|
-
|
|
916
|
-
Run the post-plan health check to validate the backlog before handing off to
|
|
917
|
-
`/deliver`. The default mode runs only the cheap checks
|
|
918
|
-
(config + git remote) and targets sub-2-second turnaround. The script itself
|
|
919
|
-
always exits 0; the structured JSON on stdout reports findings.
|
|
106
|
+
One refinement pass per invocation — do not loop.
|
|
107
|
+
|
|
108
|
+
### Gate #1 — exit of interrogate (HITL STOP)
|
|
109
|
+
|
|
110
|
+
One operator confirmation folds every interrogate outcome: the one-pager (or
|
|
111
|
+
the refined-body diff), the scope-triage verdict, the duplicate candidates,
|
|
112
|
+
and the re-plan decision. Display them together and **STOP**. Do not enter
|
|
113
|
+
step 2 until the operator explicitly confirms.
|
|
114
|
+
|
|
115
|
+
> **`--yes` (headless) auto-proceed.** Under `--yes` this gate does **not**
|
|
116
|
+
> STOP: the confirm resolves as **approved**, and a `story` / `borderline`
|
|
117
|
+
> triage verdict resolves to its **Recommended** branch. The interrogation
|
|
118
|
+
> itself runs exactly **one bounded pass** — no operator questions are asked;
|
|
119
|
+
> facts come from the codebase and every unresolved unknown lands in the
|
|
120
|
+
> one-pager's **Key Assumptions** section instead of a question, so a
|
|
121
|
+
> headless driver can never hang. See
|
|
122
|
+
> [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
123
|
+
|
|
124
|
+
## Step 2 — Author
|
|
125
|
+
|
|
126
|
+
Read the envelope with the `Read` tool and write the planning artifacts to
|
|
127
|
+
`temp/epic-[Epic_ID]/` (ideation: the `temp/plan-ideation/<slug>/` tree).
|
|
128
|
+
The single `plan-context.json` envelope supersedes the per-phase
|
|
129
|
+
`planner-context.json` / `decomposer-context.json` files the authoring
|
|
130
|
+
skills name — read the envelope wherever a skill asks for either.
|
|
131
|
+
|
|
132
|
+
1. **`techspec.md`** — activate the
|
|
133
|
+
[`epic-plan-spec-author`](../../skills/core/epic-plan-spec-author/SKILL.md)
|
|
134
|
+
skill. The Tech Spec opens with `## Delivery Slicing` and never restates
|
|
135
|
+
the Epic's Context/Goal/Scope.
|
|
136
|
+
2. **`risk-verdict.json`** — same skill; the schema-conformant verdict
|
|
137
|
+
**plus `deliveryShape: "fan-out"|"single"`** and a one-line rationale.
|
|
138
|
+
Seed the shape from the envelope's `deliveryShapeSignal` (advisory —
|
|
139
|
+
the operator vetoes it at gate #2). `"single"` means one-pass-sized or a
|
|
140
|
+
pure dependent chain: the plan ships as spec-only, with **no tickets**.
|
|
141
|
+
3. **`acceptance-spec.md`** — same skill; omit only when the Epic carries
|
|
142
|
+
the `acceptance::n-a` waiver label.
|
|
143
|
+
4. **`tickets.json`** — fan-out shape only: activate the
|
|
144
|
+
[`epic-plan-decompose-author`](../../skills/core/epic-plan-decompose-author/SKILL.md)
|
|
145
|
+
skill against the same envelope (its `systemPrompts.decompose`,
|
|
146
|
+
`ticketSchema`, and `maxTickets` fields). In single-delivery shape author
|
|
147
|
+
**no** tickets file — the Delivery Slicing table is the plan.
|
|
148
|
+
|
|
149
|
+
> **One-pass refinement contract (amend, don't regenerate).** When a critic
|
|
150
|
+
> flags Stories or the step 3 persist rejects an artifact, apply **targeted
|
|
151
|
+
> edits** to the existing files — fix only what was named — and re-run the
|
|
152
|
+
> rejected step **once**. Never loop wholesale re-authoring.
|
|
153
|
+
|
|
154
|
+
### Conditional critics (between authoring and gate #2)
|
|
155
|
+
|
|
156
|
+
Evaluate the dispatch conditions deterministically first — one git-local
|
|
157
|
+
CLI call, zero GitHub reads:
|
|
920
158
|
|
|
921
159
|
```bash
|
|
922
|
-
node .agents/scripts/
|
|
160
|
+
node .agents/scripts/plan-critics.js --epic [Epic_ID]
|
|
923
161
|
```
|
|
924
162
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
`
|
|
934
|
-
|
|
935
|
-
|
|
163
|
+
(ideation: pass `--tech-spec`/`--risk-verdict`/`--tickets` explicitly). The
|
|
164
|
+
verdict names each critic with `dispatch: true|false` and reasons. Dispatch
|
|
165
|
+
a sub-agent ONLY for a critic with `dispatch: true`; surface each skip as a
|
|
166
|
+
one-line note. Every skip decision is appended to the plan-metrics ledger
|
|
167
|
+
(`kind: "critic-skip"`, with reasons) so under-firing is auditable — the
|
|
168
|
+
persist validators remain unchanged hard gates either way.
|
|
169
|
+
|
|
170
|
+
Both critics are **fresh-context sub-agents** (`Agent` tool,
|
|
171
|
+
`subagent_type: general-purpose`) — never inline skill activations, so they
|
|
172
|
+
cannot grade their own homework. Both are report-only: they never write to
|
|
173
|
+
GitHub and never persist `tickets.json`; their findings fold into the gate #2
|
|
174
|
+
view, and accepted findings get one targeted amend pass (above).
|
|
175
|
+
|
|
176
|
+
- **Consolidation critic** (fan-out only): fires when the draft does NOT
|
|
177
|
+
already match the Tech Spec's Delivery Slicing table 1:1 in Story count
|
|
178
|
+
and dependency shape, AND the divergence is worth a dispatch — the draft
|
|
179
|
+
has more than 5 stories, or the mismatch is confirmed (a small draft
|
|
180
|
+
whose table is merely missing/unparseable skips: gate #2's single view
|
|
181
|
+
covers it). On dispatch the sub-agent reads the
|
|
182
|
+
[`epic-plan-consolidate`](../../skills/core/epic-plan-consolidate/SKILL.md)
|
|
183
|
+
skill and reconciles the draft against the slicing target. Its operations
|
|
184
|
+
are scope-preserving only — merge sibling Stories and rewire `depends_on`;
|
|
185
|
+
it MUST NOT add scope or invent tickets.
|
|
186
|
+
- **Pre-mortem critic**: fires when the risk verdict's overall level is
|
|
187
|
+
high, OR the ticket count is at least half `maxTickets`, OR any
|
|
188
|
+
`planning.riskHeuristics` phrase matches the plan text. On dispatch the
|
|
189
|
+
sub-agent reads the
|
|
190
|
+
[`epic-plan-premortem`](../../skills/core/epic-plan-premortem/SKILL.md)
|
|
191
|
+
skill, reads the actual cited code surfaces, and emits predicted-rework
|
|
192
|
+
findings to `temp/epic-[Epic_ID]/premortem-report.md`.
|
|
193
|
+
|
|
194
|
+
## Step 3 — Persist
|
|
195
|
+
|
|
196
|
+
### Gate #2 — risk-routed review (HITL STOP, before any GitHub write)
|
|
197
|
+
|
|
198
|
+
Present the whole plan in **one view**: the Tech Spec, the Acceptance Table,
|
|
199
|
+
the tickets (or the Delivery Slicing table in single shape), the risk
|
|
200
|
+
verdict, the `deliveryShape`, and any critic reports. This is the single
|
|
201
|
+
seam where the operator vetoes single-vs-fan-out routing.
|
|
202
|
+
|
|
203
|
+
- **High risk** (any risk axis high / `requiresReview`-shaped verdict) or
|
|
204
|
+
**operator override** (`--force-review`): **STOP** for explicit operator
|
|
205
|
+
approval before running the persist CLI.
|
|
206
|
+
- **Low risk**: emit a one-line auto-proceed note and continue directly to
|
|
207
|
+
the persist call — no operator wait. (`--yes` is a no-op on this branch —
|
|
208
|
+
there is no STOP to suppress.)
|
|
209
|
+
|
|
210
|
+
> **`--yes` (headless) auto-proceed.** Under `--yes` this review gate does
|
|
211
|
+
> **not** STOP, even when the verdict is high-risk or `--force-review` was
|
|
212
|
+
> passed: the review resolves as **approved** and the run continues to the
|
|
213
|
+
> persist call. `--yes` does **not** alter risk routing or the review
|
|
214
|
+
> criteria themselves — it only forces a proceed where this gate would
|
|
215
|
+
> otherwise STOP. See
|
|
216
|
+
> [`plan.md` § Headless / non-interactive mode](../plan.md#headless--non-interactive-mode---yes).
|
|
217
|
+
|
|
218
|
+
### Run the persist CLI
|
|
936
219
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
220
|
+
```bash
|
|
221
|
+
# Normal persist (artifact paths default to temp/epic-[Epic_ID]/)
|
|
222
|
+
node .agents/scripts/plan-persist.js --epic [Epic_ID]
|
|
223
|
+
|
|
224
|
+
# Re-plan (overwrite managed sections in place; close + recreate the tree)
|
|
225
|
+
node .agents/scripts/plan-persist.js --epic [Epic_ID] --force
|
|
226
|
+
|
|
227
|
+
# Ideation (opens the Epic itself; artifact paths must be explicit)
|
|
228
|
+
node .agents/scripts/plan-persist.js \
|
|
229
|
+
--one-pager temp/plan-ideation/<slug>/one-pager.md \
|
|
230
|
+
--tech-spec temp/plan-ideation/<slug>/techspec.md \
|
|
231
|
+
--risk-verdict temp/plan-ideation/<slug>/risk-verdict.json \
|
|
232
|
+
--acceptance-table temp/plan-ideation/<slug>/acceptance-spec.md \
|
|
233
|
+
--tickets temp/plan-ideation/<slug>/tickets.json
|
|
234
|
+
|
|
235
|
+
# Change-request delta (tickets carry op: add|modify|keep|close;
|
|
236
|
+
# close ops additionally require --explicit-delete after the dry-run diff)
|
|
237
|
+
node .agents/scripts/plan-persist.js --epic [Epic_ID] --amend
|
|
949
238
|
```
|
|
950
239
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
-
|
|
954
|
-
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
changes, shared auth/security, CI/CD gate changes, monorepo-wide rewrites,
|
|
990
|
-
destructive migrations).
|
|
991
|
-
- The Tech Spec introduces a **novel architectural decision** or a wide
|
|
992
|
-
**blast radius** across subsystems.
|
|
993
|
-
|
|
994
|
-
A small, single-wave, low-risk plan proceeds straight to Phase 12 with no
|
|
995
|
-
prompt. When in doubt on a borderline plan, make a one-line offer rather than
|
|
996
|
-
forcing the walkthrough.
|
|
997
|
-
|
|
998
|
-
### 2. Run the walkthrough (when offered and accepted)
|
|
999
|
-
|
|
1000
|
-
Activate the
|
|
1001
|
-
[`core/knowledge-transfer`](../../skills/core/knowledge-transfer/SKILL.md) skill
|
|
1002
|
-
with the **plan** as the subject — the Epic body (whose managed sections
|
|
1003
|
-
carry the Tech Spec), the decomposition (Stories with inline
|
|
1004
|
-
`acceptance[]` / `verify[]`), and the Phase 9 wave roadmap. The skill owns the
|
|
1005
|
-
method (restate-first, the why-ladder, mastery gates, depth levels, optional
|
|
1006
|
-
quizzing, the persistent `temp/comprehension-*.md` checklist).
|
|
1007
|
-
|
|
1008
|
-
**Interruptible at every checkpoint.** Each step MUST offer an explicit
|
|
1009
|
-
"I'm good — proceed to `/deliver`" exit. The instant the operator takes
|
|
1010
|
-
it, stop the walkthrough and advance to Phase 12. Never trap the operator in
|
|
1011
|
-
the loop.
|
|
1012
|
-
|
|
1013
|
-
### 3. Advance
|
|
1014
|
-
|
|
1015
|
-
Whether the walkthrough was skipped, declined, completed, or stopped early,
|
|
1016
|
-
proceed to Phase 12. This gate produces no label transition and no blocking
|
|
1017
|
-
condition.
|
|
1018
|
-
|
|
1019
|
-
## Phase 12: Notification & Handoff
|
|
1020
|
-
|
|
1021
|
-
1. **Notify Operator (INFO)**:
|
|
1022
|
-
- Post a summary comment on the Epic issue with work breakdown stats.
|
|
1023
|
-
- @mention the operator (informational — no webhook for planning) by running
|
|
1024
|
-
the notification script:
|
|
1025
|
-
|
|
1026
|
-
```bash
|
|
1027
|
-
node .agents/scripts/notify.js [Epic_ID] "Planning complete, review tickets. Backlog decomposition complete. Epic is ready for /deliver." --action
|
|
1028
|
-
```
|
|
240
|
+
The CLI runs every deterministic step in one ordered, fail-closed pass:
|
|
241
|
+
section gate (`## Delivery Slicing` required) → risk-verdict validation +
|
|
242
|
+
mode-coherence hard error (`deliveryShape: "single"` with tickets, or
|
|
243
|
+
fan-out without them, refuses) → `validateAndNormalizeTickets` +
|
|
244
|
+
file-assumption gate + DAG + sizing/budget (fan-out and amend) → ideation
|
|
245
|
+
Epic creation → Epic lease (`--steal` transfers a confirmed-dead claim) →
|
|
246
|
+
managed sections + `risk-verdict` comment + spec-freshness advisory → story
|
|
247
|
+
tree via the structural reconciler (single shape: the `delivery::single`
|
|
248
|
+
routing marker and a zero-ticket checkpoint instead) → inline healthcheck
|
|
249
|
+
(the **blocking** `agent::ready` exit condition; waive only via the
|
|
250
|
+
`planning::healthcheck-waived` label) → one terminal `agent::ready` flip (no
|
|
251
|
+
intermediate `agent::review-spec`) → checkpoint v2 + a single `plan-summary`
|
|
252
|
+
comment carrying the dry-run wave table → temp cleanup **only at terminal
|
|
253
|
+
success**, so a failed run leaves the artifacts in place for `--force` /
|
|
254
|
+
`--resume` reuse.
|
|
255
|
+
|
|
256
|
+
### Persist rejections and soft failures
|
|
257
|
+
|
|
258
|
+
Each rejection names the artifact and the gap; apply the one-pass amend
|
|
259
|
+
contract (step 2) and re-run the persist once:
|
|
260
|
+
|
|
261
|
+
- **Section gate / validator / file-assumption / DAG errors** — targeted
|
|
262
|
+
edit to `techspec.md` or `tickets.json`.
|
|
263
|
+
- **Reachability orphans** (deterministic route-glob vs `navRegistry` check;
|
|
264
|
+
a silent no-op when `planning.navigation` is unconfigured) — a named
|
|
265
|
+
**soft failure** (exit 3, before any GitHub write) listing the orphaned
|
|
266
|
+
surfaces: apply one targeted amend adding a navigation owner
|
|
267
|
+
(at most one reachability Story per plan), then re-persist.
|
|
268
|
+
- **Over budget** (`maxTickets`) — re-scope, or re-run with
|
|
269
|
+
`--allow-over-budget` after confirming the rationale on the Epic.
|
|
270
|
+
- **Rate-limit abort on a large Epic** — resume with `--resume`; see
|
|
271
|
+
[`plan-epic-reference.md` § `--resume` recovery](plan-epic-reference.md#persist----resume-recovery-secondary-rate-limit).
|
|
272
|
+
|
|
273
|
+
### Handoff
|
|
274
|
+
|
|
275
|
+
> "Planning is complete. Run `/deliver #[Epic_ID]` to start the wave loop,
|
|
276
|
+
> or pick a single Story from Wave 0 (the `plan-summary` comment's wave
|
|
277
|
+
> table) and run `/deliver #[Story_ID]` to drive it directly."
|
|
1029
278
|
|
|
1030
279
|
## Troubleshooting & recovery
|
|
1031
280
|
|
|
1032
|
-
|
|
1033
|
-
decomposition that persisted but left the Epic off `agent::ready`) and the
|
|
1034
|
-
large-Epic **secondary-rate-limit `--resume`** recovery path live in the
|
|
1035
|
-
reference companion — they are consulted only when a run hits the edge:
|
|
281
|
+
Edge-case procedures live in the reference companion:
|
|
1036
282
|
|
|
1037
283
|
- [`plan-epic-reference.md` § Troubleshooting](plan-epic-reference.md#troubleshooting)
|
|
1038
|
-
- [`plan-epic-reference.md` §
|
|
284
|
+
- [`plan-epic-reference.md` § Persist guards — background rationale](plan-epic-reference.md#persist-guards--background-rationale)
|
|
285
|
+
- [`plan-epic-reference.md` § `--resume` recovery](plan-epic-reference.md#persist----resume-recovery-secondary-rate-limit)
|