mandrel 1.82.0 → 1.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.agents/README.md +41 -0
  2. package/.agents/docs/SDLC.md +4 -2
  3. package/.agents/docs/agentrc-reference.json +10 -2
  4. package/.agents/docs/execution-reference.md +52 -0
  5. package/.agents/instructions.md +76 -38
  6. package/.agents/schemas/agentrc.schema.json +31 -3
  7. package/.agents/schemas/qa-ledger.schema.json +2 -2
  8. package/.agents/scripts/epic-deliver-prepare.js +41 -1
  9. package/.agents/scripts/lib/config/explain.js +4 -1
  10. package/.agents/scripts/lib/config-settings-schema.js +25 -1
  11. package/.agents/scripts/lib/epic-body-sections.js +88 -0
  12. package/.agents/scripts/lib/findings/promote-finding.js +3 -3
  13. package/.agents/scripts/lib/findings/severity.js +5 -6
  14. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +65 -2
  15. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +96 -11
  16. package/.agents/scripts/lib/orchestration/doc-reader.js +29 -0
  17. package/.agents/scripts/lib/orchestration/docs-digest.js +134 -0
  18. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +15 -1
  19. package/.agents/scripts/lib/qa/console-allowlist.js +5 -4
  20. package/.agents/scripts/lib/qa/resolve-qa-contract.js +144 -8
  21. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +7 -5
  22. package/.agents/skills/core/epic-plan-consolidate/examples.md +51 -0
  23. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +4 -22
  24. package/.agents/skills/core/epic-plan-decompose-author/examples.md +47 -0
  25. package/.agents/skills/core/epic-plan-premortem/SKILL.md +9 -8
  26. package/.agents/skills/core/epic-plan-premortem/examples.md +53 -0
  27. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +21 -81
  28. package/.agents/skills/core/epic-plan-spec-author/examples.md +91 -0
  29. package/.agents/skills/skills.index.json +3 -3
  30. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +52 -38
  31. package/.agents/workflows/helpers/deliver-epic-reference.md +514 -0
  32. package/.agents/workflows/helpers/deliver-epic.md +164 -469
  33. package/.agents/workflows/helpers/epic-deliver-story.md +35 -11
  34. package/.agents/workflows/helpers/plan-epic-reference.md +136 -0
  35. package/.agents/workflows/helpers/plan-epic.md +56 -186
  36. package/.agents/workflows/helpers/plan-story.md +31 -61
  37. package/.agents/workflows/helpers/qa-run-scenario.md +194 -0
  38. package/.agents/workflows/helpers/scope-triage-gate.md +97 -0
  39. package/.agents/workflows/helpers/single-story-deliver-reference.md +423 -0
  40. package/.agents/workflows/helpers/single-story-deliver.md +128 -392
  41. package/.agents/workflows/qa-explore.md +63 -32
  42. package/.agents/workflows/qa-run.md +293 -130
  43. package/docs/CHANGELOG.md +14 -0
  44. package/package.json +1 -1
  45. package/.agents/schemas/qa-finding.schema.json +0 -133
@@ -30,11 +30,13 @@ allowed_tools:
30
30
  ## Role
31
31
 
32
32
  Senior Project Manager + Orchestrator, acting as a **holistic critic** with
33
- fresh context. This Skill is deliberately *separate* from
34
- `epic-plan-decompose-author` (the generator): a same-pass self-critique is the
35
- weak mode this is built to escape. The generator maps Epic capabilities to
36
- Stories ~1:1; this critic steps back and looks at the *whole* decomposition
37
- against the Tech Spec's intentional grouping before any GitHub write.
33
+ fresh context deliberately *separate* from `epic-plan-decompose-author` (the
34
+ generator) so the pass is a fresh-context review, not a same-pass self-critique.
35
+
36
+ > **Read [`examples.md`](./examples.md) on demand** for the extended rationale:
37
+ > why this critic runs with fresh context, why scope conservation is your
38
+ > contract rather than a runtime guarantee, and how the below-ceiling
39
+ > coarsening case is reported.
38
40
 
39
41
  ## When to use
40
42
 
