--- status: open opened: 2026-09-15 located-in: [] fixed-by: amended-design: --- # 053 — The SDK is pinned twice, and the two disagree ## Symptom The module that carries the tool runtime names the SDK two ways, and they are not the same thing: ``` package.json @novox/mesh-sdk -> git+https:///mesh-sdk.git# package-lock.json @novox/mesh-sdk -> ../mesh-sdk ``` The manifest names a commit in a repository any machine can reach. The lock names a **sibling directory**, which exists on the workstation the lock was generated on and nowhere else. It builds anyway, because the recipe runs `npm install` — which tolerates a lock that disagrees with its manifest and re-resolves from the manifest. It is the one command that hides this. ## Why this matters **A lock file exists to make a build reproducible, and this one describes one machine.** `npm ci` — the command for exactly the case a lock is for — fails here, or worse, succeeds against whatever happens to be at that path. **It is the first thing a fresh mesh builds.** The toolchain image carries the SDK, and everything with code of its own is compiled inside it. A dependency resolved differently on the build machine than on a workstation is a difference in every module the mesh will ever build, arriving as a compile error or a runtime mismatch far from here. **And it is about to be copied.** Each language's toolchain will carry that language's SDK the same way ([ADR 0074](../../02-DECISIONS/0074-the-wire-is-specified-not-the-types.md)). Whatever this repository does, the Rust and Python ones will do, so the shape is worth getting right before there are four of them. ## Open questions - **Is a git dependency at a pinned commit the intended mechanism?** It works, needs no package registry, and reuses the forge a mesh already depends on to exist at all ([ADR 0071](../../02-DECISIONS/0071-where-genesis-gets-its-source.md)). If so, the lock should say so and the sibling path should never have been committed. - **Or should the SDK be a published package?** The catalogue holds a private registry module, and a published package is how the rest of the world does this — at the cost of a mesh needing that registry up before it can build anything, which is a bootstrap problem where there is currently none. - **What generates the lock, and on what?** A lock produced on a workstation with sibling checkouts will keep saying this. A lock produced the way the image builds would not. ## How this would be checked | Rule | Checked by | |---|---| | A build does not depend on the machine it runs on | The toolchain image builds with `npm ci` rather than `npm install`, which refuses a lock that disagrees with its manifest. | | The SDK a module compiles against is the one named | The commit baked into the toolchain image is compared with the one the manifest pins. |