97 lines
4.1 KiB
TypeScript
97 lines
4.1 KiB
TypeScript
|
|
/**
|
||
|
|
* @module tests/unit/db/log-authority-oracle-verbs
|
||
|
|
* @description The verification oracle's VERB legs — module-level pins with
|
||
|
|
* doubles (the brain-level wiring rides the owner's call site):
|
||
|
|
* 1. Wired verb legs diff verbs exactly like nouns (pre-log / state-differs /
|
||
|
|
* tombstone-vs-present / log-live-absent).
|
||
|
|
* 2. UNWIRED verb legs = an HONEST PARTIAL verdict: verbsChecked stays 0 —
|
||
|
|
* the oracle never claims scope it did not scan.
|
||
|
|
*/
|
||
|
|
import { describe, it, expect } from 'vitest'
|
||
|
|
import { runLogCompletenessOracle, recordDigest } from '../../../src/db/logAuthority.js'
|
||
|
|
import type { FactScanHandle } from '../../../src/db/factLog.js'
|
||
|
|
|
||
|
|
type Op = { kind: 'noun' | 'verb'; id: string; record: { metadata: unknown; vector: unknown } | null }
|
||
|
|
|
||
|
|
function scanOf(facts: Array<{ generation: number; ops: Op[] }>): () => FactScanHandle | null {
|
||
|
|
return () =>
|
||
|
|
({
|
||
|
|
batches: async function* () {
|
||
|
|
yield { facts: facts.map((f) => ({ ...f, timestamp: 0 })) }
|
||
|
|
}
|
||
|
|
}) as unknown as FactScanHandle
|
||
|
|
}
|
||
|
|
|
||
|
|
function pagedList(rows: string[]) {
|
||
|
|
return async ({ pagination }: { pagination: { limit: number; offset?: number } }) => {
|
||
|
|
const start = pagination.offset ?? 0
|
||
|
|
const items = rows.slice(start, start + pagination.limit).map((id) => ({ id }))
|
||
|
|
return { items, hasMore: start + pagination.limit < rows.length }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const rec = (v: number) => ({ metadata: { v }, vector: null })
|
||
|
|
|
||
|
|
describe('oracle verb legs', () => {
|
||
|
|
it('wired: verbs diff by digest — clean log goes green over nouns AND verbs', async () => {
|
||
|
|
const report = await runLogCompletenessOracle({
|
||
|
|
storage: { getNouns: pagedList(['n1']) } as never,
|
||
|
|
scanFacts: scanOf([
|
||
|
|
{ generation: 1, ops: [{ kind: 'noun', id: 'n1', record: rec(1) }] },
|
||
|
|
{ generation: 2, ops: [{ kind: 'verb', id: 'v1', record: rec(7) }] }
|
||
|
|
]),
|
||
|
|
canonicalNounDigest: async () => recordDigest(rec(1)),
|
||
|
|
factRecordDigest: recordDigest,
|
||
|
|
canonicalVerbDigest: async () => recordDigest(rec(7)),
|
||
|
|
getVerbs: pagedList(['v1'])
|
||
|
|
})
|
||
|
|
expect(report.verdict).toBe('green')
|
||
|
|
expect(report.nounsChecked).toBe(1)
|
||
|
|
expect(report.verbsChecked).toBe(1)
|
||
|
|
expect(report.matched).toBe(2)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('wired: every verb divergence class is NAMED', async () => {
|
||
|
|
const report = await runLogCompletenessOracle({
|
||
|
|
storage: { getNouns: pagedList([]) } as never,
|
||
|
|
scanFacts: scanOf([
|
||
|
|
{
|
||
|
|
generation: 1,
|
||
|
|
ops: [
|
||
|
|
{ kind: 'verb', id: 'v-differs', record: rec(1) },
|
||
|
|
{ kind: 'verb', id: 'v-tomb', record: null },
|
||
|
|
{ kind: 'verb', id: 'v-orphan', record: rec(3) }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
]),
|
||
|
|
canonicalNounDigest: async () => null,
|
||
|
|
factRecordDigest: recordDigest,
|
||
|
|
canonicalVerbDigest: async (id) =>
|
||
|
|
id === 'v-differs' ? recordDigest(rec(999)) : id === 'v-tomb' ? recordDigest(rec(2)) : null,
|
||
|
|
// canonical enumerates: v-differs (drifted), v-tomb (log says deleted),
|
||
|
|
// v-prelog (never logged); v-orphan is log-live but canonical-absent.
|
||
|
|
getVerbs: pagedList(['v-differs', 'v-tomb', 'v-prelog'])
|
||
|
|
})
|
||
|
|
expect(report.verdict).toBe('red')
|
||
|
|
const by = (id: string) => report.mismatches.find((m) => m.id === id)
|
||
|
|
expect(by('v-differs')).toMatchObject({ kind: 'verb', reason: 'state-differs' })
|
||
|
|
expect(by('v-tomb')).toMatchObject({ kind: 'verb', reason: 'log-tombstone-canonical-present' })
|
||
|
|
expect(by('v-prelog')).toMatchObject({ kind: 'verb', reason: 'pre-log-record' })
|
||
|
|
expect(by('v-orphan')).toMatchObject({ kind: 'verb', reason: 'log-live-canonical-absent' })
|
||
|
|
})
|
||
|
|
|
||
|
|
it('unwired: verbsChecked stays 0 — honest partial scope, never a silent claim', async () => {
|
||
|
|
const report = await runLogCompletenessOracle({
|
||
|
|
storage: { getNouns: pagedList(['n1']) } as never,
|
||
|
|
scanFacts: scanOf([
|
||
|
|
{ generation: 1, ops: [{ kind: 'noun', id: 'n1', record: rec(1) }] },
|
||
|
|
{ generation: 2, ops: [{ kind: 'verb', id: 'v1', record: rec(7) }] }
|
||
|
|
]),
|
||
|
|
canonicalNounDigest: async () => recordDigest(rec(1)),
|
||
|
|
factRecordDigest: recordDigest
|
||
|
|
})
|
||
|
|
expect(report.verbsChecked).toBe(0)
|
||
|
|
expect(report.nounsChecked).toBe(1)
|
||
|
|
})
|
||
|
|
})
|