# Running a court round

> **Audience:** anyone convening, chairing, or answering an adversarial review
> round in a federation repo — human maintainer or AI agent.
> **The binding rules are [`../standards/REVIEW.md`](../standards/REVIEW.md)
> (CRT-01..12).** This recipe is the *operational* layer: how to convene one, what
> to put in the prompt, how the fold loop runs, and what three real rounds proved.
>
> **This is review-time material** (CRT-12). Nothing in this file is a MUST. The
> prompt shapes here are patterns to adapt, not text to conform to.

---

## 1. When to convene one

A court round costs a fresh context and a real chair. Spend it where a wrong
CONCUR is expensive (**CRT-11**):

| Convene | Chair |
|---|---|
| A release — a tag, an index publish | scarce |
| A merge to the default branch that opens a gate | scarce |
| A wave / phase close | scarce |
| A first published surface (new library, new public API) | scarce |
| A change to a [`LOCK.md`](../../LOCK.md)-locked surface | scarce |
| An interior slice inside an open wave | cheap seat |
| A doc pass, a mechanical currency bump | cheap seat, or none |

Do **not** convene a round on a dirty tree "just to get early signal" — there is
no such thing (**CRT-02**). Commit first, or the round reviews a state that will
never exist again.

## 2. The convening prompt

A round's prompt has four parts. Parts 1–3 are per-round; part 4 is the
**standing prompt** the project maintains once and reuses.

### 2.1 The pin and the brief

```
PIN: <full SHA>        (fresh worktree: git worktree add ../<repo>-court <SHA>)
TREE: clean — `git status --porcelain` at the pin is empty
RISKIEST CHANGE: <one paragraph, the implementer's own honest answer>
RE-EXECUTE:
  <exact gate commands, unpiped, exit codes read>
  <exact repro steps for the risky change, with seeds/fixtures named>
HONEST GAPS: <what the implementer knows is untested or unproven>
```

The **riskiest change** line is load-bearing and is the implementer's job, not
the chair's. A brief that says "please review the diff" wastes the round: the
chair spends its context rediscovering what the implementer already knew was
thin. The `HONEST GAPS` line is the same discipline pointed inward — in the
`hrb-app` arc one brief carried *"the three new panes ship without
pane-specific behavior tests — the court must enumerate the required pins as
findings"*, and the round returned exactly that list, in one pass.

### 2.2 The independence clause

```
You are a FRESH-CONTEXT reviewer (CRT-01). You did not write this and hold no
memory of it. Do not edit the target — return findings only. Verify by
EXECUTION at the pin wherever you can; rank your evidence (CRT-04); name every
leg you could not run in `could_not_verify` — that list is REWARDED, never
penalized (CRT-05).
```

### 2.3 The return contract

```
Return: findings in the CRT-03 grammar (one object per finding; validate
against templates/findings-schema.json), plus EXACTLY ONE holistic verdict.
"reviewed; sound" is a first-class outcome — if that is your verdict, list the
attacks you ran and what each produced (CRT-09).
```

### 2.4 The standing prompt — the project's own attack list

The per-project half. Write it once, keep it in the repo, cite it from every
round. The reference implementation is `hrb-app`'s
`docs/gui/GUI_ACCEPTANCE.md` — a 35-line file that has been in production use
across a dozen rounds. Its shape generalizes to three sections:

**(a) A done-definition** — what "this phase is finished" means, in gates:

> A wave/phase is **done** only when: (1) the four gates are green, run
> **unpiped** with exit codes read explicitly; (2) the conformance rows the
> phase touched moved with the change; (3) a fresh-context court round returns
> `reviewed; sound` or its findings are folded.

**(b) The attack lines** — the heart of it. Each line names **a claim your
surface makes that its consumer cannot independently check.** To derive your
own, walk your surface and ask of each assertion: *how could this be shown
while being false?* That question, answered honestly, is the attack line.

`hrb-app`'s nine, for a Qt operator console driving a hardware broker:

