ADR009 RealityInTheFieldGuide
Title: Receipts in the field guide
- Status: Accepted
- Decided: D2026-08-30
- Followed by: ADR011
ExpectsBlock - Affects:
Cli,Ui - Pairings:
Cli— help and docs
Context
sprig ui --beta is a field guide: description is the content, structure is
how you find it. In a code repo the server already offers /api/receipts next
to the manifest. The original UI consumes that overlay as a score — every
targetable name is observed, declared, or drift. The beta ignored
the endpoint.
The fixture that forced the decision is sprig ui . --universe ../../universe/ --port 6338 --beta from packages/prose-parser. Twenty-four receipts, all
recognized, all on error concepts in src/errors.js. The Sprig universe has
twenty-five errors. The one without a receipt is ConceptWithoutUniverseError,
which is retired on purpose. Everything else in the universe — error codes,
ADRs, packages, syntax, Candidate — is also without a receipt, because this
scan is one package against a whole-ecosystem universe.
Prose cannot tell those names apart. Candidate is a maturity. NestedUniverseError is a thing in the parser. ConceptWithoutUniverseError is a retired error. They are the same kind of declaration. A missing mark is therefore either noise (Candidate) or a spoiler (the retired error, once you have stared at 24/25 badges long enough to notice).
A second constraint: the field guide may not be the place that does observability as a product. Showing evidence if it exists is in scope. Coverage, drift, and "what is missing from this repo" can live in a tool that is about that.
Decision
When a concept has receipts, show them as citations. When it does not, say nothing.
Concept page. A Reality section at the bottom, only if that concept has
receipts. Each receipt is file:line in monospace, then details.note if one
was written. No subject line. No "not acknowledged" empty state. Paths are
text, not links.
Reading row. A second row of the same pills, labelled Reality, with a count of references. Clicking it opens that concept and scrolls to the section. The row is shared, so the door appears on kind pages, sequence pages, and opened connection groups. Hidden when the count is zero.
Overlay. A 200 from /api/receipts turns it on. A 404 or error leaves it
off (universe mode). Opening a manifest by hand drops it, because those
receipts are about the served repo. The original UI's observed / declared /
drift reading is unchanged and remains available without --beta.
Copy. "Reality" and "references", not "observed / declared / drift".
Consequences
What this makes easy. A reader on the error kind page can see which errors have a source pointer and jump to it, without the retired one advertising itself as a gap. A universe repo is still a plain field guide.
What this costs. You cannot browse "everything this repo acknowledges" as a structure. You cannot see unrecognized names (they have no concept page). You cannot see a receipt whose target is a dimension or a relationship. Coverage of the universe is not a number on the overview. Those are the jobs this record leaves for later, possibly for a different tool.
What to watch. If prose later grows a way to mark a name as "this ought to exist in code", the silence rule should be revisited. Until then, scoring would still lie.
Alternatives considered
Reality as a fourth index grouping (Kind / Place / A–Z / Here). Observed /
not in this repo / unrecognized. Rejected. "Not in this repo" is most of this
fixture and most of any scan of one package against a large universe. The
button would also vanish in universe mode, which is correct
(NoChromeForNothing) and would still teach a fourth way-in that is empty on
the universes a new reader meets first.
Quiet marks on existing index rows. A suffix on observed concepts, silence on the rest. Rejected as a badge. On the error kind it is 24 dots and one blank, which is exactly the spoiler: you click the blank one to find out it is deprecated. The field guide's own universe had already named this as unfinished — "bolted onto the concept page as a badge" — and a rail mark is the same idea in a smaller type size.
A dedicated "here" reading page. A door from the universe overview into a list of acknowledged concepts, using the existing reading row. Rejected as the primary design: it still ducks how receipts read in the index, and a page you reach from the overview is easy to never open. The reading-row pill on kind pages is the part of this option that survived.
Scoring every targetable name observed vs declared (the original UI). Rejected for the field guide. "Declared, not yet observed" on Candidate, on SP001, and on a retired error are three different facts with the same chrome. Prose cannot distinguish them. The original UI still does this, on purpose, for readers who want that reading.
Badging the 24/25 errors so the missing one is the signal. Rejected for the same reason as quiet marks, more sharply. The missing one is meaningful only if you already know why. By the time you do, you have already clicked.
Evidence
The prose-parser fixture: 24 receipts, 0 unrecognized, 25 errors in the
universe, 84 concepts overall. details.note is empty on every receipt in
that bundle, so the section is paths until a marker carries a note. The
retired error (ConceptWithoutUniverseError) is the case that makes a
coverage badge lie.