Fix release for 1.1.2: the 14 longest skills keep their later phases after a compaction, a question nobody answered is never taken as an answer, settings read the same from a subfolder, the commit check works for a project in a subfolder of a larger repository, every skill opens with a title, and the shipped allow rules no longer approve deleting a branch or writing files without asking. Details in CHANGELOG.md; upgrade steps in UPGRADING.md (v1.1.2 → v1.1.3). Refs #83 Refs #133 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0185en4t76fPn7315q4c3nko
14 KiB
/story-done — Phase 3: Verify Acceptance Criteria
Part of
/story-done.SKILL.mdnames when to read this file; the settings it resolved and the rules under its later headings still apply here.
Phase 3: Verify Acceptance Criteria
In this file:
- Automatic verification (run without asking)
- Manual verification with confirmation (use
AskUserQuestion) - Unverifiable (flag without blocking)
- Test-Criterion Traceability
- Test Evidence Requirement
For each acceptance criterion in the story, attempt verification using one of three methods:
Automatic verification (run without asking)
- File existence check:
Globfor files the story said would be created. - Test file check: if a test file path is mentioned, confirm the file exists
with
Glob. Do not run it — this skill establishes existence only (see the note under Test Evidence Requirement), so report "test file present", never "test passes". - No hardcoded values check:
Grepfor numeric literals in gameplay code paths that should be in config files. - No hardcoded strings check:
Grepfor player-facing strings in the code root (resolve per.claude/docs/code-root-resolution.md). If the code root is unresolved, reportNOT ASSESSED — code root unresolvedrather than zero hits. that should be in localization files. - Dependency check: if a criterion says "depends on X", check that X exists.
Manual verification with confirmation (use AskUserQuestion)
- Criteria about subjective qualities ("feels responsive", "animations play correctly")
- Criteria about gameplay behaviour ("player takes damage when...", "enemy responds to...") — these are asked, never marked verified from reading the code: the code says what should happen, not what the build does
- Performance criteria ("completes within Xms") — ask if profiled or accept as assumed
Batch up to 4 manual verification questions into a single AskUserQuestion call:
question: "Does [criterion]?"
options: "Yes — passes", "No — fails", "Not tested yet"
Map each answer: Yes — passes → confirmed; No — fails → FAILS;
Not tested yet → not evaluated — UNTESTED in the traceability table below, and
the verdict is NOT ASSESSED (Phase 6) until someone checks it. If the criterion
genuinely needs a full-build playtest, mark it DEFERRED — requires playtest session instead.
Unverifiable (flag without blocking)
- Criteria that require a full game build to test (end-to-end gameplay scenarios)
- Mark as:
DEFERRED — requires playtest session
Test-Criterion Traceability
After completing the pass/fail/deferred check above, map each acceptance criterion to the test that covers it:
For each acceptance criterion in the story:
-
Ask: is there a test — unit, integration, or confirmed manual playtest — that directly verifies this criterion?
- Unit test: check
tests/unit/for a test file or function name that matches the criterion's subject (useGlobandGrep) - Integration test: check
tests/integration/similarly - Manual confirmation: if the criterion was verified via
AskUserQuestionabove with a "Yes — passes" answer, count that as a manual test - Retained screenshot: if the criterion names something on screen and a
retained image under
production/qa/evidence/[story-slug]/shows it (theRun result: OBSERVEDfrom/dev-storyPhase 6 step 4), count that as covered — put the image path in the Test column. A visual criterion verified by looking is not UNTESTED; without this row every UI story reads as >50% untested and false-escalates. "Shows it" means you opened the image withReadand saw it; a checkpoint'sRun result: OBSERVED — <text>is/dev-story's description, to quote as such, not yours. Never describe an image you have not opened.
- Unit test: check
-
Produce a traceability table:
| Criterion | Test | Status |
|-----------|------|--------|
| AC-1: [criterion text] | tests/unit/test_foo.gd::test_bar | COVERED |
| AC-2: [criterion text] | Manual playtest confirmation | COVERED |
| AC-3: [criterion text] | production/qa/evidence/[slug]/01-shop-open.png | COVERED |
| AC-4: [criterion text] | — | UNTESTED |
-
Apply these escalation rules (skip entirely at
qa.level: minimal— no evidence is required, so untested criteria never escalate):- If >50% of criteria are UNTESTED: escalate to BLOCKING — test coverage is insufficient to confirm the story is actually done. The verdict in Phase 6 cannot be COMPLETE until coverage improves.
- If some (≤50%) criteria are UNTESTED: remain ADVISORY — does not block completion, but must appear in Completion Notes.
- If all criteria are COVERED: no action needed beyond including the table in the report.
-
For any ADVISORY untested criteria, add to the Completion Notes in Phase 7:
"Untested criteria: [AC-N list]. Recommend adding tests in a follow-up story."
Test Evidence Requirement
First apply qa.level (resolved in Phase 1). At minimal, no test
evidence is required — skip the Logic, Integration and Config/Data checks below.
But still run the Visual/Feel and UI check and the Run result: check: the look
is not waived at minimal, only the tests are. At standard, require evidence
for the story's own type. At full, require evidence for every story type. The
testing.strict resolution below applies to every check that runs.
Based on the Story Type extracted in Phase 2, check for required evidence.
Resolve the gate level for this story's type. A gate level is either
BLOCKING (a gap prevents the COMPLETE verdict in Phase 6) or ADVISORY (a gap is
noted in the Completion Notes but does not block). Resolve it from the
testing.strict block already resolved in the resolved-config block at the top of this skill — not by reading
project.yaml yourself:
- Map the Story Type to a
testing.strictkey — Logic→logic, Integration→integration, Visual/Feel→visual, UI→ui, Config/Data→config. Taketesting.strict.<key>from that resolved block. If its value istrue(case-insensitive) → BLOCKING; iffalse→ ADVISORY;unset→ fall through. - Else read
testing.strictas a plain boolean (legacy single-value form). If its value istrue→ BLOCKING orfalse→ ADVISORY, it applies to every type. - Else use the default in the table below.
Use that resolved block, never
project.yamldirectly.testing.strict.*is on the/settings --localwhitelist, so a developer can settesting.strict.logic=falseinproject.local.yamlfor fast WIP commits —effects-map.mdspecifies exactly this ("stricter dev's local/story-doneblocks earlier"). Readingproject.yamlalone silently ignores that file: the setting is accepted, displayed by/settings, and has no effect. Theresolve_configblock at the top of this skill already merges local over base.
Only true and false (case-insensitive) are recognized at steps 1–2. A key
that is present but holds any other value — maybe, 1, yes, etc. — is
treated as unset: continue to the next step, and surface the unrecognized value
to the user.
| Story Type | Required Evidence | Default Gate Level |
|---|---|---|
| Logic | Automated unit test in tests/unit/[system]/ — must exist and pass (this skill verifies existence; see the note below Phase 3) |
BLOCKING |
| Integration | Integration test in tests/integration/[system]/ OR playtest doc |
BLOCKING |
| Visual/Feel | Retained screenshot + sign-off in production/qa/evidence/ |
BLOCKING |
| UI | Retained screenshot of each screen touched, in production/qa/evidence/ |
BLOCKING |
| Config/Data | Smoke check pass report in production/qa/smoke-*.md |
ADVISORY |
The Default Gate Level column applies when testing.strict is unset (the
common case). When testing.strict is configured, the resolved value from
steps 1–2 overrides it. Visual/Feel and UI default to BLOCKING because for a
game the rendered result is the product; set testing.strict.visual or
testing.strict.ui to false for an advisory gate.
Exception —
/smoke-check. The ADVISORY default for Config/Data above governs per-story evidence gates, which is what this skill checks./smoke-checkis a build-health gate, not a per-story evidence gate, so its own unset default fortesting.strict.configis BLOCKING — see.claude/skills/smoke-check/SKILL.md§ "Resolve the gate enforcement level". The divergence is intentional; do not "fix" either side to match the other.
This phase checks that evidence EXISTS. It does not run anything. The
Default Gate Leveltable above, and.claude/docs/coding-standards.md, both say a Logic story's test "must exist and pass". The checks below establish only the first half — every one of them is aGlobor aGrep. A unit test that exists and fails, or that contains no assertions, satisfies them.Say which half you verified when you report. "Test file present at
<path>" is the honest claim; "tests pass" is not one this phase can make. Pass/fail is established by/gate-check(runs the suite at a phase gate) and/smoke-check(runs it before QA hand-off), both of which do execute.Unlike
/regression-suiteand/launch-checklist, which stop at existence and carry no generalBashpre-approval, this skill hasBashpre-approved — the limit here is the instruction, not the grant (a pre-approval never restricts). Running the story's own test before closing it is a live option; it is not enabled because it needs a configured runner and a decision about what a missing runner should mean.
For Logic stories: first read the story's Test Evidence section to extract the
exact required file path. Use Glob to check that exact path. If the exact path is not
found, also search tests/unit/[system]/ broadly (the file may have been placed at a
slightly different location). If no test file is found at either location:
- Flag at the resolved gate level: "Logic story has no unit test file. Story
requires it at
[exact-path-from-Test-Evidence-section]. Create and run the test before marking this story Complete."
For Integration stories: read the story's Test Evidence section for the exact
required path. Use Glob to check that exact path first, then search
tests/integration/[system]/ broadly, then check production/session-logs/ for a
playtest record referencing this story.
If none found: flag at the resolved gate level (same rule as Logic).
For Visual/Feel and UI stories: glob production/qa/evidence/ for a
retained screenshot for this story (*.png, *.jpg, *.gif) and for an
evidence doc referencing it. What closes the story follows the evidence table
in .claude/docs/coding-standards.md, the same at every qa.level:
- A UI story is satisfied by the retained screenshot of each screen it touched. No evidence doc or sign-off is required; note one if it exists.
- A Visual/Feel story also needs the sign-off: the evidence doc
(
production/qa/evidence/[story-slug]-evidence.md, from the test-evidence template) with every sign-off row[x] Approved.
Flag at the resolved gate level:
- No screenshot: "No visual evidence found. Capture a screenshot of each screen or effect this story touched and save it under
production/qa/evidence/before final closure." For Visual/Feel add: "then record sign-off inproduction/qa/evidence/[story-slug]-evidence.mdusing the test-evidence template." - An evidence doc but no screenshot retained beside it: "Evidence doc found at
[path]but no screenshot is retained. A described check is an assertion, not evidence — capture the screen and save the image underproduction/qa/evidence/before final closure." - Visual/Feel with a screenshot but no evidence doc: "Screenshot found but no sign-off is recorded. Create
production/qa/evidence/[story-slug]-evidence.mdusing the test-evidence template and obtain sign-off before final closure." - Visual/Feel sign-off rows still unchecked — grep the doc for
| .* | .* | .* | \[ \] Approved: "Evidence file found at[path]but [N] sign-off(s) are still pending (shown as[ ] Approvedin the sign-off table). Obtain required sign-offs before final closure. Note: for solo developers, all roles may be signed off by the same person."
Otherwise note "Retained screenshot found[, sign-offs complete] — gate satisfied."
The retained image is the Run result: OBSERVED from /dev-story Phase 6
step 4 (.claude/docs/run-and-observe.md); its absence means the run was
NOT VERIFIED or never happened, and the flag above is the consequence. The
run is not waived at qa.level: minimal.
For every other story type, read the Run result: line from the
/dev-story checkpoint in production/session-state/active.md — only when its
Current task names this story; a later /dev-story overwrites it — or from
the story's ## Completion Notes. OBSERVED with a retained path: note
it. N/A — <reason>: accept only if the reason names why nothing is
observable — "it's a Logic story" is not a reason. NOT VERIFIED — <reason>
on a story whose acceptance criteria name anything on screen: flag at the
resolved gate level for the story's type. No Run result: line at all: flag
as ADVISORY — "the implementation summary carries no run result; confirm the
build was launched and looked at before closure." The exception: when the
acceptance criteria name something on screen and no retained image for this
story is under production/qa/evidence/, nothing observed the story — a user's
Yes — passes to such a criterion is an assertion, not the retained observation
.claude/docs/run-and-observe.md requires — so flag it at the resolved gate
level for the story's type, as for NOT VERIFIED.
For Config/Data stories: check for any production/qa/smoke-*.md file.
If none: flag at the resolved gate level — "No smoke check report found. Run /smoke-check."
If no Story Type is set: flag as ADVISORY —
"Story Type not declared. Add Type: [Logic|Integration|Visual/Feel|UI|Config/Data]
to the story header to enable test evidence gate enforcement in future stories."
Any BLOCKING test evidence gap prevents the COMPLETE verdict in Phase 6.