mandrel 1.68.0 → 1.69.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/docs/agentrc-reference.json +1 -2
- package/.agents/docs/configuration.md +2 -4
- package/.agents/schemas/agentrc.schema.json +1 -5
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +2 -1
- package/.agents/scripts/epic-deliver-preflight.js +30 -13
- package/.agents/scripts/epic-deliver-prepare.js +40 -53
- package/.agents/scripts/epic-execute-record-wave.js +119 -133
- package/.agents/scripts/lib/baselines/refresh-service.js +13 -1
- package/.agents/scripts/lib/config/explain.js +0 -2
- package/.agents/scripts/lib/config/limits.js +19 -8
- package/.agents/scripts/lib/config-settings-schema.js +1 -2
- package/.agents/scripts/lib/maintainability-utils.js +32 -9
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +11 -7
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +6 -6
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +11 -5
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +203 -110
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +38 -78
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +16 -13
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +10 -7
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +37 -24
- package/.agents/scripts/lib/orchestration/manifest-builder.js +6 -0
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +6 -2
- package/.agents/scripts/lib/orchestration/wave-record-io.js +18 -77
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +78 -122
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +21 -226
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +18 -1
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +77 -4
- package/.agents/scripts/lib/story-adjacency.js +14 -10
- package/.agents/scripts/lib/story-body/story-body.js +36 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +23 -3
- package/.agents/scripts/lib/wave-runner/ready-set.js +295 -0
- package/.agents/scripts/lib/wave-runner/tick.js +312 -206
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +2 -1
- package/.agents/scripts/lint-label-vocabulary.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +262 -161
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +6 -0
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +108 -101
- package/.agents/skills/skills.index.json +2 -2
- package/.agents/workflows/deliver.md +12 -9
- package/.agents/workflows/helpers/deliver-epic.md +126 -90
- package/.agents/workflows/helpers/deliver-stories.md +131 -85
- package/.agents/workflows/helpers/plan-epic.md +13 -10
- package/.agents/workflows/plan.md +1 -1
- package/docs/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/.agents/scripts/lib/wave-runner/wave-checkpoint.js +0 -91
|
@@ -62,8 +62,9 @@ reads:
|
|
|
62
62
|
- `heuristics[]` — risk heuristics surfaced from
|
|
63
63
|
`agentSettings.planning.riskHeuristics`. Apply each one against the
|
|
64
64
|
Stories you are emitting; flag matches via `risk::high` labels.
|
|
65
|
-
- `maxTickets` — reviewability budget
|
|
66
|
-
`
|
|
65
|
+
- `maxTickets` — reviewability budget; a framework constant
|
|
66
|
+
(`LIMITS_DEFAULTS.maxTickets` in `.agents/scripts/lib/config/limits.js`,
|
|
67
|
+
not operator-configurable — Story #2798, Story #4163). Default: stay
|
|
67
68
|
under. When the plan genuinely needs more, emit the full plan with
|
|
68
69
|
an `over_budget_rationale` and rely on the operator's
|
|
69
70
|
`--allow-over-budget` override at persist time. The script logs the
|
|
@@ -71,8 +72,16 @@ reads:
|
|
|
71
72
|
- `contextMode` — `"full"` or `"summary"`. When `"summary"`, work
|
|
72
73
|
from the `bodySummary` fields rather than re-fetching the bodies.
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
- `systemPrompt` — the **authoritative, fully-rendered decomposer system
|
|
76
|
+
prompt** (Story #4162). It is produced by `renderDecomposerSystemPrompt`
|
|
77
|
+
in
|
|
78
|
+
[`decomposer-prompts.js`](../../../scripts/lib/templates/decomposer-prompts.js),
|
|
79
|
+
the single source of the prompt body, with `maxTickets`,
|
|
80
|
+
`maxTokenBudget`, and the sizing thresholds already interpolated. Apply
|
|
81
|
+
**this** string as your system prompt — this SKILL deliberately does NOT
|
|
82
|
+
embed a second verbatim copy of the prompt body, so the two surfaces
|
|
83
|
+
cannot drift. The sections below are authoring guidance that complements
|
|
84
|
+
the rendered prompt; they are not a replacement copy of it.
|
|
76
85
|
|
|
77
86
|
## Outputs
|
|
78
87
|
|
|
@@ -103,11 +112,17 @@ Pin three values explicitly before writing any tickets:
|
|
|
103
112
|
3. `heuristics[]` — render the active risk heuristics in front of you
|
|
104
113
|
so the planning persona can mention them as Stories are emitted.
|
|
105
114
|
|
|
106
|
-
### Step 2 — Decompose against the system prompt
|
|
107
|
-
|
|
108
|
-
Apply the decomposer system prompt
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
### Step 2 — Decompose against the rendered system prompt
|
|
116
|
+
|
|
117
|
+
Apply the fully-rendered decomposer system prompt — the `systemPrompt`
|
|
118
|
+
field of the loaded context envelope, produced by
|
|
119
|
+
[`decomposer-prompts.js`](../../../scripts/lib/templates/decomposer-prompts.js)
|
|
120
|
+
— to the PRD + Tech Spec bodies. That rendered string is the authoritative
|
|
121
|
+
prompt body (with `maxTickets`, `maxTokenBudget`, and the sizing thresholds
|
|
122
|
+
already interpolated); the authoring-guidance sections below complement it
|
|
123
|
+
without restating it. Emit JSON only (no prose, no Markdown fence). The
|
|
124
|
+
downstream validator in
|
|
125
|
+
[`lib/orchestration/ticket-validator.js`](../../../scripts/lib/orchestration/ticket-validator.js)
|
|
111
126
|
will reject anything off-shape. Merge narrow, single-module slices into
|
|
112
127
|
their capability first; emit one Story per capability slice a frontier
|
|
113
128
|
model can deliver and self-verify in one pass.
|
|
@@ -125,75 +140,69 @@ Return control. The caller invokes
|
|
|
125
140
|
temp/epic-<Epic_ID>/tickets.json`, which validates, persists, and flips
|
|
126
141
|
the Epic to `agent::ready`.
|
|
127
142
|
|
|
128
|
-
## Decomposer system prompt (
|
|
129
|
-
|
|
130
|
-
The
|
|
131
|
-
`
|
|
132
|
-
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
- {"path": "<read-only dependency path>", "assumption": "exists"}
|
|
192
|
-
- ...
|
|
193
|
-
|
|
194
|
-
Fields `wide` and `estimated_test_files` are encoded as a `<!-- meta: {...} -->` comment appended to the serialized body string (handled by `serialize()`). They are NOT top-level ticket fields.
|
|
195
|
-
|
|
196
|
-
#### STORY BODY RULES:
|
|
143
|
+
## Decomposer system prompt — single-sourced (Story #4162)
|
|
144
|
+
|
|
145
|
+
The authoritative decomposer system-prompt **body** is single-sourced in
|
|
146
|
+
[`decomposer-prompts.js`](../../../scripts/lib/templates/decomposer-prompts.js)
|
|
147
|
+
(`renderDecomposerSystemPrompt`) and delivered to you fully rendered in the
|
|
148
|
+
`systemPrompt` field of `temp/epic-<Epic_ID>/decomposer-context.json` — with
|
|
149
|
+
`maxTickets`, `maxTokenBudget`, the `DEFAULT_TASK_SIZING` thresholds, and the
|
|
150
|
+
risk heuristics already interpolated. **Apply that rendered string as your
|
|
151
|
+
system prompt.** This SKILL deliberately does **not** embed a second verbatim
|
|
152
|
+
copy of the prompt body (the preamble, the JSON output schema, the hierarchy /
|
|
153
|
+
label / output-format rules); duplicating it here is exactly the drift the
|
|
154
|
+
single-source contract exists to prevent, and the guard test in
|
|
155
|
+
`tests/ticket-decomposer.test.js` fails if a second full copy reappears on
|
|
156
|
+
either surface.
|
|
157
|
+
|
|
158
|
+
The value `${maxTickets}` in the rendered prompt is substituted at runtime
|
|
159
|
+
from the `maxTickets` field of the loaded context. Treat it as the
|
|
160
|
+
**reviewability budget** (Story #2798) — stay under by default; over-budget
|
|
161
|
+
plans need an `over_budget_rationale` plus operator `--allow-over-budget` to
|
|
162
|
+
persist. It is **not** a hard authoring cap: the prompt no longer carries any
|
|
163
|
+
ticket-count limit directive, and you MUST never truncate the JSON array to fit
|
|
164
|
+
(Story #4162). The rendered prompt also names the delivery token
|
|
165
|
+
budget (`maxTokenBudget`) as the real one-pass sizing envelope — size each
|
|
166
|
+
Story so a single agent can deliver and self-verify it within that budget.
|
|
167
|
+
|
|
168
|
+
## Authoring guidance (complements the rendered prompt)
|
|
169
|
+
|
|
170
|
+
The sections below are the SKILL's authoring guidance. They do **not** restate
|
|
171
|
+
the rendered prompt body — they record the contract details and sizing
|
|
172
|
+
heuristics you apply when shaping the ticket array. Where a number appears it
|
|
173
|
+
is the `DEFAULT_TASK_SIZING` default; the rendered prompt interpolates the live
|
|
174
|
+
value, which always wins.
|
|
175
|
+
|
|
176
|
+
### STORY BODY SHAPE (string body, top-level acceptance/verify)
|
|
177
|
+
|
|
178
|
+
For Stories, `body` MUST be a **string** — the serialized markdown produced by
|
|
179
|
+
calling `serialize()` from `lib/story-body/story-body.js`. Do NOT emit `body`
|
|
180
|
+
as a JSON object: `createOp` in `epic-spec-reconciler-ops.js` will throw
|
|
181
|
+
`StoryBodyParseError` when it receives an object body (Story #3302
|
|
182
|
+
serialize-or-throw contract), and `composeStoryBody` in the GitHub provider
|
|
183
|
+
also discards non-string bodies producing an empty issue. The freshness gate
|
|
184
|
+
(`collectTaskChangesPaths`) and the assumption gate
|
|
185
|
+
(`collectStoryAssumptionEntries`) both parse the string body via
|
|
186
|
+
`story-body.js#parse` to recover `changes[]`/`references[]` — they operate
|
|
187
|
+
correctly only on the serialized string form.
|
|
188
|
+
|
|
189
|
+
The `acceptance[]` and `verify[]` arrays live at the **top level** of the
|
|
190
|
+
Story ticket object (not nested inside `body`). The validator's
|
|
191
|
+
`hasInlineAcceptanceAndVerify(story)` reads `story.acceptance` and
|
|
192
|
+
`story.verify` directly — nesting them inside a body object makes them
|
|
193
|
+
invisible to the validator, causing the backlog to be treated as the legacy
|
|
194
|
+
4-tier shape and producing a `Cross-Validation Failed: Backlog must contain at
|
|
195
|
+
least one Task.` error.
|
|
196
|
+
|
|
197
|
+
The serialized `body` string renders these markdown sections in order: `##
|
|
198
|
+
Goal` (one sentence), `## Changes` (object-form `{ "path", "assumption" }`
|
|
199
|
+
bullets), `## Acceptance` (checkbox items), `## Verify` (command + tier), and
|
|
200
|
+
`## References` (read-only `{ "path", "assumption": "exists" }` bullets).
|
|
201
|
+
Fields `wide` and `estimated_test_files` are encoded as a `<!-- meta: {...}
|
|
202
|
+
-->` comment appended to the serialized body string (handled by `serialize()`).
|
|
203
|
+
They are NOT top-level ticket fields.
|
|
204
|
+
|
|
205
|
+
#### STORY BODY RULES
|
|
197
206
|
|
|
198
207
|
- **slug**: MUST be hyphen-case (`^[a-z0-9][a-z0-9-]*$`). Do not use underscores.
|
|
199
208
|
- **goal** (in body string): One sentence stating WHY this Story exists within the Epic.
|
|
@@ -204,15 +213,15 @@ Fields `wide` and `estimated_test_files` are encoded as a `<!-- meta: {...} -->`
|
|
|
204
213
|
- **verify** (top-level array on ticket object): Each entry MUST name a testing tier in parentheses, drawn from `unit` / `contract` / `e2e` / `validate`. Example: `npm run test -- src/x.test.ts (unit)`, `npm run validate (validate)`. Stories with zero verify entries SHOULD fail validation; if a Story is genuinely unverifiable in isolation (e.g., a copy edit auditor will eyeball), the literal entry `manual:<reason>` is allowed so the absence is intentional, not lazy. Manual entries without a reason are rejected.
|
|
205
214
|
- **estimated_test_files** (optional, encoded in body meta comment): Integer estimate of how many test files this Story creates or modifies. Omit when the number is not estimable. Informational only — it does not gate the decompose.
|
|
206
215
|
|
|
207
|
-
#### FORBIDDEN SUBJECT-PREFIX PRESCRIPTIONS (Conventional-Commits only)
|
|
216
|
+
#### FORBIDDEN SUBJECT-PREFIX PRESCRIPTIONS (Conventional-Commits only)
|
|
208
217
|
|
|
209
218
|
- `acceptance` items MUST NOT prescribe a commit subject that begins with a non-Conventional-Commits prefix. The allowed leading types are `feat|fix|chore|refactor|perf|docs|style|test|build|ci|revert` (matching `commitlint.config.js` and `release-please-config.json`). Historic ad-hoc subject prefixes — such as the legacy `baseline-refresh` token used as a leading prefix — are FORBIDDEN as subject prescriptions, because they fail the local `commit-msg` hook and the close-time validator (`ticket-validator.js` → `validateAcceptanceSubjectPrefix`) will reject the decompose with `code: 'forbidden-subject-prefix'`. When a Story needs a baseline-refresh-style classification, prescribe a Conventional-Commits subject (e.g. `chore(baselines): refresh maintainability snapshot`) and, if a machine-readable marker is required, prescribe a body trailer such as `baseline-refresh: true` (note the trailing space and value, not a subject prefix). See Epic #2501 for the rationale.
|
|
210
219
|
|
|
211
|
-
#### STORY SIZING — DELIVERABLE GRANULARITY, COHESION FIRST (the numeric ceiling is only a backstop)
|
|
220
|
+
#### STORY SIZING — DELIVERABLE GRANULARITY, COHESION FIRST (the numeric ceiling is only a backstop)
|
|
212
221
|
|
|
213
|
-
**Decompose at deliverable granularity, not module/task level.** A Story is a **capability slice a frontier model delivers and self-verifies in one pass** — a shippable slice a reviewer would accept as a single PR, a capability or user-visible surface, **not a single module or file**. Fold module-level slices into the capability they belong to rather than emitting one Story per module. (This definition is the single source of truth in `DELIVERABLE_GRANULARITY_GUIDANCE` in `ticket-validator-sizing.js`; the decomposer prompt interpolates the same string — do not restate a divergent version here.)
|
|
222
|
+
**Decompose at deliverable granularity, not module/task level.** A Story is a **capability slice a frontier model delivers and self-verifies in one pass** — a shippable slice a reviewer would accept as a single PR, a capability or user-visible surface, **not a single module or file**. Fold module-level slices into the capability they belong to rather than emitting one Story per module. (This definition is the single source of truth in `DELIVERABLE_GRANULARITY_GUIDANCE` in `ticket-validator-sizing.js`; the rendered decomposer prompt interpolates the same string — do not restate a divergent version here.)
|
|
214
223
|
|
|
215
|
-
The first question is **cohesion, not count**: *is this one coherent change with one reason to exist?* File count cannot tell a trivial 10-file mechanical rename from a hard 3-file parser+caller+config change — so lead with the change's reason, not its size.
|
|
224
|
+
The first question is **cohesion, not count**: *is this one coherent change with one reason to exist?* File count cannot tell a trivial 10-file mechanical rename from a hard 3-file parser+caller+config change — so lead with the change's reason, not its size. Size against the real one-pass delivery envelope (`maxTokenBudget`): a Story is correctly sized when a single agent can hold its full change, acceptance, and verification in one pass within that budget.
|
|
216
225
|
|
|
217
226
|
- **One Story = one coherent change with one reason to exist.** If you cannot state that reason in a sentence, the Story is probably two Stories.
|
|
218
227
|
- **Single-consumer merge rule.** A Story whose only consumer is one sibling Story should be **merged into that sibling** rather than emitted separately — a single-consumer downstream slice is not its own unit of work.
|
|
@@ -221,11 +230,11 @@ The first question is **cohesion, not count**: *is this one coherent change with
|
|
|
221
230
|
|
|
222
231
|
**Numeric backstop.** The thresholds are defined **once**, in the `DEFAULT_TASK_SIZING` constant in `ticket-validator-sizing.js` (operator-overridable via `agentSettings.planning.taskSizing`). They are a backstop, not the primary rule — do not restate divergent numbers anywhere else. The defaults:
|
|
223
232
|
|
|
224
|
-
- A Story touching more than **`softFiles` (
|
|
233
|
+
- A Story touching more than **`softFiles` (15)** files emits an advisory width finding — a nudge to check cohesion or declare `wide`.
|
|
225
234
|
- A Story touching more than **`hardFiles` (30)** files is **rejected** unless it declares `wide` with a reason.
|
|
226
235
|
- A Story with more than **`maxAcceptance` (14)** acceptance items is **rejected**; more than **`softAcceptanceCount` (10)** emits an advisory warning.
|
|
227
236
|
|
|
228
|
-
#### DELIVERY SLICING (consume the Tech Spec target grouping when present)
|
|
237
|
+
#### DELIVERY SLICING (consume the Tech Spec target grouping when present)
|
|
229
238
|
|
|
230
239
|
The Tech Spec may carry a `## Delivery Slicing` section authored by the
|
|
231
240
|
Architect, proposing how the PRD's enumerated capabilities cluster into N
|
|
@@ -238,31 +247,31 @@ rules above, exactly as before. The Phase 8 holistic consolidation pass
|
|
|
238
247
|
Slicing target before persist, so aligning to it here reduces the work the
|
|
239
248
|
consolidation critic has to do.
|
|
240
249
|
|
|
241
|
-
#### `wide` DECLARATION (optional — for legitimately broad changes)
|
|
250
|
+
#### `wide` DECLARATION (optional — for legitimately broad changes)
|
|
242
251
|
|
|
243
252
|
A Story whose footprint is legitimately broad declares `wide` carrying a one-line human-readable reason. Encode it in the `<!-- meta: {"wide": {"reason": "..."}} -->` comment that `serialize()` appends to the body string — e.g. `"wide": { "reason": "hard contract cutover: migrate every <X> call site in one PR" }`.
|
|
244
253
|
|
|
245
254
|
Declaring `wide` with a non-empty reason **lifts the `hardFiles` rejection** — no Story is rejected for width when it states why it is broad. Omit `wide` for ordinary Stories; a wide footprint with no `wide` declaration emits only an advisory nudge (check cohesion or declare `wide`), never a rejection on its own. Glob entries in `changes[]` (bullets containing `*`) are `unknown-width`: the numeric ceiling is skipped, and a glob Story with no `wide` declaration emits the same advisory nudge.
|
|
246
255
|
|
|
247
|
-
#### UI / TESTID INVARIANCE (per CLAUDE.md safety rule)
|
|
256
|
+
#### UI / TESTID INVARIANCE (per CLAUDE.md safety rule)
|
|
248
257
|
|
|
249
258
|
- Stories that touch UI (`*.tsx`, `*.astro`, `*.svelte`, `*.vue`, components folders) MUST end `changes` with one of:
|
|
250
259
|
- `data-testid invariance: <list of testids that MUST be preserved>`, or
|
|
251
260
|
- `data-testid changes: <old> -> <new>` paired with a corresponding `tests/e2e/*.spec.ts` edit in the same Story or a depends_on Story.
|
|
252
261
|
- Renaming a testid without the matching e2e edit is FORBIDDEN.
|
|
253
262
|
|
|
254
|
-
#### BRAND / COPY / STYLE WORK
|
|
263
|
+
#### BRAND / COPY / STYLE WORK
|
|
255
264
|
|
|
256
265
|
- Stories that touch user-visible copy, brand assets, or visual style MUST cite the relevant section of `docs/style-guide.md` in `acceptance` (e.g. `"acceptance": ["Hero copy matches docs/style-guide.md §3 (voice & tone)"]`). If `docs/style-guide.md` does not exist or has no relevant section, state that explicitly: `"acceptance": ["docs/style-guide.md absent — copy reviewed against the inline brand brief in PRD §2"]`. Silence on style sourcing is a smell.
|
|
257
266
|
|
|
258
|
-
#### BINDING ACCEPTANCE vs ADVISORY CHANGES (authoring altitude)
|
|
267
|
+
#### BINDING ACCEPTANCE vs ADVISORY CHANGES (authoring altitude)
|
|
259
268
|
|
|
260
269
|
`acceptance[]` and `verify[]` are the **binding contract** the executor MUST satisfy — they are the sole definition of "done." `changes[]` and `references[]` are an **advisory implementation sketch**: your best prediction of the file footprint, which the executor is permitted to revise when the real codebase diverges from the sketch. Author at that altitude:
|
|
261
270
|
|
|
262
271
|
- Write `acceptance[]`/`verify[]` to capture the **outcome**, independent of any one file layout. Do NOT pin an incidental implementation detail (an internal helper name, a private file path) into an acceptance item that the advisory `changes[]` is free to reshape — assert the observable behaviour instead.
|
|
263
272
|
- Keep `changes[]`/`references[]` as the honest predicted footprint. They still pass through the structural file-assumption gate (the `creates`/`refactors-existing`/`deletes` probes against the base branch) and the New-File Contract unchanged — advisory does NOT mean unvalidated. The executor's latitude to revise the approach never licenses skipping `acceptance[]`/`verify[]` or any `rules/security-baseline.md` MUST.
|
|
264
273
|
|
|
265
|
-
#### NAVIGATE-DON'T-DEEP-LINK (signed-in acceptance scenarios)
|
|
274
|
+
#### NAVIGATE-DON'T-DEEP-LINK (signed-in acceptance scenarios)
|
|
266
275
|
|
|
267
276
|
When a Story's acceptance describes a **signed-in / authenticated** persona reaching a feature surface, author it so the persona starts from their authenticated home and **reaches the feature through navigation** — clicking a nav door, menu entry, or link the UI actually exposes — **never** via a hardcoded deep-link URL.
|
|
268
277
|
|
|
@@ -270,7 +279,7 @@ When a Story's acceptance describes a **signed-in / authenticated** persona reac
|
|
|
270
279
|
- Phrase it as: `"From the signed-in home, the persona navigates to Reports → Export and sees the export button"` — not `"GET /reports/export returns the export view"`.
|
|
271
280
|
- This applies to signed-in journeys only; an unauthenticated landing page or a deliberately deep-linkable share URL is exempt — say so in the acceptance item when you take that exemption.
|
|
272
281
|
|
|
273
|
-
### WAVE-0 BDD SCAFFOLD STORY (features-first; emit when the Acceptance Spec has `new`-disposition rows)
|
|
282
|
+
### WAVE-0 BDD SCAFFOLD STORY (features-first; emit when the Acceptance Spec has `new`-disposition rows)
|
|
274
283
|
|
|
275
284
|
The Acceptance Spec's AC table (columns `AC ID | Outcome | Feature File | Scenario | Disposition`) tags each row's `Disposition` with one of `new | updated | unchanged`. A `new` row names a `.feature` file + scenario that does NOT yet exist on `main`. The framework is features-first: implementation Stories reference those `.feature` paths in their `verify[]` lines, so the files MUST already exist when those Stories run — otherwise verification fails mid-delivery on a missing file (observed gap: Epic #18 in `dsj1984/athportal` had 9 `new` rows and no Story tasked with creating the feature files Stories #1457 / #1466 verified against).
|
|
276
285
|
|
|
@@ -285,7 +294,7 @@ When the Acceptance Spec contains **one or more `Disposition: new` rows**, you M
|
|
|
285
294
|
|
|
286
295
|
When the Acceptance Spec contains **zero `new`-disposition rows** (every row is `updated` or `unchanged`), do NOT emit a scaffold Story — there is nothing to create.
|
|
287
296
|
|
|
288
|
-
**Worked example.** Acceptance Spec with two `new` rows (`AC-1` -> `tests/features/billing/invoice.feature`, `AC-2` -> `tests/features/billing/refund.feature`). The scaffold Story below uses a serialized string `body`, top-level `acceptance`/`verify` arrays, and an empty `depends_on
|
|
297
|
+
**Worked example.** Acceptance Spec with two `new` rows (`AC-1` -> `tests/features/billing/invoice.feature`, `AC-2` -> `tests/features/billing/refund.feature`). The scaffold Story below uses a serialized string `body`, top-level `acceptance`/`verify` arrays, and an empty `depends_on`:
|
|
289
298
|
|
|
290
299
|
{
|
|
291
300
|
"slug": "scaffold-billing-feature-files",
|
|
@@ -306,12 +315,13 @@ When the Acceptance Spec contains **zero `new`-disposition rows** (every row is
|
|
|
306
315
|
|
|
307
316
|
The implementation Stories that later un-skip and flesh out these scenarios each carry `depends_on: ["scaffold-billing-feature-files"]`, placing them in a later wave than the scaffold.
|
|
308
317
|
|
|
309
|
-
### SCOPE-OVERLAP FLAGGING (docs/runbook downstream of config work)
|
|
318
|
+
### SCOPE-OVERLAP FLAGGING (docs/runbook downstream of config work)
|
|
319
|
+
|
|
310
320
|
When a "docs update" / "runbook" / "README" Story appears downstream of an earlier Story in the same Epic whose AC already covers updating the same document (e.g. a "config + runbook" Story followed by a "docs" Story touching the same runbook), the downstream Story's deliverable may be fully absorbed by the earlier Story. Flag the risk directly in the Story `body.acceptance` by appending an item of the form:
|
|
311
321
|
"Scope verification note: this Story's deliverable may already be satisfied by Story #<slug-or-id>'s AC — before implementing, `git diff main -- <path>` against the upstream Story branch and confirm whether a substantive edit is still required, or whether only a cross-reference remains."
|
|
312
322
|
This prevents the executing agent from redoing work the upstream Story already merged.
|
|
313
323
|
|
|
314
|
-
### CROSS-CUTTING CONFIG FILE EDITS (shared root files across Stories)
|
|
324
|
+
### CROSS-CUTTING CONFIG FILE EDITS (shared root files across Stories)
|
|
315
325
|
|
|
316
326
|
If two or more Stories in the same decomposition edit any of the shared
|
|
317
327
|
configuration files enumerated below, you MUST either:
|
|
@@ -362,7 +372,7 @@ Shared configuration files (non-exhaustive):
|
|
|
362
372
|
`.agentrc.json` (accepts `["…"]` to replace or `{ append: [...] }` to
|
|
363
373
|
add to the framework default).
|
|
364
374
|
|
|
365
|
-
### WIDELY-USED SYMBOL DELETION (Story #2962)
|
|
375
|
+
### WIDELY-USED SYMBOL DELETION (Story #2962)
|
|
366
376
|
|
|
367
377
|
When a Story's `body.changes` declares `{ path, assumption: "deletes" }`,
|
|
368
378
|
the decomposer probes the base branch at plan time via `git grep -l`
|
|
@@ -382,13 +392,10 @@ Do NOT silently allow two Stories to write the same root configuration
|
|
|
382
392
|
file in the same wave; parallel dispatch would produce a merge conflict
|
|
383
393
|
on every Story-to-Epic close after the first.
|
|
384
394
|
|
|
385
|
-
CRITICAL: Dependencies should follow execution blockers. Stories attach
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
### RISK HEURISTICS (planning metadata if any apply):
|
|
390
|
-
<rendered from `heuristics[]` in the context envelope; each item prepended with "- ".>
|
|
391
|
-
```
|
|
395
|
+
CRITICAL: Dependencies should follow execution blockers. Stories attach
|
|
396
|
+
directly to the Epic — never emit a `parent_slug` field. A Story's
|
|
397
|
+
`depends_on` MUST only reference other Stories within the SAME Epic; express
|
|
398
|
+
any logical ordering requirement via Story-level `depends_on`.
|
|
392
399
|
|
|
393
400
|
## Constraints
|
|
394
401
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-06-
|
|
2
|
+
"generatedAt": "2026-06-16T00:37:23.856Z",
|
|
3
3
|
"generator": "generate-skills-index.js@1",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"category": "core",
|
|
129
129
|
"path": ".agents/skills/core/epic-plan-consolidate/SKILL.md",
|
|
130
130
|
"description": "Run a holistic, pre-persist consolidation pass over the draft Story ticket array an Epic's decompose phase produced. Use during Phase 8 of `/plan`, after `epic-plan-decompose-author` writes `temp/epic-<Epic_ID>/tickets.json` and before `epic-plan-decompose.js` validates and persists it. Reconciles the draft against the Tech Spec \"Delivery Slicing\" target via scope-preserving operations only.",
|
|
131
|
-
"policyCapsuleBullets":
|
|
131
|
+
"policyCapsuleBullets": 9,
|
|
132
132
|
"allowedTools": ["Read", "Write", "Bash"],
|
|
133
133
|
"vendor": null
|
|
134
134
|
},
|
|
@@ -21,8 +21,8 @@ path selection only — all phase content lives in the two path helpers:
|
|
|
21
21
|
close-validation, epic-audit, code-review, retro, finalize, watch,
|
|
22
22
|
auto-merge gate, cleanup).
|
|
23
23
|
- [`helpers/deliver-stories.md`](helpers/deliver-stories.md) — the
|
|
24
|
-
standalone multi-Story path (`stories-wave-tick.js`
|
|
25
|
-
confirmation, parallel fan-out to
|
|
24
|
+
standalone multi-Story path (`stories-wave-tick.js` continuous ready-set
|
|
25
|
+
loop, operator confirmation, parallel fan-out to
|
|
26
26
|
[`helpers/single-story-deliver`](helpers/single-story-deliver.md)).
|
|
27
27
|
|
|
28
28
|
## Input matrix (authoritative)
|
|
@@ -100,13 +100,16 @@ plans).
|
|
|
100
100
|
/deliver <id> <id> … --dep <from>:<to> --concurrency <n> --yes
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
preserves the retired multi-Story command's full surface — the same flags,
|
|
104
|
+
the same operator confirmation gate (suppressed by `--yes`), and the same
|
|
105
|
+
parallel fan-out to
|
|
106
|
+
[`helpers/single-story-deliver`](helpers/single-story-deliver.md) — but
|
|
107
|
+
schedules through `stories-wave-tick.js`'s **continuous ready-set loop**
|
|
108
|
+
(the shared `selectReadySet` core) rather than a static wave plan: each
|
|
109
|
+
Story dispatches the instant its own dependencies are done, capped by the
|
|
110
|
+
resolved global `concurrencyCap` and guarded against file-overlap
|
|
111
|
+
co-dispatch, exactly as the Epic path is. The parallelism lives **inside**
|
|
112
|
+
the standalone segment; segments themselves remain strictly sequential.
|
|
110
113
|
|
|
111
114
|
## Procedure
|
|
112
115
|
|