245 lines
19 KiB
Markdown
245 lines
19 KiB
Markdown
# 02-DECISIONS
|
||
|
||
Architecture decision records — the "why" trail behind the rules in
|
||
[`00-META`](../00-META/) and the specifications in [`03-DESIGN`](../03-DESIGN/).
|
||
|
||
**Numbered `02` because a decision precedes the design it authorises.** Research concludes,
|
||
the decision is recorded here, and only then is the design written. Following the folder
|
||
numbers walks the process in the order it happens.
|
||
|
||
One file per decision, numbered, never deleted. A superseded record has its `status:` changed
|
||
and gains a pointer to what replaced it — **its reasoning is never rewritten**. The reasoning that
|
||
was rejected is the expensive half to rediscover.
|
||
|
||
## Progressive insight
|
||
|
||
A record is a decision, not a snapshot of everything that was true the day it was written, and
|
||
those two fail differently. **A fact a record asserted can turn out to be wrong while the decision
|
||
it supports stays right** — a count taken before anyone measured, a file named that does not
|
||
exist, a proof attributed to a step that cannot run it. Superseding a record for that buries a
|
||
correct decision under a second one, and teaches every reader to first work out which of two
|
||
records is live. Done a few times, the reading order stops being one.
|
||
|
||
So: **a correction of fact that leaves the decision standing is made in the record, in place,
|
||
marked and dated.**
|
||
|
||
> **Progressive insight — YYYY-MM-DD.** What was found, what the record said before, and what it
|
||
> says instead.
|
||
|
||
Three conditions, all of which hold:
|
||
|
||
- **It corrects a fact, not a judgement.** That a suite does not exist is a fact. That building it
|
||
is the wrong order is a judgement, and judgements supersede.
|
||
- **It adds; it never quietly replaces.** Where body text changes, the note says what stood there
|
||
before, so a reader who followed a citation to the old wording can find out what happened to it.
|
||
A correction nobody can see is indistinguishable from a record that was always right, which is
|
||
the failure the immutability rule exists to prevent.
|
||
- **The decision, the options weighed and the consequences stand untouched.** If the correction
|
||
changes what was decided, which alternatives were rejected, or a consequence another record
|
||
relies on, it is not an insight — write the superseding record.
|
||
|
||
**What still supersedes**, without exception: reversing a decision, changing its scope, rejecting
|
||
an option it accepted, or making a consequence false that a later record cites. The test is not
|
||
how large the edit looks in a diff; it is whether a reader who acted on the old text would now be
|
||
wrong about *what was decided* rather than about *a detail the decision did not rest on*.
|
||
|
||
**How this is checked.** `00-META/checks/records.py` requires every insight to be marked in the
|
||
form above and dated no earlier than the record's own `date:` — an unmarked edit is a rule
|
||
violation the reviewer looks for in the diff, and a marked one is legible in the record itself.
|
||
The git history is the backstop, not the record of intent; the note is the record of intent.
|
||
|
||
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 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.
|
||
|
||
## Frontmatter
|
||
|
||
```yaml
|
||
---
|
||
status: proposed | accepted | superseded
|
||
date: YYYY-MM-DD # when the decision was taken, not when it was written down
|
||
deciders: name
|
||
reconstructed: true|false # true when the record was written after the fact from evidence
|
||
superseded-by: # 02-DECISIONS/NNNN-....md, when status is superseded
|
||
extends: # 02-DECISIONS/NNNN-....md, when this record widens an earlier one
|
||
---
|
||
```
|
||
|
||
## Body
|
||
|
||
```
|
||
# N. Title in plain language
|
||
|
||
## Context what was true, with evidence
|
||
## Considered Options numbered, each with why it was rejected
|
||
## Decision what was decided
|
||
## Consequences what follows, including what got harder
|
||
## References commits, pull requests, knowledge-base entries, prior art
|
||
```
|
||
|
||
State evidence, not assertion. *"Zero of 124 modules declare `brain` as a dependency"*
|
||
outranks *"the dependency rule is not followed"*.
|
||
|
||
## Reconstructed records
|
||
|
||
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.
|
||
|
||
A reconstructed record is not a transcript. Where the deliberation is not recoverable, the
|
||
options section states what the alternatives were and why the chosen one won on the evidence
|
||
available — not a discussion that did not happen. Where a date is not establishable it says so
|
||
rather than guessing.
|
||
|
||
## Index
|
||
|
||
**A number identifies a record and never changes.** Records are referenced from outside this
|
||
repository — code comments, commit messages — so a number that moves invalidates them silently.
|
||
Renumbering once cost 96 references across two code repositories, and that is why the numbers
|
||
are now fixed.
|
||
|
||
So the folder is in creation order, and **the reading order lives here.** It is generated from
|
||
each record's `topic:` and written, because a reader looking at the folder on a forge sees the
|
||
folder rather than a command. The objection to a written index is that it drifts — which is
|
||
answered by checking it rather than by refusing to write one:
|
||
|
||
```
|
||
python3 00-META/checks/index.py --write regenerate
|
||
python3 00-META/checks/index.py fail if stale
|
||
```
|
||
|
||
<!-- index:start -->
|
||
|
||
### What the mesh is
|
||
|
||
- **0001** — [The mesh brokers capabilities; nodes host; agents think](0001-mesh-brokers-nodes-host-agents-think.md)
|
||
- **0002** — [Nodes communicate over a message broker, not over HTTP](0002-nodes-communicate-over-a-broker.md)
|
||
- **0003** — [An agent is a persistent employee, not an instance of a pool](0003-agents-are-persistent-employees.md)
|
||
- **0077** — [The parts are named controller, foundation, node — not control plane, substrate, master](0077-the-controller-and-the-foundation.md)
|
||
- **0083** — [One push leaves the mesh consistent](0083-one-push-leaves-the-mesh-consistent.md)
|
||
- **0088** — [The foundation filters before anything listens](0088-the-foundation-filters-before-anything-listens.md)
|
||
- **0090** — [A failure that repeats is said to be stuck](0090-a-failure-that-repeats-is-said-to-be-stuck.md)
|
||
- **0100** — [A node in use is adopted before it is converged](0100-a-node-in-use-is-adopted-before-it-is-converged.md)
|
||
- **0101** — [A machine's own resolver does not make it in use](0101-a-machines-own-resolver-does-not-make-it-in-use.md)
|
||
- **0102** — [The mesh writes into a shared file, never over it](0102-the-mesh-writes-into-a-shared-file-never-over-it.md)
|
||
- **0103** — [What an adopted node holds, and what its guard refuses](0103-what-an-adopted-node-holds-and-what-its-guard-refuses.md)
|
||
- **0104** — [A provision may be answered by an adapter to the predecessor](0104-a-provision-may-be-answered-by-an-adapter-to-the-predecessor.md)
|
||
- **0105** — [The mesh adopts the predecessor's tunnel in place](0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md)
|
||
- **0106** — [The bus is NATS](0106-the-bus-is-nats.md)
|
||
- **0116** — [The bus is built in five steps, and the protocol moves with it](0116-the-bus-is-built-in-five-steps.md)
|
||
|
||
### Its tiers, from the bottom up
|
||
|
||
- **0004** — [A node, and how it joins](0004-a-node-and-how-it-joins.md)
|
||
- **0005** — [The node host](0005-the-node-host.md)
|
||
- **0006** — [The substrate and the control plane](0006-the-substrate-and-the-control-plane.md)
|
||
- **0007** — [Connectivity](0007-connectivity.md)
|
||
- **0008** — [A context owns its store, exclusively](0008-a-context-owns-its-store.md)
|
||
- **0028** — [The substrate supplies the control plane and nothing else](0028-the-substrate-supplies-the-control-plane-and-nothing-else.md)
|
||
- **0029** — [A network is a shape, because an action cannot be undone](0029-a-network-is-a-shape-because-an-action-cannot-be-undone.md)
|
||
- **0030** — [Data outlives the mesh that declared it](0030-data-outlives-the-mesh-that-declared-it.md)
|
||
- **0031** — [The control plane authenticates nobody, so identity is a module](0031-the-control-plane-authenticates-nobody.md)
|
||
- **0033** — [The substrate is a store and a broker](0033-the-substrate-is-a-store-and-a-broker.md)
|
||
- **0036** — [Bootstrap ends at a usable mesh, and the first credential comes from a person](0036-bootstrap-ends-at-a-usable-mesh.md)
|
||
- **0066** — [Public routing is name-agnostic, its names are resolved inside the mesh, and an internal authority can certify them](0066-public-routing-is-name-agnostic.md)
|
||
- **0067** — [Genesis is a pivot: a temporary control plane installs the registry that makes it permanent](0067-genesis-is-a-pivot.md)
|
||
- **0070** — [The catalogue owns the module graph, and genesis builds rather than carries](0070-the-catalogue-owns-the-module-graph.md)
|
||
- **0071** — [Genesis clones from a mesh, and checks what it got](0071-where-genesis-gets-its-source.md)
|
||
- **0072** — [Two graphs, and a build chain that orders itself](0072-two-graphs-and-the-build-chain.md)
|
||
- **0073** — [The installer carries a builder, and the registry stays where it is](0073-the-installer-carries-a-builder.md)
|
||
- **0074** — [The mesh defines a module protocol; an SDK is an implementation of it](0074-the-wire-is-specified-not-the-types.md)
|
||
- **0075** — [An artifact store is a provision; a package registry is a different one](0075-two-stores-and-which-provides-what.md)
|
||
- **0078** — [The store and the broker are ordinary modules](0078-the-store-and-broker-are-modules.md)
|
||
- **0079** — [The foundation seats are named after their servers](0079-the-foundation-seats-are-named-after-their-servers.md)
|
||
- **0092** — [An operator delivers a pair credential, and the mesh never replaces it](0092-an-operator-delivers-a-pair-credential.md)
|
||
- **0094** — [A module may hold several secrets from one provider, each a pair of its own](0094-a-module-may-hold-several-secrets-from-one-provider.md)
|
||
- **0095** — [The control plane is the way to ask a module](0095-the-control-plane-is-the-way-to-ask-a-module.md)
|
||
- **0098** — [A fact a provider makes at first start is fetched from it, not carried in its manifest](0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md)
|
||
- **0108** — [A route carries the policy applied to a request, and names a secret rather than holding one](0108-a-route-carries-the-policy-applied-to-a-request.md)
|
||
- **0109** — [A package registry seat is one per ecosystem, not one for all of them](0109-a-package-registry-seat-is-one-per-ecosystem.md)
|
||
|
||
### What runs on them, and how it gets there
|
||
|
||
- **0009** — [Modules and the graph](0009-modules-and-the-graph.md)
|
||
- **0010** — [Delivery](0010-delivery.md)
|
||
- **0024** — [Model access is a provision, and a licence is a thing with a name](0024-model-access-is-a-provision.md)
|
||
- **0026** — [The mesh has a session of its own, and it is the node session's mechanism](0026-the-mesh-has-a-session-of-its-own.md)
|
||
- **0027** — [A provision names what the consumer is coupled to, not the role it plays](0027-a-provision-names-what-the-consumer-is-coupled-to.md)
|
||
- **0035** — [One implementation, several surfaces, and what that costs](0035-one-implementation-several-surfaces.md)
|
||
- **0038** — [The mesh assigns the port, and a module does not care](0038-the-mesh-assigns-the-port.md)
|
||
- **0040** — [What a module is](0040-what-a-module-is.md)
|
||
- **0041** — [Events are a relationship, the lighter sibling of provisioning](0041-events-are-a-relationship.md)
|
||
- **0042** — [The shape of an event on the wire](0042-the-shape-of-an-event-on-the-wire.md)
|
||
- **0043** — [A module's broker account is scoped by what it emits and consumes](0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md)
|
||
- **0044** — [A public name is provisioned, not registered by hand](0044-a-public-name-is-provisioned-like-any-capability.md)
|
||
- **0045** — [A machine's firewall is the sum of what its modules listen on](0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md)
|
||
- **0046** — [A module's configuration is its assignment's, not its manifest's](0046-a-module-configuration-is-its-assignments-not-its-manifest.md)
|
||
- **0047** — [A module runs its code as its own process, with its own account](0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md)
|
||
- **0048** — [A provider creates the credential the mesh minted, and seals nothing](0048-a-provider-creates-the-credential-the-mesh-minted.md)
|
||
- **0049** — [A consumer's identity is bounded by the tightest backend that must accept it](0049-a-consumers-identity-fits-the-tightest-backend.md)
|
||
- **0050** — [Model access is vendor-agnostic, and a vendor is an adapter](0050-model-access-is-vendor-agnostic.md)
|
||
- **0051** — [Shared data is the operator's, and a module is granted access to it](0051-shared-data-is-the-operators.md)
|
||
- **0052** — [An init step is a container run once to completion, gating what follows](0052-a-step-that-runs-once-before-a-container.md)
|
||
- **0053** — [A scheduled step is a container run on a recurring schedule](0053-a-step-that-runs-on-a-schedule.md)
|
||
- **0054** — [Model usage is a vendor-neutral record, produced by the adapter, at two grains](0054-model-usage-is-recorded-at-two-grains.md)
|
||
- **0055** — [Model access is answered by a licence, or by a node that hosts the model](0055-model-access-is-answered-by-a-licence-or-a-node.md)
|
||
- **0084** — [Which provider serves a consumer, when the mesh runs more than one](0084-which-provider-serves-a-consumer.md)
|
||
- **0085** — [A secret is a provision, and the vault is the module that provides it](0085-a-secret-is-a-provision.md)
|
||
- **0087** — [A seeded file is created once, and what grows in it is not the mesh's](0087-a-seeded-file-is-created-once.md)
|
||
- **0091** — [A mount is declared, and there are three things it can be](0091-a-mount-is-declared-three-ways.md)
|
||
- **0099** — [A step that runs once names what it reads, and runs again when it changed](0099-a-step-that-runs-once-names-what-it-reads.md)
|
||
- **0110** — [A seat is held by one assignment, from a closed set, and it may deliver a provision](0110-a-seat-is-a-module-assignment-from-a-closed-set.md)
|
||
- **0112** — [A module definition names no node, no mesh and no path: everything it needs is a requirement the mesh resolves](0112-a-module-definition-names-no-node-mesh-or-path.md) *(proposed)*
|
||
- **0113** — [The vault makes every shared secret, a provider makes resources and data, and the mesh carries both](0113-the-vault-makes-every-secret.md) *(proposed)*
|
||
- **0114** — [A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached](0114-a-shared-credential-rotates-over-two-credentials.md) *(proposed)*
|
||
- **0115** — [One assignment of a module per node: the module's name is the assignment's identity](0115-one-assignment-of-a-module-per-node.md) *(proposed)*
|
||
- **0117** — [A machine's uplink is a seat: the mesh configures the manager, never the link](0117-a-machines-uplink-is-a-seat.md)
|
||
|
||
### How it is built
|
||
|
||
- **0011** — [Managed files are generated onto nodes and never edited there](0011-managed-files-are-generated-never-edited.md)
|
||
- **0012** — [The mesh creates no symlinks — a derived file is a copy](0012-the-mesh-creates-no-symlinks.md)
|
||
- **0013** — [Schema and state changes are numbered migrations, in the same language as the code](0013-schema-changes-are-numbered-migrations.md)
|
||
- **0014** — [No workspace — each module is a standalone package consuming published dependencies](0014-no-npm-workspace.md)
|
||
- **0015** — [Applications live in their own repository; the monorepo is for the mesh](0015-applications-live-in-their-own-repository.md)
|
||
- **0016** — [The lab](0016-the-lab.md)
|
||
- **0037** — [Where a module lives](0037-where-a-module-lives.md) *(proposed)*
|
||
- **0039** — [What the SDK holds, and what it refuses](0039-what-the-sdk-holds-and-refuses.md)
|
||
- **0068** — [The lab takes requests, one at a time, and runs each from its own copy](0068-the-lab-takes-requests.md) *(proposed)*
|
||
- **0069** — [A module is a repository and a path within it](0069-a-module-is-a-repository-and-a-path.md)
|
||
- **0076** — [The SDK is a published package, and the toolchain resolves it by version](0076-the-sdk-is-a-published-package.md)
|
||
- **0082** — [The registry is reached by name, and the overlay is its security](0082-the-registry-is-reached-by-name-and-trusted-by-the-overlay.md)
|
||
- **0086** — [A secret reaches a process as a file, and an exception is declared](0086-a-secret-reaches-a-process-as-a-file.md)
|
||
- **0096** — [An upstream image is copied between registries, never through a machine's image store](0096-an-upstream-image-is-copied-between-registries.md)
|
||
- **0097** — [A vendor image is a declared build input, and a recipe fetches nothing undeclared](0097-a-vendor-image-is-a-declared-build-input.md)
|
||
- **0107** — [Persistent data is a directory bind, never a named volume](0107-persistent-data-is-a-directory-bind-never-a-named-volume.md)
|
||
- **0111** — [A build source is on the mesh's git seat, or it is an external repository](0111-a-build-source-is-on-the-git-seat-or-external.md)
|
||
|
||
### How it is checked
|
||
|
||
- **0017** — [A test defends a decision](0017-a-test-defends-a-decision.md)
|
||
- **0018** — [A picture of a system is read from the system, never from what asked for it](0018-a-picture-is-read-from-what-runs.md)
|
||
- **0089** — [A bed reads the catalogue it proves](0089-a-bed-reads-the-catalogue-it-proves.md)
|
||
- **0093** — [A fixture that runs a module's runtime carries the module's name](0093-a-fixture-that-runs-a-modules-runtime-carries-its-name.md)
|
||
|
||
### How we work
|
||
|
||
- **0019** — [How this repository works](0019-how-this-repository-works.md)
|
||
- **0020** — [The mesh is governed by a constitution, injected where work is decided](0020-the-mesh-is-governed-by-a-constitution.md)
|
||
- **0021** — [HQ is the source of the mesh constitution](0021-hq-is-the-source-of-the-constitution.md)
|
||
- **0022** — [The constitution absorbs what is already enforced](0022-the-constitution-absorbs-what-is-enforced.md)
|
||
- **0023** — [The approval is the checkpoint, not the second pair of hands](0023-approval-is-the-checkpoint.md)
|
||
- **0025** — [The design record is read where it is written, never copied to be found](0025-the-design-record-is-read-not-copied.md)
|
||
- **0032** — [The local account owns the mesh; a surface delegates to a module](0032-the-local-account-owns-the-mesh.md) *(superseded)*
|
||
- **0034** — [The local account owns the mesh, and a web application's login is not that](0034-the-local-account-owns-the-mesh.md)
|
||
- **0080** — [The development cycle is checked, not trusted](0080-the-development-cycle-is-checked.md)
|
||
- **0081** — [A decision nothing cites is not yet in the chain](0081-a-decision-nothing-cites-is-not-yet-in-the-chain.md)
|
||
|
||
<!-- index:end -->
|