refactor(8.0): strictConfig + brain.stats() vector field + wireConnectionsCodec feature-detect (scaffold step 5)

Step 5 of the brainy 8.0 rename scaffolding. Three small additions, all
honoring contracts locked in handoff thread BRAINY-8.0-RENAME-COORDINATION:

A. strictConfig: boolean | 'warn'  (§ B.6)

src/types/brainy.types.ts
- New BrainyConfig.strictConfig field. Three tiers:
  - false: no enforcement (knob mismatches silently accepted)
  - 'warn' (8.0 default): one-shot warning per call site listing
    ignored knobs + escape-valve recipe + docs link
  - true: hard error at init()
- Format matches the 7.30.1 teaching-error pattern (problem → escape
  valves → caller location → docs link).
- Applies to ALL provider knob mismatches, not just vector (e.g.
  config.aggregation.x with no aggregation provider).

src/brainy.ts
- normalizeConfig() defaults to 'warn'. Wiring into the actual
  enforcement sites lands in the final cleanup commit alongside the
  removal of legacy knob names.

B. brain.stats().indexHealth.vector  (§ planning § 2.6)

src/types/brainy.types.ts
- BrainyStats.indexHealth gains a new `vector: boolean` field. Existing
  `hnsw: boolean` field marked @deprecated; same boolean, kept as a
  compat alias until the final cleanup.

src/brainy.ts:6272
- Implementation populates both `hnsw` and `vector` from the same
  `vectorHealthy` boolean. Refactored to async IIFE so the graph-size
  await sits cleanly alongside the new field.

C. wireConnectionsCodec feature-detect  (integration doc lines 459-461)

src/brainy.ts:9469-9486
- Added `typeof this.index.setConnectionsCodec === 'function'` guard
  before invoking. Native vector-index providers (DiskANN-style) persist
  the graph as a single mmap'd file with no per-node connection lists
  and have no analogue. Pre-8.0 the wire was unconditional and relied
  on the native wrapper exposing a no-op method; 8.0 makes it
  feature-detected.

NO-OP scope

No behavioural change in scaffolding. brain.stats() now returns both
field names (callers see the new field but the old still works).
strictConfig is defaulted but not yet enforced. wireConnectionsCodec
behaves identically when called against an impl that has the codec
method (the JS HNSW path); guards cleanly against impls that don't.

VERIFICATION

- npx tsc --noEmit: clean
- npm test: 1468 / 1468 unit
This commit is contained in:
David Snelling 2026-06-09 13:12:39 -07:00
parent 356f044d2a
commit 3e1ef957bc
2 changed files with 49 additions and 5 deletions

View file

@ -6269,11 +6269,18 @@ export class Brainy<T = any> implements BrainyInterface<T> {
relationCount,
relationsByType,
fieldRegistry,
indexHealth: {
hnsw: this.index.size() > 0 || entityCount === 0,
metadata: metadataStats.totalEntries > 0 || entityCount === 0,
graph: (await this.graphIndex.size()) > 0 || relationCount === 0
},
indexHealth: await (async () => {
// 8.0 rename: `vector` is the canonical name; `hnsw` is a compat
// alias that's removed in the final 8.0 cleanup commit.
const vectorHealthy = this.index.size() > 0 || entityCount === 0
const graphSize = await this.graphIndex.size()
return {
hnsw: vectorHealthy,
vector: vectorHealthy,
metadata: metadataStats.totalEntries > 0 || entityCount === 0,
graph: graphSize > 0 || relationCount === 0
}
})(),
storage: {
backend: storageBackend,
rootDir: typeof rootDir === 'string' ? rootDir : undefined
@ -9304,6 +9311,9 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// { except: [...] }: strict, but listed types may omit subtype.
// Becomes the default in 8.0.0.
requireSubtype: config?.requireSubtype ?? false,
// Provider-knob mismatch strictness (8.0). Default is 'warn' (one-shot
// teaching message). See BRAINY-8.0-RENAME-COORDINATION § B.6.
strictConfig: config?.strictConfig ?? 'warn',
// Multi-process safety
mode: config?.mode ?? 'writer',
force: config?.force ?? false
@ -9464,6 +9474,13 @@ export class Brainy<T = any> implements BrainyInterface<T> {
const idMapper = this.metadataIndex.getIdMapper?.()
if (!idMapper) return
// Feature-detect: only the JS HNSW path uses per-node connection lists.
// Native vector-index providers (DiskANN-style) persist the graph as a
// single mmap'd file and have no analogue, so skip the wiring there.
// Pre-8.0 the wire was unconditional and relied on the native wrapper
// exposing a no-op setConnectionsCodec(); 8.0 makes it feature-detected.
if (typeof (this.index as { setConnectionsCodec?: unknown }).setConnectionsCodec !== 'function') return
const codec = new ConnectionsCodec(provider, idMapper)
this.index.setConnectionsCodec(codec)
if (!this.config.silent) {