A new module, mesh-catalog, which holds the module graph in a database the mesh provisions for it. It records every module version, the artifacts each published, the build edges between them, and what each version declares it requires and provides. Five tools answer over the broker — including what provides a given provision, which until now meant reading every module's file by hand.
A build edge names an artifact, because that is what the builder can see. It cannot know which module produced a pinned image; that is a fact about the graph, and the graph is here. Edges to an artifact no module here produced are kept — they resolve by themselves the day that module is registered, which is the ordinary case while a mesh is filling in.
Stale means the artifact moved, not the commit. A changed comment in a recipe is a new commit and a byte-identical image; comparing commits would have had the mesh rebuild itself entirely to arrive back exactly where it started.
postgres, mesh-catalog and amqp-ping build their own runtimes, so the mesh can make the database provider the catalogue needs rather than waiting for somebody to hand it over. postgres carries the client it provisions through; the catalogue brings its own driver; and every module now stands on a base the mesh built rather than on a digest typed in by hand.
A module that listens is served, not run.run imports an entrypoint without binding a broker — it exists for a step that works offline and exits — so both the catalogue and amqp-ping died on their first subscription.
Verified against a live mesh: a push moved a module's source, the mesh built it, the catalogue called it an upgrade, and the control plane sent it to the one machine running it with nobody driving any step.
**A new module, mesh-catalog**, which holds the module graph in a database the mesh provisions for it. It records every module version, the artifacts each published, the build edges between them, and what each version declares it requires and provides. Five tools answer over the broker — including what provides a given provision, which until now meant reading every module's file by hand.
**A build edge names an artifact**, because that is what the builder can see. It cannot know which module produced a pinned image; that is a fact about the graph, and the graph is here. Edges to an artifact no module here produced are kept — they resolve by themselves the day that module is registered, which is the ordinary case while a mesh is filling in.
**Stale means the artifact moved, not the commit.** A changed comment in a recipe is a new commit and a byte-identical image; comparing commits would have had the mesh rebuild itself entirely to arrive back exactly where it started.
**postgres, mesh-catalog and amqp-ping build their own runtimes**, so the mesh can make the database provider the catalogue needs rather than waiting for somebody to hand it over. postgres carries the client it provisions through; the catalogue brings its own driver; and every module now stands on a base the mesh built rather than on a digest typed in by hand.
**A module that listens is served, not run.** `run` imports an entrypoint without binding a broker — it exists for a step that works offline and exits — so both the catalogue and amqp-ping died on their first subscription.
Verified against a live mesh: a push moved a module's source, the mesh built it, the catalogue called it an upgrade, and the control plane sent it to the one machine running it with nobody driving any step.
A proof branch, not for main: the route-forwarding bed reads this module's literal
image and would break until the module is built.
The modelling is right regardless — the image is upstream, so the mesh should
mirror it once into its own registry and pin what that registry assigned, rather
than every machine fetching a reference somebody else can move.
Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
It links module-versions to each other and knows nothing about nodes; which
machine runs what stays the control plane's (novox/hq ADR 0070, 0072). Keeping
them apart is what lets the control plane carry on composing declarations while
this is down.
The builder announces what it built, this places it in the graph and announces
what that means, and the control plane hooks the meaning rather than the build
output. A rebuild producing the commit already current is registered and is not
an upgrade — announcing it would ripple outward forever through modules that did
not change.
Ordering is not computed. Modules stale and waiting on nothing that is itself
stale are announced as buildable; the rest stay stale and appear once whatever
they were waiting for is registered, so a chain and a diamond need no special
handling and nothing holds a plan.
Four tools over the graph: what this mesh holds, one module in full, what a
change to a module reaches, and what must be rebuilt and why. The edges are
derived from builds rather than declared, so they cannot drift from what the code
actually uses.
Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
A module's runtime image was assembled by a script copying the sdk and the tool
runtime out of neighbouring checkouts, so it could only be built on a workstation
that had them. That is why no module declared what it was made of and why
forty-seven point at a placeholder.
The tool runtime becomes an image a module's runtime is built FROM, published like
any other artifact. The module then builds from its own directory and that base —
one clone, which is what the builder can actually be asked for (novox/hq ADR 0069).
The dependency stops being a property of somebody's machine and becomes a build
edge, pinned to a digest the mesh's registry assigned.
The compiler is invoked by its real path rather than through node_modules/.bin:
those are symlinks to a launcher that requires its library relatively, and
resolving them while building the base leaves a launcher pointing at nothing.
Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
Its account is scoped from what it emits and consumes, and it declared neither —
which is why asking for a generic module account produced one that authenticated
and could do nothing, with the refusal surfacing a layer away as a permissions
error against a queue.
Declaring the announcement is not documentation here. It is what the permission
is derived from.
Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
Its provisioner container named an image nobody could produce — a zero digest
placeholder. It names an artifact instead, and the module says how to build it,
so the mesh can make the database provider the catalogue needs.
The runtime base carries what every module needs, and a database driver is not
that. Installed into an empty directory because the module's package.json also
names the sdk, which lives in the base rather than on a registry.
`run` imports an entrypoint without binding a broker — it exists for a step that
works offline and exits. Both the catalogue and amqp-ping subscribe on import,
so both died on the first on() with no broker bound.
The catalogue expected each edge to name a module and a commit. The builder
sends a pinned image reference — it cannot know which module produced it, that
is a fact about the graph. So every build that had been built on top of anything
was rejected, and only the modules built against nothing ever registered.
Versions now record what they published, and an edge resolves through that. An
edge to an artifact no module here produced is kept: it resolves by itself when
that module is registered, which is the ordinary case while a mesh fills in.
Build edges are discovered by building; requires and provides are stated by the
module about itself. Both belong in the graph and answer different questions —
and "what provides postgres-database" needed a sweep over every manifest, which
only something holding all of them can do.
The base was a digest typed in by hand, for an image nothing in the mesh could
produce — so the graph held edges pointing at it with no version on the far end,
and the one change that reaches every module at once could never be noticed.
It is a module now, and these edges resolve.
A comment changed in a build recipe is a new commit and a byte-identical image.
Comparing commits called every module standing on it stale, so the mesh would
have rebuilt itself entirely to arrive back exactly where it started — and
listed each dependent once per commit that had produced the same image.
A line in postgres's recipe and a one-line file in amqp-ping, both added to
move a commit and watch the mesh notice. The proofs worked; neither was meant
to stay. MESH_MODULE is set from the sealed credential at run time anyway, so
baking it in was dead weight as well as noise.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A new module, mesh-catalog, which holds the module graph in a database the mesh provisions for it. It records every module version, the artifacts each published, the build edges between them, and what each version declares it requires and provides. Five tools answer over the broker — including what provides a given provision, which until now meant reading every module's file by hand.
A build edge names an artifact, because that is what the builder can see. It cannot know which module produced a pinned image; that is a fact about the graph, and the graph is here. Edges to an artifact no module here produced are kept — they resolve by themselves the day that module is registered, which is the ordinary case while a mesh is filling in.
Stale means the artifact moved, not the commit. A changed comment in a recipe is a new commit and a byte-identical image; comparing commits would have had the mesh rebuild itself entirely to arrive back exactly where it started.
postgres, mesh-catalog and amqp-ping build their own runtimes, so the mesh can make the database provider the catalogue needs rather than waiting for somebody to hand it over. postgres carries the client it provisions through; the catalogue brings its own driver; and every module now stands on a base the mesh built rather than on a digest typed in by hand.
A module that listens is served, not run.
runimports an entrypoint without binding a broker — it exists for a step that works offline and exits — so both the catalogue and amqp-ping died on their first subscription.Verified against a live mesh: a push moved a module's source, the mesh built it, the catalogue called it an upgrade, and the control plane sent it to the one machine running it with nobody driving any step.