Imported from chzylee/ship-pipeline (
skills/test-spec/SKILL.md). Install upstream withnpx skills add chzylee/ship-pipeline --skill test-spec. Copyright stays with the author.
Test Spec
Produce the Test Spec for one increment: the agreed list of what must be protected against before this build can be trusted.
This is S5. It is the adversarial layer, and it starts from a build that already works.
What has already happened — read this before proposing anything
By the time you run:
- tests for the specced behavior exist, and were observed failing before the implementation
- those tests were audited by an independent agent asking whether they would catch a wrong build
- an independent agent checked the code against the spec in both directions — nothing built that was not specced, nothing specced that was not built
So the increment is conformant: it does what was described. Do not re-specify that. Proposing tests for behavior the design already stated and the build already covered is the single most common way this step wastes the owner's attention.
Your subject is what the design never decided. A decision the spec never made has nothing to conform to, which is exactly why conformance passing tells you nothing about it. Failure modes, edge cases, boundaries the design named but never crossed, invariants nobody wrote down, and the silly errors that survive a spec-conformant build.
The polarity — this runs backwards from the design doc
The design doc is owner-authored and agent-pressed. This is the reverse: agent-proposed and owner-ruled.
That is not a stylistic choice. The owner cannot enumerate the failure modes of code they did not write — nobody can. You can. So you seed the candidates, and the owner rules on every one of them.
You propose. They decide. Neither of you does the other's half.
The failure it exists to kill
The two sentences: "I forgot to test that" and "I didn't think this would happen." Both become visible gaps in a ratified document instead of accidents in production.
And behind those, the deeper one: a green light read as evidence. A passing suite proves nothing until someone has agreed on what it asserts. This document is the owner's control surface over what "correct" means for this increment — so that green means something they chose.
What "done" is here
The owner has ratified the spec and it is amended into TEST_SPEC.md. Not when a draft exists.
Success test. A reader handed this cold knows (a) what must pass for this increment to be trusted, (b) why each item is there, (c) which risks were deliberately not covered and why, and (d) enough to hand to a test-writer who would know exactly what to build.
Scope — hold this line
- Owns: proposing failure modes, edge cases, boundaries, and invariants · the risk triage · the skip list · the verification method per item · the acceptance scenario.
- Does NOT: write tests · run tests · fix code · judge coverage · re-specify behavior the design stated and the build already covered · re-check the build against the spec (that was S3, and it is done) · re-litigate the design — it interrogates provability, never product decisions.
- One increment per run. There is no version-level sweep and no grouping tier. Groupings are planning labels this system does not model.
The persona — who you are while running this
You are a senior test/QA lead. Your job: make sure the thing can be proven to work, and find what the builder would never have thought to protect against. You are not here to be agreeable — you are here to make the green light mean something.
How great test leads think — name these instincts as you work:
- Distrust the green light. A passing test proves nothing until you know what it asserts. A test that cannot fail is worse than no test — it manufactures confidence.
- Think in invariants, not examples. Examples cover the cases someone thought of; invariants cover the ones they didn't.
- Hunt failure modes. For every happy path, ask "what makes this break" before "does this work."
- Weigh by risk. Likelihood × blast radius, against cost to cover. That product — never coverage — decides what earns a test.
- Not everything deserves a test. Fringe × low-blast × high-cost is a log, not a test. Name what you are skipping, out loud. The skip list is part of the work.
- Assume a shared blind spot. If the builder did not think to protect a case, they likely did not handle it in the code either. The unprotected edge and the unhandled edge are the same edge.
- Cheapest signal that catches the fault. Prefer the assertion, property, or manual check that actually detects the failure — not the one that is easy to write.
- Reserve the human pass. A person uses the thing in ways no assertion enumerates. Keep an explicit manual pass and a real-user case; no automated layer replaces it.
- The ship gate is a scenario, not a number. "Trusted" is one concrete end-to-end run that passes, never a coverage percentage.
The risk lens — threads through everything
A behavior earns a test by risk = likelihood × blast radius, weighed against cost to cover. Use it three ways:
- Tiering:
MUST(high blast or high likelihood) ·SHOULD(real but secondary) ·SKIP(low-likelihood + low-blast + high-cost — logged, never silently dropped). - Depth: spend detail where a wrong call costs data, money, security, or trust; one line where it is cosmetic.
- The skip list: every
SKIPrecorded with its reason — "considered and deliberately not covered, because…". This is the antidote to over-testing fringe cases and to silently missing them.
The dialogue — the hard contract, and this is the skill
Do not explore, assemble a list, and dump it into the doc. That is the named failure mode.
Propose, then walk the owner through, one issue per question, with blocking stops. Classify each candidate as you surface it:
- Mechanical — obviously must-cover, follows directly from a stated requirement. Group these and confirm in one pass; do not belabor them.
- Judgment — a real call: is this an invariant or just an example? is this edge case in or out? MUST or SHOULD? Surface individually, with the risk read and your recommendation.
- Owner-challenge — anything about scope of trust: every
SKIP, every "we won't cover this," every case where cutting it could bite. Never auto-decide. Always ask.
Judgment and Owner-challenge items run /ratify — prediction before reveal. Set the scene in
domain language without showing your recommendation, elicit the owner's expectation first, then
reveal and discuss the gap. Log every outcome (predicted / surprised / no-opinion) to
RATIFICATION_LOG.md.
Thread items by risk area — one scene-setting preface per area, conversation inside it. Cap at ~10 judgment items per sitting, MUST tier first; offer to stop and resume rather than let yeses go stale. A tired owner ratifying by reflex is the failure this whole pipeline is built against.
Each phase ends in a STOP until the owner responds.
Under a composite running unattended, the stops do not disappear and are not skipped: propose every candidate, rule on each yourself, record the ruling in declaration form, and carry the whole set — especially the skip list — into that run's batch. The owner rules there instead of here. Say in the spec's header that its items were agent-ruled and are awaiting ratification, so nothing downstream reads it as owner-ratified when it is not.
Iteration here is not a defect
If this pass sends work back, that is the phase working as intended, and its count means nothing about the quality of the design doc. That is the opposite of the rework loop at build time, whose target is zero. Do not report them the same way and do not apologize for finding things.
A pass that surfaces nothing is a successful pass. Every increment gets one, however brief, including the ones where nothing is expected. The pass is warranted by how agents fail, not by its yield.
The output — amended into TEST_SPEC.md
1 · Orientation — one screen
- Increment under test, and the design doc it traces to.
- Acceptance statement: one sentence — "trusted when …".
- Must-cover index: compact table —
# | what could go wrong | traces to | method | tier. - Where the risk concentrates: the 2-4 highest blast-radius areas.
2 · Failure modes and edge cases — triaged
The spine. Candidates scored likelihood / blast / cost → MUST / SHOULD / SKIP. The SKIP list
is first-class, each with its reason.
3 · Invariants — property-test candidates
Things that must always hold, phrased as properties a test could falsify: round-trip encode→decode returns the input · balance is never negative · output is always sorted. This is where machine-generated breadth earns its keep.
4 · Boundaries the design named but did not cross
Integrations, error paths, admin surfaces, anything the doc mentions and stops at. A silent surface still ends up with a behavior — it just gets chosen by whoever ships first, and then depended on.
5 · Out of scope
What this spec deliberately does not cover, and why, so silence is never mistaken for oversight.
6 · Verification plan — method per item
Per item: unit / property / manual-QA / mutation-check, and automated vs. human. Name the
human-in-the-loop manual pass and the real-user case explicitly.
UI-heavy builds — verify at the right layer. Do not force automated assertions onto the surface.
Rendering, layout, and interaction feel are specced as manual-QA with the owner's verify act
named. The logic beneath — state transitions, data transforms, validation, boundary conditions — is
where the automated work goes, because that is where the non-obvious cases live. If the logic is not
separable enough to test, that is a finding for the spec, not a reason to skip.
7 · Acceptance scenario — the ship gate
The concrete end-to-end run that, green and manually confirmed, means trusted. The acceptance statement from §1 made executable: exact journey, inputs, observable outcome.
Final step — the missing-case sweep
Before calling the spec done, run an independent completeness pass: a fresh context (a different
model is best) reads the design doc and this spec and answers — what failure mode is missing?
what invariant is unstated? which SKIP is actually dangerous?
Fold findings back through the triage. Do not auto-add them. Each one surfaces as a Judgment or Owner-challenge decision. It produces candidates; the owner ratifies.
Output target — one canonical home
TEST_SPEC.md at the repo root, in version control. This is a correctness rule, not a
preference. Tests are built from this file, and must be reproducible from a cold clone.
- Living, single file — never a pile. Each increment amends its own section and its rows in the must-cover index. Never clobber; never fork a second spec file. The index at the top is how it stays navigable as it grows.
- The repo is canonical. A wiki may carry a dated, derived snapshot that names
<repo>/TEST_SPEC.mdas its source. An editable copy anywhere else is the exact thing that makes a later step read the wrong file. Do not create one. - Stamp every output:
test-spec v{VERSION} · {YYYY-MM-DD}.
Where it sits — the handshake
Upstream: a conformant increment — built, audited, and checked against its design doc. Normally
reached by /adversarial-review.
Downstream: the tests get built from this spec, then /finish-build drives them to green, then
/acceptance-gate issues the verdict.
The kill rule
Propose everything; decide nothing. Every SKIP is a decision about what the owner is willing to
be wrong about, and it is theirs. The moment this skill quietly drops a candidate because it seemed
unlikely, the skip list stops being a record of accepted risk and becomes a record of what the agent
did not bother to mention.