Commit graph

3 commits

Author SHA1 Message Date
1f7e365a4e chore(8.0): final pre-RC1 sweep — API consistency, named errors, orphans, zero-cast codebase 2026-06-11 14:51:00 -07:00
780fb6444b feat(8.0)!: flip requireSubtype default to true (BRAINY-8.0-SUBTYPE-CONTRACT § C-1)
Brainy 8.0 makes subtype required by default on every public write path
(`add`, `addMany`, `update`, `relate`, `relateMany`, `updateRelation`,
import). Per the locked C-1 contract, every entity and relation gets a
non-empty subtype string by the time the storage layer sees it.

OPT-OUT REMAINS FULLY SUPPORTED

The runtime flag is still consumer-controlled. Three opt-out paths
cover migration / legacy fixtures / typed escape:

- `new Brainy({ requireSubtype: false })` — last-resort: turn off the
  contract entirely. Recommended only for migration windows or test
  fixtures that legitimately can't supply a subtype.
- `new Brainy({ requireSubtype: { except: [NounType.Thing, ...] } })` —
  per-type allowlist: strict everywhere except the listed types.
- `brain.requireSubtype(type, options)` — per-type registration with
  optional vocabulary. Composes with the brain-wide flag.

Default is now `true`. Opt-out is explicit and documented; nothing
silently degrades.

TEST SWEEP

Bulk-applied `requireSubtype: false` to every `new Brainy({...})` call
site across 120 test files. Three sed patterns covered the shapes:
  - `new Brainy({` → `new Brainy({ requireSubtype: false,`
  - `new Brainy<T>({` → `new Brainy<T>({ requireSubtype: false,`
  - `new Brainy()` → `new Brainy({ requireSubtype: false })`

tests/helpers/test-factory.ts → createTestConfig() defaults
`requireSubtype: false` so test files using the helper inherit the
opt-out without per-site edits.

The test sites that DO exercise subtype semantics (the
subtype-and-facets suite, the strict-mode-self-test suite, the verb-
subtype-and-enforcement suite, etc.) already pass real subtypes — they
were the 7.30.x acceptance tests for this contract. Those tests
continue to pass unchanged.

CHANGES

src/brainy.ts
- normalizeConfig() — `requireSubtype` default `false` → `true`.
  Comment refreshed to document the three opt-out paths.

tests/* (120 files)
- Bulk-edited brain construction sites. No functional test changes; the
  opt-out preserves the test author's original intent.

tests/helpers/test-factory.ts
- createTestConfig() base config gains `requireSubtype: false`.

NO-OP for consumers who were already passing subtype on every write.

For consumers who weren't, the upgrade path is one of the three opt-out
forms above. Migration recipe documented in 8.0 release notes (next
commit).

VERIFICATION

- npx tsc --noEmit: clean
- npm test: 1408 / 1409 (same pre-existing race-condition outstanding;
  no other regressions from the flip)
2026-06-09 14:58:25 -07:00
d4cb26c604 feat: mmap-vector backend wiring — HNSWIndex consumes vectorStore:mmap (2.4.0 #2)
Cortex already registers the vectorStore:mmap provider (its Rust
NativeMmapVectorStore), but brainy has never consumed it — preloadVectors
and getVectorSafe still go straight to storage.getNounVector for every id,
even when an mmap layer is available. This wires the consumer end.

Architecture:

- NEW MmapVectorBackend (src/hnsw/mmapVectorBackend.ts) — bridges brainy's
  UUID-keyed vector reads to a int-slot mmap file via the
  vectorStore:mmap provider. Slots are addressed by the stable int id
  from the post-2.4.0 #1 EntityIdMapper (the foundation this depends on).
  Auto-grows the file (doubling) when a write lands beyond capacity, so
  HNSWIndex never has to think about sizing. The class never touches
  per-entity storage — it owns only the mmap layer.

- HNSWIndex changes — adds a vectorBackend field + a setVectorBackend
  setter. The vector read paths (preloadVectors, getVectorSafe) try the
  mmap layer first; on a storage fallback hit, they LAZILY write back into
  the mmap slot. An upgraded install converges to the zero-copy fast path
  under live traffic — no big-bang migration step. The legacy per-entity
  path is preserved and still used when no backend is set.

- brainy.ts wiring — a new private wireMmapVectorBackend() runs once
  during init, after plugin activation + metadataIndex setup. It activates
  the backend only when (a) the vectorStore:mmap provider is registered,
  (b) the storage adapter resolves a real local path via
  getBinaryBlobPath(), and (c) the metadata index exposes its idMapper.
  Cloud adapters return null on (b) and the backend is silently skipped;
  HNSWIndex's behaviour is then identical to pre-2.4.0.

- Provider interfaces in plugin.ts — VectorStoreMmapProvider and
  VectorStoreMmapInstance document the contract cortex's class fulfils
  (the class IS the provider — static factory methods). Brainy depends on
  the interfaces, not on cortex; the structural match is verified when
  cortex 2.4.0 picks up this brainy release.

Tests (1428 total, +11 vs pre-2.4.0):

- tests/unit/hnsw/mmap-vector-backend.test.ts — 6 unit tests with an
  in-memory mock provider. Covers round-trip, batch reads with interleaved
  misses, slot stability (no re-slotting on overwrite), file growth without
  data loss, idempotent open, and null returns for unwritten slots. The
  real perf integration with cortex's NativeMmapVectorStore is exercised
  when cortex 2.4.0 wires this in.

- tests/unit/utils/entity-id-mapper-stability.test.ts — moved here from
  tests/regression/ (which is NOT in the unit-config include glob, so the
  five #23 tests were not actually being run by npm test). The unit
  config matches tests/unit/**/*.test.ts.

The 2.4.0 #2 follow-up will be the chunked-segment layout for remote
storage adapters (S3 / R2 / GCS) where a single growing file doesn't fit
immutable objects. For 2.4.0 release: local-FS only.
2026-05-28 10:37:47 -07:00
Renamed from tests/regression/entity-id-mapper-stability.test.ts (Browse further)