// mesh-catalog's entrypoint — the module graph's consumer (novox/hq ADR 0070, ADR 0072). // // The builder announces what it built; this places it in the graph and announces what that means. // The control plane hooks the *meaning* — a module was upgraded — rather than the build output, so // it never has to interpret an artifact or ask this module anything. // // **Ordering is not computed here.** When a registration makes something else stale, the modules // whose own dependencies are all current are announced as needing a rebuild; the rest stay stale // and appear the next time round, once whatever they were waiting for is registered. A chain and a // diamond need no special handling, and nothing holds a plan. import { on, emit } from "@novox/mesh-sdk/events"; import { Graph, type Made } from "./store.js"; const graph = Graph.fromEnv(); // Before subscribing, and idempotent. The runtime is restarted until its store is reachable, which // is the same arrangement model-usage uses: a schema step that had to reach the provider over the // overlay would block the very apply that brings the overlay up. await graph.migrate(); /** What the builder says when it has built something. */ interface Built { module?: string; commit?: string; repository?: string; path?: string; ref?: string; manifest?: unknown; /** What this build published, each pinned as anything else would name it. */ made?: Made[]; /** * Every artifact this was built on top of, so the edge is derived rather than declared. * * References, not module names: the builder sees a pinned image and cannot see which module * produced it. Turning that into an edge between module-versions is this module's job. */ against?: string[]; /** * This is history, not news — the mesh re-announcing a build this catalogue was not there for. * * Registered exactly as any other, and announced as nothing. A module that moved months ago is * not something anything should act on now: emitting `upgraded` would have the control plane * decide about a rollout, and `rebuild-needed` would ask for builds of things that are already * current. */ replay?: boolean; } await on("module.builder.built", async (event) => { const body = event.body as Built; if (!body.module || !body.commit) { // Said rather than dropped: a build that announced itself without saying what it built is a // fault in the builder, and a silent discard here would make it look like a missing event. console.error("mesh-catalog: a build event named no module or no commit; ignored", body); return; } const { upgraded, previous } = await graph.register({ module: body.module, commit: body.commit, repository: body.repository ?? "", path: body.path ?? "", ref: body.ref ?? "", manifest: body.manifest ?? {}, }, body.made ?? [], body.against ?? []); // **A replay is registered and announced to nobody.** See `replay` above: the graph gains what // it was missing, and the mesh is told nothing happened, because nothing did. if (body.replay) return; await emit("module.mesh-catalog.registered", { module: body.module, commit: body.commit, upgraded, }); // **A rebuild that changed nothing is not an upgrade.** Announcing it would ripple outward // through modules that did not change, forever (ADR 0072). if (!upgraded) return; await emit("module.mesh-catalog.upgraded", { module: body.module, commit: body.commit, previous, }); // What can be built now — stale, and waiting on nothing that is itself stale. for (const next of await graph.buildable()) { await emit("module.mesh-catalog.rebuild-needed", { module: next.module, builtAt: next.commit, because: next.because, }); } }); // **And ask for what was built before this catalogue existed** (novox/hq 04-ISSUES/050). // // The queue above is durable, so nothing is missed once this is running. What it cannot have is // what was announced before it first ran — and on a fresh mesh that is never arbitrary: the shared // base, the store this runs on, and this module itself are each necessarily built BEFORE a // catalogue exists to hear about them. The graph's foundation is the part it never sees. // // Asked on every start, not only the first. A catalogue cannot tell whether it has a gap, and the // answer is idempotent: registering a build already held changes nothing and announces nothing. // Asked AFTER subscribing, so a build arriving during the replay is not lost between the two. await emit("module.mesh-catalog.catching-up", {});