feat(log): log authority is the fleet default — adopt-at-open, oracle-gated; plus the power-cut throw-site cures and the loud torn-record contract
THE DEFAULT FLIP (ruled on proven evidence — at-ack survived 301/301 acked-writes-through-power-cut in block-layer fault injection; deferred tree authority demonstrably loses flush-covered acks): a brain with NO stored authority artifact now ADOPTS LOG AUTHORITY AT OPEN. The oracle gates the flip exactly as the guarded adoption path always did — curable divergences baseline-backfilled, the flip lands ONLY on a green verdict — and a brain that cannot verify STAYS tree-authoritative loudly, with the refusal recorded on the switch artifact so subsequent opens are cheap. config logAuthority: 'defer' is the explicit documented opt-out (no automatic adoption; declared flush-window loss; adoptLogAuthority() flips later). A stored artifact always wins. RELEASES.md carries the posture. Two standing .fails debt pins FLIP TO HOLDING under the default: the at-ack crash-survival gap and the ack-at-log durability target — both now permanent asserted truths, not aspirations. POWER-CUT THROW SITES (fault-injection findings, brainy-alone config): - A manifest-listed-but-unloadable column segment QUARANTINES at discovery (loud once, counted always, quarantinedSegments() exposed for the heal) and the field serves its remaining segments DEGRADED — never a raw throw killing every query on the field. Real storage faults still propagate untouched. - Torn generation artifacts (NaN/garbage in manifest or counter) DISCARD with narration at the store's open and recovery re-derives — plus a defensive finite-integer guard at the init consumer. Never a RangeError killing an open. THE LOUD TORN-RECORD CONTRACT: an existing-but-unparseable stored record now surfaces as a typed, counted TornRecordError on every entity-read surface (including fifteen previously-blind per-item batch catches); ENOENT stays clean-absent; artifact readers with designed absent-recovery keep null-tolerance behind the loud floor. Disk corruption can no longer read as silent data invisibility. Suite migration: the default's pins inverted deliberately, generation baselines made relative, quarantine-contract pins rewritten to the ruled behavior. Gates: tsc 0 · unit 2065/2065 (159 files) · integration 826 (93 files) · conformance 31/31 · kill-matrix 15/15 · torn-open guards 2/2.
This commit is contained in:
parent
67c606be69
commit
214c98b4d5
23 changed files with 833 additions and 154 deletions
|
|
@ -202,6 +202,7 @@ import {
|
|||
flipToLogAuthority,
|
||||
recordDigest,
|
||||
nounEntityTruth,
|
||||
LOG_AUTHORITY_PATH,
|
||||
type LogAuthorityRecord,
|
||||
type LogAuthorityStorage,
|
||||
type OracleReport
|
||||
|
|
@ -1371,7 +1372,20 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
// gap for observability.
|
||||
for (const provider of this.versionedIndexProviders()) {
|
||||
const providerGen = provider.generation()
|
||||
const committed = BigInt(this.generationStore.committedGeneration())
|
||||
// Defensive finite-integer guard: committedGeneration() is validated
|
||||
// at the store's open (torn artifacts discard, narrated) — but a
|
||||
// RangeError here would kill the whole open, so the consumer guards
|
||||
// too. A non-finite value narrates and skips the gap check (the
|
||||
// provider's own replay contract still governs).
|
||||
const committedRaw = this.generationStore.committedGeneration()
|
||||
if (!Number.isSafeInteger(committedRaw) || committedRaw < 0) {
|
||||
prodLog.warn(
|
||||
`[Brainy] committed generation is non-integer (${String(committedRaw)}) at ` +
|
||||
`init — torn-artifact survivor; skipping the provider replay-gap check`
|
||||
)
|
||||
continue
|
||||
}
|
||||
const committed = BigInt(committedRaw)
|
||||
if (providerGen < committed) {
|
||||
prodLog.info(
|
||||
`[Brainy] Versioned index provider is at generation ${providerGen} ` +
|
||||
|
|
@ -1492,16 +1506,58 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
this._generationStampingActive = true
|
||||
}
|
||||
|
||||
// LOG-AUTHORITY SWITCH (checked at open only): a brain that has
|
||||
// flipped to log-authoritative storage gets durable-at-ack fact
|
||||
// writes (group-committed fsync covering every ack). Default 'tree'
|
||||
// = today's behavior, zero added latency.
|
||||
// LOG-AUTHORITY SWITCH (checked at open only). A STORED artifact
|
||||
// always wins: an already-flipped brain runs durable-at-ack; an
|
||||
// explicitly-recorded tree posture is honored. With NO artifact, the
|
||||
// 10.0.0 FLEET DEFAULT is ADOPT-AT-OPEN (config logAuthority:
|
||||
// 'adopt'): the verification oracle gates the flip — curable
|
||||
// divergences are baseline-backfilled, the brain flips ONLY on green,
|
||||
// and a brain that cannot go green STAYS tree-authoritative LOUDLY
|
||||
// with the refusal recorded (cheap subsequent opens; an operator
|
||||
// re-runs adoptLogAuthority() after fixing the divergence).
|
||||
// 'defer' is the documented opt-out: no automatic adoption.
|
||||
if (!this.isReadOnly) {
|
||||
const storedArtifact = await this.storage
|
||||
.readRawObject(LOG_AUTHORITY_PATH)
|
||||
.catch(() => null)
|
||||
const authority = await readLogAuthority(this.storage)
|
||||
this._logAuthority = authority
|
||||
if (authority.authority === 'log') {
|
||||
this.generationStore.setLogDurability('at-ack')
|
||||
prodLog.info('[Brainy] storage authority: generation log (durable-at-ack enabled)')
|
||||
} else if (
|
||||
storedArtifact === null &&
|
||||
this.config.logAuthority === 'adopt' &&
|
||||
this.generationStore.getFactLog() !== null
|
||||
) {
|
||||
try {
|
||||
await this.adoptLogAuthority()
|
||||
prodLog.info(
|
||||
'[Brainy] storage authority adopted at open: generation log ' +
|
||||
'(fleet default; oracle green; durable-at-ack enabled)'
|
||||
)
|
||||
} catch (err) {
|
||||
// The guarded ruling: a brain that cannot verify STAYS tree,
|
||||
// loudly, with the refusal recorded so subsequent opens are
|
||||
// cheap. Never a silent half-state; never a failed open.
|
||||
const reason = (err as Error).message
|
||||
prodLog.warn(
|
||||
`[Brainy] log-authority adoption REFUSED at open — this brain stays ` +
|
||||
`tree-authoritative until an operator resolves the divergence and ` +
|
||||
`re-runs adoptLogAuthority(). Reason: ${reason}`
|
||||
)
|
||||
try {
|
||||
const refusal: LogAuthorityRecord = {
|
||||
authority: 'tree',
|
||||
adoptRefusal: { at: Date.now(), reason: reason.slice(0, 500) }
|
||||
}
|
||||
await this.storage.writeRawObject(LOG_AUTHORITY_PATH, refusal)
|
||||
this._logAuthority = refusal
|
||||
} catch {
|
||||
// Unrecordable refusal = the next open retries the oracle —
|
||||
// the conservative outcome.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15786,7 +15842,8 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
force: config?.force ?? false,
|
||||
// Engine-owned persistence cadence — defaults resolve at the trigger
|
||||
// site (policy 'auto': 512 writes / 30s interval / 2s idle).
|
||||
persistence: config?.persistence
|
||||
persistence: config?.persistence,
|
||||
logAuthority: config?.logAuthority ?? 'adopt'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Reference in a new issue