Wiki · Architecture & Decisions
ADR-0007 — The `ui/` read-only contract is clarified, not widened
How to read this page
Three ways to read this page. Precise is the document itself, exactly as it is written in the repository. Plain and Clear were written for this website to help you meet that document — they are about it. They are not it, and they are not evidence.
This is the design record of the part of the estate whose job is to stop the rest of it claiming more than it has measured. It runs to architecture notes, numbered decision records, phase plans, and the results written after each phase ran.
It is for anyone curious how a decision was actually reached — what was considered, what was refused, and what the refusal cost. The decision records are the most durable part: each names one choice, the alternatives, and the consequence accepted in exchange. The failure-modes page is worth the detour, because it states each refusal as something you could go and test rather than as a promise.
Read the architecture page first. It opens by declaring itself part design and part built, which sets the tone for the whole section. Then the first decision record, then whichever phase interests you — read as a pair, the plan and the results page written afterwards, including the phases where a premise turned out to be wrong and the receipt says so.
What it is not: a description of running software, and not a complete record. Three documents from this area were withheld from publication, because they describe private infrastructure or the operator declined them; they are listed as withheld rather than quietly dropped, so you can see that they exist.
Your browser cannot switch reading levels, so the document itself is shown.
Precise — the source document
This is the document. Rendered from the repository at the commit above, with nothing rewritten for the web. A gate re-renders it on every deploy and fails the build if a single byte differs.
- Status: Accepted — operator-authorised 2026-07-25
- Date: 2026-07-25
- Deciders: Michael (authorised the amendment), Veritas, Custos
- Amends:
ui/mix.exs:26-29inUNI.Minecraft(branchgen2-runtime)
Context
ui/mix.exs:26-29 stated:
This is the ONLY part of the repository that takes hex dependencies. The pure
stratified_palimpsestcore stays dependency-free; the UI consumes it as a path dependency and only ever READS its state / the evidence log.
That sentence is a deliberate fence and it has held. But the lab view (ADR-0003) is a surface the operator authors verdicts from, and it renders in ui/. A surface that authors cannot be inside an app that never writes.
Two ways to resolve it: widen the fence, or make the surface a proposer. Widening a written fence to fit a new feature is the failure mode this whole architecture is built to prevent — it is how a control gets weakened one convenient exception at a time.
Decision
The fence is clarified, not widened. The amendment states what the UI still may not do, then names the single new ability precisely:
- The UI still NEVER writes engine state, and NEVER writes
evidence/gates.ndjsonor any receipt. - It gained exactly one ability: it may SUBMIT a command to
SP.ControlPlane, which validates, authorises and performs every write itself. - The UI proposes; the Control Plane authors.
- A LiveView that mutated a ledger, gate row or receipt directly violates this contract exactly as it did before.
Three consequences remain binding and are written into the amendment:
- A polled read still actuates nothing — the Door's law, inherited verbatim.
- The write path stays testable offline in the zero-dep core, with no Phoenix in the loop.
ui/remains the only place hex dependencies live.
Consequences
Positive. The lab view can exist without any component gaining write access it should not have. The rule stays one sentence a reviewer can check: does this code write, or does it ask? Because the write path is in the zero-dep core, every refusal is provable by an offline, deterministic test.
Negative. A round trip is added: the surface must ask rather than act, so a verdict cannot be written from the LiveView process. That is the intent, not a cost to be optimised away.
Neutral. The amendment adds 13 comment lines to ui/mix.exs. No code, no dependency, no behaviour changed by this ADR alone.
Alternatives considered
Widen the contract to "reads and writes". Rejected. It removes the only mechanical statement of the boundary and licenses any future LiveView to write anything.
Leave the contract untouched and put the lab view outside ui/. Viable — a fourth Mix project with its own endpoint. Rejected for now: it duplicates the Phoenix/LiveView/Bandit stack and the world.js hook machinery for one surface, and puts the room on the far side of a network boundary from the views it sits beside. Revisit if the command surface ever grows beyond proposing.
Say nothing and just add the route. Rejected outright. A silent contract change is indistinguishable from a violation, and the next reader would have no way to tell which it was.
Falsifier
Any write to engine state, evidence/gates.ndjson, or a receipt originating from ui/. Any ui/ code path that mutates canonical state without going through SP.ControlPlane. Any polled read in ui/ that actuates something.
Receipt
ui/mix.exs in UNI.Minecraft @ cdf73c89 (branch gen2-runtime), amended in place — comment-only, 13 insertions, zero code change. The pre-existing dirty file in that tree ( M test/sp/brain/mc_test.exs) is user-owned and was not touched.
sha256 5c77ba41e593557b — of the original file, so what was ingested stays checkable.
Plain — written for this website, not the source document
A rule came under pressure to be loosened, and was not. That is the whole of this short dated record.
The web part of the codebase carries a written limit: it is the only place external dependencies live, and it only ever reads the core's state and evidence log. But the lab view — the surface an operator authors verdicts from — renders in that same web part. A surface that authors cannot sit inside an app that never writes.
There were two ways out: widen the limit, or make the surface ask rather than act. The record says plainly that widening a written limit to fit a new feature is the failure mode this architecture exists to prevent, because that is how a control gets weakened one convenient exception at a time.
So the limit is clarified instead. The web part still never writes engine state, never adds to the gate ledger — a list only ever added to, never edited — and never writes a receipt, the file recording what was run. It gained exactly one new ability: it may submit a command, and the core validates, authorises and performs every write itself. The surface proposes; the core authors.
Plain · written 2026-08-01 by claude-opus-5 · not yet checked by a person · about the document whose sha256 is 5c77ba41e593557b
Clear — written for this website, not the source document
A boundary that was tempting to widen is the subject of this short dated decision record, which is marked as authorised by the operator.
The context is a sentence written into the build file of the web part of the repository. That part is the only place external dependencies live. The pure core stays dependency-free, and the web part consumes the core as a path dependency, only ever reading its state and its evidence log. The record says this limit was deliberate and that it has held. The difficulty is that the lab view is a surface an operator authors verdicts from, and it renders inside that web part. A surface that authors cannot be inside an app that never writes.
Two resolutions were available: widen the limit, or make the surface a proposer. The record chooses the second and states the reason as a general principle. Widening a written limit to fit a new feature is the failure mode this whole architecture is built to prevent, because it is how a control gets weakened one convenient exception at a time.
The decision therefore clarifies rather than widens. The amendment first restates what the web part still may not do: it never writes engine state, never adds to the gate ledger (a list only ever added to), and never writes a receipt (the file recording what was run). Then it names the single new ability precisely — it may submit a command to the core, which validates, authorises and performs every write itself. A view that mutated a ledger, a row or a receipt directly violates the contract exactly as it did before. Three older consequences are carried into the amendment and stay binding. A polled read still actuates nothing, inherited word for word from another body's law. The write path stays testable offline in the dependency-free core. And the web part remains the only place external dependencies live.
The consequences are given honestly in both directions. In favour: the lab view can exist without any component gaining write access it should not have, and the rule stays one sentence a reviewer can check — does this code write, or does it ask? Because the write path lives in the dependency-free core, every refusal can be tested offline and repeatably. Against: a round trip is added, so a verdict cannot be written from the rendering process. The record says that is the intent, not a cost to be optimised away. Neutrally, the change adds only comment lines; no code, no dependency and no behaviour changed by this record alone.
Three alternatives were rejected. Widening the contract to read-and-write would remove the only mechanical statement of the boundary. Leaving the contract untouched and moving the lab view into a separate project is viable, but it duplicates a whole stack for one surface and puts the room across a network boundary from the views beside it. It is left open to revisit. Saying nothing and simply adding the route was rejected outright, because a silent contract change is indistinguishable from a violation and a later reader would have no way to tell which it was.
What would show this violated is any write to engine state, to the gate ledger, or to a receipt originating from the web part, and any polled read there that actuates something. A short receipt at the end records the amendment as comment-only, and notes that a pre-existing modified file in that tree is owned by the user and was not touched.
Clear · written 2026-08-01 by claude-opus-5 · not yet checked by a person · about the document whose sha256 is 5c77ba41e593557b