# Testing & Verification

**When** work is proven — the standing constraints that bind on every turn. The full lifecycle procedure — deriving a plan's Test Obligations, placing tests in strata, executing the verification gate — and the framework per stack are the `testing` skill, invoked at those moments; this rule never restates them.

## Two regimes

**Deferral is a property of plans, not of code.** A plan carries a Test Obligations ledger and a gate that discharges it; nothing else does, so nothing else may defer.

| Regime | During the work | Proven |
| --- | --- | --- |
| **Plan-driven build** | No new test files. Code only — the loop stays fast | At the verification gate, before `Status: Complete` |
| **Bug fix** — a defect in shipped behavior | Fix it, drive it, then write its regression test — in the same unit of work | Before the unit ends — the pin is written once, against the fixed code |
| **Ad-hoc / non-plan change** | Make the change, drive it, then write its test **when it alters promised behavior** — layout, copy, and config tweaks owe none | Before the unit ends, same discipline as a bug fix |

- **A bug fix cannot defer past its own unit of work — but its pin comes after the fix is driven, never beside the first attempt.** A test written while the fix is still moving is rewritten on every iteration, and written by the fix's author beside the fix it proves only that the two agree. Write it once, against the fixed code; where the defect was subtle, run it once against the pre-fix code (stash the fix) so the pin is shown to bite — that is one command, not an iteration.
- **Ad-hoc work cannot defer — there is no ledger to defer into.** A non-plan change that alters no promised behavior — a pure refactor, a rename — owes no test.

## During the loop

Rules for the plan regime between the plan gate and the verification gate:

- **No new test files** — obligations wait in the plan's ledger (written at the plan gate by the `testing` skill).
- **Existing tests broken by the plan's intended behavior change are updated to the new contract before any mid-plan commit** — the shared branch never carries a red suite, and an update is never a weakening (§ Discipline).
- **A defect in the plan's own in-flux code is the loop iterating** — no test, no ledger row. **A pre-existing bug in shipped behavior is the bug-fix regime even mid-plan**: fix it now and pin it at the build pass's pin step, with the unit's other pins — or send it to the backlog via Out-of-Scope Discovery. Never fix silently.
- **The ledger is amendable while the plan is open** — the gate discharges whatever it holds at gate time.
- **A mid-plan commit is a checkpoint, not a verification claim.**

## The gate

**The tail of every build pass, never a separate ask.** When the unit's code is written the gate runs — drive the real surface, fix what driving finds, pin, discharge — and the session does not stop to ask whether it should. What the user decides is the freeze and the commit: "ship it", "commit", "looks good" close a plan whose gate has already run; they never open one. Never flip `Status: Complete` without the gate. A plan with outstanding obligations stays open, whatever the code does.

- **Inside the build pass the order is fixed — code → drive → fix → pin.** Tests written while the code is still moving are rewritten on every iteration, and written by the code's author beside the code they pass by construction; a judged drive is what finds the defects (below). Pinning is the last step and happens once, against code the drive has already shown to be right. The procedure — checklist, drive-then-pin ladder, row discharge, what earns a durable test — is the `testing` skill (gate mode).

- **Precondition: the ledger exists.** A plan with no Test Obligations section is blocked at the gate — run the `testing` skill (plan-gate mode) on it first; an empty ledger passes vacuously and the gap surfaces only after the file is frozen. A plan touching a layer a formal model binds (`docs/modeling/{system}/bindings/`) carries a **Model Obligations** ledger as well, discharged in the same pass with the same outcomes.
- **The verdict is never delegated** (the root policy file § Delegation), and where browser work is main-session-only (its § Runtime Notes), the driving is too. **The pins are written inline, by the agent that drove the surface** — the drive is what tells it which behaviors earned a test and what each one must assert, and a sub-agent briefed from the ledger alone holds neither. Nothing in the gate is dispatched: the checklist, the driving, the pinning, the cold-shell suite run and every discharge stay with one agent, so the state of the gate is never somewhere nobody is looking.
- **The gate needs no stronger tier than the session default** — a standard-tier session suffices whatever tier built the plan; the plan and its ledger carry all the state the gate needs, so gating in a fresh session loses nothing.
- **Report what happened, including failures.** A gate that reports green while a suite is red is worse than no gate; state the failing test and its output.
- **`cmd > log; echo "EXIT=$?"` reports the echo's status, never the command's** — a SIGTERM'd suite surfaces as "exit code 0", which is the worst direction for a gate to be wrong in. Read the real status from the log, or drop the trailing echo. Adjacent trap: a runner that never emits a summary is a dying **worker**, not a slow test — `--testTimeout` bounds neither, so bound the run and read the log.
- **Abandonment does not erase debt.** Before `Status: Abandoned` freezes a plan whose code was committed, convert its undischarged rows into backlog items; uncommitted code owes nothing.

## Frameworks and conventions

The framework per stack, the suite layout and the mocking rules are the `testing` skill's `references/frameworks.md`: detect the stack, use its row, and never introduce a second runner beside the repo's own.

## Discipline

- **Never weaken, skip, or delete a test to mask an unexplained failure.** When a plan or LLD intentionally changes behavior, updating the affected tests to the new contract is part of that change.
- **A behavior that is compiler-guarded owes no test** — if it cannot ship green-but-broken, a test adds cost and no safety.
- **Throwaway checks are not tests.** Temporary scripts written to drive a change live in scratch space and are deleted — never committed into the test tree.
