From b9ba50fbec4c697c8dffceedd8fd72ad95989287 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Tue, 25 Aug 2026 10:01:56 -0700 Subject: [PATCH] =?UTF-8?q?fix(plugins):=20the=20silent-degrade=20doors=20?= =?UTF-8?q?close=20=E2=80=94=20a=20broken=20accelerator=20install=20can=20?= =?UTF-8?q?never=20read=20as=20absent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-detection "not installed" heuristic accepted any resolution failure whose message merely CONTAINED the package name, unterminated — so a missing platform-binary sibling package (what a deploy replacing node_modules mid-restart leaves behind) read as "the accelerator is not installed", and brainy silently served the default WASM engines with zero journal lines. A production restart storm paid 90 seconds of throttled WASM compile behind exactly that hole. The name must now terminate where it ends (quote, whitespace, punctuation, end) — a sibling package, an inner file path, or a dependency failure is a broken install and init() throws, as the guard's own law always stated. Second door: activate() returning false (the documented graceful decline) warned on console.warn, which `silent: true` patches away — an invisible degrade. The decline now narrates via the always-on channel. Also exports CanonicalCounts from the public surface (the coverage-ledger denominator type consumers read through getCanonicalCounts()). Pinned in tests/unit/plugin-activation-loudness.test.ts (five error shapes; the decline warn under silent: true). --- src/brainy.ts | 11 ++- src/index.ts | 5 +- src/plugin.ts | 11 ++- tests/unit/plugin-activation-loudness.test.ts | 71 +++++++++++++++++++ 4 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 tests/unit/plugin-activation-loudness.test.ts diff --git a/src/brainy.ts b/src/brainy.ts index 151f5f7b..5a600402 100644 --- a/src/brainy.ts +++ b/src/brainy.ts @@ -17622,8 +17622,15 @@ export class Brainy implements BrainyInterface { private static isPackageNotInstalledError(error: unknown, pkg: string): boolean { const code = (error as { code?: string })?.code const message = error instanceof Error ? error.message : String(error) - const namesPackage = - message.includes(`'${pkg}'`) || message.includes(`"${pkg}"`) || message.includes(` ${pkg}`) + // The package name must TERMINATE where it ends: an unanchored prefix match + // read a missing platform-binary SIBLING package (e.g. "-linux-x64-gnu", + // exactly what a deploy replacing node_modules mid-restart leaves behind) as + // " is not installed" — and a present-but-broken accelerator silently + // degraded to the default JS engines. A production storm was hunted for a + // day because of that swallow. The name must be followed by a quote, + // whitespace, punctuation, or end-of-message — never a longer name's tail. + const escaped = pkg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const namesPackage = new RegExp("(^|['\"\\s])" + escaped + "(?=$|['\"\\s.,)])").test(message) const isResolutionFailure = code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND' || diff --git a/src/index.ts b/src/index.ts index 07f318c0..973136a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -389,7 +389,10 @@ import type { HNSWVerb, HNSWConfig, StorageAdapter, - DerivedFamilyDeclaration + DerivedFamilyDeclaration, + // The canonical count ledger a storage adapter maintains (counted + ALL-visibility + // scalars per family, the coverage-ledger denominators) — see StorageAdapter.getCanonicalCounts. + CanonicalCounts } from './coreTypes.js' // Export vector index implementation (the JS HNSW path) diff --git a/src/plugin.ts b/src/plugin.ts index 47a559b6..bfdc403a 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -9,6 +9,7 @@ * registered manually via `brain.use()` — there is no implicit detection. */ +import { prodLog } from './utils/logger.js' import type { StorageAdapter, Vector, @@ -1574,9 +1575,13 @@ export class PluginRegistry { this.activated.add(name) activated.push(name) } else { - // Documented graceful decline (activate() → false). Surface it loudly so - // a silent degrade to the default engine never goes unnoticed. - console.warn( + // Documented graceful decline (activate() → false). Surface it on the + // ALWAYS-ON channel: `silent: true` patches console, and a declined + // accelerator warned into a patched console is a silent degrade to the + // default engines — the exact invisible-fallback class this registry + // exists to prevent (a production storm ran the WASM engine for 90s + // behind one suppressed warn). + prodLog.warn( `[brainy] Plugin "${name}" declined activation (activate() returned false); ` + `the default engine is in use for its providers.` ) diff --git a/tests/unit/plugin-activation-loudness.test.ts b/tests/unit/plugin-activation-loudness.test.ts new file mode 100644 index 00000000..23e50a50 --- /dev/null +++ b/tests/unit/plugin-activation-loudness.test.ts @@ -0,0 +1,71 @@ +/** + * @module tests/unit/plugin-activation-loudness + * @description The plugin-activation swallow closes. Two laws: + * (1) THE NOT-INSTALLED FREE PASS IS EXACT — a resolution failure earns the + * silent skip ONLY when it names the probed package itself, terminated + * where the name ends. A missing platform-binary SIBLING package + * ("-linux-x64-gnu" — what a deploy replacing node_modules + * mid-restart leaves), an inner file path, or a dependency failure is a + * BROKEN install and must fail loud. A production storm ran 90s of + * throttled WASM behind this exact prefix-match hole. + * (2) A GRACEFUL DECLINE IS NARRATED ON THE ALWAYS-ON CHANNEL — activate() + * returning false warns via prodLog, which `silent: true` cannot patch + * away; a declined accelerator is never an invisible degrade. + */ +import { describe, it, expect, vi, afterEach } from 'vitest' +import { Brainy } from '../../src/brainy.js' +import { prodLog } from '../../src/utils/logger.js' + +const isNotInstalled = (error: unknown, pkg: string): boolean => + (Brainy as unknown as { + isPackageNotInstalledError(e: unknown, p: string): boolean + }).isPackageNotInstalledError(error, pkg) + +const resolutionError = (message: string): Error => { + const e = new Error(message) as Error & { code?: string } + e.code = 'ERR_MODULE_NOT_FOUND' + return e +} + +describe('the not-installed free pass is exact', () => { + const PKG = '@soulcraft/cor' + + it('the package itself, quoted or bare → not-installed (the one free path)', () => { + expect(isNotInstalled(resolutionError(`Cannot find package '${PKG}' imported from /app/x.js`), PKG)).toBe(true) + expect(isNotInstalled(resolutionError(`Cannot find module ${PKG}`), PKG)).toBe(true) + }) + + it('a missing platform-binary SIBLING package is a broken install, never not-installed', () => { + expect(isNotInstalled(resolutionError(`Cannot find package '${PKG}-linux-x64-gnu' imported from /app`), PKG)).toBe(false) + expect(isNotInstalled(resolutionError(`Failed to resolve ${PKG}-darwin-arm64`), PKG)).toBe(false) + }) + + it('an inner file path or a non-resolution error is never not-installed', () => { + expect(isNotInstalled(resolutionError(`Cannot find module '/app/node_modules/${PKG}/native/b.node'`), PKG)).toBe(false) + expect(isNotInstalled(new Error(`dlopen failed: wrong ELF class in ${PKG}`), PKG)).toBe(false) + }) +}) + +describe('a graceful decline is narrated on the always-on channel', () => { + afterEach(() => vi.restoreAllMocks()) + + it('activate() → false warns via prodLog even under silent: true', async () => { + process.env.BRAINY_DETERMINISTIC_EMBEDDINGS = 'true' + const warn = vi.spyOn(prodLog, 'warn') + const brain: any = new Brainy({ + requireSubtype: false, + storage: { type: 'memory' }, + silent: true, + dimensions: 384 + }) + brain.use({ name: 'declining-accelerator', activate: async () => false }) + await brain.init() + try { + expect( + warn.mock.calls.some((c) => String(c[0]).includes('"declining-accelerator" declined activation')) + ).toBe(true) + } finally { + await brain.close().catch(() => {}) + } + }) +})