brainy/src
David Snelling 9a3d1bd494 fix: ifRev CAS is atomic — the revision check now runs under the commit mutex
N concurrent update({ ifRev }) calls carrying the same expected revision all
fulfilled (zero RevisionConflictErrors, last-writer-wins) — the check ran
before the commit mutex, so interleaved callers all passed it before any apply
landed. Sequential calls conflicted correctly, which hid the race. A production
cutover rehearsal caught it: 8 parallel ifRev-guarded ledger writes all
"succeeded" and 7 were silently lost. Advisory locks built on exactly-one-winner
semantics could hand the same lock to two workers.

Fix: conditional commit. commitSingleOp and commitTransaction accept an
optional precommit(beforeImages) precondition, invoked under the commit mutex
against the just-read authoritative before-images and before anything is staged
or applied — the per-record analogue of ifAtGeneration, which always ran there.
A throw aborts the commit atomically (generation reservation returned, zero
staging I/O in the transaction path). The store stays domain-ignorant; update()
and transact() supply the ifRev predicate:

- update(): the planning-time check remains as a fast-fail (avoids embedding
  cost on an obviously stale expectation); the authoritative check re-verifies
  the before-image's _rev and re-stamps the update's _rev from it, so the
  counter is monotonic even for concurrent non-CAS updates (N plain updates
  advance _rev by N). CAS against a concurrently-removed entity now throws
  EntityNotFoundError instead of silently resurrecting it; plain updates keep
  their last-writer-wins re-create semantics.
- transact(): per-op ifRev expectations registered during planning
  (PlannedTransact.casUpdates) are re-verified in the batch's precommit; any
  conflict rejects the whole batch before staging. Multiple updates to one
  entity sequence through a running rev; ids the batch itself adds
  (PlannedTransact.createdNouns — add resets the rev baseline even on
  overwrite) keep their exact plan-time sequencing.

Regression suite (tests/integration/ifrev-concurrent-cas.test.ts): 8 parallel
same-rev updates → exactly 1 winner + 7 conflicts; same for transact batches;
_rev monotonicity; the documented read→CAS→retry ledger loop converging exactly
(8 workers, 8 decrements, 0 lost); sequential behavior unchanged; forward-ref
add+update in one batch unchanged.
2026-07-08 14:53:34 -07:00
..
aggregation docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
cli refactor(8.0): remove dead, unreachable, and unwired modules 2026-06-24 15:18:40 -07:00
db fix: ifRev CAS is atomic — the revision check now runs under the commit mutex 2026-07-08 14:53:34 -07:00
embeddings docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
errors fix: validate where-operators and align the in-memory matcher to the documented set 2026-07-07 12:23:36 -07:00
graph fix: cold-open no longer re-derives durable indexes — complete the readiness contract for all three providers 2026-07-07 10:39:00 -07:00
hnsw docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
import refactor(8.0): remove dead, unreachable, and unwired modules 2026-06-24 15:18:40 -07:00
importers refactor(8.0): remove dead/deprecated code (legacy sweep) 2026-06-29 10:09:39 -07:00
indexes/columnStore docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
integrations chore(8.0): ES2023 target + drop DOM lib + downlevelIteration (config truth-up) 2026-07-01 10:58:04 -07:00
mcp refactor(8.0): remove dead, unreachable, and unwired modules 2026-06-24 15:18:40 -07:00
migration feat(8.0)!: delete fork/branch/commit/history/versions — superseded by the Db API 2026-06-10 15:22:47 -07:00
neural docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
patterns chore(8.0): final pre-RC1 sweep — API consistency, named errors, orphans, zero-cast codebase 2026-06-11 14:51:00 -07:00
storage fix: an established store no longer boot-logs "New installation" 2026-07-07 15:49:49 -07:00
streaming chore(8.0): final pre-RC1 sweep — API consistency, named errors, orphans, zero-cast codebase 2026-06-11 14:51:00 -07:00
transaction chore(8.0): modernize toolchain + position Bun as a runtime 2026-07-01 09:16:46 -07:00
triple chore(8.0): Phase A + B — purge all @deprecated APIs + cacheManager dead branches 2026-06-09 15:33:56 -07:00
types feat: guarded plugin auto-detection — installing @soulcraft/cor is the opt-in 2026-07-02 16:19:55 -07:00
universal refactor(8.0): remove dead, unreachable, and unwired modules 2026-06-24 15:18:40 -07:00
utils fix: validate where-operators and align the in-memory matcher to the documented set 2026-07-07 12:23:36 -07:00
vfs fix: recover VFS content blobs stranded by a 7→8 upgrade, in place on open 2026-07-07 12:23:36 -07:00
brainy.ts fix: ifRev CAS is atomic — the revision check now runs under the commit mutex 2026-07-08 14:53:34 -07:00
coreTypes.ts feat(8.0): auto pre-upgrade backup — hard-link snapshot before the 7.x→8.0 migration 2026-07-01 15:04:01 -07:00
index.ts docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
internals.ts docs: rename the native provider to @soulcraft/cor across public docs and JSDoc 2026-07-02 15:11:41 -07:00
plugin.ts fix: cold-open no longer re-derives durable indexes — complete the readiness contract for all three providers 2026-07-07 10:39:00 -07:00
setup.ts chore(8.0): collapse dead defensive guards + redundant polyfills 2026-06-09 16:46:16 -07:00