feat(plugin): every provider write surface carries the real committed generation

The provider contract (metadata addToIndex/removeFromIndex, vector
addItem/removeItem, id-mapper getOrAssign/remove) gains an optional
trailing generation — evaluated lazily at operation execute time (the
graph surface's thunk pattern, generalized), threaded from all 17
construction sites: undefined during generation-0 bootstrap, the real
committed generation everywhere else. Optional = additive: no existing
provider or caller breaks; native delta logs that stamped literal zero
start hearing truth. JS twins accept the parameter with parity notes.
Pins: provider doubles capture and assert nonzero monotonic generations
across add/update/remove on both surfaces.
This commit is contained in:
David Snelling 2026-08-10 09:29:06 -07:00
parent 3484107462
commit 2d532684b4
7 changed files with 583 additions and 63 deletions

View file

@ -277,8 +277,35 @@ export interface MetadataIndexProvider {
*/
isMigrating?(): boolean
addToIndex(id: string, entityOrMetadata: any, skipFlush?: boolean, deferWrites?: boolean): Promise<void>
removeFromIndex(id: string, metadata?: any): Promise<void>
/**
* @description Index one entity's metadata.
* @param id - The entity's UUID.
* @param entityOrMetadata - Entity structure or plain metadata bag.
* @param skipFlush - Transactional atomicity: defer the flush to the commit seam.
* @param deferWrites - Batch mode: buffer postings for a later flush.
* @param generation - OPTIONAL (additive) Brainy's commit generation for
* this write: the SAME u64 counter {@link GraphIndexProvider.addVerb}
* carries, resolved at operation-execute time. A provider with per-record
* delta logs stamps it onto the durable record so its watermark
* ("this projection reflects generation N") is derivable from real data
* never a literal 0. `undefined` means the caller genuinely has no commit
* generation for this write (rebuild-from-canonical scans, bootstrap
* writes before generation stamping activates); a provider must treat
* that as "unstamped", not as generation 0. The built-in JS manager
* accepts and ignores it (single live view, no per-record log).
*/
addToIndex(id: string, entityOrMetadata: any, skipFlush?: boolean, deferWrites?: boolean, generation?: bigint): Promise<void>
/**
* @description Remove one entity from the index.
* @param id - The entity's UUID.
* @param metadata - The entity's metadata (targets exact postings; absent full scan).
* @param generation - OPTIONAL (additive) Brainy's commit generation for
* this removal, same contract as {@link MetadataIndexProvider.addToIndex}:
* a provider with per-record delta logs records the tombstone at this
* generation (so as-of reads before it still see the entity); the JS
* manager removes immediately and ignores it.
*/
removeFromIndex(id: string, metadata?: any, generation?: bigint): Promise<void>
getIds(field: string, value: any): Promise<string[]>
/**
@ -368,7 +395,14 @@ export interface MetadataIndexProvider {
* the ceiling on the JS path), so `Number(bigint)` narrowing is lossless.
*/
getIdMapper(): {
getOrAssign(uuid: string): number
/**
* Resolve-or-mint the entity's int. `generation` is OPTIONAL (additive):
* Brainy's commit generation current at mint time, so a mapper with
* per-record delta logs stamps the assignment record with a real
* watermark instead of a literal 0. Ignored when the uuid is already
* assigned (assignments are append-only) and by the JS mapper.
*/
getOrAssign(uuid: string, generation?: bigint): number
getInt(uuid: string): number | undefined
getUuid(intId: number): string | undefined
}
@ -1052,8 +1086,33 @@ export interface VectorIndexProvider {
*/
readonly name: string
addItem(item: VectorDocument): Promise<string>
removeItem(id: string): Promise<boolean>
/**
* @description Insert one vector.
* @param item - The vector document (`id` + `vector`).
* @param generation - OPTIONAL (additive) Brainy's commit generation for
* this write: the SAME u64 counter the graph provider's
* `addVerb(..., generation)` carries (and that `search`'s as-of
* `options.generation` reads back), resolved at operation-execute time.
* A provider with per-record delta logs / segment stamps records it so
* its watermark reflects real data never a literal 0. `undefined` =
* the caller has no commit generation (rebuild-from-canonical, the
* at-generation materializer's ephemeral reader); treat as "unstamped",
* not generation 0. The built-in JS index accepts and ignores it (it
* serves "now" only). The feature-detected `updateItem` capability (see
* `src/transaction/operations/IndexOperations.ts`) carries the same
* optional trailing generation.
*/
addItem(item: VectorDocument, generation?: bigint): Promise<string>
/**
* @description Remove one vector by id.
* @param id - The entity's UUID.
* @param generation - OPTIONAL (additive) Brainy's commit generation for
* this removal, same contract as {@link VectorIndexProvider.addItem}: a
* provider with durable delete records stamps the tombstone at this
* generation (as-of reads before it still see the vector); the JS index
* removes immediately and ignores it.
*/
removeItem(id: string, generation?: bigint): Promise<boolean>
search(
queryVector: Vector,
k?: number,
@ -1199,10 +1258,29 @@ export interface EntityIdMapperProvider {
* stays compatible `restore()` falls back to `init()` when this is absent.
*/
rebuild?(): Promise<void>
getOrAssign(uuid: string): number
/**
* @description Resolve-or-mint the entity's interned int (append-only:
* once assigned, a uuid's int never changes and is never recycled).
* @param uuid - The entity's UUID.
* @param generation - OPTIONAL (additive) Brainy's commit generation
* current at mint time (the same u64 counter the graph/metadata write
* surfaces carry). A mapper with per-record delta logs stamps the
* assignment record with this real watermark instead of a literal 0.
* Ignored when the uuid is already assigned, and by the JS mapper
* (which keeps no per-record log).
*/
getOrAssign(uuid: string, generation?: bigint): number
getUuid(intId: number): string | undefined
getInt(uuid: string): number | undefined
remove(uuid: string): boolean
/**
* @description Remove the uuid's mapping (the int stays reserved).
* @param uuid - The entity's UUID.
* @param generation - OPTIONAL (additive) Brainy's commit generation for
* this removal: a mapper with a per-key version chain tombstones the
* mapping at this generation (as-of reads before it still resolve);
* the JS mapper removes immediately and ignores it.
*/
remove(uuid: string, generation?: bigint): boolean
flush(): Promise<void>
clear(): Promise<void>
getAllIntIds(): number[]