Research efforts move from status.md to 00-overview.md with active / graduated / abandoned, matching papa-hq so the two repositories read the same way. Playbooks, skills, README and the ledger follow. Reverses yesterday's withdrawal of the symlink note in GENESIS. The note was right and the withdrawal was wrong: the intent is that the mesh creates no symlinks at all, so a founding document listing "symlinks, not copies" as a design principle does point the opposite way from where this is going, and that is a contradiction rather than a stale detail. ADR 0018 records the position, proposed. ADR 0011 stays as it is — it is the historical decision and the incident behind it is why anyone believes either record — and is superseded in intent, not edited. Its one editorial line, which called the wider reading false, is corrected to state what is actually true: centralising who may link narrowed the incident class without closing it, because a link the installer makes resolves exactly like one made by hand. The argument that kept linking was staleness. ADR 0004 removed it: every managed file is already derived and reconciled, so a copy is the natural form and a pointer into source is the shape the mesh's own model forbids everywhere else. What is not settled, and is marked open, is how staleness gets detected — which is the decision that makes or breaks it.
60 lines
2.9 KiB
Markdown
60 lines
2.9 KiB
Markdown
---
|
|
status: accepted
|
|
date: 2026-07-10
|
|
deciders: jochen
|
|
reconstructed: true
|
|
---
|
|
|
|
# 11. The installer owns linking; nothing else creates a symlink
|
|
|
|
> Reconstructed after the fact from the evidence cited below. The incident that earned the rule
|
|
> predates the record, and its date is not established here.
|
|
|
|
## Context
|
|
|
|
A service's definition lives in the module catalogue; its runtime directory and persistent data
|
|
live outside it. The mesh connects the two by linking the definition into the runtime location
|
|
— deliberately, so that runtime state and source stay separate while the running service reads
|
|
a current definition.
|
|
|
|
A link is also the easiest thing in the world to create by hand while fixing something, and a
|
|
container engine resolves a bind mount through it. A hand-made link pointed a volume somewhere
|
|
it should not have, and **production data was lost**.
|
|
|
|
## Considered options
|
|
|
|
1. **Copy instead of linking.** Rejected. A copy goes stale silently, which trades data loss
|
|
for a service running a definition nobody can find.
|
|
2. **Allow links, document the hazard.** Rejected. The hazard is not knowable at the moment of
|
|
the mistake — the link looks right and the resolution happens inside the container engine.
|
|
3. **One component owns linking; everyone else is forbidden.** Chosen.
|
|
|
|
## Decision
|
|
|
|
The installer creates and repairs every link the mesh needs. It reconciles them: a missing
|
|
link is created, a stale one is repointed, and a real file found where a link belongs is
|
|
adopted into the node's override location and replaced.
|
|
|
|
**Nothing else creates a symlink** — not a hook, not a fix, not an agent, not a person
|
|
debugging. The prohibition is absolute because the judgement required to make a safe exception
|
|
is exactly the judgement that was not available at the moment it mattered.
|
|
|
|
## Consequences
|
|
|
|
- The class of failure is closed, at the cost of a rule that reads as arbitrary to anyone who
|
|
has not seen the incident. That is why it is recorded here rather than only asserted.
|
|
- Links become reconcilable state rather than incidental filesystem facts.
|
|
- The rule is stated for humans and agents and is enforced by convention, not mechanism. A
|
|
check does not exist.
|
|
- The rule as written governs the mechanism rather than removing it. A link made by the
|
|
installer resolves the same way as one made by hand, so the hazard is narrowed and not
|
|
closed. [ADR 0018](0018-the-mesh-creates-no-symlinks.md) proposes widening this to "nothing
|
|
links, the installer included"; until that is accepted, this record governs.
|
|
|
|
## References
|
|
|
|
- Recorded as a non-negotiable in the governed constitution page, §2: *"Symlinks to repos or
|
|
service directories have caused production data loss via Docker volume path resolution. The
|
|
installer handles all linking. Never create symlinks manually."*
|
|
- Knowledge base: `services` — the reconciliation behaviour, including adoption of real files.
|