> Attack the phase for — an **unverifiable claim** (a screen asserting what the
> operator cannot check) · a **frozen-UI path** (any sync SDK call or unbounded
> wait reachable from the GUI thread) · an **abstention shown as error**
> (degraded/unknown rendered in the error band) · a **dead control**
> (clickable, wired to nothing — drive the REAL widget, assert observable
> change) · a **silent stale view** (a data path whose death the operator can't
> see) · a **leaked internal** (`repr(exc)` in a dialog, raw hex, off-brand
> chrome) · a **teardown crash** (close during in-flight work; second `main()`
> in one process) · a **keyboard trap or unreachable operation** · a
> **misleading confirm** (default-focused destructive affirmative, "Yes"
> buttons, un-named actions).

Those nine are right for *that* surface. Translations for other shapes:

| Shape | Attack lines that tend to earn their keep |
|---|---|
| **Published library** | a claim in `PUBLIC_API.md` the wheel does not export · a version pin whose floor is below the API actually used · a deprecation shim with no test · an exception that escapes the documented type · a doc example that does not run |
| **Web console / API** | an endpoint whose error body leaks internals · a page tested only through `TestClient` (never rendered in a browser) · an auth check on the happy path only · a timeout absent on an outbound call · a migration with no down-path |
| **CLI** | an exit code that does not match the documented table · an error message naming a flag/variable that does not exist · a `--json` envelope that differs from the human output's facts · a destructive verb with no confirm |
| **Any test suite** | a test whose assertion cannot fail (**decorative**) · a bound satisfiable by any behaviour (**vacuous**) · a mock of the system under test · a gate whose exit code is read through a pipe |

**(c) A scope-honesty rule** — the standing-prompt half of CRT-05:

> Every phase's verification report states its limits explicitly: what was
> driven programmatically vs by a human cursor; what was judged from
> screenshots. Overclaims are corrected in the record, not smoothed over.

## 3. The fold loop

```
   findings (by id)
        │
        ▼
   ┌─────────────────────────────────────────────┐
   │ For EACH finding id, in order (CRT-07):     │
   │   fix  ·  decline-with-rationale  ·  partial│
   │   (stated as partial). Never silence.       │
   └─────────────────────────────────────────────┘
        │
        ▼  for every id that lands a regression pin
   ┌─────────────────────────────────────────────┐
   │ SEEDED SABOTAGE (CRT-08)                    │
   │  1. pin green with the fix                  │
   │  2. seed the defect back out → pin RED  ◀── the proof
   │  3. restore the fix         → pin GREEN     │
   │  4. record 2–3 in the fold report, by id    │
   └─────────────────────────────────────────────┘
        │
        ▼
   ┌─────────────────────────────────────────────┐
   │ RECONCILE (CRT-10): each finding to exactly │
   │ ONE durable home — tracker row / FEEDBACK   │
   │ paper / conformance ledger row /            │
   │ LESSON_LEARNED / the code + its pin.        │
   └─────────────────────────────────────────────┘
        │
        ▼
   fold report, keyed by id → round closes
```

Three things go wrong here often enough to name:

- **Answering the easy ids and summarising the rest.** "F3 and F5 were minor
  and are addressed by the refactor" is silence wearing a sentence. Each id
  gets its own line and its own `done_when` result.
- **Keeping the court's plants as the only pins.** The court's repro is the
  *starting* pin, not the finished one — it proves the defect existed. Ask
  whether it also catches the *next* variant, and widen it if not.
- **Skipping step 2 because "obviously it would fail."** That intuition is
  wrong often enough that the whole rule exists. Run it.

If a fold has to reach for something outside the implementer's lane — a
governance call, a standards change, a sibling library fix — that finding is
`lane=founder` and is escalated, not self-enacted (**CRT-06**), or filed as a
`FEEDBACK.md` paper in the target repo (AG-11/AG-13).

## 4. Three rounds that earned the protocol

All three are from the `hrb-app` v0.2.0 release arc (2026-07-29/30, **14 merged
PRs, #10–#23**; PR #24 carried the ledger row and merged 2026-07-30). Ten rounds
in that arc are enumerable by name; verdicts and folds are in that project's
`docs/inprogress/RELEASE_MASTERPLAN.md` §5 append-only ledger.

### 4.1 C-1 — the false broker death (what CONFIRMED buys)

**Round:** W2 pass-1 wave court, scarce chair, 2026-07-29.
**Finding:** `C-1`, severity HIGH, verdict **CONFIRMED**.

The GUI tracked broker health with a **single global failure counter**. Any
verb that failed incremented it; past a threshold the UI declared the broker
dead. The chair's attack line was *silent stale view / unverifiable claim*: the
screen asserts "the broker is down," and the operator cannot check it. The
failure scenario the chair wrote out: **one** broken verb — against a perfectly
healthy broker — drives the counter to threshold, the UI reports the broker
dead, and the retry path then re-arms against a broker it has already
declared gone, producing a livelock.

The chair did not stop at reading it. It **reproduced** the sequence, which is
what let the finding ship as `CONFIRMED` with `VERIFIED_BY_EXECUTION` rather
than `PLAUSIBLE`/`VERIFIED_AGAINST_CODE`. That distinction decided the wave:
the merge was gated on the fold, which is a proportionate response to a
reproduced HIGH and an over-reaction to a plausible one.

That **same 2026-07-29 W2 pass-1 round** returned five more findings alongside
C-1 — including **C-2**, a shutdown-budget test whose bound was ~33.5 s while
the doc claimed ~1.5 s: a **vacuous** bound, satisfiable by any behaviour the
code could have had.

**The tail is the interesting part.** C-1's fix introduced a quorum — declare
the broker down only when ≥2 poll loops are down. A *later, separate* round
(W3 pass-3, finding F4) attacked that fix and found the quorum still wrong: two
Part-13 auxiliary loops could reach quorum alone and condemn a healthy broker.
The ruling tightened it to require **≥1 core loop** among the failures.

**What it proved:** `CONFIRMED` vs `PLAUSIBLE` (**CRT-03**) is not bookkeeping
— it sizes the response. And a fix that closes a court finding is itself a
claim, and gets courted: SEAT-07 puts the scarce tier in the chair for
gate-opening rounds (**CRT-11**), *including* the wave that only fixes things.

### 4.2 F2 — the vanished device (what a wholesale attack buys)

**Round:** W3 final wave court, 2026-07-30.
**Finding:** `F2`, severity **HIGH**, category **product honesty**.

> *Recorded as "MEDIUM-HIGH" in the ledger.* That round predates CRT-03, and
> the scale it used is **not conformant** with the grammar's four values —
> `CRITICAL` / `HIGH` / `MEDIUM` / `LOW` admit no hyphenated midpoints. Stated
> plainly, because it is the honest shape of this whole family: **the grammar
> was codified cleaner than the practice that proved it.** The historical
> record is not rewritten; it is cited as-was and mapped into the enum here.

The chair was convened against a wave that had touched the brand, the panes,
and the a11y floor. It attacked *wholesale* — running the standing prompt's
attack lines across the whole surface rather than reviewing the diff — and hit
this: when a broker poll dropped the currently-inspected device URN, the
Inspect pane went on rendering the **previous** detail as current. A device
that had been unplugged still showed as present, with live-looking fields.

The pane's own empty-state copy disclaimed exactly this dishonesty. Nothing in
the change under review had caused it; the defect predated the wave and would
have survived any diff-scoped review indefinitely.

The fold: the controller now tracks the inspected URN, clears the model on the
pass that drops it, and emits `inspected_vanished`; the window rewords the
emptiness ("… no longer listed by the broker — it was unplugged or released …")
**distinctly** from nothing-selected, so the two are not conflated. Pinned
end-to-end (real tree selection → inspect task → vanish) *and* on the
not-sticky half (re-inspect swaps detail back; a second vanish rewords for the
new URN).

The same round's `F1` is the companion lesson: `test_tab_order_sane` walked
`nextInFocusChain()`, which enumerates the raw circular list — so a
keyboard-dead (`NoFocus`) table **passed**, and a real keyboard trap could
never have failed it. A **decorative** test: green, and structurally incapable
of catching its own subject. The fold rebuilt it as a real reachability walk
filtered on `focusPolicy() & TabFocus` over a shown, populated window, and kept
the court's sabotage as a permanent pin
(`test_the_tab_order_pin_catches_a_keyboard_dead_table`) — seeded red, restored
green (**CRT-08**).

**What it proved:** the standing attack list (§2.4) finds defects **the diff
does not contain**. A review scoped to the change under review cannot, by
construction, find a pre-existing lie; a review scoped to the *claims the
surface makes* can.

### 4.3 F1 — the misattributed config error (what the last round buys)

**Round:** the **final** round of the arc, after the plan was already
ready to declare closed. 2026-07-30, fold in `f94011b`.
**Finding:** `F1`, the user-facing error seam.

An unknown key in the config file — an operator typo, or a field from a future
version — produced an error message telling the operator to **fix an
environment variable that does not exist**. The mechanism: an unknown key
cannot arrive from the environment at all (there is no model field for
`HRB_APP_*` to bind to), but the heal probe's partial-write overlay *refuses*
unknown names, and that refusal was being misread as "the file cannot fix
this" — so the classifier fell through to the environment branch and named a
variable that was never involved. Worse, the unknown key **masked sibling
classification**: a file with a typo *and* a real problem reported only the
wrong one.

The fold short-circuits unknown keys to the file-oriented error, stops the
masking (the mixed case keeps file remediation first; the next load classifies
cleanly), and carries **the court's exact repro as two regression pins**. The
same round's `F2` caught a second invisible defect in the same commit range:
the `mgf-common` floor still admitted `0.50`, under which the
`save_settings(fields=…)` call the new code depends on is a `TypeError` that
escapes *both* persist guards — a dependency-contract defect that no test in a
dev environment holding `0.51` could ever surface.

**What it proved:** the marginal round is not wasted. Eleven rounds in, on the
release cut, the round still found (a) a user-facing message that blames the
operator for the wrong thing and (b) a pin floor that is a latent crash for
anyone installing at the low end. Error seams and dependency floors are the two
surfaces with the least test pressure per line — attack them last, not never.

## 5. Failure modes of the round itself

| Symptom | What it actually is | Fix |
|---|---|---|
| Chair returns "looks good" with no attack list | Indistinguishable from not looking | CRT-09: no kill attempts, no verdict |
| `could_not_verify` is empty | Almost always false | CRT-05: require the justification line |
| Every finding is `VERIFIED_BY_EXECUTION` | Levels are being used as praise, not measurement | CRT-04: re-derive; readings are readings |
| Findings are prose in chat | No hash, not a descendant of the pin, drifts on transcription | CRT-03: findings are a file, in the grammar |
| Round run on the working tree | Reviewed a state that no longer exists | CRT-02: commit, then pin |
| Fold summarises instead of answering ids | The round is open and looks closed | CRT-07: one line per id |
| Pin added, sabotage skipped | False confidence, worse than no pin | CRT-08: seed it out, see it red |
| The chair fixes what it finds | Nobody left to review the fix | CRT-01: findings only |

## 6. Paste-able round header

The field names below are the schema's own tokens, spelled exactly as
`templates/findings-schema.json` requires them — so the header and the
`findings.json` beside it never disagree about what a thing is called.

```markdown
## Court round <round> — <what is under review>

- **`pin`:** `<full SHA>` (worktree `../<repo>-court`, `git status --porcelain` empty)
- **`chair`:** <seat / model>, fresh context, no prior involvement (CRT-01)
- **`standing_prompt`:** <path to the project's attack list>
- **Gates re-executed at the pin (unpiped):**
  - `ruff check src tests` → exit <n>
  - `mypy --strict src/` → exit <n>
  - `pytest --cov --cov-fail-under=80` → exit <n>
  - `lint-imports` → exit <n>
- **`kill_attempts`:** <one line per attack run, with what it produced>
- **`could_not_verify`:** <one line per leg not executed>
  (empty ⇒ **`could_not_verify_justification`** is required)
- **`findings`:** `<path>/findings.json` (CRT-03 grammar) — N findings
- **`verdict`:** `reviewed_sound` | `upheld_with_findings` | `could_not_verify` | `rejected`
```

The prose verdict names in CRT-09 (*reviewed; sound*) and the schema's enum
(`reviewed_sound`) are the same four outcomes in two spellings — the artifact
takes the snake_case token.

---

## Attribution

The round protocol is ported **copy-with-attribution** from the
[`sooth-standard`](https://codeberg.org/magogi-admin/sooth-standard) court
family (`docs/standards/REVIEW.md` CRT-01..16 + `docs/recipes/court-return-format.md`);
MGF holds no dependency edge on Sooth. The standing-prompt pattern in §2.4 is
generalized from `hrb-app`'s production `docs/gui/GUI_ACCEPTANCE.md`. The three
case studies are drawn from that project's `docs/inprogress/RELEASE_MASTERPLAN.md`
§5 ledger and the fold commits it cites. The binding form is
[`../standards/REVIEW.md`](../standards/REVIEW.md).
