Tier 0: the questions answered, the decisions taken, and the design #9
@@ -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.
|
||||
Reference in New Issue
Block a user