Decided after measuring what renumbering actually costs: 96 references in code comments across two repositories, none of which would have failed to compile. They would have pointed at the wrong reasoning, which is worse than a broken link because nothing reports it. So a number identifies a record and never changes. It cannot also be a position -- a position moves when the set changes, and an identity that moves is not one. The reading order moves into an index generated from each record's `topic:`. Six topics, in the order somebody learns the system. The index is WRITTEN rather than only generated on demand, which reverses what this repository previously said. The reason it said otherwise is that a hand-written index drifts -- but a reader looking at the folder on a forge sees the folder, not a command, and the drift objection is answered by checking rather than by refusing to write one. That is §5's own rule: a rule states how it is checked. Two checks, both confirmed to bite. index.py fails when the written order no longer matches the records. records.py fails when a record has no topic or one nobody defined -- the quiet failure being a record that vanishes from the order rather than appearing in the wrong place.
95 lines
4.5 KiB
Markdown
95 lines
4.5 KiB
Markdown
---
|
|
topic: the tiers
|
|
status: accepted
|
|
date: 2026-08-26
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 0009-modules-and-the-graph.md
|
|
---
|
|
|
|
# 8. A context owns its store, exclusively
|
|
|
|
## Context
|
|
|
|
[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
|
|
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 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.
|
|
- **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 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
|
|
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 0004](0004-a-node-and-how-it-joins.md) — why asking or subscribing is derived.
|