0030, found by asking what the conversion actually needs rather than by reviewing anything. The host deleted a directory and everything under it when it stopped being declared — which happens when a module is unassigned, or when a manifest is edited to move a data folder, which is the exact operation this plan needs. A database's files, a mail spool. The report said "removed". A directory still holding something is now kept and said so. No flag and nothing to remember: emptiness is the test, and it works because the removal order was already right — the mesh's own contents are gone by the time the directory is reached, so what remains is by definition something nobody declared. The plan now says data outranks its own ordering: copy, read back through the service that owns it, and only then point anything at the new location. Never move and then check. And it records where this starts — the node holding all the production data — with what that costs stated rather than argued with. Everything proven so far was proven on machines that could be destroyed and raised again. A scenario proves the mechanism, not the state on that machine.
97 lines
4.9 KiB
Markdown
97 lines
4.9 KiB
Markdown
---
|
|
topic: the tiers
|
|
status: accepted
|
|
date: 2026-08-31
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 02-DECISIONS/0005-the-node-host.md
|
|
---
|
|
|
|
# 30. Data outlives the mesh that declared it
|
|
|
|
## Context
|
|
|
|
**The conversion runs on live services holding real data**, and starts on the node that holds all
|
|
of it. Identity, mail, everything. The requirement stated plainly: a data directory may be
|
|
*moved*, and may never be *lost*.
|
|
|
|
**The host deleted them.** A directory that stopped being declared was an orphan, and an orphan
|
|
directory was removed with `os.RemoveAll` — everything under it — while the report said
|
|
`removed`. A module unassigned took its database's files with it, and nothing anywhere said what
|
|
had been in there.
|
|
|
|
Reproduced before it was fixed: assign a module, let a service write into its directory, unassign
|
|
the module, and the file is gone.
|
|
|
|
**A directory stops being declared for ordinary reasons**, which is what makes this sharp rather
|
|
than theoretical. A module unassigned from a node. A manifest edited to move a data folder — the
|
|
exact operation the conversion needs. A resource renamed. A typo. **Every one of those is a normal
|
|
day's work, and every one of them was destructive.**
|
|
|
|
**The removal order was already right, and that is what makes a fix possible.** Everything the
|
|
mesh puts inside a directory is itself a declared resource, and orphans are removed in reverse
|
|
declaration order — so by the time a directory is reached, what the mesh wrote there is already
|
|
gone. Anything still present was put there by something else.
|
|
|
|
## Considered Options
|
|
|
|
1. **A `keep` flag on the directory.** A module declares which of its directories hold data, and
|
|
the host leaves those. **Rejected.** It is safe only when somebody remembered, and the failure
|
|
of forgetting is total and silent. A rule that protects data only when it was asked to is not
|
|
a rule about data, it is a rule about attentiveness — and this is the one place in the system
|
|
where being wrong does not recover.
|
|
|
|
2. **Never remove a directory.** Simple and unarguably safe. **Rejected**, narrowly: every module
|
|
ever assigned would leave its directories behind for ever, and a machine that accumulates
|
|
things nobody can account for is one where nobody can tell what is still in use. The clean-up
|
|
that is genuinely the mesh's is worth keeping.
|
|
|
|
3. **Remove a directory only when it is empty.** **Adopted.**
|
|
|
|
## Decision
|
|
|
|
**A directory that still holds anything is kept, and the mesh says so.** An empty one is removed.
|
|
|
|
**This is the host's existing line applied to the one shape where getting it wrong is
|
|
unrecoverable** — *it removes what it made and leaves what it merely configured*
|
|
([ADR 0005](0005-the-node-host.md)). An empty directory is what the host made. A full one is not.
|
|
|
|
**No flag, no declaration, nothing to remember.** Emptiness is the test, and it is derived from
|
|
the removal order rather than asserted: the mesh's own contents are gone by then, so what remains
|
|
is by definition something nobody declared.
|
|
|
|
**It is reported, not silent.** The outcome is `kept`, naming how many items are inside and saying
|
|
they are for a person to deal with. A directory quietly left behind is how a machine accumulates
|
|
things nobody can account for — which is the objection to option 2, and it is answered by saying
|
|
so rather than by deleting.
|
|
|
|
**Files are unchanged.** A declared file is the mesh's own — it wrote it, it owns it, and losing a
|
|
configuration file is not the failure this is about. The distinction is deliberate: **directories
|
|
hold what other things produced; files are what the mesh itself put there.**
|
|
|
|
## Consequences
|
|
|
|
**Moving a data directory is now safe by default.** The manifest changes, the old path stops being
|
|
declared, and the data stays where it is until somebody has looked at it. That was the operation
|
|
most likely to destroy something during the conversion, and it is now the operation that does the
|
|
least.
|
|
|
|
**Unassigning a module leaves its data.** Correct, and it means unassignment is no longer a way to
|
|
clean up — removing data is a person's act, done knowingly. Given what unassignment did before,
|
|
that is the trade being made and it is the right way round.
|
|
|
|
**A machine can accumulate directories nobody removed.** Accepted, and mitigated by saying so
|
|
every time rather than by a periodic sweep. A sweep would be the deletion this record exists to
|
|
prevent, on a timer, with nobody watching.
|
|
|
|
**It is not a backup, and must not be mistaken for one.** This stops the mesh destroying data. It
|
|
does nothing about a disk, a mistaken `rm`, or a service corrupting its own store. The conversion
|
|
still needs backups taken and **restored** before anything is moved — a backup nobody has restored
|
|
is a belief, not a copy.
|
|
|
|
## References
|
|
|
|
- [ADR 0005](0005-the-node-host.md) — the host removes what it made
|
|
- [`03-DESIGN/01-to-be/00-work-breakdown.md`](../03-DESIGN/01-to-be/00-work-breakdown.md) — the
|
|
conversion this was found by planning
|