fix(adoption): the baseline backfill runs to completion — one call adopts a pre-log baseline of any size
A production brain with a 12.7k-row pre-log baseline advanced exactly 800 rows per adoptLogAuthority() call (a five-pass ceiling × the oracle's 200-row listing cap), refused the flip, and sat tree-authoritative for hours across restarts. The bound was sized for drift, never for a baseline. Now: the adoption path runs the oracle uncapped so ONE scan yields the ENTIRE curable set, every pass cures all of it, and the loop runs to completion with the no-progress guard as its only stop. Pace rides the write path (one full-brain scan amortizes over thousands of cures, not two hundred): 1,000 drifted rows adopt green in one call in ~10s. Progress is narrated for a live operator. The wire report keeps its 200-row cap. Pinned: a baseline above the old ceiling adopts green in a single call.
This commit is contained in:
parent
3915180f7b
commit
a5a1883819
3 changed files with 145 additions and 9 deletions
92
tests/integration/adopt-large-baseline.test.ts
Normal file
92
tests/integration/adopt-large-baseline.test.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* @module tests/integration/adopt-large-baseline
|
||||
* @description Adoption runs the baseline backfill TO COMPLETION in one call.
|
||||
* A production brain with a 12.7k-row pre-log baseline once advanced exactly
|
||||
* 800 rows per `adoptLogAuthority()` call (a five-pass ceiling × the oracle's
|
||||
* 200-row listing cap), refused the flip, and sat tree-authoritative for
|
||||
* hours across restarts. The pin: a baseline larger than that old ceiling
|
||||
* — every row oracle-visible as `state-differs` drift — adopts GREEN in a
|
||||
* SINGLE call, and the row count proves the whole set was cured, not a page.
|
||||
*/
|
||||
import { describe, it, expect, afterEach } from 'vitest'
|
||||
import { mkdtempSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { Brainy } from '../../src/index.js'
|
||||
import { NounType } from '../../src/types/graphTypes.js'
|
||||
|
||||
type RawBox = {
|
||||
storage: {
|
||||
readNounRaw(id: string): Promise<{ metadata: unknown; vector: unknown }>
|
||||
writeNounRaw(id: string, r: { metadata: unknown; vector: unknown }): Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
const dirs: string[] = []
|
||||
const brains: Brainy[] = []
|
||||
afterEach(async () => {
|
||||
for (const b of brains.splice(0)) await b.close().catch(() => {})
|
||||
for (const d of dirs.splice(0)) rmSync(d, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
describe('adoption backfill runs to completion', () => {
|
||||
it('a pre-log baseline larger than the old 800-row ceiling adopts GREEN in ONE call', async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'brainy-large-baseline-'))
|
||||
dirs.push(dir)
|
||||
const brain = new Brainy({
|
||||
storage: { type: 'filesystem', path: dir },
|
||||
requireSubtype: false,
|
||||
logAuthority: 'defer'
|
||||
})
|
||||
await brain.init()
|
||||
brains.push(brain)
|
||||
|
||||
// Above the old ceiling (5 passes × 200 = 800): every row must be cured
|
||||
// in the one call for the flip to be legal.
|
||||
const ROWS = 1000
|
||||
const ids: string[] = []
|
||||
for (let i = 0; i < ROWS; i++) {
|
||||
ids.push(
|
||||
await brain.add({
|
||||
data: `baseline row ${i}`,
|
||||
type: NounType.Document,
|
||||
metadata: { i },
|
||||
vector: Array.from({ length: 384 }, (_, k) => ((i + k) % 7) / 7)
|
||||
})
|
||||
)
|
||||
}
|
||||
await brain.flush()
|
||||
|
||||
// Manufacture the production shape on EVERY row: pre-hydration-law drift
|
||||
// (a stored wrapper whose denormalized fields disagree with its own
|
||||
// metadata leg) — each is a curable `state-differs` mismatch, so the
|
||||
// oracle's full curable set is ROWS, well past any per-pass page.
|
||||
const storage = (brain as unknown as RawBox).storage
|
||||
for (const id of ids) {
|
||||
const raw = await storage.readNounRaw(id)
|
||||
const wrapper = raw.vector as Record<string, unknown>
|
||||
await storage.writeNounRaw(id, {
|
||||
metadata: raw.metadata,
|
||||
vector: { ...wrapper, noun: 'thing', legacyField: 'pre-law residue' }
|
||||
})
|
||||
}
|
||||
const before = await brain.verifyLogAuthority()
|
||||
expect(before.verdict, 'the whole baseline is oracle-red').toBe('red')
|
||||
// The wire report is capped at 200 — the truncation flag is what the old
|
||||
// loop bounded itself on; the cure path no longer reads through it.
|
||||
expect(before.mismatchListTruncated).toBe(true)
|
||||
|
||||
// THE PIN: one call, green, log-authoritative — no restarts, no loop.
|
||||
const report = await brain.adoptLogAuthority()
|
||||
expect(report.verdict).toBe('green')
|
||||
expect(brain.logAuthority().authority).toBe('log')
|
||||
expect(report.nounsChecked).toBeGreaterThanOrEqual(ROWS)
|
||||
|
||||
// Nothing degraded: a sample of rows still serves with intact metadata.
|
||||
for (const id of [ids[0], ids[499], ids[ROWS - 1]]) {
|
||||
const row = await brain.get(id)
|
||||
expect(row).not.toBeNull()
|
||||
expect(typeof (row!.metadata as { i: number }).i).toBe('number')
|
||||
}
|
||||
}, 600000)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue