Files
hq/02-DECISIONS/0011-the-installer-owns-linking.md
T
jschoubben c0b35652d0 The numbering is the flow: decisions are 02, design is 03
papa-hq reads 01 research -> 03 decision -> 02 design. The order is a
scar, not a choice: 02-DESIGN existed from its initial commit, and when
adr/ was finally promoted on 2026-07-13 it took the next free number
rather than its place in the sequence. By then design was too settled to
renumber.

hal-hq was three commits old, so it is not. adr/ becomes 02-DECISIONS and
02-DESIGN becomes 03-DESIGN, and following the folder numbers now walks
the process in the order it happens: research produces a decision, the
decision authorises a design.

00-GENESIS becomes 00-META, matching papa's rename from the same
restructure.

Every path reference rewritten across documents, frontmatter, playbooks
and skills. All links resolve; all 58 frontmatter blocks parse and their
path fields still point at files that exist.
2026-08-23 18:05:11 +02:00

2.9 KiB

status, date, deciders, reconstructed
status date deciders reconstructed
accepted 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

  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 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.