docs(releases): 10.4.1 and 10.4.2 consumer notes; 10.4.2 is the last MIT release under this name, Open Brainy continues at @soulcraftlabs/brainy
This commit is contained in:
parent
6f93108648
commit
a082e0efdd
1 changed files with 89 additions and 0 deletions
89
RELEASES.md
89
RELEASES.md
|
|
@ -31,6 +31,95 @@ is sometimes cited as a 7.x removal — those methods never existed on 7.x; the
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## v10.4.2 — 2026-08-28 (a zero-norm vector is not a vector)
|
||||||
|
|
||||||
|
**This is the last release of the MIT engine under the `@soulcraft/brainy` name.**
|
||||||
|
The MIT package continues as **Open Brainy** — `@soulcraftlabs/brainy`: the open API,
|
||||||
|
client library, types and protocol, an openly specified canonical format, and the TypeScript
|
||||||
|
reference engine, scoped honestly as a single-node engine for stores up to roughly one
|
||||||
|
million rows. The `@soulcraft/brainy` name passes to the native engine, **Brainy**, at a
|
||||||
|
major version bump; that engine implements the same API over the same open format at
|
||||||
|
production scale, requires a license, and refuses loudly without one. Nothing changes
|
||||||
|
for existing installs until that major ships; the move is announced with it.
|
||||||
|
|
||||||
|
Six fixes, one law: a vector with no magnitude carries no information, so it must
|
||||||
|
never reach a vector index — in any engine — and the canonical store must say so.
|
||||||
|
|
||||||
|
- **The permanently-unvectored row.** `add({ ..., vector: [] })` (and the same item
|
||||||
|
shape in `addMany` / `transact`) is now the sanctioned "no vector" row: persisted
|
||||||
|
with an empty vector leg, never embedded, never indexed, counted as unvectored in
|
||||||
|
the canonical ledger. Metadata-only rows — telemetry tallies, counters, plumbing —
|
||||||
|
no longer need a placeholder vector and never enter the vector leg. `vector: []`
|
||||||
|
together with `deferEmbedding: true` is refused with a typed error (a supplied
|
||||||
|
vector has nothing to defer). Previously `vector: []` threw a dimension error.
|
||||||
|
- **The unvector door.** `update({ id, vector: [] })` (and its `transact()` twin) is
|
||||||
|
the sanctioned way to strip a vector from an existing row: canonical vector → `[]`,
|
||||||
|
removal from the vector index, the vectored ledger decremented exactly once — and
|
||||||
|
idempotent, so a resumed cleanup pass may simply re-issue. It never re-embeds, and
|
||||||
|
it clears a pending deferred-embed marker durably so the background worker cannot
|
||||||
|
re-vector the row later. Note that a rebuild never sheds vectors (it re-derives the
|
||||||
|
index from canonical rows); shedding historical vectors needs this door.
|
||||||
|
- **Zero-norm vectors are normalized at the write.** An explicit all-zero vector on
|
||||||
|
any write path is persisted as unvectored (`[]`) with one warning naming the row;
|
||||||
|
the vector-index operations keep their own refusal as a second line. The engine's
|
||||||
|
own VFS root, which used to persist a deliberate all-zero placeholder (harmless
|
||||||
|
under cosine distance, a false attractor under a downstream engine's
|
||||||
|
squared-euclidean serving — a production incident this week), is now created
|
||||||
|
unvectored, and an existing store's legacy root is migrated on open by a single
|
||||||
|
fixed-path read before the health gate runs — never a walk.
|
||||||
|
- **Enumeration keys on the identity record.** `getNouns()` / `getVerbs()` and the
|
||||||
|
cursor walks behind them enumerate by the metadata record, the same key the
|
||||||
|
canonical ledger counts by — previously the walk keyed on the vector file, so a
|
||||||
|
row holding metadata but no vector was counted yet never yielded (a permanent
|
||||||
|
"missing" phantom in coverage math), while an orphaned vector-only directory
|
||||||
|
could be yielded as a phantom id. The recovery fold also never deletes an existing
|
||||||
|
vector when it replays a metadata-only after-image (preserve-if-absent). One
|
||||||
|
documented gap remains: a verb's endpoints live only in its vector leg, so a
|
||||||
|
metadata-only verb is counted and loudly skipped, never fabricated — the fix is a
|
||||||
|
canonical-format change and lands with the open format.
|
||||||
|
- **The ledger's one-time derivation counts identity records.** Stores upgraded from
|
||||||
|
pre-ledger versions derived their ALL-visibility scalars once by counting id
|
||||||
|
directories, which included ghost and scar containers left by an old partial-delete
|
||||||
|
defect — an inflated denominator whose coverage row could never reach exact. The
|
||||||
|
derivation now counts only directories holding a metadata record, `counts.json`
|
||||||
|
carries a derivation-rule stamp, and a ledger derived under the old rule is marked
|
||||||
|
`suspect` at open (one O(1) field read, one warning) so the online `repairIndex()`
|
||||||
|
path clears it with a real recount.
|
||||||
|
- **The vector index refuses what it cannot hold.** `rebuild()` skips unvectored and
|
||||||
|
zero-norm rows (one summary line), re-pins the vector dimension from the first real
|
||||||
|
vector after a restart (previously a restart left the pin unset, so a wrong-length
|
||||||
|
insert became the new pin instead of being rejected), and `addItem` / `updateItem`
|
||||||
|
throw a typed `EmptyVectorIndexError` on a length-0 vector instead of ever storing
|
||||||
|
a vector-less node.
|
||||||
|
- **Smaller:** a failing plugin activation now rethrows with the original error as
|
||||||
|
`cause` (the originating file and line survive to the caller's log); build
|
||||||
|
generators stamp from the repository history of their inputs instead of wall clock,
|
||||||
|
so two builds of the same tree are byte-identical.
|
||||||
|
|
||||||
|
Adoption: one restart, paired with its native-engine release. The first open of an
|
||||||
|
existing store runs the legacy-root migration (one narrated line) and, on stores that
|
||||||
|
upgraded from pre-ledger versions, marks the ledger suspect until the next sanctioned
|
||||||
|
recount — no rebuild in either case.
|
||||||
|
|
||||||
|
## v10.4.1 — 2026-08-26 (reads refuse per family; an unchanged write never re-embeds)
|
||||||
|
|
||||||
|
Two production defects from the same week, fixed together as a patch to 10.4.0.
|
||||||
|
|
||||||
|
- **The read gate is per family.** A read now refuses only when the index family it
|
||||||
|
actually consults is unhealthy: a metadata filter is served while the vector leg is
|
||||||
|
rebuilding; a semantic query is refused only by the vector family; a graph
|
||||||
|
traversal only by the graph family. Previously any unhealthy family refused every
|
||||||
|
read on the brain — under a long vector rebuild, a production deployment's
|
||||||
|
metadata-only reads were refused for the duration, and the retries became a write
|
||||||
|
pump of their own.
|
||||||
|
- **Unchanged data never re-embeds.** `update()` compares the incoming `data`
|
||||||
|
structurally with the stored record; an update carrying identical data (a common
|
||||||
|
shape for periodic upserts) no longer embeds again and no longer churns the vector
|
||||||
|
leg. Previously every such update re-embedded and re-inserted, which under load
|
||||||
|
saturated the vector index with near-identical vectors.
|
||||||
|
|
||||||
|
Adoption: one restart, paired with its native-engine release.
|
||||||
|
|
||||||
## v10.4.0 — 2026-08-25 (the health report has a name)
|
## v10.4.0 — 2026-08-25 (the health report has a name)
|
||||||
|
|
||||||
Three related cures, one root cause: an index deciding whether it could be trusted
|
Three related cures, one root cause: an index deciding whether it could be trusted
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue