ADR 0269 (proposed): what the mesh's own declaration stopped is started again when it goes
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

Unassigning the zfs module would leave the home server's pool with no scrub schedule: ADR 0118 never
starts a unit on the way out, so the distribution's timer the module held stopped would stay stopped.
The host now records what it declared beside what it found, and gives back only what the mesh's own
declaration stopped.
This commit is contained in:
jochen
2026-10-09 01:45:40 +02:00
parent 5b0b550157
commit 68018c7c1b
@@ -0,0 +1,90 @@
---
topic: what runs on it
status: proposed
date: 2026-10-09
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md
---
# 269. What the mesh's own declaration stopped is started again when it goes
## Context
[ADR 0118](0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md) gives a unit back as the
host found it, with one rule written against the direction of the rest: **never started on the way
out.** A unit the mesh stopped is not started again when its declaration goes, because "starting
something is a decision, and the operator makes it". When 0118 was written, the units the mesh stopped
were its own: a packet filter it had started, a guard it had loaded. Stopping a unit the machine ran
before the mesh came was not a case that existed.
It exists now. The `zfs` module owns the scrub schedule of the home server's pool. The machine already
had one: the distribution's `zfs-scrub-weekly@<pool>.timer`, enabled by hand, found running and enabled.
To leave exactly one schedule, the module declares that timer stopped and disabled, in the same
declaration that starts its own. Unassigning the module removes the module's own timer, which the mesh
made, and gives the distribution's back under 0118. That keeps it stopped and disabled. So the pool
would be left with **no scrub schedule at all**, and nothing would say so. The second review of
mesh-catalog #147 found this on 2026-10-09.
The host already knows half of what it needs. It records what it **found** (the unit's state and boot
before the mesh touched it) and keeps that from the first apply on. It did not record what the mesh
**declared**. So at removal it could not tell a unit that the mesh's own declaration stopped from one
that somebody stopped since.
## Considered Options
1. **Keep 0118 as it is, and have the module say a required step before unassigning** (re-enable the
distribution's timer by hand), with a health warning. Rejected: a step a person must remember is the
failure mode of the record ("failure must be loud", ADR 0227). The warning would come from a module
that is going away, so it would be said once and then never again.
2. **Start again, on removal, every unit found running.** Rejected: a unit somebody else stopped after
the mesh, an operator or another module, would be started behind their back. That is what 0118
refused, for good reason.
3. **Start again only what the mesh's own declaration stopped** — chosen. The host records what it
declared beside what it found. At removal, a unit found running that the declaration held stopped is
started again, and one found enabled that the declaration held disabled is enabled again.
## Decision
**The host records, for every service with a state, what the mesh declared: running or stopped, and
enabled or disabled where the declaration says. When the service's declaration goes, a unit that was
found running and declared stopped is started again, and one found enabled and declared disabled is
enabled again. The report says so as `restored`, and the plan says it before the apply does.**
0118's other rules stand:
- a unit found running and never stopped by the mesh is left running;
- a unit the mesh started is stopped again;
- a unit whose file the mesh wrote is the mesh's own;
- a unit with an unknown history is left alone. A record written before the host kept what it declared
says nothing about who stopped the unit, so nothing is started.
"Never started on the way out" now reads **never started on the way out, but for what the mesh's own
declaration stopped**. Starting something is still the operator's decision. Here the operator made it
before the mesh came, and the mesh's stopping it was the mesh's doing, which goes with the mesh.
## Consequences
- Unassigning the `zfs` module gives the home server back its distribution scrub timer, running and
enabled. A module may take over a machine's unit by holding it stopped, and its removal undoes that.
- A unit that is moved to another unit name (ADR 0118's "moved") is given back by the same rule.
- **Harder:** a unit the mesh stopped and that should *stay* stopped after the module goes (one that
was found running by accident) is now started again. Saying so needs a field on the resource, which
no module needs yet. Until one does, the plan's `restore … started … again` step is where it is seen.
## How it is checked
| Rule | Checked by |
|---|---|
| A unit found running and enabled, declared stopped and disabled, is started and enabled again when undeclared, and the report says `restored` | mesh-host `internal/apply/giveback_test.go`, `TestAUnitTheMeshStoppedAndDisabledIsGivenBackRunningAndEnabled` |
| A unit found stopped stays stopped; one never stopped by the mesh is left as it is | `TestOnlyWhatTheMeshsDeclarationStoppedIsStartedAgain` |
| A record without what was declared starts nothing | `TestARecordWithoutWhatWasDeclaredStartsNothing` |
| The plan says it before the apply does | `TestThePlanSaysAStoppedUnitIsStartedAgain` |
| 0118's existing rules | mesh-host `internal/apply/found_test.go`, unchanged and passing |
## References
- [ADR 0118](0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md), which this widens.
- mesh-host: the host's record (`store.Applied.Declared`), `startUnitBack` in `internal/apply/apply.go`,
and the plan's step in `internal/apply/plan.go`.
- mesh-catalog #147 (the `zfs` module's scrub schedule) and its second review, 2026-10-09.