@@ -0,0 +1,51 @@
1
+ # epic-plan-consolidate — examples & extended rationale
2
+
3
+ Read this file on demand. The SKILL.md carries the operating contract (Policy
4
+ Capsule, Inputs / Outputs, Procedure, Constraints); this file carries the
5
+ extended rationale behind the consolidation pass — why it is a separate,
6
+ fresh-context critic and how the scope-conservation invariant is enforced by
7
+ you rather than by the runtime.
8
+
9
+ ## Why a separate fresh-context critic
10
+
11
+ This Skill acts as a **holistic critic** with fresh context (a Senior Project
12
+ Manager + Orchestrator lens). It is deliberately *separate* from
13
+ `epic-plan-decompose-author` (the generator): a same-pass self-critique is the
14
+ weak mode this is built to escape. The generator maps Epic capabilities to
15
+ Stories ~1:1; this critic steps back and looks at the *whole* decomposition
16
+ against the Tech Spec's intentional grouping before any GitHub write.
17
+
18
+ ## Scope conservation is your contract, not a machine guarantee
19
+
20
+ Scope conservation is the load-bearing invariant: you MUST NOT add scope,
21
+ invent tickets, or drop acceptance criteria. Every acceptance item and every
22
+ `verify` entry present in the draft MUST survive into the consolidated array
23
+ (possibly re-homed onto a merged Story).
24
+
25
+ **This is your contract, not a machine guarantee.** There is **no runtime
26
+ acceptance-union diff** on your output. The only deterministic runtime backstop
27
+ the validator applies after you run is the standard ticket-structure
28
+ validation — it does not re-derive the pre-consolidation acceptance/verify
29
+ union, so a critic that silently dropped an acceptance item would **not** be
30
+ caught downstream. (The repo's unit test exercises a *pure model* of the merge
31
+ over an over-fragmented fixture to document the intended invariant; it does not
32
+ inspect this Skill's actual output.) Conserve scope yourself, deliberately, on
33
+ every merge — and if you cannot preserve an item, you have over-reached, so back
34
+ the operation out.
35
+
36
+ ## Ceiling, not target — the below-ceiling coarsening case
37
+
38
+ Consume the Tech Spec **"Delivery Slicing"** section as a **ceiling, not a
39
+ target** when present: the Architect's proposed N shippable Stories is an
40
+ *upper bound*, not a floor. Cluster the draft's Stories toward that grouping,
41
+ and go **below** N when slices form dependent single-consumer chains — a slice
42
+ whose "Independent? No" row carries no justification (parallelism, risk
43
+ isolation, or delivery-envelope pressure) folds into its consumer. You **never**
44
+ split above N.
45
+
46
+ When the consolidated count drops **below** the Delivery-Slicing count, the
47
+ consolidation report MUST surface that fact explicitly — e.g.
48
+ `Below ceiling: N (Delivery Slicing) → M (consolidated)` — and attach a one-line
49
+ rationale to each below-ceiling merge (which dependent single-consumer slice
50
+ folded into which consumer, and why it was unjustified), so the operator sees
51
+ the coarsening at the Phase 8.3 advisory diff.
@@ -302,28 +302,10 @@ When the Acceptance Table contains **one or more `Disposition: new` rows**, you
302
302
 
303
303
  When the Acceptance Table contains **zero `new`-disposition rows** (every row is `updated` or `unchanged`), do NOT emit a scaffold Story — there is nothing to create.
304
304
 
305
- **Worked example.** Epic #42, Acceptance Table 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, an empty `depends_on`, and tags each scenario with both `@skip` and its namespaced `@epic-42-ac-N` tag:
306
-
307
- {
308
- "slug": "scaffold-billing-feature-files",
309
- "type": "story",
310
- "title": "Scaffold @skip-tagged billing feature files",
311
- "depends_on": [],
312
- "labels": ["type::story", "persona::qa-engineer"],
313
- "acceptance": [
314
- "tests/features/billing/invoice.feature and tests/features/billing/refund.feature both exist on the branch",
315
- "every Scenario in the two new feature files is preceded by an @skip tag (grep for un-skipped scenarios returns zero matches)",
316
- "the invoice.feature scenario carries @epic-42-ac-1 and the refund.feature scenario carries @epic-42-ac-2"
317
- ],
318
- "verify": [
319
- "test -f tests/features/billing/invoice.feature && test -f tests/features/billing/refund.feature (validate)",
320
- "test -z \"$(grep -rL '@skip' tests/features/billing/*.feature)\" (validate)",
321
- "grep -q '@epic-42-ac-1' tests/features/billing/invoice.feature && grep -q '@epic-42-ac-2' tests/features/billing/refund.feature (validate)"
322
- ],
323
- "body": "## Goal\nbdd-scaffold: create the @skip-tagged, @epic-42-ac-N-tagged feature files the billing-flows implementation Stories verify against, so wave-0 lands them before any implementation Story runs.\n\n## Changes\n- {\"path\": \"tests/features/billing/invoice.feature\", \"assumption\": \"creates\"}\n- {\"path\": \"tests/features/billing/refund.feature\", \"assumption\": \"creates\"}\n\n## Acceptance\n- [ ] tests/features/billing/invoice.feature and tests/features/billing/refund.feature both exist on the branch\n- [ ] every Scenario in the two new feature files is preceded by an @skip tag\n- [ ] the invoice.feature scenario carries @epic-42-ac-1 and the refund.feature scenario carries @epic-42-ac-2\n\n## Verify\n- test -f tests/features/billing/invoice.feature && test -f tests/features/billing/refund.feature (validate)\n- test -z \"$(grep -rL '@skip' tests/features/billing/*.feature)\" (validate)\n- grep -q '@epic-42-ac-1' tests/features/billing/invoice.feature && grep -q '@epic-42-ac-2' tests/features/billing/refund.feature (validate)\n"
324
- }
325
-
326
- 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. They MUST NOT add the `@epic-42-ac-N` tag themselves — it is already present from the scaffold pass; their job is to remove `@skip` once the scenario passes.
305
+ > **Read [`examples.md`](./examples.md) on demand** for a fully worked wave-0
306
+ > scaffold Story (Epic #42, two `new` rows) showing the serialized string
307
+ > `body`, the top-level `acceptance`/`verify` arrays, the empty `depends_on`,
308
+ > and the paired `@skip` + `@epic-<id>-ac-N` tagging.
327
309
 
328
310
  ### SCOPE-OVERLAP FLAGGING (docs/runbook downstream of config work)
329
311
 
@@ -0,0 +1,47 @@
1
+ # epic-plan-decompose-author — examples & extended rationale
2
+
3
+ Read this file on demand while authoring the ticket array. The SKILL.md carries
4
+ the operating contract (Policy Capsule, Inputs / Outputs, Procedure, the
5
+ authoring guidance that complements the rendered decomposer prompt, and
6
+ Constraints); this file carries the worked WAVE-0 BDD scaffold Story and the
7
+ extended reasoning behind it.
8
+
9
+ ## WAVE-0 BDD scaffold Story — worked example
10
+
11
+ The contract lives in SKILL.md under **WAVE-0 BDD SCAFFOLD STORY**: when the
12
+ Epic body's `## Acceptance Table` carries one or more `Disposition: new` rows,
13
+ emit **exactly one** dedicated wave-0 scaffold Story whose sole job is to create
14
+ those `.feature` files with `@skip`-tagged scenarios (each also carrying its
15
+ namespaced `@epic-<id>-ac-N` tag) BEFORE any implementation Story runs.
16
+
17
+ **Worked example.** Epic #42, Acceptance Table with two `new` rows
18
+ (`AC-1` -> `tests/features/billing/invoice.feature`,
19
+ `AC-2` -> `tests/features/billing/refund.feature`). The scaffold Story below
20
+ uses a serialized string `body`, top-level `acceptance`/`verify` arrays, an
21
+ empty `depends_on`, and tags each scenario with both `@skip` and its namespaced
22
+ `@epic-42-ac-N` tag:
23
+
24
+ {
25
+ "slug": "scaffold-billing-feature-files",
26
+ "type": "story",
27
+ "title": "Scaffold @skip-tagged billing feature files",
28
+ "depends_on": [],
29
+ "labels": ["type::story", "persona::qa-engineer"],
30
+ "acceptance": [
31
+ "tests/features/billing/invoice.feature and tests/features/billing/refund.feature both exist on the branch",
32
+ "every Scenario in the two new feature files is preceded by an @skip tag (grep for un-skipped scenarios returns zero matches)",
33
+ "the invoice.feature scenario carries @epic-42-ac-1 and the refund.feature scenario carries @epic-42-ac-2"
34
+ ],
35
+ "verify": [
36
+ "test -f tests/features/billing/invoice.feature && test -f tests/features/billing/refund.feature (validate)",
37
+ "test -z \"$(grep -rL '@skip' tests/features/billing/*.feature)\" (validate)",
38
+ "grep -q '@epic-42-ac-1' tests/features/billing/invoice.feature && grep -q '@epic-42-ac-2' tests/features/billing/refund.feature (validate)"
39
+ ],
40
+ "body": "## Goal\nbdd-scaffold: create the @skip-tagged, @epic-42-ac-N-tagged feature files the billing-flows implementation Stories verify against, so wave-0 lands them before any implementation Story runs.\n\n## Changes\n- {\"path\": \"tests/features/billing/invoice.feature\", \"assumption\": \"creates\"}\n- {\"path\": \"tests/features/billing/refund.feature\", \"assumption\": \"creates\"}\n\n## Acceptance\n- [ ] tests/features/billing/invoice.feature and tests/features/billing/refund.feature both exist on the branch\n- [ ] every Scenario in the two new feature files is preceded by an @skip tag\n- [ ] the invoice.feature scenario carries @epic-42-ac-1 and the refund.feature scenario carries @epic-42-ac-2\n\n## Verify\n- test -f tests/features/billing/invoice.feature && test -f tests/features/billing/refund.feature (validate)\n- test -z \"$(grep -rL '@skip' tests/features/billing/*.feature)\" (validate)\n- grep -q '@epic-42-ac-1' tests/features/billing/invoice.feature && grep -q '@epic-42-ac-2' tests/features/billing/refund.feature (validate)\n"
41
+ }
42
+
43
+ The implementation Stories that later un-skip and flesh out these scenarios each
44
+ carry `depends_on: ["scaffold-billing-feature-files"]`, placing them in a later
45
+ wave than the scaffold. They MUST NOT add the `@epic-42-ac-N` tag themselves —
46
+ it is already present from the scaffold pass; their job is to remove `@skip`
47
+ once the scenario passes.
@@ -28,14 +28,15 @@ allowed_tools:
28
28
 
29
29
  ## Role
30
30
 
31
- Senior Engineer + Architect, acting as a **fresh-context pre-mortem critic**.
32
- This Skill is deliberately *separate* from `epic-plan-decompose-author` (the
33
- generator) and from `epic-plan-consolidate` (the scope-preserving merge critic):
34
- a same-pass self-critique is the weak mode this is built to escape. The
35
- generator maps Epic capabilities to Stories against the spec text; this critic
36
- opens the **actual cited code** and asks "if I tried to deliver this exact
37
- backlog, where would it rework?" before any GitHub write makes the rework
38
- expensive.
31
+ Senior Engineer + Architect, acting as a **fresh-context pre-mortem critic** —
32
+ deliberately *separate* from `epic-plan-decompose-author` (the generator) and
33
+ `epic-plan-consolidate` (the scope-preserving merge critic) so it is a
34
+ fresh-context, code-reading review, not a same-pass self-critique.
35
+
36
+ > **Read [`examples.md`](./examples.md) on demand** for the extended rationale:
37
+ > why this critic opens the actual cited code, why it is additive-recommendation
38
+ > (not scope-preserving) unlike consolidation, and the three predicted-rework
39
+ > finding classes in full.
39
40
 
40
41
  ## When to use
41
42
 
@@ -0,0 +1,53 @@
1
+ # epic-plan-premortem — examples & extended rationale
2
+
3
+ Read this file on demand. The SKILL.md carries the operating contract (Policy
4
+ Capsule, Inputs / Outputs, Procedure, Constraints); this file carries the
5
+ extended rationale behind the pre-mortem critic — why it is a separate,
6
+ fresh-context, code-reading pass and how its three predicted-rework finding
7
+ classes differ from the structural gates.
8
+
9
+ ## Why a separate fresh-context code-reading critic
10
+
11
+ This Skill acts as a Senior Engineer + Architect in the role of a
12
+ **fresh-context pre-mortem critic**. It is deliberately *separate* from
13
+ `epic-plan-decompose-author` (the generator) and from `epic-plan-consolidate`
14
+ (the scope-preserving merge critic): a same-pass self-critique is the weak mode
15
+ this is built to escape. The generator maps Epic capabilities to Stories
16
+ against the spec text; this critic opens the **actual cited code** and asks "if
17
+ I tried to deliver this exact backlog, where would it rework?" — before any
18
+ GitHub write makes the rework expensive.
19
+
20
+ **You MUST read the actual cited code surfaces.** This is the load-bearing
21
+ difference between this critic and the structural file-assumption gate: that
22
+ gate proves a path **exists** (or does not); this critic reads what the file
23
+ actually **contains**. A pre-mortem that did not open the cited files has not
24
+ run.
25
+
26
+ ## Not scope-preserving (unlike consolidation)
27
+
28
+ Unlike `epic-plan-consolidate` (merge-and-rewire only), this critic MAY
29
+ recommend splitting an under-specified Story, tightening or rewording an
30
+ acceptance criterion, or flagging an over-specified Story — because it only
31
+ *recommends* in a report; it never applies the change itself. The conservation
32
+ invariant belongs to consolidation; this pass is deliberately a separate,
33
+ additive-recommendation lens. It never writes to GitHub, never persists
34
+ `tickets.json`, and never flips a label — re-authoring on its findings is the
35
+ author skill's job (the workflow re-runs `epic-plan-decompose-author` on the
36
+ report before the persist call).
37
+
38
+ ## The three predicted-rework finding classes
39
+
40
+ Hunt for the three classes the structural gates cannot catch:
41
+
42
+ 1. **Unverifiable acceptance criteria** — an AC no `verify[]` command or
43
+ readable code state can prove. Recommend a concrete verify command or a
44
+ reworded, checkable AC.
45
+ 2. **Over- or under-specified Stories** — a Story whose `acceptance[]` is far
46
+ broader or narrower than its `changes[]` footprint and the cited code
47
+ support. Recommend a split (under-specified: one Story doing the work of
48
+ several) or a tightening.
49
+ 3. **Semantically-wrong assumptions** — the cited file exists (so the
50
+ file-assumption gate passes) but does not contain the seam / export /
51
+ function / data shape the Story assumes — the file-assumption gate passes,
52
+ the work would still rework. Recommend the corrected target or an explicit
53
+ "create the seam first" Story.
@@ -181,88 +181,28 @@ before reaching for names that appear only in the documentation. Write to
181
181
 
182
182
  #### Delivery Slicing section (authoritative ceiling for Phase 8 consolidation)
183
183
 
184
- The Tech Spec MUST carry a `## Delivery Slicing` section in which the Architect
185
- who holds the full design proposes how the Epic's enumerated capabilities
186
- **cluster into N shippable Stories**. This section is the intentional grouping
187
- the Phase 8 consolidation pass
184
+ The Tech Spec MUST carry a `## Delivery Slicing` section proposing how the
185
+ Epic's enumerated capabilities **cluster into N shippable Stories** the
186
+ intentional grouping the Phase 8 consolidation pass
188
187
  ([`epic-plan-consolidate`](../epic-plan-consolidate/SKILL.md)) reconciles the
189
- decomposer's draft against before any GitHub write. Without it, the decompose
190
- phase maps Epic capabilities to Stories ~1:1 and cannot produce a coarser,
191
- holistic plan; with it, the consolidation critic has a well-defined reference
192
- instead of a guess.
193
-
194
- **The proposed count is a ceiling, not a target.** Consolidation reconciles
195
- the draft *toward* your grouping, but it treats the count as an upper bound: it
196
- may **merge below** your proposed count when slices form dependent
197
- single-consumer chains, and it **never splits above** it. Over-slicing here
198
- therefore locks in fragmentation only when the extra slices are genuinely
199
- independent so keep a slice separate only when it earns its own delivery
200
- session.
201
-
202
- **Write the Delivery Slicing section before any other section — it is the
203
- primary input to Phase 8 consolidation.** Author it first so the rest of the
204
- spec (Core Components, API Changes, Data Models) hangs off a deliberate
205
- slicing decision rather than being reverse-engineered into one at the end.
206
- Drafting it last is exactly how the model omits it under the weight of the
207
- other sections.
208
-
209
- Author the section as a table — one row per proposed slice — naming the
210
- capability cluster each slice would deliver, what ships in it, and whether it
211
- can ship independently. Use **noun phrases** for slice names ("Foundation",
212
- "Transport seam", "Send helper") so they map cleanly onto Feature titles in the
213
- resulting decomposition — never verb phrases ("Add transport") or file names
214
- ("`sender.ts`"). Do **not** coarsen the Epic's capability enumeration to produce
215
- the slicing: the granularity lever is *this* grouping recommendation, not a
216
- dumbed-down Epic enumeration.
217
-
218
- **What "Independent?" means:** can this slice ship to production and provide
219
- value *without the next slice landing*? A `Yes` slice is releasable on its own;
220
- a `No` slice only becomes valuable once a later slice lands on top of it.
221
-
222
- **"Independent? No" is a smell that must be justified.** A dependent,
223
- single-consumer slice (one that only feeds the next slice) folds into its
224
- consumer by default — it is not worth its own delivery session's hydration,
225
- branch, PR, and CI ceremony. Mark a slice `No` only when you can name a
226
- one-line reason to keep it separate anyway: **parallelism** (two `No` slices
227
- that can be delivered concurrently by different sessions), **risk isolation**
228
- (a blast-radius or reviewability reason to land it as its own reviewable PR),
229
- or **delivery-envelope pressure** (folding it in would push the consumer past a
230
- single-session sizing envelope). Absent such a justification, do not author the
231
- slice as its own row — fold it into its consumer and let the merged slice carry
232
- the combined capability.
233
-
234
- Worked example:
235
-
236
- ```text
237
- ## Delivery Slicing
238
-
239
- Proposed shippable slices (consolidation ceiling for Phase 8):
240
-
241
- | Slice | What ships | Independent? |
242
- | -------------- | ------------------------------------------------------ | ------------ |
243
- | Foundation | Config schema, types, and the no-op default path | Yes |
244
- | Transport seam | The pluggable transport interface + in-memory adapter | Yes |
245
- | Send helper | The send() helper + retries, built on the transport | No (justified: risk isolation) |
246
-
247
- - **Foundation** folds Epic capabilities "config surface" + "type model" — they
248
- share a reason to exist and ship as one reviewable PR.
249
- - **Transport seam** is the pluggable boundary; it provides value on its own
250
- (in-memory adapter is usable for tests) so it is independently shippable.
251
- - **Send helper** depends on the transport seam landing first, so it is *not*
252
- independent. It stays its own slice only because the retry/backoff logic is a
253
- large, high-blast-radius surface worth isolating in its own reviewable PR
254
- (risk isolation). Absent that justification it would fold into Transport
255
- seam — a bare "depends on the previous slice" is not a reason to keep it
256
- separate.
257
- ```
258
-
259
- The consolidation pass degrades gracefully when this section is absent (it
260
- falls back to cohesion + single-Story-Feature rules only), so authoring it is
261
- how the Architect steers the decomposition toward fewer, right-sized Stories.
262
- Because the count is a **ceiling**, an over-sliced table is coarsened back
263
- during consolidation — but only where the extra slices are dependent
264
- single-consumer chains, so an unjustified `No` slice is the one you should fold
265
- in yourself rather than leaning on the consolidator to catch.
188
+ decomposer draft against before any GitHub write. Author it as a table (one row
189
+ per slice: `Slice | What ships | Independent?`), using **noun-phrase** slice
190
+ names, and **write it before any other section** it is the primary input to
191
+ consolidation and the section the model most often omits when it drafts it last.
192
+
193
+ - **The count is a ceiling, not a target.** Consolidation may merge below it
194
+ (dependent single-consumer chains) but never splits above it.
195
+ - **"Independent?"** answers: can this slice ship to production and provide
196
+ value without the next slice landing? Mark a slice `No` only with a one-line
197
+ justification (parallelism, risk isolation, or delivery-envelope pressure);
198
+ an unjustified dependent single-consumer slice folds into its consumer.
199
+ - Do **not** coarsen the Epic's capability enumeration to produce the slicing;
200
+ the grouping recommendation is the granularity lever.
201
+
202
+ > **Read [`examples.md`](./examples.md) on demand** for the worked Delivery
203
+ > Slicing table and the extended rationale behind these rules (what
204
+ > "Independent?" means, why an unjustified `No` slice is a smell, and how the
205
+ > consolidation pass degrades gracefully when the section is absent).
266
206
 
267
207
  #### Tech Spec system prompt (authoritative)
268
208
 
@@ -0,0 +1,91 @@
1
+ # epic-plan-spec-author — examples & extended rationale
2
+
3
+ Read this file on demand while authoring the Tech Spec. The SKILL.md carries
4
+ the operating contract (Policy Capsule, Inputs / Outputs, Procedure, the two
5
+ authoritative system prompts, and Constraints); this file carries the worked
6
+ Delivery Slicing example and the extended rationale behind the slicing rules.
7
+
8
+ ## Delivery Slicing — extended rationale
9
+
10
+ The Tech Spec MUST carry a `## Delivery Slicing` section in which the Architect
11
+ — who holds the full design — proposes how the Epic's enumerated capabilities
12
+ **cluster into N shippable Stories**. This section is the intentional grouping
13
+ the Phase 8 consolidation pass
14
+ ([`epic-plan-consolidate`](../epic-plan-consolidate/SKILL.md)) reconciles the
15
+ decomposer's draft against before any GitHub write. Without it, the decompose
16
+ phase maps Epic capabilities to Stories ~1:1 and cannot produce a coarser,
17
+ holistic plan; with it, the consolidation critic has a well-defined reference
18
+ instead of a guess.
19
+
20
+ **The proposed count is a ceiling, not a target.** Consolidation reconciles
21
+ the draft *toward* your grouping, but it treats the count as an upper bound: it
22
+ may **merge below** your proposed count when slices form dependent
23
+ single-consumer chains, and it **never splits above** it. Over-slicing here
24
+ therefore locks in fragmentation only when the extra slices are genuinely
25
+ independent — so keep a slice separate only when it earns its own delivery
26
+ session.
27
+
28
+ **Write the Delivery Slicing section before any other section — it is the
29
+ primary input to Phase 8 consolidation.** Author it first so the rest of the
30
+ spec (Core Components, API Changes, Data Models) hangs off a deliberate
31
+ slicing decision rather than being reverse-engineered into one at the end.
32
+ Drafting it last is exactly how the model omits it under the weight of the
33
+ other sections.
34
+
35
+ Author the section as a table — one row per proposed slice — naming the
36
+ capability cluster each slice would deliver, what ships in it, and whether it
37
+ can ship independently. Use **noun phrases** for slice names ("Foundation",
38
+ "Transport seam", "Send helper") so they map cleanly onto Feature titles in the
39
+ resulting decomposition — never verb phrases ("Add transport") or file names
40
+ ("`sender.ts`"). Do **not** coarsen the Epic's capability enumeration to produce
41
+ the slicing: the granularity lever is *this* grouping recommendation, not a
42
+ dumbed-down Epic enumeration.
43
+
44
+ **What "Independent?" means:** can this slice ship to production and provide
45
+ value *without the next slice landing*? A `Yes` slice is releasable on its own;
46
+ a `No` slice only becomes valuable once a later slice lands on top of it.
47
+
48
+ **"Independent? No" is a smell that must be justified.** A dependent,
49
+ single-consumer slice (one that only feeds the next slice) folds into its
50
+ consumer by default — it is not worth its own delivery session's hydration,
51
+ branch, PR, and CI ceremony. Mark a slice `No` only when you can name a
52
+ one-line reason to keep it separate anyway: **parallelism** (two `No` slices
53
+ that can be delivered concurrently by different sessions), **risk isolation**
54
+ (a blast-radius or reviewability reason to land it as its own reviewable PR),
55
+ or **delivery-envelope pressure** (folding it in would push the consumer past a
56
+ single-session sizing envelope). Absent such a justification, do not author the
57
+ slice as its own row — fold it into its consumer and let the merged slice carry
58
+ the combined capability.
59
+
60
+ ### Worked example
61
+
62
+ ```text
63
+ ## Delivery Slicing
64
+
65
+ Proposed shippable slices (consolidation ceiling for Phase 8):
66
+
67
+ | Slice | What ships | Independent? |
68
+ | -------------- | ------------------------------------------------------ | ------------ |
69
+ | Foundation | Config schema, types, and the no-op default path | Yes |
70
+ | Transport seam | The pluggable transport interface + in-memory adapter | Yes |
71
+ | Send helper | The send() helper + retries, built on the transport | No (justified: risk isolation) |
72
+
73
+ - **Foundation** folds Epic capabilities "config surface" + "type model" — they
74
+ share a reason to exist and ship as one reviewable PR.
75
+ - **Transport seam** is the pluggable boundary; it provides value on its own
76
+ (in-memory adapter is usable for tests) so it is independently shippable.
77
+ - **Send helper** depends on the transport seam landing first, so it is *not*
78
+ independent. It stays its own slice only because the retry/backoff logic is a
79
+ large, high-blast-radius surface worth isolating in its own reviewable PR
80
+ (risk isolation). Absent that justification it would fold into Transport
81
+ seam — a bare "depends on the previous slice" is not a reason to keep it
82
+ separate.
83
+ ```
84
+
85
+ The consolidation pass degrades gracefully when this section is absent (it
86
+ falls back to cohesion + single-Story-Feature rules only), so authoring it is
87
+ how the Architect steers the decomposition toward fewer, right-sized Stories.
88
+ Because the count is a **ceiling**, an over-sliced table is coarsened back
89
+ during consolidation — but only where the extra slices are dependent
90
+ single-consumer chains, so an unjustified `No` slice is the one you should fold
91
+ in yourself rather than leaning on the consolidator to catch.
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-07-04T00:29:42.907Z",
2
+ "generatedAt": "2026-07-04T23:12:38.376Z",
3
3
  "generator": "generate-skills-index.js@1",
4
4
  "skills": [
5
5
  {
@@ -567,8 +567,8 @@
567
567
  "tier": "stack",
568
568
  "category": "qa",
569
569
  "path": ".agents/skills/stack/qa/qa-explore-driving/SKILL.md",
570
- "description": "Conventions for agent-driven exploratory QA driving — how the agent itself drives a surface during `/qa-explore` (agent-led), as opposed to the human-led `/qa-assist`. Use when the agent explores a running app via the browser MCP (navigation-first, the default) or walks a static surface (the documented interim until consumer persona-seeding lands), under a strictly read-only capture invariant. The exploration procedure lives in `.agents/workflows/qa-explore.md`; this skill is the driving-conventions reference it leans on.",
571
- "policyCapsuleBullets": 8,
570
+ "description": "Conventions for agent-driven exploratory QA driving — how the agent itself drives a surface during `/qa-explore` (agent-led), as opposed to the human-led `/qa-assist`. Use when the agent explores a running app via the browser MCP (navigation-first, the default) — including authenticated deployed surfaces reached through the resolved environment's sign-in seam — or walks a static surface (the documented interim where no seam resolves), under a strictly read-only capture invariant. The exploration procedure lives in `.agents/workflows/qa-explore.md`; this skill is the driving-conventions reference it leans on.",
571
+ "policyCapsuleBullets": 9,
572
572
  "allowedTools": null,
573
573
  "vendor": null
574
574
  },
@@ -4,9 +4,10 @@ description:
4
4
  Conventions for agent-driven exploratory QA driving — how the agent itself
5
5
  drives a surface during `/qa-explore` (agent-led), as opposed to the
6
6
  human-led `/qa-assist`. Use when the agent explores a running app via the
7
- browser MCP (navigation-first, the default) or walks a static surface (the
8
- documented interim until consumer persona-seeding lands), under a strictly
9
- read-only capture invariant. The exploration procedure lives in
7
+ browser MCP (navigation-first, the default) including authenticated
8
+ deployed surfaces reached through the resolved environment's sign-in seam —
9
+ or walks a static surface (the documented interim where no seam resolves),
10
+ under a strictly read-only capture invariant. The exploration procedure lives in
10
11
  `.agents/workflows/qa-explore.md`; this skill is the driving-conventions
11
12
  reference it leans on.
12
13
  ---
@@ -16,9 +17,10 @@ description:
16
17
  ## Policy Capsule
17
18
 
18
19
  - Drive the running app **by default** through the browser MCP, navigation-first: start at a root and reach each surface only via UI affordances — never URL-jump to a deep link.
19
- - Treat **static driving** (reading source, routes, and rendered markup without a live runtime) as the **documented interim** method, chosen at Plan time when a live runtime is not reachable — never the silent fallback.
20
+ - Resolve the target **environment** at Plan time (via `resolveQaEnvironment`) and record its name in the ledger; each environment keys its own `baseUrl`, `signInSeam`, and `allowWrites`.
21
+ - Treat **static driving** (reading source, routes, and rendered markup without a live runtime) as the **documented interim** method, chosen at Plan time **only where no seam resolves** for the target environment — never the silent fallback.
20
22
  - Hold the **read-only capture invariant** absolutely: the agent makes no source edits and no product mutations while driving; the only write is appending to the `temp/qa/<sessionId>` ledger.
21
- - Authenticated driving depends on **consumer persona-seeding infrastructure that this Epic does not deliver**; without it, drive only the unauthenticated surface or fall back to static, and record the gap never enter real credentials or fabricate a session.
23
+ - Authenticated driving follows the resolved environment's **`signInSeam`**: sign in through a dev `url` seam (persona name substituted into the template) or a `skill` seam (a stored `credentialRef` read by the named sign-in skill), with **mandatory redaction** of all captured evidence. Never type real credentials inline or fabricate a session; where an environment resolves no seam, drive the unauthenticated surface or fall back to static and record the gap.
22
24
  - Pick the driving method explicitly in the Plan phase (drive vs. static) and record it in the ledger; do not switch methods mid-surface without a new Plan note.
23
25
  - Every phase transition and every GitHub write is HITL-gated; the agent drives and captures, but never files or promotes findings autonomously.
24
26
  - Broken navigation, a missing affordance, or a guard redirect loop is a **finding**, not a workaround — record it and move on; do not route around it with a direct URL.
@@ -65,15 +67,14 @@ state rather than an artifact of a deep link.
65
67
 
66
68
  ## 2. Static Driving — the Documented Interim
67
69
 
68
- Static driving is the **explicitly documented interim** method for when a live
69
- runtime is not reachable most commonly because authenticated driving needs
70
- consumer persona-seeding infrastructure that does not yet exist (§ 4). It walks
71
- the surface from source, route definitions, and rendered markup rather than a
72
- running browser.
70
+ Static driving is the **explicitly documented interim** method for when **no
71
+ seam resolves** for the target environment the resolved environment carries
72
+ no `signInSeam` the agent can drive through (§ 4). It walks the surface from
73
+ source, route definitions, and rendered markup rather than a running browser.
73
74
 
74
75
  - **Choose it at Plan time, never silently.** Static is a deliberate Plan-phase
75
- decision recorded in the ledger ("method: static, reason: no reachable
76
- authenticated runtime"), not an unannounced fallback the agent slips into
76
+ decision recorded in the ledger ("environment: preview, method: static,
77
+ reason: no seam resolves"), not an unannounced fallback the agent slips into
77
78
  when the browser MCP hiccups.
78
79
  - **It is interim, not equivalent.** Static driving cannot exercise real
79
80
  authorization, routing guards, or runtime console/network signal. Treat its
@@ -81,10 +82,10 @@ running browser.
81
82
  same coverage a driven pass would.
82
83
  - **Same read-only invariant.** Static driving reads source and routes; it makes
83
84
  no edits. The read-only capture invariant (§ 3) applies identically.
84
- - **Promote to driving when the runtime lands.** Static is the bridge until the
85
- consumer's persona-seeding infrastructure (§ 4) makes authenticated driving
86
- possible. When that lands, re-run the surface driven; do not leave a surface
87
- permanently static when it could be driven.
85
+ - **Promote to driving when a seam lands.** Static is the bridge until the
86
+ target environment resolves a `signInSeam` (§ 4) that makes driven
87
+ authenticated exploration possible. When that lands, re-run the surface
88
+ driven; do not leave a surface permanently static when it could be driven.
88
89
 
89
90
  ## 3. The Read-Only Capture Invariant
90
91
 
@@ -109,29 +110,42 @@ and the Epic's security considerations — it is not a soft preference.
109
110
  (ticket creation, promotion) happen only behind an operator confirmation gate.
110
111
  The agent never files or promotes findings autonomously.
111
112
 
112
- ## 4. Authenticated Driving Depends on Consumer Infra (Not Delivered Here)
113
-
114
- Driving an **authenticated** surface requires signing in as a seeded persona.
115
- That seeding provisioning a test persona with the right org, role, and data
116
- so the agent can reach a logged-in surface navigation-first — is **consumer
117
- persona-seeding infrastructure that this Epic does not deliver**. It is an
118
- explicit non-goal of the `/qa-explore` rebuild.
119
-
120
- - **Unauthenticated surface only, by default.** Without persona-seeding infra in
121
- the consumer project, drive only the surface reachable without sign-in, or
122
- fall back to static driving (§ 2) for the authenticated surface. Record the
123
- gap in the ledger so the partial coverage is visible.
124
- - **Never enter real credentials.** The agent MUST NOT type real usernames,
125
- passwords, or tokens to reach an authenticated surface, and MUST NOT fabricate
126
- or forge a session. This is a hard security boundary, not a convenience to
113
+ ## 4. Authenticated Driving Follows the Per-Environment Seam
114
+
115
+ Driving an **authenticated** surface requires signing in. The resolved target
116
+ environment (via `resolveQaEnvironment`, § Policy Capsule) carries the
117
+ `signInSeam` the agent drives through the same discriminated-union shape
118
+ `qa-harness` resolves via its contract. When a seam resolves, authenticated
119
+ deployed surfaces are **driven**, not statically deferred.
120
+
121
+ - **Drive the authenticated surface through the seam.** When the target
122
+ environment carries a `signInSeam`, sign in via that seam and then reach the
123
+ authenticated surface navigation-first including authenticated **deployed**
124
+ hosts. The two seam kinds:
125
+ - **`kind: 'url'` (dev impersonation).** Substitute the persona **name** into
126
+ the seam's URL template and navigate there. The persona name is the sole
127
+ input; no per-persona auth material is read.
128
+ - **`kind: 'skill'` (procedural / credential).** Invoke the named consumer
129
+ sign-in skill, which reads a per-persona **`credentialRef`** — an indirect
130
+ handle to a stored credential, never an inline secret. Read the skill's
131
+ `SKILL.md` and follow it.
132
+ - **Never enter real credentials inline.** The agent MUST NOT type real
133
+ usernames, passwords, or tokens to reach an authenticated surface, and MUST
134
+ NOT fabricate or forge a session. Sign-in flows only through the seam, which
135
+ consumes a persona name or a `credentialRef` indirection — never a
136
+ hand-typed secret. This is a hard security boundary, not a convenience to
127
137
  work around.
128
- - **The dependency is the consumer's to satisfy.** When a consumer wants driven
129
- authenticated exploration, the consumer supplies a dev sign-in seam and seeded
130
- personas (the same shape `qa-harness` resolves via its contract). Until then,
131
- authenticated coverage is static or deferred say which in the ledger.
132
- - **Surface the gap, don't paper over it.** A surface that could not be driven
133
- because authenticated seeding is absent is itself a coverage signal worth
134
- recording, not a silent skip.
138
+ - **Redaction is mandatory.** Every captured evidence string console,
139
+ network, headers — passes through the shared redaction path 3) before it
140
+ reaches disk or GitHub; bearer tokens, session cookies, `Authorization`
141
+ headers, and PII are masked. Authenticated driving raises the stakes on
142
+ redaction, it does not relax it.
143
+ - **Static only where no seam resolves.** When the target environment resolves
144
+ **no** `signInSeam`, drive only the unauthenticated surface or fall back to
145
+ static driving (§ 2), and record the gap in the ledger so the partial
146
+ coverage is visible. A surface that could not be driven because the
147
+ environment carries no seam is itself a coverage signal worth recording, not
148
+ a silent skip.
135
149
 
136
150
  ## 5. Cross-References
137
151