# ADR 0300: Checkpoint observation is drained metadata, never a verdict

**Status**: accepted; fills the observation seam of [ADR 0299](0299-checkpoint-read-surfaces-mirror-the-gate-without-effects.md) under the Logbook charter of [ADR 0160](0160-local-logbook-advisory-readers.md), the follow-through framework of [ADR 0207](0207-hint-follow-through-is-declared-and-episode-based.md), the counting rules of [ADR 0229](0229-practice-stats-are-counted-local-and-never-comparative.md) and [ADR 0256](0256-patterns-findings-bounded-per-detector.md), and the recommendation bar of [ADR 0276](0276-patterns-recommendations-require-project-local-decision-evidence.md)

## Context

The checkpoint contract records judgments, but nothing recorded how the mechanism itself behaves: which checkpoints fire and how often, whether declarations follow relevant revisions or land on unchanged subjects, how long a question waits for its conclusion, which land under owner-authorized variances, and which open questions an effort simply ends on. Without that memory an owner cannot tell a checkpoint earning its tax from one taxing every change, and the external design review fixed the interpretive line early: a declaration on an unchanged subject does **not** prove rubber-stamping — the agent may have reviewed before invoking the gate — so the record must observe without judging.

Three constraints shaped where the record could live. The done and accept envelopes are published contracts, and the observation facts (reconcile outcomes, subject-revision flags, elapsed times) are Logbook material, not caller material. The refusal that serves a question — the firing moment — must record even though no gate ran. And the unmet rationale, durable Proof evidence, is prose that must never enter the metadata-only Logbook.

## Decision

**One additive `checkpoints` block on the verb event carries the open question and variance lifecycle as metadata, collected where each fact becomes true and drained at the recording points; every reader of it observes checkpoint fit and none can gate anything.**

- **The vocabulary.** Servings (`fired`, `reopened`, and `advise` — advise checkpoints have no open questions, so the serving itself is the record), `declared` entries carrying the conclusion, the definition hash and subject fingerprint, a `revised` flag, and `elapsed_ms`, plus `variances` and `abandoned` on the landing's event. Ids, outcomes, fingerprints, and timing only: no field for the rationale exists, so the metadata bar is structural. Older events without the block stay readable; readers censor what is missing.
- **Mechanical honesty for the unchanged/revised split.** `revised` is true exactly when the declaring invocation's own reconciliation reopened the subject — a relevant in-scope revision separated the serving from the conclusion. Elapsed time runs from the serving the agent actually answered: on a same-invocation reopen, the replaced subject's serving time, which `reconcileEpisode` now reports.
- **Collection by accumulator, drain at the recording points.** The gate pre-flight and the acceptance transition observe into a process-local accumulator (`result_capture.ts`, beside the observed-result mailbox); the CLI wrapper and the MCP completion boundary drain it into the event (the drain-parity guard already forces every mailbox through those two recording points), and the recorder's fallback take clears any remainder. Both surfaces therefore record equivalently, refusals record their firings, and a refused landing observes no variance — the landing's observations are recorded only after the trunk transition completes.
- **The follow-through family is stream-walked, so checkpoints auto-enrol.** `checkpoint-declare` declares a new rule kind in the ADR 0207 framework: its follow-through episodes are enumerated per (branch, checkpoint id) from the recorded blocks rather than per delivered hint — a serving opens one, a reopen while pending folds into it, the resolving declaration's `revised` flag decides followed or not followed, and a missing block, missing flag, or history's end censors. Any checkpoint id that ever appears in observations is measured; no registry names checkpoints anywhere.
- **One economics reader, three consumers.** A single tally (`checkpoint_economics.ts`) feeds the practice-stats card, the `checkpoints` verb's observed-history section, and the hygiene detectors, so the numbers cannot disagree. Rows are plain counts beside their denominators, bounded to the most-served 16 with the remainder counted — the wire scales with configuration, never history.
- **Hygiene advisories judge configuration, never agents.** `checkpoint-dead` (configured, never fired) and `checkpoint-noisy` (served on ≥80% of efforts) count gate-run efforts since the newest recorded `[checkpoints]` config change — the epoch machinery's own attribution, so a revised definition earns fresh evidence — and speak only after 8; `checkpoint-varied` speaks once at least 3 variance-carrying landed efforts where the checkpoint fired exist and they cover at least half of such landings. Each recommends reviewing the trigger, question, or mode, with its counts.
- **The boundary is a guard, not a convention.** An import-graph test walks every checkpoint decision module (the package minus the always-ok `report.ts`, plus the acceptance interlock) and fails if any path reaches the Logbook; black-box tests prove the rationale never lands in a Logbook byte while the same invocations serve it as Proof evidence.

## Consequences

- The owner's questions become answerable from counts: which checkpoints fire, on what share of efforts, how conclusions arrive, and which routinely need a variance — while no number claims anything about the agent's diligence.
- The verb-event schema gains one optional field for the life of the feature; every existing reader tolerates it, and every new reader must censor its absence.
- Abandonment is observed only where an effort observably ends (a completed landing). A worktree deleted by hand ends unobserved — accepted: the Logbook records verbs, and no verb ran.
- Advise servings record once per gate run, so their `fires` measure served context, not distinct occasions; the per-effort share carries the deduplicated view.
- The dead/noisy denominator resets on any `[checkpoints]` edit, since the section hashes as one unit — conservative in the delaying direction.

## Alternatives considered

- **Lift observations from the result envelopes by shape**, like consent and landing state. Rejected: the facts (reconcile outcomes, revision flags, elapsed times) would have to join the published done/accept contracts first, growing caller-facing surface for reader-only material — and the accept envelope's variances carry the rationale, which a shape-lift would have to deliberately strip forever.
- **Dedicated event kinds** (`checkpoint-fired`, `checkpoint-declared`, …). Rejected: the lifecycle is per-invocation context, exactly what the verb event exists to carry; new kinds would multiply the compatibility surface and orphan the begin/completion pairing.
- **Count a declaration on an unchanged subject as not-followed evidence of neglect.** Rejected on the review's correction: the revision flag records what happened to the subject, not what the agent read; interpretation stays with the owner.
- **Enrol checkpoints in follow-through by id**, mirroring the hint-id tables. Rejected: a registry of checkpoint ids would let project-authored checkpoints escape measurement — the observation block already names every id.
- **Let the noisy detector read live config thresholds to suggest exact replacement trigger values.** Rejected under the recommendation bar: the counts justify a review, not a computed configuration change.
