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
2 changed files with 130 additions and 2 deletions
Showing only changes of commit e20a09ae80 - Show all commits
@@ -17,7 +17,18 @@ touches:
Whether the catalogue's missing structure is a **graph** — modules declaring what they need,
what they offer, and what they exclude — and what that replaces.
**Measured, and the premise was wrong: the graph is not missing.**
**The design is in [`proposal.md`](proposal.md): one kind of edge.** A module provides names
and requires names, and that single relation absorbs requiring a module, requiring a resource,
and the interface-and-adapter idea — an interface is simply a name with more than one provider.
A **node provides names too**, which makes capability checking stop being a separate mechanism
and makes the host's own capability report an input to resolution rather than something a
person reads.
[`analysis.md`](analysis.md) measured the current catalogue. Its value to the design is two
lessons rather than its machinery — a field that means *depends on* should say so, and
placement does not belong in a manifest — and the rest is recorded as as-is evidence.
**Measured, and the premise was wrong for the existing system: the graph is not missing there.**
[`analysis.md`](analysis.md) — 126 manifests, 103 edges, no cycles, nothing dangling, and a
resolver that topologically sorts them, already called by the tool loader, the installer and
the delivery coordinator. What the effort assumed would need building is a thing to call.
@@ -119,7 +130,9 @@ integration being wrong looks like from the outside.
| Question | Why it is open |
|---|---|
| ~~What does the graph **delete**?~~ | **Answered, and not as expected** — nothing, because it already exists. The honest list of what a graph would remove is currently empty, and what it would *add* is three declarations. [`analysis.md`](analysis.md). |
| ~~What does the graph **delete**?~~ | **Answered for the design** in [`proposal.md`](proposal.md): the distinction between requiring a module and requiring a resource, the interface as a kind of thing, capability checking as a separate mechanism, domain grouping, and possibly tiers as a separate concept. *(For the existing system the answer was nothing, because it is already there — [`analysis.md`](analysis.md).)* |
| When two modules provide one name, who chooses? | The substance of the interface idea, and the proposal does not settle it: a node setting, a mesh setting, or an explicit pin. |
| Does an abstract name need a type? | A consumer of `database` needs credentials; a consumer of `terminal` needs a command to run. Whether that difference lives in the name, beside it, or in what the provider hands back is undecided. |
| Would the missing declarations be used? | Zero manifests declare exclusions or capabilities, which is equally consistent with *nobody needs them* and *nobody can express them*. Nothing measured separates those. |
| Should `provider:` become a real edge, or should the relationship be declared twice? | It names a module and means *depends on*. Reading it as an edge fixes the closure; the alternative is requiring the consumer to also list it under `dependencies:`, which is duplication a resolver already avoids elsewhere. |
| Should placement leave the catalogue? | A provision pins itself to a named node, in the manifest. Which node runs what is an inventory decision — tier 2 by the skeleton's own test — and having it in tier 4 means a second node cannot provide the mesh's database without editing the module that consumes it. |
@@ -0,0 +1,115 @@
# One kind of edge
A design, not an account of what exists. [`analysis.md`](analysis.md) measured the current
catalogue and its value here is two lessons rather than its machinery: a field that means
*depends on* should say so, and placement does not belong in a manifest.
## The idea
**A module provides names. A module requires names. That is the only edge.**
Everything the effort listed as separate declarations turns out to be one relation with
different kinds of name on either end.
```
postgres provides postgres, database
lavinmq provides lavinmq, message-bus
kitty provides kitty, terminal
anthropic provides anthropic, ai-assistant
meshboard requires database, message-bus
vscode requires terminal, display-server
```
A name is either **concrete** — a module's own name, so `requires: postgres` means that module
and no other — or **abstract**, so `requires: database` means whatever provides it. The
difference is not in the schema; it is whether more than one thing offers the name.
That single relation absorbs three things the effort had listed separately:
| Was going to be | Is |
|---|---|
| requires another module | requires a concrete name |
| requires a resource — a database, a bucket | requires an abstract name |
| an interface, with adapters providing it | an abstract name several modules provide |
The interface stops being a kind of module. It is a name with more than one provider, and
nothing has to declare that it is one.
## The node provides names too
The move that makes capabilities stop being a separate system.
A node's profile is a set of provided names. `display-server`. `container-runtime`.
`amd64`. A module requiring `display-server` is satisfied by **the node**, exactly as a module
requiring `database` is satisfied by another module.
So there is one resolution rather than two: *is this name provided by anything available here?*
A graphical application cannot be installed on a node with no display server for the same
reason, and through the same code, that it cannot be installed without its libraries.
And it means the host's capability detection — which already reports what a machine can be
asked to do, with a reason for each verdict — **is the node's provides-list**. It was built to
be read by a person; it turns out to be an input to resolution.
## What a module declares
```yaml
name: meshboard
provides: [meshboard]
requires: [database, message-bus, container-runtime]
excludes: []
runs: # what applying it means
- container: ...
constraints: # what must be true of a node that runs it
- architecture: amd64
```
**`excludes`** is the one genuinely new relation: naming something that cannot coexist with
this. It is not derivable from requires and provides, and without it two modules that both
provide `message-bus` look interchangeable when installing both would break the machine.
**`constraints` are not placement.** They say what must be true of a node, never which node.
Which node runs what is an inventory decision, and the measurement found the current catalogue
deciding it in the manifest — a module pinning its database to a named node, so that a second
node cannot provide it without editing the module that consumes it. That is the mistake this
separation exists to avoid.
## What it deletes
The question the effort opened with, answered for the design rather than for what exists.
- **The distinction between requiring a module and requiring a resource.** One relation.
- **The interface as a kind of thing.** A name with several providers.
- **Capability checking as a separate mechanism.** The node is a provider.
- **Domain grouping** ([ADR 0017](../../02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md)).
Folders assert relationships; edges record them. A domain is then a *query* over the graph —
what changes together — rather than a directory somebody has to keep true.
- **Tiers as a separate concept**, possibly. A tier is a level in the graph, and levels are
computed. Whether the coarse boundary is still worth naming is below.
## What it does not delete, and should not
**A resolver still has to exist**, and it will need version constraints, conflict handling and
a story for when two modules provide one name and nothing says which to use. That is the
long-solved and easily-botched part, and the design should say what it **delegates** to the
platform's package manager rather than reimplementing.
## Open
- **When two modules provide one name, who chooses?** The consumer names `database` and gets
something. Whether the choice is a node setting, a mesh setting, or an explicit pin is the
substance of the interface idea, and this proposal does not settle it.
- **Are tiers a view of the graph, or a boundary that survives it?** If tiers are levels, the
concept is derived and the word is a convenience. If the tier rule — *a tier may depend only
on tiers below it* — is meant to be enforceable, it is a constraint on the graph rather than
a description of it, and it has to be stated as one.
- **Does a name need a type?** `database` and `terminal` are both abstract names, and a consumer
of the first needs credentials while a consumer of the second needs a command to run. Whether
that difference lives in the name, in a type beside it, or in what the provider hands back is
undecided.
- **What does a node provide that is not a capability?** Its architecture is a provided name
under this scheme, and so is its operating system. That may be elegant or may be one
abstraction too far; nothing here tests it.