A documentation request COMPOSES a selection over what each kind offers — and composing a book is not publishing one
Accepted
Context
THE BOOK WAS ONE BOOK, FOR NOBODY. Measured 2026-08-27 on ontoref-self, before the repairs that prompted this ADR: the projected mdBook was 48k words, 41k of them decision pages — 85% ADR prose — while the glossary, the accepted answers, the practices, the open items, the capabilities, the reference kinds and the refusals all existed in the spine and reached no reader. `compose-doc-data` even COMPUTED practices, gates, edges and scenarios and the projector discarded all four.
The defect was not missing content. It was that nothing decided what a reader should get, so the projector shipped whatever its author had wired, and the only available answer to "who is this for?" was "whoever opens it". A surface with no admission criterion does not become neutral; it becomes a dump, and the reader pays the sorting cost that the projection refused.
ONTOREF ALREADY HAS THE PROFILE AXIS, AND IT IS THE WRONG ONE FOR THIS. `reflection/ disciplines.ncl` and `reflection/views/*.ncl` (adr-046) declare six disciplines with `actors`, `levels[]` and a measured `budget.max_levels` whose `on_exceed = 'Abstain` makes growth announce itself. That is a genuine who / for-what / how-deep model — for an ACTOR OPERATING on the substrate. A reader is not an operator: they mount nothing, mutate nothing, and their question is answered once. Reusing the operator's six disciplines as reader profiles would have been the cheap move and would have mismatched every one of them.
AND THE REQUEST IS THE THING THAT VARIES. A CI job wants a slice nobody will ever name twice. An adopter wants the vocabulary and the modes and none of the jurisprudence. A reviewer wants the constraints of one ADR and everything it routes to. Requiring each of those to pre-exist as a committed `.ncl` file makes the common case — ask a question, get a book — the expensive one, and guarantees the declared profiles drift from what anyone actually asks for.
WHAT A KIND OFFERS IS NOT WHAT A READER TAKES, and conflating them is the specific error this ADR exists to refuse. The first draft of this design put a documentation recipe — template, depth, structure — on each ADR. That reproduces exactly the failure `reflection/schemas/ rendering.ncl` was written after: «a rendering was a fact about a STRING in a FILE, not a property of the concept», measured as 2 concepts diverging between surfaces and 5 incoherent INSIDE the single file whose job was to fix the vocabulary. 103 ADRs each declaring their own presentation are 103 decisions nothing contrasts.
THE PUBLICATION HALF HAS ITS OWN PAID CASE. `qa:dissemination-deck-projection-howto` states it about decks and it generalises without change: «the manifest is curated BY HAND on purpose: which deck ships, under which slug, in which language IS A DECISION, and a script that rewrites a manifest decides what gets published». Meanwhile docserver already serves N books under per-path `roles` and `is_restricted`. If a request could both compose AND mount, an anonymous HTTP call would become a governed publication act.
Decision
A documentation surface separates three things that were one, and the separation is the whole decision.
1. OFFER — STATIC, PER KIND, IN THE REGISTRY. `reflection/kinds.ncl` (adr-103) gains an OPTIONAL declaration of what a kind makes available for projection: which of its fields are projectable, which of its references may be followed, and how far. It is a property of the KIND, true of every occupant, and it says nothing about who is reading. Absent means the whole kind is offered, which is what the projector does today — so a registry that declares nothing behaves exactly as it does now.
2. SELECTION — DYNAMIC, COMPOSED IN THE REQUEST. Which kinds, which of their offered fields, what depth of reference-following, which language. A selection is built from the request and is NEVER required to pre-exist. Any caller — a person, a CI job, an agent — may compose one and receive a book. This is the axis that carries "who are you / for what / how deep", and it carries it per question rather than per committed file.
3. PUBLICATION — GOVERNED, AND ONLY HERE. A selection given a NAME, a MOUNT and an AUDIENCE. Only at this point does the selection get recorded, and only at this point is authorization involved. A published book's selection MUST be recorded, because a generated tree may be ignored only when a tracked source plus a recipe reproduce it (adr-070 WITNESS) — and for a request-composed book the selection IS half of that recipe.
COMPOSING A BOOK IS NOT PUBLISHING ONE. A projection request may produce any selection and is free, anonymous and uncontrolled. Mounting the result under a route with an audience is a separate act with its own authorization at the point of execution. The two must not be reachable by one call, in either direction: a request may not create or alter a mount, and a mount may not be filled by an unrecorded selection.
PER-ITEM OVERRIDE IS AN AMENDMENT TO THE OFFER, NEVER TO THE SELECTION. An individual occupant — one ADR, one Q&A entry — MAY withdraw from or add to what its kind offers, and the declaration MUST carry its reason. It is still OFFER: it says what this occupant makes available, never how a reader should see it. Without the reason it is an escape hatch, and this project already fixed the shape twice — `gateable = false` must name what WOULD verify the claim, `denied.why` must say what the rejected rendering costs.
WHAT THIS DECIDES BEYOND DOCUMENTATION, AND WHAT IT DOES NOT. The offer is not a doc field: it declares what a kind exposes to ANY projection, so a context-mounting, a query result and a verification report read the same declaration. That is the reason it belongs on the registry rather than in the projector. It does NOT decide the shape of those other consumers' selections, and it does not migrate any of them; each arrives with its own case or not at all.
Constraints
- Hard A projection request MUST NOT create, alter or remove a publication mount, its route or its permissions. Publishing a composed selection is a separate act with its own authorization at the point of execution.
- Hard A book served under a named mount MUST carry the recorded selection it was composed from. An unrecorded selection may be projected and read; it may not be published.
- Soft The offer declaration on a reference kind MUST be optional, and its absence MUST mean the kind's whole surface is offered. A registry that declares nothing behaves exactly as it does today.
- Hard An occupant that withdraws from or adds to what its kind offers MUST declare why. An override without its reason is refused.
- Soft A selection SHOULD declare the language it is composed for, and a projection SHOULD report any surface it could not serve in that language rather than silently substituting another.
Alternatives considered
- Reuse the six existing disciplines (adr-046) as reader profiles. — rejected: They model an ACTOR OPERATING on the substrate — actors, mountable levels, mutation actions, a measured level budget. A reader mounts nothing and mutates nothing, and none of the six describes someone arriving without the vocabulary. Reuse would have been cheap and would have mismatched every one.
- Declare a documentation recipe (template, depth, structure) on each ADR, Q&A entry and backlog item. — rejected: It puts presentation on the occupant, which is `rendering.ncl`'s measured failure one layer up: a rendering became a fact about a string in a file rather than a property of the concept, and the arbiter stopped agreeing with itself. 103 ADRs each declaring their own presentation are 103 decisions nothing contrasts.
- Declared profiles only — every profile is a committed .ncl file. — rejected: It makes the varying thing static. The CI slice asked once, the reviewer's one-ADR closure and the adopter's vocabulary-plus-modes cut would each need a file, so the declared set drifts from the asked set and automation is the worst served. Naming a selection stays available — it is what publication requires — but it is not the price of asking.
- One request that composes and mounts in a single call. — rejected: It makes an anonymous HTTP request into a governed publication act. docserver already gates per-path by role; a call that both selects and mounts would route around that gate rather than through it.
- Do nothing beyond the fixed page set already shipped. — rejected: The fixed set is a real improvement and is the reason this ADR can be written at all, but it is one book for everyone. It cannot serve a newcomer and a reviewer and a CI job without being wrong for two of the three, and it gives automation no handle at all.
Anti-patterns
- The item declares how it should be documented — A presentation recipe — template, depth, section order — declared on an individual ADR, Q&A entry or backlog item. It reads as local control and is 103 uncontrasted decisions: the occupant starts describing a reader it cannot see, and two occupants of the same kind diverge with nothing comparing them.
- Composing and mounting in one call — A single endpoint that takes a selection and serves the result under a route. It routes around the per-path role gate rather than through it, and turns an anonymous question into a publication act.
- Every slice becomes a committed profile — Answering each new documentation request by adding a declared profile file. The declared set grows without bound, drifts from what anyone asks, and automation — the case with the strongest claim on composability — is the worst served.
- A general field with one consumer — The offer is declared on the registry with the claim that context mounting, queries and verification will read it, and only the documentation projector ever does. The name promises generality the wiring does not deliver, and the next consumer copies the field instead of reading it.
Related ADRs
ADR-046 · ADR-060 · ADR-062 · ADR-070 · ADR-096 · ADR-097 · ADR-103