Files
hq/04-ISSUES/044-the-runtime-every-module-builds-on-cannot-be-built-by-the-mesh/00-report.md
T
jschoubben 3983188b8a Issue 044 resolved — the mesh builds its own floor
Cloned alone, built by the mesh, every module moved onto it, and the base then
changed for real: all three went stale naming what moved. A comment-only change
correctly makes nothing stale, which found a second bug — staleness compared
commits where it should compare artifacts.
2026-09-13 02:51:08 +02:00

6.4 KiB

status, opened, located-in, fixed-by, amended-design
status opened located-in fixed-by amended-design
resolved 2026-09-13
mesh-tools
mesh-sdk
mesh-tools feat/run-once-entry, mesh-sdk feat/adr-ref-resync

044 — The runtime every module builds on cannot be built by the mesh

Symptom

Every module written in the mesh's own toolchain is compiled on top of one shared base image — the tool runtime. The mesh can build the modules. It cannot build the base.

Two things stop it, and each is enough on its own:

  • The base's container definition copies a compiled output directory that is not in the repository. A clean clone has the sources and no compiled output, so the definition refers to something that is not there.

  • The base's dependency on the mesh's own software development kit resolves to a sibling checkout on disk, not to anything a clone can fetch:

    "node_modules/@novox/mesh-sdk" -> { "resolved": "../mesh-sdk", "link": true }
    

Both mean the same thing: the base can only be produced on a workstation that happens to have the neighbouring repositories laid out beside it, and has run a compile step by hand first.

Observed while building four modules through the mesh from a repository and a path. All four succeeded, and all four recorded that they were built on top of the same base — an image reference that no module in the mesh produced, because no module could have.

Why this matters

This is the one artifact the whole build chain rests on, and it is the one artifact outside the chain. Three separate consequences, all visible today:

  • The base is pinned by hand. Every module's container definition names it as a literal digest written by a person. Nothing checks that digest against anything.
  • Nothing can tell when it moves. A build records what it was built on top of, so an artifact is stale when anything beneath it moved. That rule cannot fire for the base: the graph has edges pointing at it, and no version on the other end, because no build ever registered one. The mesh is therefore unable to answer "what must be rebuilt" for the change that would reach every module at once.
  • Nobody can check what is in it. The published base and the definition in the repository have already drifted: the definition names one operating system base, and the image actually serving every module is built on a different one. This was found by running the image, not by reading anything, and it went unnoticed for exactly as long as nobody could rebuild it.

A rule the design states — an artifact is stale when anything it was built against moved — is unenforceable for the artifact it matters most for. That is the shape of a wrong rule, not a missing feature: everything downstream reports "nothing to rebuild" and is believed.

How it would be checked

The check is the fix's own test: clone the repository alone, into an empty directory, and build it. Nothing else present, no neighbouring checkouts, no compile run first. If that produces the base, the mesh can produce it too, because that is all the mesh does.

Open questions

  • Where should the software development kit come from, for a build that has only one repository? The mesh already knows how to run a package registry as a module, and it already publishes container images to one of its own — so this may be the same answer twice rather than a new one.
  • Is the base a module like any other, or is it one of the few things a new mesh must arrive carrying? It behaves like both: everything is built on top of it, and it is built out of the same sources as everything else.
  • If it becomes an ordinary module, is there a circularity? No — checked rather than assumed: the builder is a compiled program built from its own sources and a language toolchain, and does not stand on this base at all. Only modules written in the mesh's scripted toolchain do. So the thing that would build the base is not one of the things built on it, and the chain has an end.

Resolved, 2026-09-13

The base is a module now, built from its own repository and nothing else.

Three things were in the way, and the third was not visible until the first two were gone:

  • The toolkit could not be fetched. It is named by a pinned commit of its own repository rather than by a folder on somebody's disk. Both repositories turned out to be readable without a credential, so no package registry was needed after all — which is why this was smaller than it looked.
  • The toolkit shipped no compiled output. Every one of its entry points pointed into a directory that is not in source control. It declares the hook that is meant to compile it on install, and the package manager in use does not run that hook — so the build compiles it explicitly instead. Relying on a hook firing would have failed the same invisible way this issue is about.
  • The base is also the compile environment. Every module's recipe starts from this image and invokes the compiler out of it. A first attempt dropped the build tools to make a smaller image, which broke every module built on it. They stay, and the recipe now says why.

The drift this issue reported is also closed: the recipe claimed one operating system while serving another, and now states what is actually in service. Changing that was deliberately not folded in — a module already builds against it with that system's package manager, and moving the ground under every module at the same moment as making it buildable would be two changes wearing one commit.

Checked the way the report said it should be. The repository was cloned alone, with nothing beside it, and built by the mesh. Then every module was moved onto the result, and the base changed for real: all three went stale, each naming the base and the commits it moved between, each listed once. Changing only a comment in the base — a new commit, a byte-identical image — correctly makes nothing stale, which is a second bug this exercise found and fixed: staleness was comparing commits when it should compare artifacts, so the mesh would have rebuilt itself entirely to arrive back exactly where it started.

What is still true

A module names its base as a literal fingerprint in its own recipe. So the mesh can now say that three modules must be rebuilt, and rebuilding them still produces the old base until somebody edits that fingerprint. Noticing is solved; acting on it is the build chain driving itself, which is separate work.