dev-story
Implements one story in code with tests, following its ADRs. Use when the user says to build, implement or start working on a story. Not for checking readiness beforehand or closing the story after.
No fetch-and-execute, destructive, credential, exfiltration or rules-override patterns in 4 files, checked on Oct 21, 2026. This is not a guarantee of safety. How the scan works.
npx degit Donchitos/Claude-Code-Game-Studios/.claude/skills/dev-story .claude/skills/dev-story12 alternatives · Best testing skills · Author? Prove this skill
SKILL.md
!`bash "${CLAUDE_SKILL_DIR}/../../hooks/yaml-helper.sh" resolve_config --keys automation,workflow,qa.level,gate_level,engine_values,specialists,perf_budgets,system_overrides`
Resolved above — use as-is. No block → defaults in
`.claude/docs/config-resolution.md`.
Ask with the question tool. Without one, list the options numbered and end your turn
to wait. A skipped or dismissed question is not an answer: ask again in plain text.
# Dev Story
Implements one story: checks it can start, marks it In Progress, hands the code to
the right programmer agent with a brief built from the story, then checks the
result parses and looks right, and hands off.
**Writes** (each after an ask that names it): the story's `Status`, `Last Updated`
and `## Files`, and its `production/sprint-status.yaml` entry. The programmer agents
write the code and tests under the code root and test root
(`.claude/docs/code-root-resolution.md`). The session checkpoint and the evidence
stamp are written without an ask.
**Next:** `/story-done [story-path]`; at `standard` and `full`, `/code-review
[files]` first.
Asks follow `.claude/docs/automation-modes.md` (collaborative always asks, guided
asks on major choices, autonomous logs and goes on; `automation_always_ask`
categories always ask).
Checklist (copy it and tick as you go):
```
- [ ] Phase 1: story found, tier resolved
- [ ] Phase 2: prerequisites checked (ADR, manifest, dependencies)
- [ ] Phase 3: story marked In Progress (one ask)
- [ ] Phase 4: programmer and engine specialist chosen
- [ ] Phase 5: work handed out with a full brief
- [ ] Phase 6: finished? parse check, run and look, one Run result line
- [ ] Phase 7: summary, ## Files, checkpoint, closing block
```
## Phase 1: The story
The text typed after the skill name is the story path. With nothing typed, take the
story the checkpoint in `production/session-state/active.md` names and confirm it:
"Continuing work on [title] — is that correct?" None there: ask "Which story are we
implementing?" with the `TODO` rows of `bash .claude/scripts/story-facts.sh --all`
as options. No story file under `production/epics/` at all: stop with
"No stories found in `production/epics/` — run `/create-stories [epic-slug]` to make them (at `standard` and `full`, run `/create-epics` first if there is no epic yet)."
`bash .claude/scripts/story-facts.sh <story-path>` gives its type, layer, TR-IDs,
ADRs and dependencies. Then read the story in full.
**Tier.** `workflow` for the story's system, keyed on the GDD filename stem of its
`GDD:` path (`design/gdd/<stem>.md` → `<stem>`; the `[system]` of its TR-ID only when
there is no GDD path): that system's `system_overrides` row if there is one, else the
project value. Say it in your first reply: `Tier: workflow [value]`. That value, never
`rigor` or `qa.level`, picks the Phase 2 column and the closing Next step.
## Phase 2: Can it start?
| Input | `full` | `standard` | `minimal` |
|---|---|---|---|
| TR registry `docs/architecture/tr-registry.yaml` | missing: **stop**, "Run `/architecture-review` to bootstrap it" | optional | not expected |
| The ADR the story names | missing, `Proposed`, `Deprecated` or `Superseded`: **stop** | the same, only when the story names one | the same, only when the story names one |
| Control manifest `docs/architecture/control-manifest.md` | missing: warn, go on | missing: warn, go on | not expected |
A story whose ADR field reads `N/A — [reason]` names none.
**The ADR.** `bash .claude/scripts/adr-status.sh <ADR id>` gives its status and
`version`. Stop messages: `Proposed` — "ADR-NNNN is still Proposed. Accept it with
`/architecture-decision accept ADR-NNNN` before implementing." `Deprecated` or
`Superseded` — "ADR-NNNN is [status]; point the story at [successor] (edit its ADR
field; `/create-stories` never rewrites an existing story) before implementing."
`NOT_FOUND` — "ADR file not found. Run `/architecture-decision`, or correct the
story's ADR field."
**Do not open the ADR.** The story's ADR Decision Summary and Implementation Notes
are what the programmer reads instead. Compare the ADR's `version` with the story's
`ADR Version`:
- equal, or the story has no `ADR Version` (written before the stamp), or both are
`unversioned`: trust the story. Note the last two in the summary ("summary
trusted unverified").
- they differ, or the ADR is `unversioned` but the story names a date: read
`references/version-mismatch.md`. On a match, do not load it.
Never treat an absent stamp as a stale one: unknown falls back to the story, and
`/story-readiness` and `/code-review` are the checks that catch drift later.
**The manifest.** When it exists, compare its `Manifest Version` line with the
story's. They differ: read `references/version-mismatch.md`.
**Dependencies.** For each path on the story's Depends on line,
`bash .claude/scripts/story-facts.sh <path>`. Not `COMPLETE`: ask "[this story]
depends on [dependency], which is [status], not Complete. How do you want to go
on?" Options: Proceed anyway — I accept the risk / Stop — I'll finish the
dependency first / It is done, the status was never updated — mark it Complete.
Stop: the story is BLOCKED (Phase 7), no agent. Mark it: ask "May I set [path]
Status to Complete?" first. Proceed: note it under Deviations. `NOT_FOUND`: warn
"Dependency story not found: [path]".
Any stop above: no agent runs. Go to Phase 7 and write the BLOCKED checkpoint.
## Phase 3: Mark it In Progress
Before any agent, ask once: "May I mark this story In Progress? This sets `Status`
and `Last Updated` in [story path] and its entry in `production/sprint-status.yaml`,
and at the end lists the files changed under its `## Files`." Leave the sprint file
out of the question when it does not exist. Put what you will do next before the
question: the question is the last thing in the reply. On yes:
- `bash .claude/scripts/md-section.sh set-field <story> Status "In Progress"`, then
`set-field <story> "Last Updated" <YYYY-MM-DD> --after Status`. At `minimal` the
story file is the only record of progress.
- `production/sprint-status.yaml`: this story's entry gets `status: in-progress` and
the top-level `updated` today. No file: say `Sprint status not updated:
production/sprint-status.yaml absent`.
Declined: change neither, print `Story not marked In Progress — declined` in the
summary, and go on.
## Phase 4: Who implements it
**Config/Data stories need no agent.** Show each value change as from → to, ask "May
I write [data file]?", and edit it. Go to Phase 6.
| Story | Programmer agent |
|---|---|
| Foundation layer, any type | `engine-programmer` |
| Type UI | `ui-programmer` |
| Type Visual/Feel | `gameplay-programmer` |
| Core or Feature, gameplay | `gameplay-programmer` |
| Core or Feature, AI or pathfinding | `ai-programmer` |
| Core or Feature, networking | `network-programmer` |
**The engine specialist, for every code story.** From the `specialists` line above:
`code` (`specialists.code`, the language specialist: it tells a Godot C# project
from a GDScript one, which `engine.name` cannot), plus `shader` when the story
touches shaders or materials and `ui` for a UI story; `primary` (the generic engine
specialist) when `code` is `unset`. Name no specialist from memory: the line is the project's
choice. Every value `unset` and no `primary`: say "No engine specialist configured"
and go on without one.
## Phase 5: Hand out the work
Hand the implementation to the programmer agent. The brief points at files; never
paste whole documents into it.
- **Objective:** implement [story path] against its acceptance criteria, within its
Out of Scope boundaries.
- **Sources:**
1. The story file. It carries the criteria, the boundaries and the QA test cases.
2. Each TR-ID (`TR-[system]-NNN`) to look up: in
`docs/architecture/tr-registry.yaml`, the lines matching `id: <TR-ID>\s*$`.
Its `requirement` text is the source of truth, not text quoted in the story.
3. The ADR guidance, **inline**: the story's ADR Decision Summary and
Implementation Notes. Never the ADR path.
4. The control manifest's [Layer] Layer Rules, by section.
5. `naming.*` in `project.yaml` and the `perf_budgets` line above.
6. The test file path from the story's Test Evidence section.
- **Tests,** by `qa.level`, for Logic and Integration stories:
- `minimal`: "Do not write a test file for this story — test evidence is waived at
`qa.level: minimal`." Say it: an omitted line and a forgotten one look the same
to the agent.
- `standard`: "Write the test at [path] with the code. Each acceptance criterion
needs at least one test function." Naming per engine from
`.claude/rules/test-standards.md`. No random seeds, no time-dependent
assertions, no external I/O.
- `full`: the same, and "every criterion must be covered: `/story-done` blocks on
any untested one."
- **Output:** the files created or changed, the test functions written, any
question or blocker, and whether it finished.
- **Boundaries:** write code only under the resolved code root and tests only under
the test root. Code root unresolved: write nothing and report it. Touch no file
outside the story's scope: report the need instead. Doc comments on public APIs.
An architectural choice the ADR does not cover: report the plan, don't decide it.
No helper or abstraction for a one-time use, no error handling for cases that
cannot happen (validate at input and data-file boundaries only), and general logic,
never a value special-cased so a test passes.
**Say what the brief leaves out, to both readers.** Inputs Phase 2 found absent
(the TR registry, an ADR, the manifest) are left out: tell the agent "No [input] is
passed — it does not exist at this tier; do not go looking for it", and tell the
user one line before handing out: `Briefing omits: [inputs] — implementing against
the acceptance criteria and the [GDD | brief].`
**Then hand the engine check to the specialist** once the programmer reports.
Brief: check the changed files for engine-API use that is wrong or changed in
[engine + version], against `docs/engine-reference/<engine>/`; return a list of
file, line, problem, fix; edit nothing. A problem that breaks a criterion goes back
to the programmer once; the rest go in the summary.
## Phase 6: Check the result
**Did the agent finish?** It stopped early, hit a limit, or its report ends
mid-task: the story is **INCOMPLETE**. Say so as the headline, list what exists,
name the breakage, offer to resume the agent. Never print "Implementation Complete"
over it, and leave the status as it is.
**Did it write what it said?** First verify the artifact: a file the agent's report
names that is not on disk is a failed step, however fluent the report: resume the agent and name the missing
file. A report of BLOCKED, an error, or "cannot complete": say so at once, go no
further past what it blocks, and give a partial report
(`.claude/docs/error-recovery-protocol.md`). Scope too large: split it with
`/create-stories`. The ADR and the story disagree: report the conflict; never guess.
**Is the test there?** At `qa.level: standard` and `full`, for a Logic or Integration
story, look for the file at the Test Evidence path. Missing: at the type's BLOCKING
level on the `gate_level` line, flag "story unverifiable — test required before
`/story-done`"; at ADVISORY, note it as a recommendation. At `minimal` there is
nothing to check: tests are waived.
**Does it parse?** Run the check whose exit code answers it, and report the
command, the exit code and any error lines:
- **Godot:** `godot --headless --path . --import` once, then
`godot --headless --path . -s res://.claude/scripts/godot-parse-check.gd -- res://<each changed .gd>`.
Exit 1 means a script did not load; its `PARSE FAIL:` line names it. Never use
`--check-only`, and `--import` alone is no parse check. The executable is the one
`commands.test` names when its file name says godot (a wrapper such as `bash
run-tests.sh` is not the editor), else `engine.path`, else `godot`. Never search the disk for
one: when none of the three runs, the check is not verified.
- **Unity, Unreal:** read `references/engine-checks.md`.
- **No engine executable:** `parse NOT VERIFIED — engine binary not available`.
Never infer from reading the code that it parses.
**Run it and look** (`.claude/docs/run-and-observe.md`). For every story a player
can see (every Visual/Feel and UI story, and any other with a surface): launch
straight into the scene the story touched, capture a screenshot straight into
`production/qa/evidence/[story-slug]/` (`[story-slug]` is the story file's name
without `.md`; `mkdir -p` the folder first), stamp it with
`bash .claude/scripts/review-receipts.sh hash <each changed file> > production/qa/evidence/[story-slug]/stamp.txt`,
then open the image and compare it with the criteria. Not waived at `qa.level:
minimal`. Report exactly one line, plain text starting `Run result:` (no bold:
`/story-done` reads it as written):
- `Run result: OBSERVED — <what was on screen>` with the retained path;
- `Run result: NOT VERIFIED — <reason>` (no engine, nothing captured); at the
default gate level this blocks a Visual/Feel or UI story at `/story-done`;
- `Run result: N/A — <reason>` for a story with nothing observable; the reason says
why nothing is (`/story-done` does not accept "it's a Logic story").
Only the feel half of a Visual/Feel criterion (timing, weight) is DEFERRED to
`/team-qa`: a still cannot show it.
This skill pre-approves `godot` on PATH. An engine run through `engine.path` or
the executable `commands.test` names asks first: the user approves it once, or
adds `Bash(<that executable> *)` to `.claude/settings.local.json`.
## Phase 7: Summary, files, checkpoint
```
## Implementation Complete: [title]
**Files changed**:
- `[code root path]` — created / modified ([what])
- `[test path]` — [N] test functions
**Test evidence**: waived at `qa.level: minimal` — no test was required or written (Logic/Integration at minimal, instead of the test line)
**Verification**: [command] — [exit code and result | parse NOT VERIFIED — <reason>]
**Acceptance criteria**:
- [x] [criterion] — [file:function | test name | OBSERVED in <image path>]
- [ ] [criterion] — DEFERRED: feel, needs a playtest
**Deviations**: [None | files outside scope, an accepted dependency or version risk]
**Engine risks**: [None | the specialist's findings]
**Blockers**: [None | each]
```
At `qa.level: standard` and `full`, add: "Run your test suite and confirm the new
tests pass before `/story-done`: it checks that the test file exists, and runs
nothing." At `minimal` leave that out: there are no tests to run. For a Visual/Feel
or UI story with no retained image, add: "A retained screenshot under
`production/qa/evidence/[story-slug]/` is required before `/story-done`" (and, for
Visual/Feel, the sign-off in `production/qa/evidence/[story-slug]-evidence.md`).
**`## Files`.** Replace the section's body with the list of changed code and test
files, one per line, as the Phase 3 ask said:
`bash .claude/scripts/md-section.sh replace <story> "Files"` with the list on stdin.
A story with no such section: add `## Files` and the list at its end, with a file
edit, never a shell command (a shell write skips the project's write checks). Skip
this when the Phase 3 ask was declined.
**The checkpoint**, on every outcome. Pipe the block to
`bash .claude/scripts/session-state.sh checkpoint`, which replaces the old one:
```
**Updated:** [date]
**Branch:** `[current git branch]`
**Current task:** /dev-story — [story path] ([title])
**Next step:** [the Next step below]
**Blocked on:** [nothing, or the blocker]
**Files in progress:** [changed files, comma-separated]
**Run result:** [OBSERVED — … | NOT VERIFIED — … | N/A — …, as in the closing block: /story-done reads it here]
**Open questions:** [none, or one line each]
```
**The run's last reply ends with this block**, the only place the `Run result:` line
is printed: print it nowhere else in the reply. The Next step goes by the tier you stated in Phase 1 (the `workflow` line,
never `rigor` or `qa.level`). The Next step: at `minimal`, `/story-done [path]`. At `standard` and `full`,
`/code-review [files]`, then `/story-done [path]`. INCOMPLETE: `/dev-story [path]`
to resume. BLOCKED: the action that unblocks it (`/architecture-decision accept
ADR-NNNN`, the dependency to finish, the diff to review).
```
Story: [path] — [Implemented | INCOMPLETE — <breakage> | BLOCKED — <reason>]
Run result: [OBSERVED — … | NOT VERIFIED — … | N/A — …]
Next step: [Implemented, Tier: workflow minimal → /story-done [path] | Implemented, Tier: workflow standard or full → /code-review [changed files], then /story-done [path] | INCOMPLETE → /dev-story [path] to resume | BLOCKED → the unblocking action]
```Shown under the source repo’s MIT license. Read the full folder on GitHub.