ADR 0217, to-be 44: no change to a machine takes effect unseen

Two incidents in two days had one shape: a change took effect that nobody saw first. Three guards
where the change is made — a settings layer read before it is replaced, a push that says what it
will change, a running module's data move acknowledged — each silent when nothing is at stake.
This commit is contained in:
2026-10-05 15:14:28 +02:00
parent d222091fe9
commit e7126bc1e6
3 changed files with 141 additions and 0 deletions
@@ -0,0 +1,59 @@
---
layer: to-be
status: designed
code: []
updated: 2026-10-05
decisions:
- 02-DECISIONS/0217-no-change-to-a-machine-takes-effect-unseen.md
- 02-DECISIONS/0030-data-outlives-the-mesh-that-declared-it.md
---
# 44 — No change to a machine takes effect unseen
**A change that removes, moves or replaces something a machine runs is shown before it takes effect;
a change that only adds goes through as before** (ADR 0217). Three guards, each at the place the
change is made.
## 1. A settings layer is read before it is replaced
A module's settings are layers — the whole mesh, and one per machine — each replaced whole when set.
That stays. Around it:
- **Reading.** `settings show <module> [--node]` prints the layers as they are, and the console verb
answers the same.
- **Saying what changed.** Setting a layer answers with the keys it adds, changes and removes,
compared with the layer it replaces.
- **Refusing a silent removal.** A set that would remove a key is refused, naming the keys, unless
`--replace` says the removal is meant. A set that only adds or changes keys needs nothing.
- **Keeping the previous layer.** Each set and each clear records the layer it replaced and when, so
the previous value is read back with `settings show --history`, not from a backup.
## 2. A push says what it will change
- **What was sent is kept.** Each send records the declaration it sent the machine, beside the digest
already kept. It is read only to compare; what a machine *should* be is still composed from the
mesh's records every time.
- **`plan <node> --diff`** compares what would be sent now with what was sent last: resources added,
removed and changed by id, and for a changed container the fields that differ — which is what
recreates it. A machine with nothing to change shows nothing.
- **`push` with no machine is refused** unless `--all` names that intent. A push to one machine, or
`--behind`, is unchanged; the console verb never pushed every machine and still does not.
## 3. Moving a running module's data is acknowledged
Before sending, each machine's declaration is compared with what it was last sent. **A container
that keeps a mount at the same place inside it, with a different directory on the machine behind
it**, is a module's data moving — or, as on 2026-10-05, a module about to run on an empty directory
because a placement was lost. That machine's push is held, saying the module, the mount and both
directories; the other machines in the same push go ahead. `push … --move <module>` sends it.
A first send to a machine, a new container, a mount added or removed, and a changed image are not
moves and are not held.
## How it is checked
Controller tests: a set dropping a key without `--replace` is refused and changes nothing, and with
it the answer names the removed key and the history holds the layer it replaced; `plan --diff` of an
unchanged machine is empty and names a changed container's changed field; a push changing a running
container's mount source is held for that machine only and goes with `--move`; `push` with no machine
and no `--all` is refused.