# Behavioral Checklist (docs/tests/{feature}.md)

Truth document. Use for: **how to verify one feature's promised behavior by driving the real product** — the browser, the CLI binary, HTTP against the running service. One file per feature, named for the feature doc that owns the code (`docs/features/auth.md` → `docs/tests/auth.md`).

**Frontmatter is two keys, and `verifies:` is the load-bearing one:**

```yaml
---
type: checklist                            # required tooling key — genre stamp
verifies: [docs/features/auth.md]          # required — the doc(s) whose promises this proves
---
```

A checklist owns no code, so it carries no `owns:`. What it carries instead is the edge back to
its subject. **The filename convention is not the edge** — a same-named file is a coincidence a
resolver cannot trust, it breaks the moment a checklist spans two docs or a feature is renamed, and
it leaves nothing to implicate this file when its subject changes. Without `verifies:` a checklist
is unreachable: no change reaches it, so it goes stale and the staleness is undetectable. Several
values are correct where one checklist proves promises two docs decide; the inverse — which
checklists cover a feature — is generated, never listed in the feature doc.

**This is the driving script, and it is the source the suite is drawn from.** At the verification gate it does double duty: rung 1 drives the real surface *against these steps*, and rung 2 pins the durable subset *of these assertions* as suite tests. Writing it is therefore not a report written afterwards — it is authored or updated **before** the driving, so the gate has something to execute and the tests have something to be derived from. Which assertions earn a permanent test is decided by the `testing` skill (gate mode) § What earns a suite test, and each section records the answer on its `Pinned:` line.

A checklist outlives the tests it seeds. Where a Playwright script breaks on every selector change, a checklist states the behavior and lets the implementation move under it — so it is written in the product's own vocabulary (labels, URLs, rows) and never in the code's (function names, props, internal state).

Required: **Conventions**, at least one numbered behavior section, **Failure Report**. Optional: Test Inputs, Wait Strategy, Driving notes — include only when they carry real content.

## Writing the assertions

- **A section is one behavior, its heading stated as a fact** — `3. A stale pointer falls back to the sole membership`, not `3. Test the resolver`. The heading is the promise; the steps are how you see it.
- **A step is an action; an `**Assert**` is the observable it must produce.** Interleave them in the order a person meets them, so the file reads as a session rather than a spec.
- **Assert what is observable, at the altitude a user meets it** — a visible label, a URL, a toast, a row in the database, a response envelope. Never a private function's return value: that is a unit test's job, and stating it here couples the checklist to code that will move.
- **Prefer exact literals** — the real prompt text, the real button label, the real count. A vague assertion passes a broken product.
- **Assert persistence separately from display.** "It appeared on screen" and "it was written" are two claims; a surface that streams unsaved content satisfies the first while failing the second.
- **State each section's trace.** `Pinned:` names the suite test that now guards it, or says `driven only` **with the reason** — an unmarked section is indistinguishable from an unexamined one.
- **State each section's `AC:` keys.** Where the repo carries a keyed acceptance-criteria source, each section names the keys it proves, so the chain from criterion to checklist to suite test survives the last hop; the gate reports coverage and backlogs uncovered keys. `none — not AC-mapped` is the honest line where no keyed source exists.
- **A known-failing assertion stays written as the contract.** A checklist states what the feature promises; the *run* records the failure and the backlog carries the defect. Never soften an assertion to make a run go green.
- **No section carries a date.** This file is a truth document: it states what must be true and what guards it, and the *event* of driving — the date, the environment, the evidence the assertion turned on, a `FAIL` and the backlog item it raised — is recorded in the changelog entry of the unit that drove it, where a dated line belongs. What separates a promise somebody verified from a promise somebody typed is the `Pinned:` trace, and it is stronger than a stamp because it re-checks itself on every suite run. A section a drive could not reach says `not reachable — {what blocks it}` on its trace line, in the present tense, and keeps saying it until the blocker is gone.
- **The file is maintained by whoever drives it.** Nothing else watches it — a checklist owns no code, so no ownership glob implicates it and no hook fires when the surface underneath it changes. Retiring dead sections and repairing traces is part of running the gate, not a separate cleanup someone schedules.

```
# {Feature} Test Plan

{What this covers, and what neighbouring checklists cover instead, with links. Close with:}
Each step is an action; each **Assert** is the observable it must produce.

## Test Inputs

- **URL / entry point** — where the run starts
- **Identity** — the account or role required, and how it is obtained
- **State** — the fixture or seeded state the run assumes, stated as counts a reader can check

## Conventions

- {The invariants a reader needs to interpret the assertions — the vocabulary, the scoping rule,
  the guarantee that makes a surprising assertion correct. Not a restatement of the LLD.}

---

### 1. {The behavior, stated as a fact}

- [ ] {action}
- [ ] **Assert**: {the observable, with exact literals}
- [ ] **Assert**: {the persistence claim, where the behavior writes something}

> Pinned: `{tests/path/to/file.test.ts}` — or `driven only — {why no durable test is bought}` — or `none yet — {the test this section owes}`
> AC: {the acceptance-criteria keys this section proves} — or `none — not AC-mapped`

### 2. {The next behavior}

- [ ] {action}
- [ ] **Assert**: {observable}

> Pinned: {…}

---

## Wait Strategy

State-based signals, never fixed sleeps:

- **{Milestone}** — {the signal that proves it, as a selector or an observable}

Timeouts: {operation} **{n}s**.

## Driving notes

- {Hard-won facts about driving this surface that cost someone an hour — an environment trap, a
  harness quirk, a signal that lies. Not general advice.}

## Failure Report

For each failed step, capture:

- Section and step name, and the expected signal
- The URL or command at the time of failure
- The state assertion's actual value, where the step checks one
- Any visible error, toast, or console exception
- A screenshot or transcript
```
