Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24
@@ -1,6 +1,11 @@
|
|||||||
---
|
---
|
||||||
status: active
|
status: graduated
|
||||||
initiated: 2026-08-25
|
initiated: 2026-08-25
|
||||||
|
became:
|
||||||
|
- 02-DECISIONS/0044-a-module-declares-presence-instantiation-and-exclusion.md
|
||||||
|
- 02-DECISIONS/0045-a-context-owns-its-store.md
|
||||||
|
- 03-DESIGN/01-to-be/06-the-control-plane.md
|
||||||
|
- 03-DESIGN/01-to-be/07-the-substrate.md
|
||||||
touches:
|
touches:
|
||||||
- 02-DECISIONS/0002-everything-is-a-module.md
|
- 02-DECISIONS/0002-everything-is-a-module.md
|
||||||
- 02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md
|
- 02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: proposed
|
status: superseded
|
||||||
|
superseded-by: 02-DECISIONS/0044-a-module-declares-presence-instantiation-and-exclusion.md
|
||||||
date: 2026-08-23
|
date: 2026-08-23
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
status: accepted
|
||||||
|
date: 2026-08-26
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
supersedes: 0017-modules-outside-the-core-are-grouped-by-domain.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 44. A module declares presence, instantiation and exclusion
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[Research 011](../01-RESEARCH/011-the-module-graph/00-overview.md) set out to find the
|
||||||
|
catalogue's missing structure. It opened with *what does a graph delete?* and the answer for the
|
||||||
|
existing system was **nothing — it is already there**: 126 manifests, 103 edges, no cycles,
|
||||||
|
nothing dangling, and a resolver already used for build, load and install order.
|
||||||
|
|
||||||
|
So the work became a design question rather than a discovery one, worked through twenty cases
|
||||||
|
and one provider in full.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
### Two kinds of edge, one graph
|
||||||
|
|
||||||
|
**Presence** — the thing must exist and be reachable. Nothing is created, nothing flows back.
|
||||||
|
*An editor requires a terminal. A dashboard requires a container runtime.*
|
||||||
|
|
||||||
|
**Instantiation** — a provider is asked to make something *for this consumer*, and hands back
|
||||||
|
what the consumer needs to use it. *A game requires a database from the store, and receives one,
|
||||||
|
with credentials.*
|
||||||
|
|
||||||
|
Instantiation implies presence; presence does not imply instantiation. They differ in whether
|
||||||
|
something is created, whether a payload returns, whether it can be revoked, and whether the
|
||||||
|
provider holds state about it — which is too much to collapse into one relation for tidiness.
|
||||||
|
|
||||||
|
### Names are concrete unless providers are genuinely substitutable
|
||||||
|
|
||||||
|
A requirement names either a **concrete** thing — that module and no other — or an **abstract**
|
||||||
|
name satisfied by whatever provides it.
|
||||||
|
|
||||||
|
**An abstract name is legitimate only where a consumer can be switched between providers without
|
||||||
|
changing.** `terminal` passes. `database` fails: a consumer speaking one store's protocol does
|
||||||
|
not speak another's, so the name would promise what no provider delivers and the resolver would
|
||||||
|
report a requirement satisfied that is not.
|
||||||
|
|
||||||
|
**The adapter is what creates an interface.** A name has several providers *and a contract they
|
||||||
|
all satisfy*, or it is not an interface. Nothing declares itself to be one.
|
||||||
|
|
||||||
|
**Where there is no contract there is a tag.** *Database* remains a useful word for finding
|
||||||
|
things and grouping them in a catalogue. Tags describe; edges bind; keeping them apart is what
|
||||||
|
stops a second relationship appearing that looks like a dependency and is not.
|
||||||
|
|
||||||
|
### Exclusion is the third relation, and it is not derivable
|
||||||
|
|
||||||
|
`excludes` names what cannot coexist with this. Two modules providing one name look
|
||||||
|
interchangeable, and two things wanting one port look independent, right up until installing the
|
||||||
|
second breaks the first.
|
||||||
|
|
||||||
|
### A node provides names too
|
||||||
|
|
||||||
|
A node's profile is a set of provided names — a display server, a container runtime, an
|
||||||
|
architecture. A module requiring one is satisfied by **the node**, exactly as one requiring a
|
||||||
|
store is satisfied by another module.
|
||||||
|
|
||||||
|
**So capability checking is not a separate mechanism.** There is one question — *is this name
|
||||||
|
provided by anything available here?* — and a graphical application cannot land on a node
|
||||||
|
without a display server for the same reason, through the same code, that it cannot land
|
||||||
|
without its dependencies.
|
||||||
|
|
||||||
|
This makes the host's capability detection an **input to resolution** rather than a report for a
|
||||||
|
person. And what a node provides is partly *derived*: installing a container runtime makes the
|
||||||
|
node provide `container-runtime` thereafter.
|
||||||
|
|
||||||
|
### Constraints, never placement
|
||||||
|
|
||||||
|
A module says what must be true of a node and never which node. Which node runs what is an
|
||||||
|
inventory decision, and today's catalogue decides it in manifests — a module pinning its store
|
||||||
|
to a named node, so a second node cannot provide it without editing the consumer.
|
||||||
|
|
||||||
|
### Scope decides which provider, and the binding is written down
|
||||||
|
|
||||||
|
A consumer of an instantiation edge declares the **scope of its own need**: one instance shared
|
||||||
|
across every instance of itself, or one each. That decides without naming a node — a shared need
|
||||||
|
cannot be met by something each node runs separately.
|
||||||
|
|
||||||
|
The mesh then binds, and **the binding is recorded and sticky**. Not recomputed: a resolver that
|
||||||
|
re-derives which store serves a consumer will one day derive a different answer and relocate a
|
||||||
|
database. **Where it is recorded follows the scope** — a shared grant belongs to the module, a
|
||||||
|
per-instance grant to the assignment.
|
||||||
|
|
||||||
|
### A module, a node, and an assignment
|
||||||
|
|
||||||
|
Three entities. The assignment carries what belongs to neither end: where state lives, how it is
|
||||||
|
reached, configuration derived from the hardware, and which provider instance serves this
|
||||||
|
consumer.
|
||||||
|
|
||||||
|
Recorded because the alternative was tried: modules were once node-agnostic, and it did not
|
||||||
|
survive — there was nowhere for these to live.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **[ADR 0017](0017-modules-outside-the-core-are-grouped-by-domain.md) is superseded.** Folders
|
||||||
|
assert relationships; edges record them. What grouping was for — finding things, seeing what
|
||||||
|
belongs together — is a tag and a query over the graph, neither of which anybody keeps true by
|
||||||
|
hand. The domain module goes with it: there is no `networking` thing to install, there are
|
||||||
|
concrete modules named individually.
|
||||||
|
- **Provider stops being a category**, as service and application already had. Any hosted thing
|
||||||
|
can be a factory — an identity provider grants clients, a mail server grants mailboxes. It is
|
||||||
|
a facet, not a kind.
|
||||||
|
- **`excludes` and node capabilities do not exist in any manifest today**, so this adds
|
||||||
|
declarations rather than removing them. What it removes is listed in
|
||||||
|
[ADR 0045](0045-a-context-owns-its-store.md), which is the other half of this design.
|
||||||
|
- **A resolver is still needed**, with version constraints and conflicts. What it delegates to
|
||||||
|
the platform's package manager rather than reimplementing is **not decided here**.
|
||||||
|
- **Two axes remain undeclarable**: how many instances a module should have — not derivable, and
|
||||||
|
opposite for a broker and a store — and what a provider hands back, which differs between
|
||||||
|
credentials and a command.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Research 011](../01-RESEARCH/011-the-module-graph/00-overview.md) — the measurement, the
|
||||||
|
cases, the worked provider, and what happens to `feature`.
|
||||||
|
- [ADR 0045](0045-a-context-owns-its-store.md) — the ownership half.
|
||||||
|
- [ADR 0043](0043-a-declaration-is-an-ordered-list-of-owned-resources.md) — what the graph
|
||||||
|
produces for a node.
|
||||||
|
- [ADR 0002](0002-everything-is-a-module.md) — survives; this says what a module declares.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
status: accepted
|
||||||
|
date: 2026-08-26
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 0044-a-module-declares-presence-instantiation-and-exclusion.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 45. A context owns its store, exclusively
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 0044](0044-a-module-declares-presence-instantiation-and-exclusion.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
|
||||||
|
schema*, and states the cost: several domains share one forty-five-table schema, which is why
|
||||||
|
work belonging to one context keeps having to be implemented in another.
|
||||||
|
|
||||||
|
That was written as a principle. Counted, it is thirteen foreign tables belonging to three
|
||||||
|
separate contexts, living in the mesh's own registry database.
|
||||||
|
|
||||||
|
## Considered options
|
||||||
|
|
||||||
|
1. **A schema per consumer inside a shared database.** Namespaced, revocable by dropping the
|
||||||
|
schema, with a cross-context join possible but deliberate. Rejected: it keeps the letter of
|
||||||
|
§4 and leaves the temptation in place, and a boundary that is merely inconvenient to cross
|
||||||
|
gets crossed.
|
||||||
|
2. **Read-only roles on another context's store.** Rejected for the same reason and one worse:
|
||||||
|
reading another context's tables couples you to its layout exactly as firmly as writing them,
|
||||||
|
and the coupling is invisible until the owner changes a column.
|
||||||
|
3. **Exclusive ownership.** Chosen.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
> **A context is granted only what it exclusively owns.**
|
||||||
|
|
||||||
|
No shared writes. No read-only role on another context's store. If you need what another context
|
||||||
|
holds, you ask it or you subscribe to it.
|
||||||
|
|
||||||
|
**The unit is the context, not the process.** Everything inside a context — its service, its
|
||||||
|
surface, its tools — reads its own store freely. A board showing the mesh's own nodes and
|
||||||
|
modules is the mesh showing its own data, not a boundary crossing. What is forbidden is a
|
||||||
|
*different* context reading it.
|
||||||
|
|
||||||
|
### Asking or subscribing is derived, not chosen
|
||||||
|
|
||||||
|
[ADR 0036](0036-a-node-is-a-managed-machine.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.
|
||||||
|
- **Anything where a stale answer is worse than none cannot subscribe.** A display may lag; a
|
||||||
|
decision about whether a grant is still valid may not.
|
||||||
|
|
||||||
|
Neither is a query against another store, whatever transport it travels over.
|
||||||
|
|
||||||
|
## What this removes
|
||||||
|
|
||||||
|
The first clear list of what the design deletes rather than adds:
|
||||||
|
|
||||||
|
- **Grant kinds.** There is one: an exclusive resource. No schema grants, no read roles, no
|
||||||
|
rules about who may see what inside a shared thing.
|
||||||
|
- **The question of who owns which table**, and the guessing at revocation time. Removing a
|
||||||
|
consumer drops what it was granted, whole.
|
||||||
|
- **Cross-context migration ordering.** Two contexts migrating one database must be ordered
|
||||||
|
against each other. Exclusive ownership means a context's migrations are ordered only against
|
||||||
|
itself.
|
||||||
|
- **A class of permission modelling** a shared store would otherwise need.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Cross-context reporting is harder, and that is the point.** Anything wanting to see across
|
||||||
|
contexts consumes their events or calls their interfaces. That is §4's argument, and the cost
|
||||||
|
it names is the one already paid.
|
||||||
|
- **A single surface over several contexts still works** — that is what a surface is. It reads
|
||||||
|
interfaces, not stores. This holds while the contexts sit behind **one** interface; splitting
|
||||||
|
a context into its own deployable costs that, and the composition would have nowhere to live
|
||||||
|
that tier 3 permits. **A real constraint on how far the control plane may be split.**
|
||||||
|
- **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 0037](0037-the-host-applies-it-does-not-decide.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
|
||||||
|
point, ask once and resume, or rebuild. The question every projection has.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [`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 0036](0036-a-node-is-a-managed-machine.md) — why asking or subscribing is derived.
|
||||||
Reference in New Issue
Block a user