| … | … | @@ -51,12 +51,39 @@ function engineFloor () { |
| 51 | 51 | // >=1.29.4 yet the membrane boots on 1.28.0, because the runtime uses its own bundled copy and the one |
| 52 | 52 | // in node_modules is never required. So gating on the scan would fail a working install. The scan's job |
| 53 | 53 | // is to EXPLAIN a boot failure, which is where its value was in the first place. |
| 54 | | function preflight () { |
| 55 | | let have = null |
| 56 | | try { have = execFileSync('bare', ['-v'], { encoding: 'utf8' }).trim().replace(/^v/, '') } catch { |
| 57 | | throw new Error('`bare` is not on PATH — install it with `npm i -g bare-runtime`') |
| 54 | // WHICH bare. The gate used to shell out to `bare` from PATH — but nothing in production runs that |
| 55 | // binary. pgh launchers exec a runtime out of the tree store, and the supervisor picks its own; on |
| 56 | // 2026-08-17 PATH was v1.28.0 while the live membrane ran v1.28.7 out of pgh/runtime/bare. So "smoke |
| 57 | // OK" was evidence about a build that never executes the closure, which is the same shape of blind |
| 58 | // spot as the gate being dark in the first place (#46). |
| 59 | // |
| 60 | // Resolution order, most-production-like first. SMOKE_BARE overrides everything so a specific build |
| 61 | // can be pinned in CI or bisected by hand. |
| 62 | const RUNTIME_CANDIDATES = [ |
| 63 | process.env.SMOKE_BARE, |
| 64 | process.env.HOME && `${process.env.HOME}/.local/share/store/apps/pgh-sidecar/runtime/bare`, |
| 65 | process.env.HOME && `${process.env.HOME}/.local/share/store/apps/pgh/runtime/bare` |
| 66 | ].filter(Boolean) |
| 67 | |
| 68 | function resolveBare () { |
| 69 | for (const bin of RUNTIME_CANDIDATES) { |
| 70 | try { |
| 71 | const v = execFileSync(bin, ['-v'], { encoding: 'utf8' }).trim().replace(/^v/, '') |
| 72 | return { bin, have: v, source: bin === process.env.SMOKE_BARE ? 'SMOKE_BARE' : 'pgh runtime (as production runs)' } |
| 73 | } catch { /* not installed on this box — try the next */ } |
| 74 | } |
| 75 | try { |
| 76 | const v = execFileSync('bare', ['-v'], { encoding: 'utf8' }).trim().replace(/^v/, '') |
| 77 | // Deliberately loud. Passing here does NOT establish that the closure boots in production. |
| 78 | return { bin: 'bare', have: v, source: 'PATH — ⚠ NOT the runtime production uses' } |
| 79 | } catch { |
| 80 | throw new Error('no bare runtime found — install one (`npm i -g bare-runtime`) or set SMOKE_BARE=<path>') |
| 58 | 81 | } |
| 59 | | return { have, need: engineFloor() } |
| 82 | } |
| 83 | |
| 84 | function preflight () { |
| 85 | const { bin, have, source } = resolveBare() |
| 86 | return { bin, have, source, need: engineFloor() } |
| 60 | 87 | } |
| 61 | 88 | |
| 62 | 89 | // Appended to a boot failure: if a dep declares a floor above the running bare, say so explicitly. |
| … | … | @@ -80,10 +107,11 @@ function req (path, { host, method = 'GET', body } = {}) { |
| 80 | 107 | }) |
| 81 | 108 | } |
| 82 | 109 | |
| 83 | | const { have, need } = preflight() |
| 84 | | console.log(`smoke: bare v${have}${need ? ` (dependency floor >=${need.version} from ${need.pkg})` : ''}`) |
| 110 | const { bin, have, source, need } = preflight() |
| 111 | console.log(`smoke: bare v${have} via ${source}`) |
| 112 | console.log(`smoke: runtime ${bin}${need ? ` (advisory floor >=${need.version} from ${need.pkg} — see engineFloor)` : ''}`) |
| 85 | 113 | |
| 86 | | const child = spawn('bare', ['index.js'], { |
| 114 | const child = spawn(bin, ['index.js'], { |
| 87 | 115 | cwd: new URL('..', import.meta.url).pathname, |
| 88 | 116 | env: { |
| 89 | 117 | ...process.env, |