Every remaining cluster merged. Each was one design that had been split across
several records because it was worked out over days rather than at once.
the node host 8 -> 1 applies not decides, depends on nothing,
per operating system, root service, the
launcher, episodic, what a declaration is,
actions from the bundle only
a node and how it joins 4 -> 1 what a node is, joining, the link as
security boundary, the enrolment token
modules and the graph 7 -> 1 everything is a module, no domain modules,
three edges, provisioning, the core library
substrate and control 6 -> 1 the test, seven contexts, one control plane,
plane the authority is not a database, the named
products, the pinned bundle
connectivity 3 -> 1 a route is a grant, reachability declared,
filter rules
delivery 5 -> 1 reconciliation not a pipeline, artifacts,
the three silos, a failed step, the verdict
the lab 5 -> 1 (earlier)
how this repository 10 -> 1 (earlier)
works
Nothing was dropped. Each consolidated record carries the reasoning of the ones
it absorbs -- the measurements, the incidents, the alternatives rejected --
because that reasoning is the only reason to keep a record at all. What is gone
is the fragmentation: eight files to read to understand tier 0, when tier 0 is
one component.
The four superseded records went too. They existed to point at their
successors, and the successors now contain what they said.
The checker made this safe. Each merge left dangling links -- 38 files after
the host merge alone -- and it named every one. Nothing was found by reading,
and a manual pass would certainly have missed some, including references inside
AGENTS.md which every session loads.
139 lines
5.8 KiB
Markdown
139 lines
5.8 KiB
Markdown
# The graph is not missing
|
|
|
|
Measured against `origin/main` of the code repository, 2026-08-26. Every manifest, read through
|
|
git refs rather than a checkout.
|
|
|
|
The effort was opened to ask whether the catalogue's missing structure is a graph. It is not
|
|
missing. **It exists, it is healthy, and three separate parts of the system already use it.**
|
|
|
|
That is the finding, and it changes what is worth asking.
|
|
|
|
## Finding 1 — the graph is already declared, and it is clean
|
|
|
|
| | |
|
|
|---|---|
|
|
| manifests | 126 |
|
|
| declare a dependency on another module | 64 |
|
|
| declare a requirement on a provision | 11 |
|
|
| **edges** | **103** |
|
|
| declare neither | 62 — 49% |
|
|
| **cycles** | **0** |
|
|
| **dependencies declared but absent** | **0** |
|
|
| deepest chain | 5 |
|
|
|
|
Half the catalogue is unconnected, which matches
|
|
[research 005](../005-domain-grouping/analysis.md)'s finding that fifty modules co-change with
|
|
nothing. The connected half is well formed: no cycles, nothing dangling.
|
|
|
|
## Finding 2 — it is already resolved, and already used
|
|
|
|
The platform SDK carries a dependency resolver that topologically sorts modules, and it is
|
|
called from three places: the tool loader at startup, the installer when syncing modules onto a
|
|
node, and the delivery coordinator when expanding what a change affects.
|
|
|
|
It also already does something the effort assumed would need designing: **a requirement on
|
|
another module's provision is treated as an implicit edge to that module**, so a consumer does
|
|
not have to declare the same relationship twice.
|
|
|
|
So *ordering by the graph* — which
|
|
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) says
|
|
the control plane will do — is not a thing to build. It is a thing to call.
|
|
|
|
## Finding 3 — the most important edges in the mesh are invisible
|
|
|
|
The one place the graph is wrong, and it is wrong about the substrate.
|
|
|
|
A module that needs a database declares it like this:
|
|
|
|
```yaml
|
|
provisions:
|
|
- name: mesh-db
|
|
provider: postgres # ← a dependency on the postgres module
|
|
node: <a named node> # ← and where it must run
|
|
```
|
|
|
|
`provider:` names a module. It is a dependency, declared, in the manifest — and it sits inside
|
|
`provisions:`, which is what a module *offers*. The resolver reads `dependencies:` and
|
|
`requires:`, so it never sees it.
|
|
|
|
| | |
|
|
|---|---|
|
|
| provider references that name a real module | 4 |
|
|
| **invisible to the resolver** | **3, across 2 modules** |
|
|
|
|
Three edges is nothing, and they are the mesh's own database, the mesh's own broker, and the
|
|
work engine's database. The most load-bearing relationships in the system are the ones the
|
|
graph cannot see.
|
|
|
|
**The consequence is measurable.** Computing what a working mesh needs, from the declared
|
|
graph:
|
|
|
|
```
|
|
registry → sdk → mesh → meshware 4 modules, 4 levels
|
|
```
|
|
|
|
No database. No broker. A closure that is arithmetically correct and obviously wrong, and wrong
|
|
for exactly one reason: a field that means *depends on* is not read as one.
|
|
|
|
This is [04-ISSUES/003](../../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md)
|
|
again, in a new place — not a key nothing reads, but a key read as something other than what it
|
|
means.
|
|
|
|
## Finding 4 — the resolver continues past faults it should stop on
|
|
|
|
Two behaviours, both contrary to
|
|
[ADR 0058](../../02-DECISIONS/0058-delivery.md):
|
|
|
|
- **A cycle warns and falls back to input order.** A cycle means no correct order exists; the
|
|
resolver proceeds with an arbitrary one and logs a line.
|
|
- **A dependency that does not exist warns and continues.** The validation is documented as
|
|
*non-fatal, logged as warnings*.
|
|
|
|
Neither has fired in the current catalogue — there are no cycles and nothing dangling — which
|
|
is why nobody has noticed. They are latent, and they are in the component that
|
|
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) makes
|
|
responsible for the ordering a host will apply without question.
|
|
|
|
## Finding 5 — placement is decided in the catalogue
|
|
|
|
`node:` in a provision pins it to a named node, in the manifest. Two modules do this today,
|
|
and they are the substrate ones.
|
|
|
|
Which node runs what is an inventory and placement decision — tier 2 by the skeleton's own
|
|
test. Having it in a manifest means the catalogue decides placement, and a second node cannot
|
|
provide the mesh's database without editing the module that consumes it.
|
|
|
|
## What this means for the effort
|
|
|
|
**The opening question — "what does the graph delete?" — has an answer: nothing, because the
|
|
graph is already there.** The premise was wrong, and finding that out is the effort's first
|
|
result rather than a setback.
|
|
|
|
The questions that survive are narrower and answerable:
|
|
|
|
| Missing declaration | Manifests using it today |
|
|
|---|---|
|
|
| `excludes` — installing A makes B unavailable | **0** |
|
|
| a required node capability | **0** |
|
|
| an interface, with adapters providing it | **0** |
|
|
|
|
Those are what a graph would *add*. What it would delete is a different and smaller list, and
|
|
the honest version of it is: nothing yet.
|
|
|
|
**And two defects worth fixing regardless of what else this effort concludes:**
|
|
|
|
1. `provider:` is a dependency edge and is not read as one. Fixing it makes the closure correct
|
|
— which is what [research 012](../012-the-minimum-viable-node/00-overview.md) needs in order
|
|
to answer what a one-node mesh requires.
|
|
2. The resolver continues past a cycle and past a missing dependency. Both should refuse.
|
|
|
|
## What was not measured
|
|
|
|
- **Whether the missing declarations would be used.** Zero manifests declare exclusions or
|
|
capabilities, and that is equally consistent with *nobody needs them* and *nobody can express
|
|
them*. Nothing here separates those.
|
|
- **Whether the interface-and-adapter idea has a consumer.** It is a good shape, and it is
|
|
argued for rather than measured.
|
|
- **What the closure should be.** Finding 3 says the computed one is wrong. It does not say
|
|
what the right one is; that needs the fix first.
|