Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24

Merged
jschoubben merged 177 commits from reconcile-init-into-main into main 2026-09-05 10:27:11 +00:00
Showing only changes of commit 4c8515507a - Show all commits
@@ -107,9 +107,15 @@ That is a property of the consumer, expressible without naming anything. And it
that actually decides: a shared need cannot be satisfied by a provider each node runs
separately, and a per-instance need should not be satisfied by a shared one.
**Then the mesh binds, and the binding is recorded on the assignment.** Not recomputed. A
resolver that re-derives which store serves a consumer will one day derive a different answer and
relocate a database — so the binding is made once, written down, and changed only deliberately.
**Then the mesh binds, and the binding is written down.** Not recomputed: a resolver that
re-derives which store serves a consumer will one day derive a different answer and relocate a
database, so the binding is made once and changed only deliberately.
**Where it is written down follows the scope.** A shared grant belongs to the *module* and every
assignment of it references the same one — which is the answer for one module installed on two
nodes wanting one database between them. A per-instance grant belongs to the *assignment*. Same
relation, two homes, and which home is not a detail: it is what makes two instances share
something or not.
The pieces that follow, none of them settled here:
@@ -153,6 +159,41 @@ case, and reaching it is not the host's job. So the same operation is local at b
remote afterwards, which is either two mechanisms or one mechanism with a boundary crossing in
it. Undecided, and it is the sharpest unresolved thing in this file.
## Several modules, one database
Asked directly, and it is three different needs wearing one sentence. They want different
answers and one of them collides with a rule already in force.
**A grant is not always a whole database.** A provider offers *kinds* of grant, and the kind is
what separates these:
| The need | The grant |
|---|---|
| my own tables, nobody else's business | a **database** |
| read what another module holds | a **read-only role** on an existing database |
| my own tables *inside* a database others also use | a **schema** within it — a namespace the consumer owns |
The third is the interesting one, and the one asked about.
**Loose tables in a shared database is what `how-we-build` §4 warns against**, in as many words:
*contexts integrate through the record, never through a shared schema — today several domains
share one forty-five-table schema, which is why work belonging to one context keeps having to be
implemented in another.* That is not a style objection; it is the observed cost, already paid.
**A per-consumer schema inside a shared database keeps what the request wants and drops what §4
objects to.** The consumer gets tables in the same database — same connection, same backup, and
a cross-schema read is physically possible when it is genuinely needed. What it also gets is
ownership: its migrations touch its own namespace, two modules cannot collide over a table name,
and revoking the grant drops the schema rather than guessing which tables belonged to whom.
Which leaves the fault §4 names — *joining across a boundary* — possible but no longer
accidental. That is the honest position: physically co-located, logically owned, and a
cross-context join is now a thing somebody has to deliberately write rather than the path of
least resistance.
**And it makes revocation answerable**, which the whole-database version was not. Uninstalling a
consumer drops its schema. Nothing has to work out which of forty-five tables were whose.
## A migration belongs to the consumer and runs on the provider
A game defines migrations. They run against the database the store granted **it**. So the