The effort was opened to ask whether the catalogue's missing structure is a graph. It is not missing. 126 manifests, 103 edges, no cycles, nothing dangling, deepest chain of five — and a resolver in the SDK that topologically sorts them, already called by the tool loader at startup, the installer when syncing modules onto a node, and the delivery coordinator when expanding what a change affects. It already does something this effort assumed would need designing: a requirement on another module's provision is treated as an implicit edge to the module that provides it. So "ordering by the graph", which ADR 0043 makes the control plane's job, is a thing to call rather than a thing to build. The one place the graph is wrong, it is wrong about the substrate. A module needing a database declares `provider: postgres` inside `provisions:` — which is what a module OFFERS — so the resolver, which reads `dependencies:` and `requires:`, never sees it. Three edges are invisible this way, and they are the mesh's own database, the mesh's own broker, and the work engine's database. The consequence is measurable: computing what a working mesh needs from the declared graph gives registry -> sdk -> mesh -> meshware. Four modules, four levels, no database. Arithmetically correct and obviously wrong, for exactly one reason — a field that means "depends on" is not read as one. That is 04-ISSUES/003 in a new form: not a key nothing reads, but a key read as something other than what it means. Two latent defects, both contrary to ADR 0008 and both in the component ADR 0043 makes responsible for ordering a host will apply without question: a cycle warns and falls back to input order, and a dependency that does not exist warns and continues. Neither has fired, because the catalogue currently has no cycles and nothing dangling, which is why nobody has noticed. And placement is decided in 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 — so a second node cannot provide the mesh's database without editing the module that consumes it. What the graph would DELETE is currently nothing. What it would add is three declarations that no manifest uses today: excludes, a required node capability, and an interface with adapters. Whether they would be used is not measured, and zero usage is equally consistent with nobody needing them and nobody being able to express them.
9.3 KiB
status, initiated, touches
| status | initiated | touches | ||||||
|---|---|---|---|---|---|---|---|---|
| active | 2026-08-25 |
|
011 — The module graph
What is being investigated
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.
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.
What survives is narrower: three declarations that do not exist (excludes, a required node
capability, an interface with adapters), and two defects worth fixing whatever else is
concluded — provider: is a dependency edge that is not read as one, which makes the closure
for a working mesh come out without a database; and the resolver continues past a cycle and
past a missing dependency, contrary to ADR 0008.
ADR 0017 proposes grouping modules by domain. Research 005 measured that proposal and found its evidence holds in exactly one place — reachability — which ADR 0037 has since absorbed into the host. The measured case for domain grouping has therefore been consumed by a decision taken for unrelated reasons, and what remains is fifty modules that co-change with nothing.
That leaves the original complaint unanswered: the catalogue records what was installed rather than what anything is for, and nothing in the system can see a relationship between two modules. Grouping asserts relationships. A graph records them.
Why now
A proposal to split modules into provisioning services and applications was worked through and abandoned in favour of one concept with facets, for a reason worth keeping:
- The split cannot be filed consistently. A git forge is consumed as a service and operated through a web interface. An analytics service grants tracking identity and is a dashboard somebody reads. An identity provider grants authentication and has an admin console.
- The operator's correction is the sharper form: what runs on the machine is a supervised container, not something a user started. That is a fact about how a thing runs, not about what kind of thing it is — so it is a facet, not a taxonomy.
Filing decisions that follow from nothing are the disease research 005 measured. A second taxonomy would reproduce it.
So ADR 0002 survives, and the question becomes what a module must be able to declare.
The shape being investigated
Five declarations, of which two exist today.
| Declaration | Today | Notes |
|---|---|---|
| requires a resource — a database, a bucket | yes | provisioning, ADR 0005 |
| provides a resource | yes | as above |
| requires another module | no | the dependency edge — the graph's substance |
| excludes another module | no | installing A makes B unavailable |
| requires a node capability | no | a graphical session, a container runtime, an architecture |
And one structural idea on top: an interface module carries no implementation. Adapters provide it. An assistant interface with several model-provider adapters; a terminal interface with several terminal adapters. A dependent names the interface and never an implementation.
Prior art to measure against, not invent past. This is a package manager's model, and the
platform's own package manager already has all of it: depends is the dependency edge,
conflicts is exclusion, and provides is the interface — several packages provide one
virtual name, and a dependent names the virtual one. That the design arrived at the same shape
independently is evidence for it. It is also a warning: dependency resolution, version
constraints, conflict handling and rollback are a long-solved and easily-botched problem, and
the effort should establish what to delegate rather than reimplement.
Capabilities, and what may be installed
The operator's formulation: system specs are capabilities, and capabilities unlock installable modules — you cannot install a graphical application on a node with no display server.
The question that follows is whether the mesh may install a capability. The effort's working position, to be tested:
- Intrinsic capabilities — hardware, architecture, network position — are facts about a machine. They are detected, never installed, and a module requiring one it does not have is not unresolved but impossible on that node.
- Provided capabilities — a display server, a container runtime — are not a separate kind of thing at all. They are modules that provide a capability, and requiring one is an ordinary edge the graph resolves by installing it.
If that holds, "may the mesh install a capability" is not a policy question. It is dependency resolution, and the only genuinely new thing is detection.
Which is where issue 007 bears directly: an installed package is not a capability. A provided capability is not real because a package is present — it is real when it is present, running and working, and the difference is exactly the class of fault this repository keeps recording.
What it touches beyond the catalogue
The operator's assessment is that the machinery around a module is wanted and its integration is not: scheduled tasks, hooks, migrations, configuration and environment settings are worth keeping; seeds are not; and the current integration is wrong enough to need a major refactor.
That is a claim to test rather than adopt. Research 005 already found supporting evidence from a different direction — that the densest apparent coupling in the catalogue is manifest boilerplate churn, cross-cutting changes to the machinery applied N times — which is what an integration being wrong looks like from the outside.
Open questions
| Question | Why it is open |
|---|---|
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. |
|
| 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. |
| Where does resolution happen — mesh or platform package manager? | The mesh must model mesh-level edges. Whether it also resolves operating-system packages, or delegates, decides whether a solver has to be written. |
| Is an interface a module, or a name? | Arch makes it a name that packages claim. Making it a module gives it a manifest, an owner and a place to document the contract — and a thing with no implementation to install. |
| What does an exclusion mean for something already installed? | Refuse the install, or make the conflict visible and let it be decided. The second is a policy surface; the first is a package manager. |
| Does node adoption scan for capabilities, applications, or both? | The operator proposes scanning an adopted node and enabling what it finds. Under the working position above, the scan is for capabilities — but a machine with a terminal already installed is also a module already satisfied, and whether that is adoption or drift is undecided. |
| One installation image, or several? | Proposed: pre-built images carrying different capability sets, so a machine is adopted quickly. Several images bake capability sets at image time, which is the filing problem in a new form and reintroduces what detection exists to avoid. One image carrying the host and nothing else is ADR 0038's one binary installed by hand, automated. The effort should settle which. |
| What happens to domain grouping? | ADR 0017 is still proposed. If the graph is the answer, 0017 is superseded rather than narrowed — its text is never edited. |