Files
hq/03-DESIGN/01-to-be/11-a-board.md
T
jschoubben 1b5308c9cc Review of the to-be layer: check what the documents claim against what runs
First pass of a design review, done by reading documents against code
and against a raised mesh rather than against each other. Every error
below was invisible to a proofread.

**Statuses were stale, and nothing checked them.** Ten to-be documents
said `designed` while naming working, lab-proven code — several with a
*What was built* or *Raised, and observed* section. Added a
`status-vs-code` check: naming a file is a claim that the file
implements this, so a document that points at one has stopped being
merely designed. It failed on all ten before it passed, per the rule
this folder sets for its own checks.

**The bundle carries three images, not two.** 07 reasoned about which
substrate services go in and overlooked that the control plane is in
there too — it is what the substrate exists to start, and there is
nothing to fetch it with yet. Counted, not deduced.

**The bootstrap uses four shapes, not six.** It listed `file` and
`directory`, which substrate-first-node.lock never asks for. The claim
that mattered — nothing is blocked on the host — was true either way,
which is why the wrong count survived.

**The eight capabilities were documented nowhere.** Implemented in
internal/profile/detectors.go and enumerated in no document, including
the one about the host that detects them. A vocabulary modules write
against, readable only by reading the code. Now written down, with the
seat/graphical-session distinction that is wrong in both directions if
collapsed.

**MinIO swept out of the to-be layer** per 0028.

The gate now fails on one thing left deliberately: ADR 0024 is
`proposed` while two documents rest on it and the feature it decides is
built and lab-proven. Accepting a decision is not mine to do.
2026-08-31 17:20:47 +02:00

6.0 KiB

layer, status, code, updated, decisions
layer status code updated decisions
to-be in-progress
mesh-control cmd/mesh-control/board.go
mesh-control cmd/mesh-control/readable.go
2026-08-31
02-DECISIONS/0008-a-context-owns-its-store.md
02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md

A board

A place to see the mesh. Read from a survey of the one that exists, so what is proposed here is a shorter list than what is there, deliberately.

What the existing board does, and what of it belongs here

Eight sections. Four are about work and workers and are held back with the rest of that domain; the other four are about the mesh itself.

what it shows where it stands here
the mesh every node, what each runs, what each takes from another, module versions everything behind it exists — it is a reader, not a second source
what is wrong machines not doing what they were told, machines not answering the page nobody had thought to ask for, and the one a person opens first
builds a build, its stages, its log everything behind it exists — every result is kept, failures included
sessions model sessions, their usage, and switching between accounts ADR 0024
channels nodes messaging each other the agent layer

The constraint that matters, and it is not a feature

The existing board is one service that reads every context's database. It joins nodes to provisions to modules to sessions by querying each store directly, because that is the shortest path to a page that shows all of them at once.

That is ADR 0008 violated by the one component with a reason to violate it, and the cost is not hypothetical — it is the same cost the shared library has: a boundary nothing may cross is a boundary that can move; one thing crossing it is enough to freeze it. A board that reads the provisioning tables directly is a board that breaks when provisioning changes its tables, and the change then gets weighed against the board.

So a board reads through interfaces and holds nothing. Everything on the mesh page above is already answerable by asking the control plane — what nodes exist, what each resolves to, what it takes from elsewhere, which module came from which commit. A board that asks those questions is a client. A board that queries inventory is a second control plane with a worse contract.

It stores nothing of its own. No cache that can disagree, no table of "what the mesh looked like last time". If a question is slow to answer, the answer belongs in the context that owns it, where everything else asking gets it too.

What it is not

  • Not the way to change things. Reading is the whole of it to begin with. Every action the board could offer already exists as a command, and a button that does something no command does is a second implementation of a decision.
  • Not a dashboard of graphs. What a person needs from a mesh is which machine is not doing what it was told, and that is a list, not a chart.

The three questions, in order

Written down because the order is the design. A person opens this when something is wrong, and a page that led with the third would bury the first:

  1. Is anything broken? A machine whose last declaration was refused or partly failed. It has consequences now.
  2. Is anything not answering? A machine not heard from. It may be new, switched off or unreachable — which is not the same as tried and could not, and collapsing the two sends somebody to debug a machine that was never sent anything.
  3. Is anything out of date? A module behind its source, and the machines running the old one. A plan for later rather than a problem now.

Refused and failed stay distinct all the way to the page. Refused means the machine is exactly as it was and what is wrong is in what was sent; failed means it is in a state nobody declared and what is wrong is on the machine. They are fixed in different places, so a page that said "error" for both would send half its readers to the wrong one.

What was built

2026-08-31.

One reading, three ways of saying it. The questions are asked once, by one function, and answered as a person's status, as its JSON, and as this page. Three implementations of which machine is not doing what it was told would be three chances to disagree about it — and the disagreement would surface as two people looking at two screens arguing about which machine is broken.

It holds nothing and changes nothing. Every request reads the mesh now. There is no cache to go stale, no table of what the mesh looked like last time, and no button: every action a board could offer already exists as a command, and one that did something no command does would be a second implementation of a decision.

It never touches a context's store. That is the whole constraint above, kept: the board is a client of the same functions the commands use, so provisioning can change its tables without the change being weighed against a page.

A board that cannot read the mesh says so. An empty page says nothing is wrong in the one situation where nobody can know that, so the failure is rendered instead — and it says explicitly that it is a statement about the page rather than about the mesh.

A machine's own words are shown, and are not markup. They are the whole reason the page is useful — a board that said only failed would send a person to ask the thing they opened the board to avoid asking. They are also the only text on the page that nobody in this repository wrote, which is why the escaping is a test rather than an assumption.

Checked by giving a machine a declaration it cannot apply and requiring the page to name that machine, say failed rather than error, and quote what the host said — then by comparing the page's own JSON against the command's, because two answers to "which machine is broken" would be worse than either alone.