ADR 0035 (proposed): a picture is read from what runs

Drawing a scenario forced a choice that looks cosmetic and is not. A diagram
built from the declaration and captioned "as raised" answers "is what is running
what I asked for?" with the request, which always agrees with itself.

So: a picture captioned as raised reads only the running system, and where the
hypervisor does not hold a fact the picture needs, the raise records it on the
resource. With the rule that makes the recording worth anything — a behavioural
tag is written after the behaviour works, never at creation, because a failed
raise leaves wreckage standing and a picture of wreckage must not badge what the
wreckage was supposed to be.

It earned itself on the first comparison: every VM showed no addresses, because
a container's interface carries the device's name and a VM names its own. The
two pictures disagreed, so a whole class of machine silently losing its
addresses was visible in seconds.

§5 of how-we-build gains the general form, marked proposed. The constitution
sync is deliberately NOT done — a rule the mesh enforces before a second person
agreed to it is what §6 exists to prevent.
This commit is contained in:
2026-08-24 22:54:30 +02:00
parent 8efa063f21
commit b225b07625
2 changed files with 126 additions and 0 deletions
+26
View File
@@ -172,6 +172,32 @@ quietly stop being true, and nobody will learn that from a document.
Not test-driven development as a blanket rule — a test written first against undiscovered
behaviour asserts a guess. The obligation is that every decision has a defender.
### A report is read from the system, never from what asked for it
*Proposed — [ADR 0035](../02-DECISIONS/0035-a-picture-is-read-from-what-runs.md), pending
review.*
The same rule as the two above, pointed at reporting rather than at verification. **Anything
that describes the state of the mesh — a status view, an inventory, a diagram, a health
check — is assembled from the running system.** Assembling it from the intended state produces
a report that always agrees with itself and can never disagree with reality, which is not a
report.
Where the system does not natively hold a fact the report needs, **the thing that applied the
fact records it** — and:
> **A record of behaviour is written after the behaviour works, never when the resource is
> created.**
Written up front it restates the request in a new place and inherits none of the authority of
having happened. A failed run leaves its wreckage standing, and a report of that wreckage must
not describe what the wreckage was supposed to be.
This is the production form of the mesh's most expensive fault: a firewall key declared in five
manifests and read by no code
([04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md)). The
declaration was never wrong. Nothing ever asked the system.
### Search the record before forming a hypothesis
The first action on any error message, failing service or unexpected behaviour is to search the
@@ -0,0 +1,100 @@
---
status: proposed
date: 2026-08-24
deciders: jochen
reconstructed: false
extends: 0034-a-test-defends-a-decision.md
---
# 35. A picture of a system is read from the system, never from what asked for it
## Context
A scenario declaration is a file. A raised scenario is a set of machines, links and rulesets.
The two are supposed to correspond, and the entire value of the lab rests on noticing when
they do not — [ADR 0034](0034-a-test-defends-a-decision.md) says a claim nothing checks is a
claim that will quietly stop being true.
Drawing a scenario makes that concrete, and forces a choice that looks cosmetic and is not.
A diagram of a running system can be produced two ways: parse the declaration and lay it out,
or interrogate the system and lay *that* out. The first is far easier — the declaration is
already parsed, already validated, already in memory.
It is also worthless for the only question worth asking of such a picture: *is what is running
what I asked for?* A drawing built from the request and captioned **as raised** answers that
question with the request, which always agrees with itself.
This is the same fault as
[04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md) — a firewall key declared in
five manifests and read by no code, so a manifest appears to restrict a port and restricts
nothing. The declaration was never wrong. Nothing ever asked the system.
## Considered options
1. **Draw the declaration, and label it honestly.** Cheap, and useful for review before
anything is raised. Insufficient alone: it can never disagree with itself.
2. **Draw the system, inferring the rest from the declaration where the system is silent.**
The tempting middle. Rejected — a picture where some facts are observed and some are
assumed has no honest caption, and the assumed ones are exactly the interesting ones.
3. **Two pictures, one layout, neither borrowing from the other.** Chosen.
## Decision
**A picture captioned *as raised* reads only the running system.** It never opens the
declaration, not even for a fact the system happens not to record.
Where the hypervisor does not natively hold a fact the picture needs — whether a segment is
public, what a gateway translates, whether a machine refuses inbound — **the raise records it
on the resource** as metadata, and the picture reads it back from there.
That recording carries its own rule, which is the substance of this decision rather than an
implementation note:
> **A behavioural tag is written after the behaviour works, never when the resource is
> created.**
Written at creation, a tag restates the request in a new location and inherits none of the
authority of having happened. A failed raise deliberately leaves its wreckage standing, so a
tag written up front would let a picture of that wreckage badge translation the gateway was
never configured to do — reproducing, inside the tool built to catch the fault, exactly the
fault.
So: the gateway is tagged after its ruleset applies; the machine after the read-back proves
its firewall loaded.
Both pictures render through **one layout**, so they can be put side by side and the
difference read off directly.
## Consequences
- **It earned itself on the first comparison.** Drawn side by side, every virtual machine in
the live picture held no addresses at all. A container's interface carries the name of the
device it was configured as; a virtual machine names its own — so joining addresses to
devices by name attached every address to a container and none to a VM. Nothing failed;
a whole class of machine silently lost its addresses. The two pictures disagreed, so it was
visible in seconds. It is now joined on MAC.
- Raise does more work, and writes metadata it does not itself consume. Accepted: the cost is
a few config keys, and it is what makes a raised instance self-describing.
- A resource raised before a tag existed is missing it. The reader says so rather than filling
the gap from the declaration — an untagged link draws as unknown, not as what the file said
it should be.
- **The rule generalises past diagrams.** Anything reporting on the mesh — a status view, an
inventory, a health check — is subject to it. A report assembled from the intended state is
not a report.
- The declared picture stays, and stays useful: it is review before raising, and it is one half
of the comparison. It carries no runtime status, because it cannot know any.
- **The constitution sync is not done and must not be.** `how-we-build.md` §5 carries this rule
marked *proposed*, and playbook
[05](../00-META/process/05-constitution-sync.md) publishes the derived page only for rules the
mesh should enforce now. A rule the mesh enforces before a second person has agreed to it is
the failure mode §6 exists to prevent. The sync happens when this record is accepted, and this
line is what makes the gap visible rather than silent.
## References
- [ADR 0034](0034-a-test-defends-a-decision.md) — a claim nothing checks stops being true.
- [ADR 0031](0031-the-lab-provides-the-underlay.md) — why the lab must not supply what the
mesh is responsible for; the same instinct, applied to facts rather than to configuration.
- [04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md) — the fault in production
form.