Three concrete bugs surfaced when first-running the spike in community:
1) NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN flag never detected:
The previous readEnvFlag used dynamic bracket access
(`env?.[name]`), which Next.js / Turbopack does NOT substitute at
build time. Only literal `process.env.NEXT_PUBLIC_FOO` references
get inlined into the client bundle. Switched to literal access
plus a `typeof process` guard. Spawn now toggles via the flag as
designed.
2) Shelf cursor appeared offset from the mouse:
The cursor mesh lives inside the ToolManager's building-local
group, but the tool was setting `cursorRef.current.position` to
level-local coordinates (computed via `worldToLocal(level)`).
Result: cursor shifted by (building-pos − level-pos) in worst
case. Switched cursor display to use `event.localPosition`
(already building-local) with grid snap — matches the legacy
spawn-tool pattern. The commit path keeps the worldToLocal(level)
conversion since the shelf node's `position` field is stored
relative to its level parent.
3) Shelf rendered invisibly after click (suspected):
The renderer used a useEffect-swap pattern where it mounted an
empty <group> and imperatively added Three.js children from a
buildShelfGeometry() Group. Plausibly fragile under StrictMode
double-invoke or fast HMR. Switched to inline R3F JSX — top
board + brackets as plain <mesh> primitives. The pure geometry
function still exists in geometry.ts for tests and AI-authored
consumers; renderer just doesn't go through it.
Diagnostics added (dev-only; removed once spawn parity ships):
- `[shelf] placed <id> level-local <pos> parent <levelId>` on click
- `[shelf] rendered <id> at <pos>` on mount
Also: types: ["node"] in nodes/tsconfig.json so the typeof process
guard typechecks cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>