sprig and prose docs classic docs
Reference› ADRs› ADR009 RealityInTheFieldGuide

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.