# Read-the-declaration — design, and why it should not be built

IR-21 step 4, item 3. The question doyle scoped: a standalone *read-the-declaration*
mechanism — a site declares how its fixture bin is built, and a checker reads that
declaration instead of inferring — carrying the 4-of-39 number and **answering what it does
at the other 35**, which is the interesting half.

**Recommendation: do not build it.** Derive the build command instead, and reserve
declarations for the case that does not yet exist in this tree. The measurements below are
what changed my mind about it, and the section that would change it back is at the end.

Measured on `hfenduleam`, cargo 1.93.0, 2026-08-03, tree `6d291e0` (worktree
`.worktrees/ir21-xtask`), via `xtask binedge-check --sites` plus a window scan over each
site. Nothing here is recalled; every number below has a command behind it.

## The population, and the number that was carried into this design

`binedge-check` classifies 185 consumer sites: 146 already guaranteed by cargo, **39 that
need an explicit build**, 11 of them with no prebuild anywhere. The 39 are the population a
declaration mechanism would serve. They split by target shape as:

| | sites |
|---|---|
| integration test, cross-package | 38 |
| unit test, same-package (`crates/spt/src/cli.rs`) | 1 |

and by how the site names its bin: `sibling_bin` 33, the shared resolver `fixture_bin` 4,
`format!` over `current_exe()` 2.

The number carried into this design was **4 of 39 carry a declaration**. That number is
correct for a *machine-readable* declaration and it is not the whole picture. Scanning a
window below each site for a `cargo build` command:

| carrier | sites | form |
|---|---|---|
| resolver argument | 4 | `fixture_bin("capture-player", "cargo build -p mock-adapter --bin capture-player")` — a real argument, parseable |
| assert message | 12 | `assert!(mock.exists(), "build the dummy harness: cargo build -p mock-adapter --bin mock-session")` — prose |
| nothing | 23 | the site names the bin and says no more |

**The window is a parameter and the number moves with it**, so it is reported with one: at 3
lines the prose class reads 3, at 6 it reads 12, and at 10 and 20 it is still 12. Six lines
is where it stabilises; three is too narrow to contain a wrapped `assert!`. A declaration
count quoted without its window is not a measurement.

## What the other 35 actually look like

The 12 prose declarations are not spread across the population. **Every one of them names
`mock-session`** — one bin, one idiom, copied. And that same bin has **14 sites that do not
carry it**:

| bin | resolver arg | prose | silent |
|---|---|---|---|
| `mock-session` | — | 12 | 14 |
| `mock-shell` | — | — | 6 |
| `capture-player` | 3 | — | — |
| `console-mode-probe` | 1 | — | — |
| `service_fixture` | — | — | 1 |
| `spt` | — | — | 1 |
| `translate_proof_fixture` | — | — | 1 |

So for the one bin where anybody adopted a per-site declaration convention, the convention
reached **12 of its 26 sites**. Not because the other 14 are different in kind — they are the
same call, in the same shape of test — but because a convention that lives in each copy of a
line decays across copies of that line. That is the same failure this whole class keeps
producing: a rule written in prose, shipped as an enumeration, and then outlived by its own
population.

A mechanism that reads declarations therefore does not get 4-of-39 or even 16-of-39
coverage. It gets *whatever coverage the next copy-paste happens to preserve*, and it has no
way to tell a site that declares nothing from a site that needs nothing.

## The finding that decides it

All 16 declarations in the tree were extracted and normalised (Rust string continuations
rejoined, whitespace collapsed):

```
  8  cargo build -p mock-adapter --bin mock-session)
  3  cargo build -p mock-adapter --bin capture-player
  3  cargo build -p mock-adapter --bin mock-session
  1  cargo build -p mock-adapter --bin console-mode-probe
  1  cargo build --workspace --bins
```

Two things fall out.

