Tandem: split-into-stories (PM hat)
Operate as PM hat. The user has an approved Feature and needs to decompose it into work-sized Stories. Each Story ships with its paired Testplan in the same response — this is the kit's most load-bearing rule.
This skill is the slash-command wrapper for the kit's canonical story-decomposition prompt. The prompt is the source of truth; this file is the entry point.
Source of truth
@_00-Project-Management/92-Prompts/04-split-feature-into-stories.md
Follow that prompt verbatim. The sections below add only the slash-command-specific glue + the paired-testplan enforcement contract — do not re-declare the prompt's content here.
Paired-testplan enforcement (MANDATORY)
Per SOP §11 — "Story → Testplan pairing — MANDATORY" — every Story file under the resolved stories folder (via node _00-Project-Management/93-Scripts/lib/pm-paths.js resolve stories), organized as EPIC-NN/FEAT-NN.M/STORY-NN.M.PP-*.md, requires a paired Testplan in the resolved testplans folder, organized as EPIC-NN/FEAT-NN.M/TESTPLAN-NN.M.PP-<slug>.md, written in the same response. Folder locations are resolved through the path map (pm-paths.json) rather than hardcoded.
This skill enforces the rule structurally, not as an honour-system convention:
- Draft both the Story and its Testplan in memory first — every AC, every TC, every runnable
Command:.
- Only when both drafts are complete and consistent (every AC maps to ≥1 TC, every TC has a real runnable command), commit both files to disk in the same response.
- If for ANY reason the paired Testplan cannot be drafted (AC is not machine-testable, command convention unknown, etc.), abort — do not write the Story file. Tell the user which AC blocked the pairing and ask them to either rewrite the AC to be testable or accept dropping it. Do not silently leave an orphan Story.
- Story + Testplan numbering is lockstep:
STORY-NN.M.PP ↔ TESTPLAN-NN.M.PP for the same PP. The skill picks PP once and uses it for both files.
This contract IS the differentiator vs. the plain paste-prompt. The prompt advises pairing; this skill rejects the violation.
Inputs needed
- Path to the source Feature file under the
features folder (resolve via the path map: node _00-Project-Management/93-Scripts/lib/pm-paths.js resolve features; e.g. _00-Project-Management/31-Features/EPIC-NN/FEAT-NN.M-<slug>.md).
- If the user didn't supply one, ask: "Which Feature file should I decompose? Paste the path or the FEAT-NN.M id."
Load into context
Use Read / Glob to detect existence. Treat missing files as "not present" rather than throwing. Folder locations are resolved through the path map (pm-paths.js / pm-paths.json) rather than hardcoded.
- Source Feature — at the resolved path above. Read fully — especially the
## Acceptance criteria checklist (each criterion ~ 1 story).
- Parent Epic — resolve the
epics folder via the path map and read EPIC-NN-*.md for upstream context.
- SOP —
_00-Project-Management/90-Standards/SOP.md for DoR, estimation, status enum, frontmatter contract.
- Project context —
_00-Project-Management/90-Standards/PROJECT-CONTEXT.md for runnable test command conventions (which test runner, what ports, how to invoke).
- Story + Testplan templates — resolve the
templates folder via the path map and read STORY.template.md + TESTPLAN.template.md. Use both verbatim — do not redraft section headings from memory.
- Existing Stories under this Feature — resolve the
stories folder via the path map and glob EPIC-NN/FEAT-NN.M/STORY-NN.M.PP-*.md to find next-free PP (zero-padded to 2 digits). Same PP is used for the paired Testplan. Create both subfolders if missing.
- Project root
CLAUDE.md — for project-specific overrides.
Task
- Read the source Feature's
## Acceptance criteria. Each criterion is typically one Story. If a criterion implies > L work, split into multiple Stories.
- Split gate (artefact economy —
90-Standards/ARTEFACT-ECONOMY.md): produce the fewest Stories that carry the Feature's testable claims. An AC without a falsifiable claim of its own folds into a sibling Story — it never becomes a thin Story. When unsure whether to split, don't.
- For each derived story, draft both files in memory first (Story + Testplan). Do not write to disk until both drafts are complete and consistent.
- Story file content:
- Use
STORY.template.md verbatim.
- Fill
As / I want / So that, AC checkboxes, technical notes, dependencies, references.
- Frontmatter:
status: not-started, created_at: <ISO 8601 now>, other timestamps empty, epic: EPIC-NN, feature: FEAT-NN.M, id: STORY-NN.M.PP, ai_review: pending.
- Set
type_of_work: to a concrete discipline (frontend / backend / infra / data / docs) — never leave the template placeholder (it's a DoR gate per SOP §6 and fuels sub-agent assignment). Add suggested_agents: [agent, …] only when a specific specialist clearly fits — it overrides the PROJECT-CONTEXT type_of_work → sub-agent map (SOP §11.3 / FEAT-03.1). Most stories need only type_of_work.
- Paired Testplan file content:
- Use
TESTPLAN.template.md verbatim.
- One row in the AC→TC map per Story AC, ≥1 TC per AC.
- Every TC has a runnable
Command: (no manual steps, no "have a human verify"). Use commands appropriate to the stack (see PROJECT-CONTEXT.md).
- Never assert by grepping the whole
DASHBOARD.html for a substring that could appear in indexed artefact prose. The generator embeds every artefact's bodyHtml into the window.__DATA payload, so a bare grep <substring> DASHBOARD.html is confounded — the substring almost always also appears in some artefact's prose (including the very story/testplan that discusses the feature under test). This produced three false results (BUG-20260606-02, TESTPLAN-15.2.02, TESTPLAN-15.2.04). Instead: (a) parse the window.__DATA payload and assert the relevant array/field, (b) assert a specific rendered element (e.g. grep '<h1 class="app-title">'), or (c) use a sentinel that cannot appear in any artefact body. Extraction one-liner for (a): node -e 'const D=JSON.parse(require("fs").readFileSync("…/DASHBOARD.html","utf8").match(/window\.__DATA = (\{[\s\S]*?\});<\/script>/)[1]); /* assert on D */'.
- Frontmatter:
id: TESTPLAN-NN.M.PP, story: STORY-NN.M.PP, feature: FEAT-NN.M, epic: EPIC-NN, status: not-started, created_at: <ISO 8601 now>.
- Validate before writing: every AC in the draft Story is covered by ≥1 TC in the draft Testplan; every TC has a real
Command:. If validation fails, abort per the enforcement contract above.
- Commit both files to disk in the same response. Number stories sequentially within the Feature (.01, .02, .03 …).
- As each Story file is written, run the usage-estimate producer step (below) so the story leaves this skill carrying a
usage_estimate: and its basis line.
- As each Story file is written, apply the
write-outcomes rules inline to the story's title, acceptance criteria, and technical content to produce the outcome line; dispatch a sub-agent only under ADR-0105 — write-outcomes runs inline first, rather than dispatching a sub-agent per artefact's named conditions (this producer's own context is near its limit, or an isolation-worthy batch run). Write the single-line outcome (verbatim, no markdown) into that Story's outcome: frontmatter field — in the same response.
- Update the Feature's
## Stories section with relative links to the new Story files.
- Show the file tree of what you'll create before writing. Wait for user approval.
Usage-estimate producer step (MANDATORY at authoring)
Every story this skill writes leaves carrying a usage_estimate:. Run it once per story file,
immediately after the file is written:
node _00-Project-Management/93-Scripts/usage-estimate.js --story <story-path>
- Fill-if-missing, never overwrite — a populated estimate is preserved verbatim and the file
is left byte-identical.
- It writes the number and a one-line basis into
## Technical notes. Do not hand-write
either.
- No number comes back when
estimate: is missing or XL. Both are already blockers here (this
skill proposes splitting an XL before writing it), so fix the band rather than the estimate.
- List the produced figures in the end-of-session summary.
The derivation itself is written in exactly one place — do not restate it here or in any
skill: _00-Project-Management/90-Standards/USAGE-ESTIMATE-HEURISTIC.md.
Output rules
- If any AC is not machine-testable, stop and flag: "AC-3 'looks good' is not testable. Rewrite as e.g. 'matches mockup screenshot within 5% pixel delta', or accept dropping it." Do not invent a fake TC to hide the gap.
- If a Story estimates to XL, propose splitting before writing.
- If a Story has > 5 ACs, propose splitting (AC explosion → TC bloat).
- Do not mark any Story
ready — that requires DoR gate via a separate refine step.
Non-negotiable rules from CLAUDE.md
- Frontmatter timestamps (quoted ISO 8601 with offset, from system clock).
- Status enum (
not-started on creation).
- Story → Testplan pairing — MANDATORY (enforced above; reject violations, don't bend).
- Templates rule — use
STORY.template.md and TESTPLAN.template.md verbatim.
- Project-specific test conventions from
PROJECT-CONTEXT.md (ports, commands, etc.).
End-of-session summary (always emit)
- Stories created: N (list paths)
- Testplans created: N (list paths)
usage_estimate produced per story: list of STORY-NN.M.PP = <tokens> (or the stated reason none was produced)
- Pairing verified: yes / no (must be yes — otherwise the writes shouldn't have happened)
- ACs flagged as not machine-testable: list, or "none"
- Estimated total: <days/weeks>
- Suggested first story to pull: STORY-NN.M.PP (lowest dependency, smallest)
Next command
Next: /tandem:refine-backlog — promote selected stories not-started → ready after DoR gate.
Or, if a story is obviously DoR-clean already: /tandem:execute-story <story-path> — start work directly.
1---2name: split-into-stories3description: Split a Feature into Stories AND paired Testplans in the same response. Use when the user asks to split / decompose a feature into stories, asks to write stories for a feature, asks to create stories + testplans, or invokes /tandem:split-into-stories. Operates as PM hat. Enforces the kit's MANDATORY Story → Testplan pairing rule — aborts (writes nothing) if it cannot produce both story and matching testplan in the same response.4---56# Tandem: split-into-stories (PM hat)78Operate as **PM hat**. The user has an approved Feature and needs to decompose it into work-sized Stories. Each Story ships with its paired Testplan in the **same response** — this is the kit's most load-bearing rule.910This skill is the slash-command wrapper for the kit's canonical story-decomposition prompt. The prompt is the source of truth; this file is the entry point.1112## Source of truth1314@_00-Project-Management/92-Prompts/04-split-feature-into-stories.md1516Follow that prompt verbatim. The sections below add only the slash-command-specific glue + the paired-testplan enforcement contract — do not re-declare the prompt's content here.1718## Paired-testplan enforcement (MANDATORY)1920Per SOP §11 — "Story → Testplan pairing — MANDATORY" — every Story file under the resolved `stories` folder (via `node _00-Project-Management/93-Scripts/lib/pm-paths.js resolve stories`), organized as `EPIC-NN/FEAT-NN.M/STORY-NN.M.PP-*.md`, requires a paired Testplan in the resolved `testplans` folder, organized as `EPIC-NN/FEAT-NN.M/TESTPLAN-NN.M.PP-<slug>.md`, written **in the same response**. Folder locations are resolved through the path map (`pm-paths.json`) rather than hardcoded.2122This skill enforces the rule structurally, not as an honour-system convention:23241. Draft **both** the Story and its Testplan in memory first — every AC, every TC, every runnable `Command:`.252. Only when both drafts are complete and consistent (every AC maps to ≥1 TC, every TC has a real runnable command), **commit both files to disk in the same response**.263. If for ANY reason the paired Testplan cannot be drafted (AC is not machine-testable, command convention unknown, etc.), **abort — do not write the Story file**. Tell the user which AC blocked the pairing and ask them to either rewrite the AC to be testable or accept dropping it. Do not silently leave an orphan Story.274. Story + Testplan numbering is lockstep: `STORY-NN.M.PP` ↔ `TESTPLAN-NN.M.PP` for the same `PP`. The skill picks `PP` once and uses it for both files.2829This contract IS the differentiator vs. the plain paste-prompt. The prompt advises pairing; this skill rejects the violation.3031## Inputs needed3233- Path to the source Feature file under the `features` folder (resolve via the path map: `node _00-Project-Management/93-Scripts/lib/pm-paths.js resolve features`; e.g. `_00-Project-Management/31-Features/EPIC-NN/FEAT-NN.M-<slug>.md`).34- If the user didn't supply one, ask: "Which Feature file should I decompose? Paste the path or the FEAT-NN.M id."3536## Load into context3738Use `Read` / `Glob` to detect existence. Treat missing files as "not present" rather than throwing. Folder locations are resolved through the path map (`pm-paths.js` / `pm-paths.json`) rather than hardcoded.3940- **Source Feature** — at the resolved path above. Read fully — especially the `## Acceptance criteria` checklist (each criterion ~ 1 story).41- **Parent Epic** — resolve the `epics` folder via the path map and read `EPIC-NN-*.md` for upstream context.42- **SOP** — `_00-Project-Management/90-Standards/SOP.md` for DoR, estimation, status enum, frontmatter contract.43- **Project context** — `_00-Project-Management/90-Standards/PROJECT-CONTEXT.md` for runnable test command conventions (which test runner, what ports, how to invoke).44- **Story + Testplan templates** — resolve the `templates` folder via the path map and read `STORY.template.md` + `TESTPLAN.template.md`. Use both verbatim — do not redraft section headings from memory.45- **Existing Stories under this Feature** — resolve the `stories` folder via the path map and glob `EPIC-NN/FEAT-NN.M/STORY-NN.M.PP-*.md` to find next-free `PP` (zero-padded to 2 digits). Same `PP` is used for the paired Testplan. Create both subfolders if missing.46- **Project root `CLAUDE.md`** — for project-specific overrides.4748## Task49501. Read the source Feature's `## Acceptance criteria`. Each criterion is typically one Story. If a criterion implies > L work, split into multiple Stories.51 - **Split gate (artefact economy — `90-Standards/ARTEFACT-ECONOMY.md`):** produce the *fewest* Stories that carry the Feature's testable claims. An AC without a falsifiable claim of its own folds into a sibling Story — it never becomes a thin Story. When unsure whether to split, don't.522. For each derived story, **draft both files in memory first** (Story + Testplan). Do not write to disk until both drafts are complete and consistent.533. Story file content:54 - Use `STORY.template.md` verbatim.55 - Fill `As / I want / So that`, AC checkboxes, technical notes, dependencies, references.56 - Frontmatter: `status: not-started`, `created_at: <ISO 8601 now>`, other timestamps empty, `epic: EPIC-NN`, `feature: FEAT-NN.M`, `id: STORY-NN.M.PP`, `ai_review: pending`.57 - Set `type_of_work:` to a concrete discipline (`frontend` / `backend` / `infra` / `data` / `docs`) — **never leave the template placeholder** (it's a DoR gate per SOP §6 and fuels sub-agent assignment). Add `suggested_agents: [agent, …]` only when a specific specialist clearly fits — it overrides the PROJECT-CONTEXT `type_of_work → sub-agent` map (SOP §11.3 / FEAT-03.1). Most stories need only `type_of_work`.584. Paired Testplan file content:59 - Use `TESTPLAN.template.md` verbatim.60 - One row in the AC→TC map per Story AC, ≥1 TC per AC.61 - Every TC has a runnable `Command:` (no manual steps, no "have a human verify"). Use commands appropriate to the stack (see PROJECT-CONTEXT.md).62 - **Never assert by grepping the whole `DASHBOARD.html` for a substring that could appear in indexed artefact prose.** The generator embeds every artefact's `bodyHtml` into the `window.__DATA` payload, so a bare `grep <substring> DASHBOARD.html` is confounded — the substring almost always also appears in some artefact's prose (including the very story/testplan that *discusses* the feature under test). This produced three false results (BUG-20260606-02, TESTPLAN-15.2.02, TESTPLAN-15.2.04). Instead: **(a)** parse the `window.__DATA` payload and assert the relevant array/field, **(b)** assert a **specific rendered element** (e.g. `grep '<h1 class="app-title">'`), or **(c)** use a sentinel that cannot appear in any artefact body. Extraction one-liner for (a): `node -e 'const D=JSON.parse(require("fs").readFileSync("…/DASHBOARD.html","utf8").match(/window\.__DATA = (\{[\s\S]*?\});<\/script>/)[1]); /* assert on D */'`.63 - Frontmatter: `id: TESTPLAN-NN.M.PP`, `story: STORY-NN.M.PP`, `feature: FEAT-NN.M`, `epic: EPIC-NN`, `status: not-started`, `created_at: <ISO 8601 now>`.645. **Validate before writing**: every AC in the draft Story is covered by ≥1 TC in the draft Testplan; every TC has a real `Command:`. If validation fails, **abort** per the enforcement contract above.656. Commit both files to disk in the same response. Number stories sequentially within the Feature (.01, .02, .03 …).66 - As each Story file is written, run the **usage-estimate producer step** (below) so the story leaves this skill carrying a `usage_estimate:` and its basis line.67 - As each Story file is written, apply the `write-outcomes` rules inline to the story's title, acceptance criteria, and technical content to produce the outcome line; dispatch a sub-agent only under ADR-0105 — write-outcomes runs inline first, rather than dispatching a sub-agent per artefact's named conditions (this producer's own context is near its limit, or an isolation-worthy batch run). Write the single-line outcome (verbatim, no markdown) into that Story's `outcome:` frontmatter field — in the same response.687. Update the Feature's `## Stories` section with relative links to the new Story files.698. **Show the file tree of what you'll create before writing.** Wait for user approval.7071## Usage-estimate producer step (MANDATORY at authoring)7273Every story this skill writes leaves carrying a `usage_estimate:`. Run it once per story file,74immediately after the file is written:7576```bash77node _00-Project-Management/93-Scripts/usage-estimate.js --story <story-path>78```7980- **Fill-if-missing, never overwrite** — a populated estimate is preserved verbatim and the file81 is left byte-identical.82- It writes the number **and** a one-line basis into `## Technical notes`. Do not hand-write83 either.84- No number comes back when `estimate:` is missing or `XL`. Both are already blockers here (this85 skill proposes splitting an XL before writing it), so fix the band rather than the estimate.86- List the produced figures in the end-of-session summary.8788**The derivation itself is written in exactly one place — do not restate it here or in any89skill:** [`_00-Project-Management/90-Standards/USAGE-ESTIMATE-HEURISTIC.md`](../../_00-Project-Management/90-Standards/USAGE-ESTIMATE-HEURISTIC.md).9091## Output rules9293- If any AC is not machine-testable, **stop and flag**: "AC-3 'looks good' is not testable. Rewrite as e.g. 'matches mockup screenshot within 5% pixel delta', or accept dropping it." Do not invent a fake TC to hide the gap.94- If a Story estimates to XL, propose splitting before writing.95- If a Story has > 5 ACs, propose splitting (AC explosion → TC bloat).96- Do **not** mark any Story `ready` — that requires DoR gate via a separate refine step.9798## Non-negotiable rules from CLAUDE.md99100- Frontmatter timestamps (quoted ISO 8601 with offset, from system clock).101- Status enum (`not-started` on creation).102- **Story → Testplan pairing — MANDATORY** (enforced above; reject violations, don't bend).103- Templates rule — use `STORY.template.md` and `TESTPLAN.template.md` verbatim.104- Project-specific test conventions from `PROJECT-CONTEXT.md` (ports, commands, etc.).105106## End-of-session summary (always emit)107108- Stories created: N (list paths)109- Testplans created: N (list paths)110- `usage_estimate` produced per story: list of `STORY-NN.M.PP = <tokens>` (or the stated reason none was produced)111- Pairing verified: yes / no (must be yes — otherwise the writes shouldn't have happened)112- ACs flagged as not machine-testable: list, or "none"113- Estimated total: <days/weeks>114- Suggested first story to pull: STORY-NN.M.PP (lowest dependency, smallest)115116## Next command117118Next: `/tandem:refine-backlog` — promote selected stories `not-started` → `ready` after DoR gate.119120Or, if a story is obviously DoR-clean already: `/tandem:execute-story <story-path>` — start work directly.