// Are the two autobase patches this daemon depends on actually present?
//
// ⛔ WHY A BOOT CHECK AND NOT TRUST. `scripts/patch-autobase-warmup.cjs` re-applies both patches on
// every `npm install`, so a reinstall cannot revert them — proven 2026-09-15 against a pristine
// upstream copy. What it does NOT survive is an autobase BUMP: the anchors move, the script prints
//
// anchor not found (autobase version changed?) — SKIPPING; re-verify manually
//
// and the install SUCCEEDS. Both guards vanish, and what they prevent is a SIGABRT crash-loop on boot
// (pear-git #3) and a daemon wedged in a microtask chain that accepts nothing and reaps nothing
// (measured on stortron: 800,000 `_advance` calls, no banner). A console line at install time is not
// a defence against either — nobody reads install output, and the symptom arrives days later wearing
// a different face.
//
// `overrides: { "autobase": "7.28.1" }` pins the version and is the only thing holding this shut
// today. This makes the pin's failure LOUD instead of silent.
//
// ⛔ FAIL CLOSED ON A POSITIVE ANSWER ONLY. A file we can read whose marker is missing is a fact, and
// we refuse. A file we cannot read at all is NOT knowledge — the layout could have changed, this
// could be a bundled tree, the check itself could be wrong — and refusing to boot a production forge
// over our own uncertainty is worse than the thing we are guarding against. Unknown proceeds, loudly.
// Same posture as chooseBundle's addon guard, for the same reason.
//
// PURE + INJECTED: `read` is a parameter, so every branch is testable with no node_modules.
export const GUARDS = [
{
file: 'node_modules/autobase/lib/apply-state.js',
marker: 'GUARD (pgv3 patch)',
protects: 'null view.core during _drain warmup — without it the daemon SIGABRT-crash-loops on boot (pear-git #3)'
},
{
file: 'node_modules/autobase/index.js',
marker: 'BOUND (pgv3 patch)',
protects: 'the _appendBatch flush loop — without it a mirror whose writer the origin has not enrolled spins forever and the daemon never serves (stortron, 800k _advance calls)'
}
]
/**
* @param {object} o
* read (relativePath) => string | null — source text, or null when it cannot be read
* @returns {{ ok: boolean, missing: object[], unreadable: object[] }}
*/
export function checkAutobaseGuards ({ read } = {}) {
if (typeof read !== 'function') throw new Error('checkAutobaseGuards requires read(path)')
const missing = []
const unreadable = []
for (const g of GUARDS) {
let src = null
try { src = read(g.file) } catch { src = null }
if (src == null) { unreadable.push(g); continue }
if (!src.includes(g.marker)) missing.push(g)
}
return { ok: missing.length === 0, missing, unreadable }
}
/**
* Boot gate. Throws when a guard is positively absent; warns when it cannot tell.
*
* ⚠ `PEAR_GIT_ALLOW_UNPATCHED_AUTOBASE=1` overrides, for the operator who has upstreamed the patches
* or is deliberately testing without them. An override that must be typed is a decision; a check that
* can be skipped by accident is not a check.
*/
export function assertAutobaseGuards ({ read, env = {}, log = console.error } = {}) {
const { ok, missing, unreadable } = checkAutobaseGuards({ read })
for (const g of unreadable) {
log('[autobase-guards] cannot read ' + g.file + ' — NOT verifying: ' + g.protects)
}
if (ok) return { ok: true, missing, unreadable }
const detail = missing.map((g) => ' ' + g.file + ' is missing "' + g.marker + '"\n guards: ' + g.protects).join('\n')
const msg = 'autobase is not patched for this daemon.\n' + detail +
'\n\nRun `npm install` (postinstall re-applies both), or if autobase was bumped the anchors have' +
'\nmoved and scripts/patch-autobase-warmup.cjs needs re-pointing — it warns and succeeds in that' +
'\ncase, which is why this check exists.' +
'\n\nTo boot anyway: PEAR_GIT_ALLOW_UNPATCHED_AUTOBASE=1'
if (env.PEAR_GIT_ALLOW_UNPATCHED_AUTOBASE === '1') {
log('[autobase-guards] OVERRIDDEN — booting unpatched.\n' + msg)
return { ok: false, overridden: true, missing, unreadable }
}
throw new Error('[autobase-guards] ' + msg)
}
export default { GUARDS, checkAutobaseGuards, assertAutobaseGuards }