--- status: resolved opened: 2026-10-03 located-in: - mesh-tools fixed-by: mesh-tools #32 amended-design: --- # 209 — A bundle's own copy of the SDK registers into a registry the runtime never reads ## What was observed 2026-10-03, preparing [design 38](../../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP4 — the first module whose tools bundle the node's runtime would load beside its own. Before the manifest changed, a probe on the laptop did what the runtime does: a process holding one copy of `@novox/mesh-sdk` imported a bundle in another directory that carried its own copy, and the bundle called `registerModuleTools` as every tools entrypoint does. ``` registrations seen by host after importing bundle: 0 ``` The import succeeds, the bundle registers, and the runtime's `collectTools` sees nothing. The runtime would log the bundle as loaded and answer `tools` for the module with an empty list: silent, and indistinguishable from a module that serves nothing by choice. ## Why it matters beyond this instance WP3 found that *a TypeScript bundle must carry its dependencies*, and the toolchain copies them in so a bundle starts anywhere. Among them is the SDK. The runtime imports a bundle in-process, and the language resolves a bare import from the importing file's own tree — so every bundle brings a second SDK into the process: its own registry of tools and its own broker handle. The SDK's registry is a module-level list, and the runtime reads only the one it imported itself. Every module from WP4 on is loaded this way. The runtime's tests never met it because their fixture bundles sit under the runtime's own tree and resolve the same copy. Nothing in design 38 WP1 or WP3 says which SDK a loaded bundle speaks to, and the one-runtime record ([ADR 0175](../../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md)) assumes without saying that it is the runtime's. ## Diagnosis Owner mesh-tools (`node-tools`): the runtime imports a bundle's entrypoint and counts the registrations that appear after it, through the SDK it imported; the bundle's `import "@novox/mesh-sdk/tools"` resolves to the copy under the bundle's own `node_modules`. **Fix direction:** the runtime resolves every import of the SDK, from whichever bundle, as if the runtime had written it — one registry, one broker — and leaves everything else a bundle carries to the bundle's own tree. A test loads a bundle from a directory holding its own copy of the SDK and asserts its tools are served. The toolchain keeps copying dependencies in: a bundle launched as a process ([ADR 0188](../../02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)) needs them, and an imported one is simply not allowed to bring a second SDK. ## Resolution 2026-10-03, mesh-tools #32: the runtime installs a synchronous resolve hook before the first bundle is imported, sending every import of the SDK, from whichever bundle, to its own copy; a bundle's other dependencies still resolve from its own tree, and a bundle launched as a process is untouched. The test loads a bundle from a directory holding its own copy of the SDK and its own dependency, and sees its tool served with the dependency's answer. Proven live 2026-10-03 by WP4's proof in design 38: the packet filter's bundle, the first loaded beside the runtime's own, serves its four tools on all four machines.