Files
hq/01-RESEARCH/011-the-module-graph/analysis.md
T
jschoubben 333356cff3 Order the records the way the system is learned
Jochen asked whether the order made sense. It did not -- it followed when
things happened to be decided, which after consolidation is fictional anyway
since record 5 alone folds decisions taken across a week.

Concretely wrong before: the domain statement sat at 8, after five engineering
rules; the constitution was scattered across 5, 12 and 17; the tiers landed at
15, 16, 21 and 22 with process records in between.

Now it walks: what the mesh is (1-3), its tiers from the bottom up (4-8), what
runs on them and how it gets there (9-10), how it is built (11-16), how it is
checked (17-18), how we work (19-23).

Two things made this safe rather than free. It is a permutation, not a
compaction, so the renames go through temporary names -- otherwise two files
want one slot and one is lost. And the reference rewrite is a single
simultaneous pass, because almost every number moved into a slot another number
was vacating; replacing one at a time would have cascaded and pointed things at
the wrong record while still resolving.

Verified: 284 [ADR NNNN](path) links across the repository, all with matching
text and target.

The ordering principle is now stated in 19 rather than left implicit -- the
repository already said "the numbering is the flow" about its folders, and
there was no reason for the records to be the exception.
2026-08-28 23:30:42 +02:00

5.8 KiB

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'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 0005 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:

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 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 0010:

  • 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 0005 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 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.