## Classifying the failure first

Per **Match the Form to the Failure**, I classify before writing anything:

The subagents already *produce* review reports. They're not skipping the review or violating a rule under pressure — they're **omitting a required element from something they already produce**. The commit range is a missing slot in their output.

| Candidate form | Fit |
|---|---|
| Prohibition + rationalization table + red flags | ✗ Wrong — that's for discipline failures (knows rule, skips under pressure). Prohibitions backfire on shaping/omission problems. |
| Positive recipe / contract | ~ Close, but heavier than needed |
| **Structural: REQUIRED field/slot in the template they fill in** | ✓ **This is an omission of a required element. The fix is a required slot in the report template.** |

So: do **not** add "always remember to include the commit range" prose. Add a **required structural slot** to the report template the subagents fill in, keyed to data they can observe.

## (1) The exact change

In the code-review skill, locate the report template/output contract the subagents fill in. Add a **required header field as the first element of every report**, populated from an observable command rather than memory:

```markdown
## Review Report Format

Every review report MUST begin with this header before any findings:

**Reviewed range:** `<BASE>..<HEAD>`  (`<short-sha-base>..<short-sha-head>`)
**HEAD commit:** `<full-sha>` — `<subject line>`

Obtain these values directly — do not reconstruct from memory:

    git rev-parse HEAD                      # HEAD sha
    git log -1 --format='%h %s' HEAD        # short sha + subject
    git rev-list --count <BASE>..HEAD       # commits in range (sanity check)

The range is the slot, not a sentence. A report whose first line is not a
populated **Reviewed range:** header is incomplete and must not be returned.
```

Key properties this gives us, straight from the writing-skills guidance:
- **Structural, not prose** — the range is a field in the template, so it can't be "forgotten" without leaving an obviously empty/missing slot.
- **Keyed to an observable predicate** — values come from `git rev-parse`/`git log`, not the agent's recollection of what it reviewed. This is what closes the stale-push gap: the report states the *actual* HEAD it ran against.
- **No nuance/exemption clauses** — no "include the range when relevant." It's always the first element.

I would **not** also sprinkle reminders elsewhere ("Prose reminders near the template" is the listed *wrong* form for omission).

## (2) Verification plan before deploying

This is an **edit to an existing skill**, so the Iron Law applies: **no skill change without a failing test first.** I won't deploy on the strength of it looking obviously correct.

**RED — establish baseline (reproduce the failure without my change):**
1. Run the *current* skill through 5+ fresh-context subagents, each given a repo and a code-review task, including cases where HEAD advanced (a newer push exists than what was reviewed).
2. **No-guidance control built in:** this baseline IS the control. Confirm the reports actually omit the commit range / fail to identify which HEAD they reviewed. If they already include it reliably, there's nothing to fix — stop.
3. Record verbatim what the header/top of each report looks like.

**Micro-test the wording before full scenarios** (cheap iteration before slow runs):
4. 5+ reps, system prompt = the *full edited skill* (not the snippet in isolation), user message = a review task on a repo where HEAD has moved.
5. Manually read every report header — don't trust a grep for "Reviewed range", since template echoes and quoted instructions masquerade as hits.
6. **Variance is the metric:** all 5 reps should converge on a populated `BASE..HEAD` header with a real SHA. Five different interpretations = the slot isn't binding; tighten the form before adding words.

**GREEN — confirm the fix:**
7. Re-run the original baseline scenarios *with* the edited skill. Pass criteria: every report's first line is a populated `Reviewed range:` header with a SHA matching the actual repo HEAD at review time.
8. **Critical stale-push check:** in a scenario where I advance HEAD after dispatching, verify the reported SHA matches the HEAD the subagent actually reviewed — proving the field reflects observed git state, not an assumption. This is the controller's whole problem, so it's the gate.

**REFACTOR — close loopholes:**
9. Watch for new failure shapes: range present but as a vague sentence instead of `BASE..HEAD`; SHA pulled from the prompt rather than `git rev-parse`; header buried below findings. Tighten the structural requirement (e.g., enforce `BASE..HEAD` format, mandate the git command) until reps are consistent.

**Deploy only after** the stale-push scenario passes consistently across reps — then commit and push.