feat(vector): the vectored-noun scalar joins the count ledger; the open gate closes the vector leg
The coverage denominator the health-by-accounting ratification named for
the vector family — never built until now, and its absence was measured as
the exact outage class it existed to prevent: a migrated store with
canonical vectors and no derived index opened with the vector leg EMPTY,
served [] from vector search with no error, and the report-driven read gate
had nothing to refuse on (the provider's coverage invariant was honestly
unledgered — the denominator was ours to supply).
- getCanonicalCounts() gains vectors: { all } — the count of canonical
nouns holding a REAL vector. Incremented where a vector lands (the
isNew-gated metadata seam for explicit vectors — the same discipline that
keeps HNSW neighbor-link re-saves from inflating counts; a narrow
noteVectorLanded hook for the deferred-embed landing, gated on the
worker's own pre-embed read). Decremented on a proven delete of a
vectored noun; a vector-uncertain delete marks the ledger suspect rather
than guessing (no new reads on the delete path). Recounted by the
sanctioned recount; legacy counts.json derives it once (a deferred noun's
vector file exists with an empty vector, so presence requires one
content read at derivation — never on the hot path).
- The open gate's vector leg: when a health-reporting provider claims
serving while the index holds zero nodes and the ledger proves vectored
canonical rows exist, open BUILDS (narrated) — routed through the
provider's idempotent fillFromCanonical() when exposed (the joint door;
a partial shortfall stays repair()'s operator business), the JS rebuild
otherwise — or fails typed pre-serve. Scoped exactly: bare isReady()
providers, migrating providers, and white-box size stubs open as before.
Pinned end-to-end from the partner gate's probe shape (store with vectored
canonical rows, no derived index, reopen → search serves N, never []),
red-proved against the pre-fix path; the inverse (zero vectored rows) opens
without building and serves [] honestly.
This commit is contained in:
parent
bce2593e24
commit
9730835bdf
10 changed files with 851 additions and 32 deletions
|
|
@ -52,7 +52,15 @@ export class SaveNounMetadataOperation implements Operation {
|
|||
private readonly storage: StorageAdapter,
|
||||
private readonly id: string,
|
||||
private readonly metadata: NounMetadata,
|
||||
private readonly isNew: boolean = false
|
||||
private readonly isNew: boolean = false,
|
||||
/**
|
||||
* OPTIONAL vectored-noun ledger hint: `true` when this write's paired
|
||||
* vector (the SAME insert's `vector` local) is real/non-empty — see
|
||||
* {@link StorageAdapter.saveNounMetadata}'s JSDoc for the isNew-gated,
|
||||
* double-count-proof seam this rides. Default `false`: a deferred-embed
|
||||
* insert (or any caller that doesn't know) never counts here.
|
||||
*/
|
||||
private readonly hasVector: boolean = false
|
||||
) {}
|
||||
|
||||
async execute(): Promise<RollbackAction> {
|
||||
|
|
@ -62,7 +70,7 @@ export class SaveNounMetadataOperation implements Operation {
|
|||
: await tornHealsToNull(this.storage.getNounMetadata(this.id), 'noun metadata')
|
||||
|
||||
// Save new metadata
|
||||
await this.storage.saveNounMetadata(this.id, this.metadata)
|
||||
await this.storage.saveNounMetadata(this.id, this.metadata, this.hasVector)
|
||||
|
||||
// Return rollback action
|
||||
return async () => {
|
||||
|
|
@ -70,8 +78,10 @@ export class SaveNounMetadataOperation implements Operation {
|
|||
// Restore previous metadata
|
||||
await this.storage.saveNounMetadata(this.id, previousMetadata)
|
||||
} else {
|
||||
// Delete newly created metadata
|
||||
await this.storage.deleteNounMetadata(this.id)
|
||||
// Delete newly created metadata. `this.hasVector` is the SAME fact
|
||||
// this operation's own execute() used to (maybe) count the vectored
|
||||
// ledger — reversing with it on rollback needs no new read.
|
||||
await this.storage.deleteNounMetadata(this.id, undefined, this.hasVector)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +150,9 @@ export class SaveNounOperation implements Operation {
|
|||
// Note: Not all adapters implement deleteNoun
|
||||
// This is acceptable - metadata deletion makes entity invisible
|
||||
if ('deleteNoun' in this.storage && typeof this.storage.deleteNoun === 'function') {
|
||||
await this.storage.deleteNoun(this.noun.id)
|
||||
// `this.noun.vector` is the SAME record just written — the
|
||||
// vectored-noun ledger fact is free (no added read) and exact.
|
||||
await this.storage.deleteNoun(this.noun.id, undefined, this.noun.vector.length > 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -198,14 +210,21 @@ export class DeleteNounMetadataOperation implements Operation {
|
|||
return async () => {}
|
||||
}
|
||||
|
||||
// Vectored-noun ledger fact: `previousNoun` is already read above for the
|
||||
// before-image capture — no added read. `undefined` (noun genuinely
|
||||
// absent, metadata-only ghost) is passed through honestly; the storage
|
||||
// layer marks the ledger suspect rather than guessing.
|
||||
const hadVector = previousNoun ? previousNoun.vector.length > 0 : undefined
|
||||
|
||||
// Full removal: both canonical legs + the entity container + count decrement
|
||||
// (the prior record keeps the decrement honest on a null canonical read).
|
||||
await this.storage.deleteNoun(this.id, previousMetadata)
|
||||
await this.storage.deleteNoun(this.id, previousMetadata, hadVector)
|
||||
|
||||
// Return rollback action
|
||||
return async () => {
|
||||
// Restore the vector leg, then the metadata leg through the count-aware
|
||||
// save so deleteNoun()'s decrement is reversed.
|
||||
// save so deleteNoun()'s decrement is reversed (hadVector's mirror:
|
||||
// re-increments the vectored ledger iff the restored vector is real).
|
||||
if (previousNoun) {
|
||||
await this.storage.saveNoun({
|
||||
id: previousNoun.id,
|
||||
|
|
@ -215,7 +234,7 @@ export class DeleteNounMetadataOperation implements Operation {
|
|||
})
|
||||
}
|
||||
if (previousMetadata) {
|
||||
await this.storage.saveNounMetadata(this.id, previousMetadata)
|
||||
await this.storage.saveNounMetadata(this.id, previousMetadata, hadVector === true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue