feat(log): system commits carry their origin; the attested per-id reconcile door

Two consumer-driven cures sharing one stamp. (1) TX-LOG ORIGIN: engine-
originated commits stamp an optional origin on their tx-log entry AND the
commit fact's meta — 'system:embed-landing' (the deferred vector landing),
'system:adoption-backfill' (baseline re-commits), 'system:reconcile'. A
downstream activity feed showed a double tick because the landing commit was
indistinguishable from a user save, and the consumer rightly refused a
time-window collapse as a quiet loss; feeds now filter on fact. User writes
stay unstamped — absent origin is the user shape, every existing consumer
unchanged. (2) reconcileLogDivergence(id, {attest}): the human's door for
log-live-canonical-absent, the one class adoption refuses by design because
a lost-tombstone deletion is indistinguishable from canonical loss.
'deleted' mints the missing tombstone (history keeps the earlier live
record); 'restore' folds the log's only copy back into canonical; wrong-
class calls refuse typed with nothing written. Loud, narrated, single-row,
origin-stamped. From a production adoption's one surviving divergence.
This commit is contained in:
David Snelling 2026-08-17 16:21:25 -07:00
parent f4653e47c9
commit 9ac9e70686
4 changed files with 308 additions and 7 deletions

View file

@ -2245,7 +2245,8 @@ export class Brainy<T = any> implements BrainyInterface<T> {
},
undefined,
undefined,
[{ type: 'embed.landed', id, vector: newVector }]
[{ type: 'embed.landed', id, vector: newVector }],
'system:embed-landing'
)
this.clearPendingEmbed(id)
} catch (err) {
@ -2479,7 +2480,8 @@ export class Brainy<T = any> implements BrainyInterface<T> {
run: TransactionFunction<void>,
precommit?: (before: CommitBeforeImages) => void,
pendingEvents?: PendingChangeEvent[],
records?: FactMarkerRecord[]
records?: FactMarkerRecord[],
origin?: string
): Promise<{ generation?: number; timestamp: number; degraded?: string[] }> {
// Change-feed capture: when this write will emit, hold a reference to the
// commit's before-images so `remove` events can carry the record's last
@ -2542,6 +2544,7 @@ export class Brainy<T = any> implements BrainyInterface<T> {
touched,
precommit: captureAndCheck,
...(records && records.length > 0 ? { records } : {}),
...(origin ? { origin } : {}),
execute: () =>
this.transactionManager.executeTransaction(run, {
timeout: transactTimeoutBudget(
@ -8358,7 +8361,7 @@ export class Brainy<T = any> implements BrainyInterface<T> {
}
}
})
})
}, undefined, undefined, undefined, 'system:adoption-backfill')
}
const next = await this.runOracle({ listAll: true })
// THE ONLY STOP: no progress. With uncapped listings both counts are
@ -8392,6 +8395,137 @@ export class Brainy<T = any> implements BrainyInterface<T> {
return report
}
/**
* @description THE ATTESTED PER-ID RECONCILE DOOR for the one divergence
* class the adoption backfill refuses BY DESIGN: `log-live-canonical-absent`
* the log holds a live record for a row the canonical tree says does not
* exist. The engine cannot tell a legitimate pre-log deletion (the log
* missed the tombstone the deferred-durability-era ack-window class) from
* canonical LOSS (the log holds the only surviving copy); auto-curing would
* silently destroy data in one of the two readings. A HUMAN attests which:
*
* - `attest: 'deleted'` the row was legitimately deleted; mint the
* tombstone fact the log always lacked (canonical stays absent). The
* log's history keeps the old live record as-of reads before the
* tombstone still see it.
* - `attest: 'restore'` canonical lost the row; fold the log's latest
* after-image back into canonical (both sides now agree it lives).
*
* Loud, narrated, single-row, and stamped `origin: 'system:reconcile'` on
* both the tx-log entry and the commit fact. Refuses (typed) when the id's
* log and canonical already agree, when `restore` is attested but the log
* holds no record, and when canonical is PRESENT-but-different (that is
* `state-differs` `adoptLogAuthority()`'s backfill owns it).
*
* @param id - The single entity id to reconcile.
* @param options.attest - The human's word on which reading is true.
* @returns What was done and the generation that recorded it.
* @throws When the divergence is not the attested class (nothing is written).
*/
async reconcileLogDivergence(
id: string,
options: { attest: 'deleted' | 'restore' }
): Promise<{ reconciled: 'tombstoned' | 'restored'; id: string; generation: number }> {
await this.ensureInitialized()
this.assertWritable('reconcileLogDivergence')
// Fold the log for THIS id (one scan; a rare operator door).
const scan = this.scanFacts()
if (!scan) {
throw new Error('reconcileLogDivergence: this store has no fact log — nothing to reconcile against')
}
let logLatest: { tombstoned: boolean; record: { metadata: unknown; vector: unknown } | null } | null = null
for await (const batch of scan.batches()) {
for (const fact of batch.facts) {
for (const op of fact.ops) {
if (op.kind === 'noun' && op.id === id) {
logLatest =
op.record === null
? { tombstoned: true, record: null }
: { tombstoned: false, record: { metadata: op.record.metadata, vector: op.record.vector } }
}
}
}
}
const canonical = await this.storage.readNounRaw(id)
const canonicalAbsent = canonical.metadata === null && canonical.vector === null
// Only the log-live + canonical-absent shape passes; everything else
// names its actual state and the door that owns it.
if (!logLatest || logLatest.tombstoned) {
throw new Error(
`reconcileLogDivergence(${id}): the log's latest state is ` +
`${logLatest ? 'a tombstone' : 'no record at all'} — there is no ` +
`log-live-canonical-absent divergence here. If the oracle reports this id, ` +
`re-run verifyLogAuthority() for the current class.`
)
}
if (!canonicalAbsent) {
throw new Error(
`reconcileLogDivergence(${id}): canonical is PRESENT — this is not the ` +
`log-live-canonical-absent class. If canonical differs from the log ` +
`(state-differs), adoptLogAuthority()'s backfill cures it; nothing was written.`
)
}
if (options.attest === 'deleted') {
// Mint the tombstone fact the log always lacked. writeNounRaw with null
// parts is an idempotent delete; the commit fact reads canonical back
// after execute (absent) and records the tombstone.
const receipt = await this.persistSingleOp(
{ nouns: [id] },
async (tx) => {
tx.addOperation({
name: 'ReconcileTombstone',
execute: async () => {
await this.storage.writeNounRaw(id, { metadata: null, vector: null })
return async () => {
// Undo of an idempotent delete of an absent row: nothing.
}
}
})
},
undefined,
undefined,
undefined,
'system:reconcile'
)
prodLog.warn(
`[Brainy] reconcileLogDivergence: ${id} attested DELETED — tombstone fact minted ` +
`at generation ${receipt.generation}; the log now agrees the row is gone ` +
`(its history keeps the earlier live record).`
)
return { reconciled: 'tombstoned', id, generation: receipt.generation! }
}
// attest: 'restore' — the log's copy is the survivor; fold it back.
const record = logLatest.record!
const receipt = await this.persistSingleOp(
{ nouns: [id] },
async (tx) => {
tx.addOperation({
name: 'ReconcileRestore',
execute: async () => {
await this.storage.writeNounRaw(id, record)
return async () => {
await this.storage.writeNounRaw(id, { metadata: null, vector: null })
}
}
})
},
undefined,
undefined,
undefined,
'system:reconcile'
)
prodLog.warn(
`[Brainy] reconcileLogDivergence: ${id} attested RESTORE — the log's latest ` +
`after-image was folded back into canonical at generation ${receipt.generation}. ` +
`Derived indexes reconcile at next open/repairIndex; the row serves from canonical now.`
)
return { reconciled: 'restored', id, generation: receipt.generation! }
}
/**
* @description Read the reified transaction log one entry per committed
* generation, carrying the committed generation, the commit timestamp, and