Research 026/03: what the predecessor taught; issue 231: a misspelled placeholder is written out as text

This commit is contained in:
jochen
2026-10-04 11:49:19 +02:00
parent 61e70b9395
commit 8712d666bf
4 changed files with 94 additions and 0 deletions
@@ -60,3 +60,4 @@ desktops. Measured in [01](01-what-the-workstations-run.md):
- [01 — What the workstations run](01-what-the-workstations-run.md): evidence.
- [02 — The questions and the options](02-the-questions-and-the-options.md)
- [03 — What the predecessor taught](03-what-the-predecessor-taught.md): its 128 modules and 3,395 commits, as patterns to keep and failures not to repeat; shared with research 027.
@@ -0,0 +1,51 @@
# 03 — What the predecessor taught
A study on 2026-10-04 of the retired predecessor:
- its 128 module manifests, their hooks, its installer and its sync engine;
- 3,395 commits of history;
- what it left on four machines.
This document holds what bears on the graphical session and on the system layer
([research 027](../027-the-system-layer-as-modules/00-overview.md)). The evidence is in the
predecessor's history. A commit is cited here by what it fixed, not by its hash, because the
repository is private.
## Keep: what worked
| pattern | where it shows | in the mesh |
|---|---|---|
| ownership marked inside the file: inside the markers is reconciled, outside is kept verbatim | a block marker in a shared file, after an engine that rewrote whole files | kept regions (ADR 0174, ADR 0204) |
| two writers get two files and an `include`, the include first | the ssh client's configuration, after two writers fought over one file | ADR 0203's two files; research 026 C1 |
| one writer per file, one authority per action | only the reload watcher restarts the window manager, after three mechanisms each did | ADR 0182 |
| refuse to write when the source of truth is unreadable; never empty a block because a query found nothing | a block of names was emptied by a failed query | — keep |
| an unresolved template variable fails the install | a literal unfilled path was installed green | [issue 231](../../04-ISSUES/231-a-misspelled-placeholder-is-written-out-as-text/00-report.md): the mesh still has this gap |
| prune only what you can prove you placed | stale files from earlier deliveries | ADR 0189 |
| ensuring never rotates a credential | a silent rotation caused a retry storm, a ban of the shared address and a lost registry | ADR 0114 |
| vendor only the files you use; never clone and link | three files instead of 77 MB | ADR 0205 |
| copy, never symlink | a recursive delete followed a link, and every reinstall failed | ADR 0012 |
| verification says what it did not check | a verifier said *clean* while the secret was still on disk | — keep |
| alert once per condition | 411 alerts hid a 28-hour outage | ADR 0090 |
## Do not repeat
| failure | what it did | the mesh instead | where the mesh is still exposed |
|---|---|---|---|
| **Flavors** | variant files and packages per machine type: a gate dropped, the first-seen variant won, packages never installed, the verifier ignored the gate. On the day of the study a desktop carried a laptop model's fragments | one module per piece, assignment per machine (ADR 0174, research 026 §1) | a setting that switches which whole file is rendered is a flavor under another name |
| **The freeze** | existing values outranked new defaults; templated files were rendered once (*copy if absent*) | files are generated (ADR 0011) | a created-once file (ADR 0087) is a deliberate freeze, and a push must say *kept* |
| **Adopting drift** | a *merge* strategy made a local edit the record forever; switching strategies clobbered a person's model choice | nothing is read back (ADR 0174) | an edit outside a kept region is overwritten **silently**. The predecessor's *why is this back* loop: the push should name what it overwrote |
| **Environment templating** | `${VAR}` matched any name; unresolved names stayed literal; comments and destination paths were interpolated | namespaced placeholders; `$` forbidden in contributed values (ADR 0203) | issue 231 |
| **Hooks with privilege** | install hooks ran `sudo`, `chsh`, `systemctl`, `git clone` and `curl`, and swallowed failures into a warning | the `user` shape, the service shape, archives (ADR 0176, 0177, 0205) | the agent module writes under `/etc` from its own tool through `sudo` (no keep-original, no give-back); the prompt's helper downloads itself unpinned; that the operator escalates without a prompt is assumed by three modules and declared by none (research 027) |
| **Secrets in environment files** | `.env` files left world-readable; the decryption key beside what it decrypts; a deleted secret stayed in the file, so rotation was a no-op | the vault (ADR 0113, 0114) | a predecessor file of secrets is still sourced into the graphical session on two machines (research 027 Q2) |
| **Symlinks into a home** | a system file linked into a person's home | ADR 0012 | on the control machine, a fail2ban action file is still a predecessor link into its home tree. Deleting that tree would silently break the repeat-offender jail. The mesh's fail2ban module must own it as a file first |
| **Green while broken** | a recorded version frozen for four months; a verifier passing what it skipped | ADR 0134, 0145, 0184 | issue 230: a plan waiting for ever reads as healthy |
## What it means here
- **Research 026:** the desktop's 88 flavor-gated files and 92 theme variables are the flavor and
templating failures in one module. Question 1 (one module per piece) and question 6 (hardware
modules, settings later) are the answer, and nothing in the new modules may switch whole files on a
setting.
- **Research 027:** the hooks that installed the AUR helper, enabled the login manager and changed
shells are what the `package`, `service` and `user` shapes replace. Every remaining `sudo` in a module's
own code is a debt to be named, starting with the agent module.
@@ -51,3 +51,4 @@ matters on their own.
- [01 — What the machines run](01-what-the-machines-run.md): evidence.
- [02 — Candidates and questions](02-candidates-and-questions.md)
- The predecessor's lessons, shared with research 026: [026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md)
@@ -0,0 +1,41 @@
---
status: open
opened: 2026-10-04
located-in:
- mesh-controller
fixed-by:
amended-design:
---
# 231 — A misspelled placeholder is written out as text
## What was observed
2026-10-04, while studying the predecessor's failures. A manifest was checked whose one file holds four
placeholders: `${shel:zsh:first}` (a misspelled namespace), `${setting:Undeclared}` (a setting the module
does not declare), `${machnie:address}` (a misspelled namespace) and `${XDG_CACHE_HOME:-x}` (shell
syntax, which must pass through). The catalogue check, which runs the same functions registration does,
answered `ok`.
The controller fills each namespace it knows with its own pattern (`machine`, `setting`, `bound`, `dir`,
`port`, `secret`, `environment`, `shell`, …). A word in that shape that no pass consumes is left in the
file as it was written. A misspelling therefore reaches a machine as literal text, in a configuration
file that then reads it as a value.
## Why it matters beyond this instance
This is exactly the predecessor's failure: an unresolved template variable in a destination path
installed green, and a literal `${...}` path stood under `/etc/ssl` until somebody looked. The mesh's
namespaced placeholders were meant to end it ([ADR 0164](../../02-DECISIONS/0164-a-setting-is-declared-with-its-default-its-meaning-and-what-changing-it-costs.md)),
and they do for every name spelled right.
## What a fix has to settle
After every pass, a final sweep refuses any remaining `${<word>:` whose word is a lower-case
namespace-shaped token, naming the module, the field and the token, at the catalogue check and at
composition. Shell syntax (`${NAME:-…}`, `${(%):-…}`, `${1:-.}`) is not namespace-shaped and passes. So
does contributed shell code, which no pass reads (ADR 0204). A setting a module uses but does not
declare is refused the same way.
How it is checked: the controller's test with the four placeholders above, three refused by name and
one passed through.