diff --git a/01-RESEARCH/011-the-module-graph/00-overview.md b/01-RESEARCH/011-the-module-graph/00-overview.md index 221915c..890af48 100644 --- a/01-RESEARCH/011-the-module-graph/00-overview.md +++ b/01-RESEARCH/011-the-module-graph/00-overview.md @@ -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. | diff --git a/01-RESEARCH/011-the-module-graph/proposal.md b/01-RESEARCH/011-the-module-graph/proposal.md new file mode 100644 index 0000000..a208b15 --- /dev/null +++ b/01-RESEARCH/011-the-module-graph/proposal.md @@ -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.