fix(add): empty string is real data, not a missing field
validateAddParams() treated '' as falsy and rejected it with "Missing
required field 'data'" — so a legitimate empty file's first write always
failed. Only null/undefined data (with no vector either) is genuinely
absent; '' is real content. Fixed the check, plus the identical bug in
validateUpdateParams() (truncating a file to empty via overwrite hit the
same falsy check) and in update()/transact()'s update planner, where a
plain `Boolean(params.data)`/truthy check on the resolved vector would have
silently skipped both the deferred-embed marker and the eager re-embed for
an emptied value — a stale vector with no path to ever correct itself.
Verified end-to-end: vfs.writeFile('/empty.txt', '') now succeeds,
readFile() returns '', the file lists, and stat() reports size 0; the
existing "should reject empty string as data" tests (unit + integration)
asserted the old buggy behavior and are updated to assert the fixed
contract instead.
This commit is contained in:
parent
fc516da6eb
commit
258e9042af
6 changed files with 128 additions and 20 deletions
|
|
@ -3562,11 +3562,20 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
// new `data`); otherwise new `data` re-embeds; otherwise the existing
|
||||
// vector is kept. Any vector change re-indexes HNSW below.
|
||||
let vector = existing.vector
|
||||
// 'data' is a real new value whenever it's not null/undefined — an
|
||||
// empty string ('') is legitimate content (e.g. truncating a file to
|
||||
// empty via overwrite), matching validateUpdateParams's absent-vs-empty
|
||||
// distinction. Using `Boolean(params.data)` here would treat '' as "no
|
||||
// new data", silently skipping BOTH the deferred marker and the eager
|
||||
// re-embed below — a stale vector left behind with no path to ever
|
||||
// correct itself (a quiet loss, not the deferred-but-eventually-
|
||||
// correct flicker the deferEmbedding contract promises).
|
||||
const hasNewData = params.data !== undefined && params.data !== null
|
||||
// MT5 deferred re-embedding: the OLD vector keeps serving semantic
|
||||
// search — stale-but-present, never absent (the flicker law) — until
|
||||
// the background worker embeds the new data and swaps it atomically.
|
||||
const deferringEmbed =
|
||||
params.deferEmbedding === true && Boolean(params.data) && !params.vector
|
||||
params.deferEmbedding === true && hasNewData && !params.vector
|
||||
if (params.vector) {
|
||||
if (this.dimensions && params.vector.length !== this.dimensions) {
|
||||
throw new Error(
|
||||
|
|
@ -3574,13 +3583,13 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
)
|
||||
}
|
||||
vector = params.vector
|
||||
} else if (params.data && !deferringEmbed) {
|
||||
} else if (hasNewData && !deferringEmbed) {
|
||||
vector = await this.embed(params.data)
|
||||
}
|
||||
// A deferred data change does NOT reindex now (the vector is unchanged;
|
||||
// the worker's atomic swap carries the real reindex later).
|
||||
const needsReindexing = Boolean(
|
||||
(params.data && !deferringEmbed) || params.type || params.vector
|
||||
(hasNewData && !deferringEmbed) || params.type || params.vector
|
||||
)
|
||||
|
||||
// Always update the noun with new metadata
|
||||
|
|
@ -10501,6 +10510,11 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
// Resolve the updated vector — mirror of update(): an explicit `vector`
|
||||
// always wins, new `data` re-embeds, otherwise the existing vector is
|
||||
// kept. Any vector change re-indexes HNSW below.
|
||||
// 'data' is present whenever it's not null/undefined — '' is real
|
||||
// content (see the identical hasNewData in update()); a plain truthy
|
||||
// check would silently skip re-embedding an emptied value and leave a
|
||||
// stale vector with no path to ever correct itself.
|
||||
const hasNewData = params.data !== undefined && params.data !== null
|
||||
let vector = existing.vector
|
||||
if (params.vector) {
|
||||
if (this.dimensions && params.vector.length !== this.dimensions) {
|
||||
|
|
@ -10509,10 +10523,10 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
)
|
||||
}
|
||||
vector = params.vector
|
||||
} else if (params.data) {
|
||||
} else if (hasNewData) {
|
||||
vector = await this.embed(params.data)
|
||||
}
|
||||
const needsReindexing = Boolean(params.data || params.type || params.vector)
|
||||
const needsReindexing = Boolean(hasNewData || params.type || params.vector)
|
||||
|
||||
const newMetadata =
|
||||
params.merge !== false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue