Order the records the way the system is learned

Jochen asked whether the order made sense. It did not -- it followed when
things happened to be decided, which after consolidation is fictional anyway
since record 5 alone folds decisions taken across a week.

Concretely wrong before: the domain statement sat at 8, after five engineering
rules; the constitution was scattered across 5, 12 and 17; the tiers landed at
15, 16, 21 and 22 with process records in between.

Now it walks: what the mesh is (1-3), its tiers from the bottom up (4-8), what
runs on them and how it gets there (9-10), how it is built (11-16), how it is
checked (17-18), how we work (19-23).

Two things made this safe rather than free. It is a permutation, not a
compaction, so the renames go through temporary names -- otherwise two files
want one slot and one is lost. And the reference rewrite is a single
simultaneous pass, because almost every number moved into a slot another number
was vacating; replacing one at a time would have cascaded and pointed things at
the wrong record while still resolving.

Verified: 284 [ADR NNNN](path) links across the repository, all with matching
text and target.

The ordering principle is now stated in 19 rather than left implicit -- the
repository already said "the numbering is the flow" about its folders, and
there was no reason for the records to be the exception.
This commit is contained in:
2026-08-28 23:30:42 +02:00
parent e1febe8e0f
commit 333356cff3
85 changed files with 471 additions and 465 deletions
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 8. The mesh brokers capabilities; nodes host; agents think
# 1. The mesh brokers capabilities; nodes host; agents think
## Context
@@ -182,7 +182,7 @@ existing pipeline. Nothing here requires a flag day, and nothing here is cheap.
- `modules/hal/sdk/src/feature-handlers/index.ts` — `FEATURE_HANDLERS`, the fixed handler
array that makes a feature a singleton per module
- `modules/postgres/tools/index.ts` — the adoption path that rotates a shared credential
- Mediahuis `papa-hq`, ADR 0005 *Composable, independently-shippable modules* — the
- Mediahuis `papa-hq`, ADR 0020 *Composable, independently-shippable modules* — the
constraints that make a unit independently shippable, applicable unchanged to features
- impire.io / soulstream — *the record* as integration substrate, personas over services,
and "cheap awareness and expensive thinking"
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: true
---
# 1. Nodes communicate over a message broker, not over HTTP
# 2. Nodes communicate over a message broker, not over HTTP
> Reconstructed after the fact from the evidence cited below. The decision was taken in
> implementation, not in a record; this document states what was decided and why, not a
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: true
---
# 7. An agent is a persistent employee, not an instance of a pool
# 3. An agent is a persistent employee, not an instance of a pool
> Reconstructed after the fact from the evidence cited below.
@@ -59,7 +59,7 @@ itself is an agent of a kind exempt from the hiring lifecycle.
or another agent is hired — both deliberate acts.
- The transition was not free. Lifecycle columns had to reach every query that selects an
agent, and the ones that were missed failed at the moment of hiring rather than at startup.
- This is the decision [ADR 0008](0008-mesh-brokers-nodes-host-agents-think.md) generalises:
- This is the decision [ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md) generalises:
one kind of participant, differing only in modality.
## References
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 15. A node, and how it joins
# 4. A node, and how it joins
*Consolidated 2026-08-28 from four records.*
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 16. The node host
# 5. The node host
*Consolidated 2026-08-28 from eight records. Tier 0 is one component and was decided over a
week; the reasoning is kept, the fragmentation is not.*
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 21. The substrate and the control plane
# 6. The substrate and the control plane
*Consolidated 2026-08-28 from six records.*
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 22. Connectivity
# 7. Connectivity
*Consolidated 2026-08-28 from three records. Overlay, resolution, exposure, filtering and
certificates are one design.*
@@ -3,14 +3,14 @@ status: accepted
date: 2026-08-26
deciders: jochen
reconstructed: false
extends: 0019-modules-and-the-graph.md
extends: 0009-modules-and-the-graph.md
---
# 20. A context owns its store, exclusively
# 8. A context owns its store, exclusively
## Context
[ADR 0019](0019-modules-and-the-graph.md) settles what a module
[ADR 0009](0009-modules-and-the-graph.md) settles what a module
declares. This settles what a grant may be, and it is the half that **removes** things.
`how-we-build` §4 already says *contexts integrate through the record, never through a shared
@@ -45,7 +45,7 @@ modules is the mesh showing its own data, not a boundary crossing. What is forbi
### Asking or subscribing is derived, not chosen
[ADR 0015](0015-a-node-and-how-it-joins.md) makes disconnection an ordinary situation. So:
[ADR 0004](0004-a-node-and-how-it-joins.md) makes disconnection an ordinary situation. So:
- **Anything that must keep working while disconnected cannot ask** — there is nobody to ask. It
keeps a local copy, which means subscribing.
@@ -79,7 +79,7 @@ The first clear list of what the design deletes rather than adds:
- **Three contexts must move out of the registry database**, taking thirteen tables with them.
Their dependency on the registry then shrinks to almost nothing — one of them needs a single
table.
- **The node appliers were already handled.** [ADR 0016](0016-the-node-host.md)
- **The node appliers were already handled.** [ADR 0005](0005-the-node-host.md)
stopped the host querying the mesh database for tier reasons unrelated to this, and it removes
most of the remaining direct readers as a side effect.
- **What a consumer does about events missed while disconnected is not decided** — replay from a
@@ -90,4 +90,4 @@ The first clear list of what the design deletes rather than adds:
- [`how-we-build.md`](../00-META/how-we-build.md) §4 — the rule this makes enforceable.
- [Research 011](../01-RESEARCH/011-the-module-graph/worked-provider.md) — the count, the worked
provider, and the dashboard case.
- [ADR 0015](0015-a-node-and-how-it-joins.md) — why asking or subscribing is derived.
- [ADR 0004](0004-a-node-and-how-it-joins.md) — why asking or subscribing is derived.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 19. Modules and the graph
# 9. Modules and the graph
*Consolidated 2026-08-28 from six records.*
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 23. Delivery
# 10. Delivery
*Consolidated 2026-08-28 from five records.*
@@ -5,13 +5,13 @@ deciders: jochen
reconstructed: true
---
# 2. Managed files are generated onto nodes and never edited there
# 11. Managed files are generated onto nodes and never edited there
> Reconstructed after the fact from the evidence cited below.
## Context
[ADR 0021](0021-the-substrate-and-the-control-plane.md) put every binding in the mesh
[ADR 0006](0006-the-substrate-and-the-control-plane.md) put every binding in the mesh
database. But the things that consume those bindings — environment files, service
definitions, daemon configuration, firewall rules — are files on a node's disk, because that
is what the software reading them requires.
@@ -26,7 +26,7 @@ directions at once.
1. **Bidirectional sync** — a node's edits flow back to the database. Rejected, and removed.
Two writers and no arbiter: whichever synced last wins, and neither is authority.
2. **Files are authoritative; the database is a cache of them.** Rejected — it inverts
ADR 0003 and returns to state that cannot be reconciled across nodes.
ADR 0013 and returns to state that cannot be reconciled across nodes.
3. **Strictly one-directional: the database is written, files are generated.** Chosen.
## Decision
@@ -5,11 +5,11 @@ deciders: jochen
reconstructed: false
---
# 10. The mesh creates no symlinks — a derived file is a copy
# 12. The mesh creates no symlinks — a derived file is a copy
## Context
[ADR 0010](0010-the-mesh-creates-no-symlinks.md) responded to production data loss — a hand-made
[ADR 0012](0012-the-mesh-creates-no-symlinks.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.
@@ -24,7 +24,7 @@ Two things have changed since, and together they remove the argument that kept i
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 0002](0002-managed-files-are-generated-never-edited.md) established that
**It is not.** [ADR 0011](0011-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
@@ -33,12 +33,12 @@ 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 0021](0021-the-substrate-and-the-control-plane.md) and ADR 0002 exist to prevent.
[ADR 0006](0006-the-substrate-and-the-control-plane.md) and ADR 0011 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.
1. **Keep ADR 0019 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
@@ -53,12 +53,12 @@ State is derived onto nodes; it does not reach back.
**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 0002](0002-managed-files-are-generated-never-edited.md)).
([ADR 0011](0011-managed-files-are-generated-never-edited.md)).
The prohibition in ADR 0011 stands and widens: it ceases to be "only the installer may link"
The prohibition in ADR 0019 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
When this is accepted, ADR 0019 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
@@ -72,7 +72,7 @@ the rule exists at all, and the incident behind it is the reason anyone believes
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 0023](0023-delivery.md)).
this mesh keeps producing ([ADR 0010](0010-delivery.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.
@@ -89,12 +89,12 @@ the rule exists at all, and the incident behind it is the reason anyone believes
- **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.
Until those are answered this record stays `proposed`, and ADR 0019 remains the governing rule.
## References
- [ADR 0010](0010-the-mesh-creates-no-symlinks.md) — the incident, and the rule this widens.
- [ADR 0002](0002-managed-files-are-generated-never-edited.md) — the machinery that makes a
- [ADR 0012](0012-the-mesh-creates-no-symlinks.md) — the incident, and the rule this widens.
- [ADR 0011](0011-managed-files-are-generated-never-edited.md) — the machinery that makes a
copy safe.
- [`03-DESIGN/00-as-is/05-runtime-and-installation.md`](../03-DESIGN/00-as-is/05-runtime-and-installation.md)
— what the installer does today, including reconciliation and adoption.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: true
---
# 3. Schema and state changes are numbered migrations, in the same language as the code
# 13. Schema and state changes are numbered migrations, in the same language as the code
> Reconstructed after the fact from the evidence cited below.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: true
---
# 4. No workspace — each module is a standalone package consuming published dependencies
# 14. No workspace — each module is a standalone package consuming published dependencies
> Reconstructed after the fact from the evidence cited below.
@@ -47,7 +47,7 @@ its dependencies' freshly published versions.
- Development and the pipeline resolve imports identically. The divergence is gone by
construction rather than by discipline.
- A module in its own repository is not a special case. It builds exactly as a module in the
monorepo does — which is what makes [ADR 0006](0006-applications-live-in-their-own-repository.md)
monorepo does — which is what makes [ADR 0015](0015-applications-live-in-their-own-repository.md)
cheap.
- A cross-package change costs a publish-and-consume round trip. This is the real price, paid
on every shared-library change.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: true
---
# 6. Applications live in their own repository; the monorepo is for the mesh
# 15. Applications live in their own repository; the monorepo is for the mesh
> Reconstructed after the fact from the evidence cited below.
@@ -46,7 +46,7 @@ reject it.
- An application's cadence is its own. It is not reviewed as mesh code and does not queue
behind mesh work.
- The separation is safe **only because** the pipeline and provisioning are identical either
side of it — which [ADR 0004](0004-no-npm-workspace.md) is what makes true. Without
side of it — which [ADR 0014](0014-no-npm-workspace.md) is what makes true. Without
standalone packages this decision would fork the build.
- The monorepo stops being an inventory of the installation, which is a precondition for
publishing anything about it.
@@ -59,6 +59,6 @@ reject it.
- The rule is stated in the governed constitution page authored 2026-07-10, §3, as a
convention violation reviewers must reject.
- [ADR 0008](0008-mesh-brokers-nodes-host-agents-think.md) decision 4 extends this from *new*
- [ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md) decision 4 extends this from *new*
applications to the modules already in the monorepo.
- Knowledge base: `troubleshooting/unregistered-module-source`.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 9. The lab
# 16. The lab
*Consolidated 2026-08-28 from five records. The lab is one design and was split across five
decisions taken over three days; the reasoning is kept, the fragmentation is not.*
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 13. A test defends a decision
# 17. A test defends a decision
## Context
@@ -3,16 +3,16 @@ status: accepted
date: 2026-08-24
deciders: jochen
reconstructed: false
extends: 0013-a-test-defends-a-decision.md
extends: 0017-a-test-defends-a-decision.md
---
# 14. A picture of a system is read from the system, never from what asked for it
# 18. A picture of a system is read from the system, never from what asked for it
## Context
A scenario declaration is a file. A raised scenario is a set of machines, links and rulesets.
The two are supposed to correspond, and the entire value of the lab rests on noticing when
they do not — [ADR 0013](0013-a-test-defends-a-decision.md) says a claim nothing checks is a
they do not — [ADR 0017](0017-a-test-defends-a-decision.md) says a claim nothing checks is a
claim that will quietly stop being true.
Drawing a scenario makes that concrete, and forces a choice that looks cosmetic and is not.
@@ -91,8 +91,8 @@ difference read off directly.
## References
- [ADR 0013](0013-a-test-defends-a-decision.md) — a claim nothing checks stops being true.
- [ADR 0009](0009-the-lab.md) — why the lab must not supply what the
- [ADR 0017](0017-a-test-defends-a-decision.md) — a claim nothing checks stops being true.
- [ADR 0016](0016-the-lab.md) — why the lab must not supply what the
mesh is responsible for; the same instinct, applied to facts rather than to configuration.
- [04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md) — the fault in production
form.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 11. How this repository works
# 19. How this repository works
*Consolidated 2026-08-28 from ten records that were one decision seen from ten angles. The
reasoning is kept; the fragmentation is not.*
@@ -36,7 +36,7 @@ company-scoped one does not. So this is `hq` and the mesh's are `mesh-*`.
| `novox/mesh-substrate` | 1 | the pinned tier-1 services, as declarations |
| `novox/mesh-control` | 2 | the control plane and its contexts |
| `novox/mesh-surfaces` | 3 | tools, web, cli — thin, no logic |
| `novox/mesh-sdk` | — | the mesh's own domain ([ADR 0019](0019-modules-and-the-graph.md)) |
| `novox/mesh-sdk` | — | the mesh's own domain ([ADR 0009](0009-modules-and-the-graph.md)) |
| `novox/mesh-lab` | — | the lab: scenario lifecycle, networking, placement |
| `novox/hq` | — | this one |
@@ -77,6 +77,12 @@ settled. Everything else belongs in the design document, where the reasoning is
**There is no ledger** — no separate document summarising, ranking or tracking decisions. A
chronological view is generated from frontmatter, which is what a ledger was actually for.
**The numbering is the flow here too.** Records are ordered the way somebody would learn the
system — what the mesh is, then its tiers from the bottom up, then what runs on them and how it
gets there, then how it is built, how it is checked, and how we work. **Not chronologically**: the
date is in the frontmatter and a consolidated record holds decisions taken across a week, so
ordering by age would order by an accident that no longer exists.
**The design layer is what you read.** These records explain *why* a thing is as it is. They are
not a description of the system, and needing to read them to understand it would mean the design
documents had failed.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: true
---
# 5. The mesh is governed by a constitution, injected where work is decided
# 20. The mesh is governed by a constitution, injected where work is decided
> Reconstructed after the fact from the evidence cited below.
@@ -5,11 +5,11 @@ deciders: jochen
reconstructed: false
---
# 12. HQ is the source of the mesh constitution
# 21. HQ is the source of the mesh constitution
## Context
[ADR 0005](0005-the-mesh-is-governed-by-a-constitution.md) established a canonical rule set,
[ADR 0020](0020-the-mesh-is-governed-by-a-constitution.md) established a canonical rule set,
injected into every eligible design session and checked before output is accepted. It lives in
the knowledge base, where the orchestrator reads it.
@@ -39,7 +39,7 @@ directly.
Publishing is a playbook step, not a manual act, and it ends with **reading the page back and
verifying the change is present**. A publish that reported success and did nothing is exactly
the failure class this mesh keeps producing
([ADR 0023](0023-delivery.md)).
([ADR 0010](0010-delivery.md)).
Section numbering is stable, because the orchestrator and the review fragments cite sections by
number.
@@ -47,7 +47,7 @@ number.
## Consequences
- One source, many surfaces — the same argument HQ's separation already rests on
([ADR 0011](0011-how-this-repository-works.md)), applied to the rules themselves.
([ADR 0019](0019-how-this-repository-works.md)), applied to the rules themselves.
- Each rule keeps the incident that earned it, in a place that is reviewed as a diff.
- An edit to the derived page survives until the next sync and then vanishes. The playbook says
so, and nothing mechanically prevents it.
@@ -63,5 +63,5 @@ number.
- [`00-META/process/05-constitution-sync.md`](../00-META/process/05-constitution-sync.md) —
the sync, including the read-back.
- [ADR 0005](0005-the-mesh-is-governed-by-a-constitution.md) — the governed page and why it
- [ADR 0020](0020-the-mesh-is-governed-by-a-constitution.md) — the governed page and why it
exists.
@@ -5,11 +5,11 @@ deciders: jochen
reconstructed: false
---
# 17. The constitution absorbs what is already enforced
# 22. The constitution absorbs what is already enforced
## Context
[ADR 0012](0012-hq-is-the-source-of-the-constitution.md) makes this repository the source
[ADR 0021](0021-hq-is-the-source-of-the-constitution.md) makes this repository the source
and the knowledge base a derived copy, and playbook
[05](../00-META/process/05-constitution-sync.md) publishes the copy whenever a rule changes.
@@ -97,8 +97,8 @@ trusting the second success message either.
## References
- [ADR 0012](0012-hq-is-the-source-of-the-constitution.md) — source and copy.
- [ADR 0005](0005-the-mesh-is-governed-by-a-constitution.md) — why the copy is injected at all.
- [ADR 0021](0021-hq-is-the-source-of-the-constitution.md) — source and copy.
- [ADR 0020](0020-the-mesh-is-governed-by-a-constitution.md) — why the copy is injected at all.
- [Playbook 05](../00-META/process/05-constitution-sync.md) — the sync this record interrupts.
- [ADR 0013](0013-a-test-defends-a-decision.md), [ADR 0014](0014-a-picture-is-read-from-what-runs.md),
[ADR 0010](0010-the-mesh-creates-no-symlinks.md) — the three rules whose sync surfaced this.
- [ADR 0017](0017-a-test-defends-a-decision.md), [ADR 0018](0018-a-picture-is-read-from-what-runs.md),
[ADR 0012](0012-the-mesh-creates-no-symlinks.md) — the three rules whose sync surfaced this.
@@ -5,7 +5,7 @@ deciders: jochen
reconstructed: false
---
# 18. The approval is the checkpoint, not the second pair of hands
# 23. The approval is the checkpoint, not the second pair of hands
## Context
@@ -73,5 +73,5 @@ the previous sync reported success and changed nothing.
## References
- [`how-we-build.md`](../00-META/how-we-build.md) §2 — the rule, now carrying this.
- [ADR 0023](0023-delivery.md) — the standard a checkpoint is held to: a
- [ADR 0010](0010-delivery.md) — the standard a checkpoint is held to: a
step that reports success without doing anything is the fault, not the shortcut.
+1 -1
View File
@@ -15,7 +15,7 @@ The records run in the order the decisions were taken, oldest first.
**Every decision is a record.** There is no ledger and no index file — if a decision is worth
recording it is worth a record, and if it is not worth a record it is not recorded
([ADR 0011](0011-how-this-repository-works.md)). A "decision" small enough to be one line is
([ADR 0019](0019-how-this-repository-works.md)). A "decision" small enough to be one line is
almost always a **rule**, and a rule belongs in
[`00-META/how-we-build.md`](../00-META/how-we-build.md), where it is enforced and keeps the
incident that earned it.