Follow papa-hq's research convention; the mesh links nothing

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.
This commit is contained in:
2026-08-23 09:29:09 +02:00
parent 702efca6bb
commit f05e4a0dce
17 changed files with 143 additions and 28 deletions
+4 -3
View File
@@ -46,9 +46,10 @@ is exactly the judgement that was not available at the moment it mattered.
- 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 is regularly misread as "the mesh does not use symlinks", which is false and makes
the design documentation look self-contradictory. It uses them; it centralises who may make
them.
- 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
+101
View File
@@ -0,0 +1,101 @@
---
status: proposed
date: 2026-08-23
deciders: jochen
reconstructed: false
extends: 0011-the-installer-owns-linking.md
---
# 18. The mesh creates no symlinks — a derived file is a copy
## Context
[ADR 0011](0011-the-installer-owns-linking.md) responded to production data loss — a hand-made
link, resolved through a container engine's volume handling, pointing a mount somewhere it
should not have — by centralising linking in the installer and forbidding it everywhere else.
That narrowed the incident class. It did not close it. The hazard is not *who* made the link;
it is that a path can resolve somewhere other than where it appears to. A link made by the
installer resolves exactly the same way as a link made by hand. The rule made the mechanism
rarer and better-governed while leaving the mechanism in place.
Two things have changed since, and together they remove the argument that kept it.
**The original case for linking was staleness.** A copy of a service definition goes stale
silently while the catalogue moves on, so a link was the cheap way to guarantee the running
node reads a current definition. That argument assumes the node's copy is unmanaged.
**It is not.** [ADR 0004](0004-managed-files-are-generated-never-edited.md) established that
everything on a node's disk is derived from the mesh and regenerated when its inputs change,
and the installer already **reconciles** links rather than assuming them — repointing stale
ones, adopting real files it finds where a link belongs. Reconciling content is the same
operation as reconciling a pointer, plus a comparison.
So the mesh already has the machinery that makes a copy safe, and is using a link to solve a
problem that machinery solves better. Worse, a link is conceptually the wrong shape: it makes
the node's runtime state a *pointer into source*, which is the one thing
[ADR 0003](0003-the-mesh-database-is-the-source-of-truth.md) and ADR 0004 exist to prevent.
State is derived onto nodes; it does not reach back.
## Considered options
1. **Keep ADR 0011 as the final position** — centralised linking, forbidden elsewhere.
Rejected as the status quo. It governs the mechanism rather than removing it, and the
failure it was written for remains reachable by any code path the installer trusts.
2. **Keep links but harden them** — canonicalise before mounting, refuse a link that escapes
an expected root. Rejected: it is a check bolted onto a hazard, and it has to be correct in
every consumer, including container engines the mesh does not control.
3. **Copy, reconciled by the installer, with staleness detected rather than assumed away.**
Proposed here.
## Decision
*Proposed — the position is settled; the migration is not designed. See "Open" below.*
**The mesh creates no symlinks.** A file a node needs is placed on that node as a real file,
derived from the mesh and reconciled by the installer like every other managed file
([ADR 0004](0004-managed-files-are-generated-never-edited.md)).
The prohibition in ADR 0011 stands and widens: it ceases to be "only the installer may link"
and becomes "nothing links, the installer included".
When this is accepted, 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.
## Consequences
- The path-resolution hazard is removed rather than governed. There is no link for a container
engine to resolve, so the class of failure that cost production data is closed by
construction.
- A node's runtime state stops pointing into source. What a node holds is derived output, which
is what the mesh's model already says it is everywhere else.
- **Staleness becomes a real problem that must be answered, not assumed away.** This is the
cost, and it is the whole cost: today a link cannot be stale, and a copy can. The answer has
to be detection — the installer comparing what is on disk against what the mesh says should
be — and it must be loud, because a silently stale definition is exactly the failure shape
this mesh keeps producing ([ADR 0008](0008-a-failed-step-fails-the-job.md)).
- Reconciliation gets more expensive: comparing content rather than checking a pointer's
target, on every module, on every node.
- Disk usage rises, trivially, and is not a consideration.
- Existing links must be converted. A node mid-migration holds both forms, so reconciliation
has to handle finding a link where a file now belongs — the mirror image of the adoption it
already does.
## Open
- **How staleness is detected.** Content hash, version marker, or regeneration on every
reconcile. This is the decision that makes or breaks the change and it is not taken here.
- **Whether anything must keep a link** for reasons outside the mesh's control. If something
does, that is a finding worth recording rather than an exception worth granting quietly.
- **Migration order.** Converting a node's links is a change to how its services resolve their
own definitions, which is not a change to make everywhere at once.
Until those are answered this record stays `proposed`, and ADR 0011 remains the governing rule.
## References
- [ADR 0011](0011-the-installer-owns-linking.md) — the incident, and the rule this widens.
- [ADR 0004](0004-managed-files-are-generated-never-edited.md) — the machinery that makes a
copy safe.
- [`02-DESIGN/00-as-is/05-runtime-and-installation.md`](../02-DESIGN/00-as-is/05-runtime-and-installation.md)
— what the installer does today, including reconciliation and adoption.
+1 -1
View File
@@ -36,7 +36,7 @@ outranks *"the dependency rule is not followed"*.
## Reconstructed records
Records 0001–0014 were written on 2026-08-23, after the decisions they describe. Those
Records 0001–0014 were written on 2026-08-23, after the decisions they describe. Records 0015 onward were taken as records. Those
decisions were taken in implementation rather than in a document; the records state what was
decided and the evidence it was decided from, and each carries `reconstructed: true` and says
so in its first lines.