**Fifteen of the sixteen are exactly `cargo build -p <owning package> --bin <bin>`** — which
is precisely what `binedge-check` already knows without being told. The owning package comes
from `cargo metadata`; the bin name comes from the site itself. The declaration is a
*restatement* of two facts the checker holds already. It cannot be authoritative about
anything, because it carries no information the derivation lacks.

**The sixteenth disagrees with its own siblings.** `endpoint_autostart_e2e.rs:275` declares
`cargo build --workspace --bins` for `mock-session`, where eleven other sites declare
`-p mock-adapter --bin mock-session`. Neither is wrong — the broad one works and does more —
but a corpus of declarations that disagrees with itself about one bin is a corpus you cannot
promote to authority. A reader would have to pick, and picking is inference.

**Zero declarations carry a non-derivable flag.** No `--features`, no `--release`, no
`--profile`, no `--target`, no environment. Checked across all 39 windows. There is, today,
nothing a declaration could say that a derivation could not.

## Options considered

**(1) Parse the resolver argument only (the 4).** Sound and cheap — it is a real argument in
a real function call. Also pointless: those 4 are the sites already best served, all 4 name
`mock-adapter` bins, and all 4 declarations reduce to the derived form. It authorises
nothing that is not already known, and it answers nothing at the other 35. This is the
option that "explains the 4 it can fix" and has not met its population.

**(2) Parse prose too (the 4 + 12).** This is the option to refuse. Extracting a command
from an assert message means pattern-matching English around a `cargo build` substring,
inside a window whose size changes the answer, over a corpus that contradicts itself. It
would produce a table that *looks* like declarations and is actually inference — an
inference engine wearing a declaration's clothes. And it would silently reward the 12 sites
that copied the idiom while saying nothing about the 14 that did not, which is the exact
inversion of what the class needs.

**(3) Require a declaration at all 39 and gate on its presence.** Buys a uniform corpus at
the cost of a rule enforced per-copy, which is the mechanism that already decayed to 12-of-26
without anybody noticing. It also puts 39 restatements of derivable facts into the tree, each
one able to go stale on its own. The remedy would age worse than the defect.

**(4) Derive the command; keep declarations for the case that needs them.** Below.

## Recommendation

**Derive it.** For every red site, `binedge-check` already holds the owning package and the
bin name, so it can print the exact command that fixes that site:

```
crates/spt/tests/shell_e2e.rs:89  mock-shell (owned by mock-adapter, consumer spt, integration)
    fix: cargo build -p mock-adapter --bin mock-shell
```

That reaches **39 of 39** — including all 23 silent sites, which no declaration mechanism can
reach at all — and it cannot go stale, because it is recomputed from the manifest every run
rather than stored next to a call. The one site that would print differently from its current
prose (`endpoint_autostart_e2e.rs:275`) prints a narrower command that is also correct.

Then **retire the declaration as a source of authority and keep it as an error message**. The
assert messages should stay: a developer reading a test failure wants that sentence, and
`fixture_bin`'s second argument is a good place for it. What changes is only the claim made
about them — they are how the failure explains itself, not an input to a checker.

I am **not proposing a REQ for this**, because the recommendation is not to build the
mechanism. The derived-fix line is small enough to ride the next binedge-check change if
doyle wants it; it is not filed here as a commitment.

## What would change this recommendation

The recommendation rests on one measured premise: **every fixture bin in this tree is built
by the derivable command.** It stops being true the day a fixture needs something the
manifest does not imply — a feature flag, a release profile, a specific target triple, an
env var, or a build that is not `cargo build` at all.

On that day a declaration earns its keep, and it should be minted in the form that is already
proven to survive: **an argument to the shared resolver**, not a sentence in an assert. That
form has 4-of-4 fidelity in this tree, versus the copied idiom's 12-of-26, for the structural
reason that an argument cannot be dropped by the next copy-paste without the call failing to
compile.

The falsifier is cheap and worth wiring on that day: compare each declaration against the
derived command and report only the ones that DIFFER. A declaration that equals the
derivation is noise; a declaration that differs is the only one that ever carried
information. Today that report has exactly one row, and it is a disagreement rather than a
requirement.
