0034: a test defends a decision. §5 carried it marked "proposed, pending review"; the marker is removed and the rule now stands unqualified. The lab was already built to it, which is the inversion §5 exists to catch — closed now rather than left standing. 0018: the mesh creates no symlinks. §3 said the installer owns the links today and the INTENT was that the mesh creates none. It is no longer an intent, so the wording says so, and ADR 0011 becomes superseded rather than edited — its reasoning is why the rule exists at all, and the incident behind it is the reason anyone believes either record. The links the installer still reconciles are a migration, not a permission. The constitution sync (§6 step 4, playbook 05) is NOT done. An unsynced rule is a rule the mesh does not enforce, whatever this document says — and publishing it changes what every design meeting is checked against, so it wants saying out loud rather than doing quietly.
2.9 KiB
status, superseded-by, date, deciders, reconstructed
| status | superseded-by | date | deciders | reconstructed |
|---|---|---|---|---|
| superseded | 02-DECISIONS/0018-the-mesh-creates-no-symlinks.md | 2026-07-10 | jochen | 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
- Copy instead of linking. Rejected. A copy goes stale silently, which trades data loss for a service running a definition nobody can find.
- 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.
- 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 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.