Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24
@@ -53,8 +53,8 @@ and receiving credentials. Same edge.
|
||||
**Provider stops being a category.** Any hosted thing can be a factory — an identity provider
|
||||
grants clients, a mail server grants mailboxes. It is a facet, not a kind.
|
||||
|
||||
**A module may also declare exclusion**, because some things cannot coexist on one machine and
|
||||
that is a fact about the module rather than about a particular node.
|
||||
**A module may also declare what it claims**, because some things cannot coexist and that is a
|
||||
fact about the module rather than about a particular node. What that means precisely is below.
|
||||
|
||||
### Why the build edge is a different kind
|
||||
|
||||
@@ -82,6 +82,92 @@ and the values are derived onto the consumer. **Neither the credential nor the t
|
||||
written by hand.** A requirement may name a provider on another node, so cross-node wiring is the
|
||||
same declaration.
|
||||
|
||||
## What a module claims, and why it is not a list of rivals
|
||||
|
||||
*Written 2026-08-29, replacing pairwise exclusion.*
|
||||
|
||||
**Exclusivity is not a property of a module. It is a property of a singular resource the module
|
||||
takes over.** Two shells do not compete for anything and any number may be installed. Two display
|
||||
servers both want the seat, and only one may have it.
|
||||
|
||||
> **A module declares what it *claims*. Two modules claiming the same thing cannot both be
|
||||
> assigned within that claim's scope.**
|
||||
|
||||
**Not "xorg conflicts with wayland".** Pairwise exclusion has a property that only shows up later:
|
||||
adding a third display server means **editing xorg and wayland to know about it**. Every new
|
||||
module requires changing modules nobody who wrote it owns, and the edits grow as the square of
|
||||
the count. With a claim, the third one says `claims: the seat` and nothing else changes anywhere.
|
||||
**The new module is the only thing that has to know anything** — which is the difference between
|
||||
a catalogue that grows and one that calcifies.
|
||||
|
||||
The pattern is common enough to be worth listing, because seeing it is most of understanding it:
|
||||
|
||||
| these coexist | these claim one thing |
|
||||
|---|---|
|
||||
| shells — bash, zsh, fish | display servers — xorg, wayland (*the seat*) |
|
||||
| editors — vim, emacs, helix | init — systemd, openrc (*pid 1*) |
|
||||
| language runtimes | container runtime — docker, podman |
|
||||
| terminal emulators | reverse proxies — nginx, caddy, traefik (*ports 80/443*) |
|
||||
| browsers | time — chrony, timesyncd, ntpd (*the clock*) |
|
||||
| | resolvers — resolved, dnsmasq, unbound (*`/etc/resolv.conf`*) |
|
||||
| | network management — NetworkManager, networkd, netctl |
|
||||
| | mail — postfix, exim, msmtp (*port 25*) |
|
||||
| | audio — pipewire, pulseaudio (*the device*) |
|
||||
|
||||
**A claim has a scope**, because not everything singular is singular per machine:
|
||||
|
||||
| scope | example |
|
||||
|---|---|
|
||||
| **node** | the seat, pid 1, port 443 |
|
||||
| **site** | a DHCP server on a segment |
|
||||
| **mesh** | the hub, the control plane |
|
||||
|
||||
The last is not new — the mesh already enforces exactly one hub with a unique index
|
||||
([ADR 0007](0007-connectivity.md)). Scope is that idea, said once rather than hard-coded per case.
|
||||
|
||||
**Some conflicts need no claim at all.** Two modules declaring the same file, or binding the same
|
||||
port, are visible from *what they declare* — the mesh already holds every resource of every
|
||||
declaration. So a claim is only written for the abstract ones, where nothing in the declaration
|
||||
reveals the clash. That keeps the manifest small, which is worth protecting.
|
||||
|
||||
## A requirement with several answers is refused, never guessed
|
||||
|
||||
A module requiring *a shell* may be satisfied by three. The mesh does not pick.
|
||||
|
||||
| candidates | what happens |
|
||||
|---|---|
|
||||
| exactly one | assigned, silently — there was no choice to make |
|
||||
| none | refused, naming what is missing |
|
||||
| several | **refused, naming them**, and a person chooses |
|
||||
|
||||
**This is what makes a solver unnecessary.** Counting candidates is a few lines and has no
|
||||
surprising behaviour; a solver that picks has to be understood before its answer can be trusted,
|
||||
and it is understood by whoever is debugging it at the time. Nothing here is lost by waiting —
|
||||
a solver can be added later without changing a single manifest, and the reverse is not true.
|
||||
|
||||
**Requiring a module and requiring a capability are different fields**, because the remedies
|
||||
differ and the message should say which:
|
||||
|
||||
- *i3 needs xorg, which is not assigned here* — assign it.
|
||||
- *this machine has no seat* — wrong machine; nothing can be installed to fix it.
|
||||
|
||||
## "Flavor" is retired
|
||||
|
||||
It was carrying three unrelated meanings — variants of a thing, a subset of one module a node
|
||||
installs, and whatever the current system does, which earned two knowledge-base entries about
|
||||
going wrong. **A word with three meanings cannot be reasoned about**, and every attempt to design
|
||||
around it produced a rule that was right for one meaning and wrong for the others.
|
||||
|
||||
What it was reaching for is two ordinary things:
|
||||
|
||||
- **Different modules that provide the same thing.** `zsh` and `fish` both provide *a shell*. They
|
||||
are two modules, not one module with a switch: they share a name and nothing else — different
|
||||
packages, different configuration, different everything.
|
||||
- **One module with a setting.** A monitoring module that is an agent here and a server there is
|
||||
one module, configured. Nothing varies but a value.
|
||||
|
||||
If something is neither, it is probably two modules.
|
||||
|
||||
## The core library is the mesh's domain
|
||||
|
||||
One module everything may depend on. It holds **what is true of the mesh regardless of which
|
||||
|
||||
@@ -84,7 +84,7 @@ The decomposition is impossible while a feature is a singleton per module.
|
||||
|---|---|---|
|
||||
| 1.1 | Decision record — named features, per-node opt-in (next free number) | accepted |
|
||||
| 1.2 | Manifest: declared `features:` with type + directory | a module declares two of one kind and both build |
|
||||
| 1.3 | Selection: `always` / flavor-selected / `optional` | a node installs a subset; artifacts stay flavor-blind |
|
||||
| 1.3 | Selection: `always` / `optional` | a node installs a subset; artifacts stay selection-blind |
|
||||
| 1.4 | `requires:` moves onto the feature | a schema feature's database is not provisioned where the feature is not installed |
|
||||
| 1.5 | Assignment carries the opted-in feature set | opting a node in requires no rebuild |
|
||||
|
||||
@@ -103,7 +103,7 @@ Cheapest first, and each one proves the extraction pattern before the expensive
|
||||
| 2.2 | `hal/stream` — the record; notifications and messaging as views | axon, synapse, notifications, meetings, conversations | medium |
|
||||
| 2.3 | `hal/agents` — identity, licence, runs, memory, thoughts | noxflow agents, `hal/thoughts` | **high** — touches credentials |
|
||||
| 2.4 | `hal/work` — what remains of noxflow | noxflow tasks | medium |
|
||||
| 2.5 | `hal/ai` — provider integration, flavored | `hal/claude*` | medium |
|
||||
| 2.5 | `hal/ai` — one module per provider, each providing *a model provider* | `hal/claude*` | medium |
|
||||
|
||||
Each extraction is expand-then-contract: new context alongside, dual-write, verify, cut
|
||||
over, remove. **Never a move commit.**
|
||||
|
||||
Reference in New Issue
Block a user