Repository navigation
dream(intelligence): #4249 agentdb-backend.ts calls a native HNSW controller that has never existed in the real agentdb package (evaluated, ACCEPT) - #4250
Conversation
…troller that has never existed in the real agentdb package
storeInAgentDB(), searchWithAgentDB(), and getStats() in
v3/@claude-flow/memory/src/agentdb-backend.ts each called
this.agentdb.getController('hnsw') inside a try/catch, expecting an
optional native HNSW controller from the agentdb npm package. The real
agentdb@3.0.0-alpha.20's AgentDB.getController() switch recognizes only
'memory'/'reflexion', 'skills', and 'causal'/'causalGraph' — anything
else, including 'hnsw', hits `default: throw new Error('Unknown
controller: ' + name)`. All three call sites always threw and silently
caught; searchWithAgentDB additionally logged a false "HNSW search
failed" error to stderr on every single search call, even though the
code unconditionally fell back to the already-correct bruteForceSearch()
either way.
Removed the three dead call sites. Zero change to returned search
results — bruteForceSearch() was the only path that ever actually ran.
Added two regression tests: one pinning that console.error is never
called with "HNSW search failed" during a normal store+search+getStats
cycle (stash-isolated: fails on baseline, passes on candidate), and one
tripwire asserting the real agentdb package's getController('hnsw') still
throws (so a future agentdb release that adds real HNSW support fails
this test loudly instead of the dead code silently staying dead forever).
Evaluated via Vitest against a real (non-mocked) agentdb instance:
agentdb-backend.test.ts 21/21 -> 23/23 (+2 new), 0 regressions. Full
@claude-flow/memory suite 614/616 both baseline and candidate (2
pre-existing, unrelated environmental failures). tsc --noEmit clean both
ways. Independent adversarial critique: CONFIRMED-WITH-CAVEATS (no
blocking issues).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01KmvMMVh9xNQdRGXfk9vwjt
…4250) Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01KmvMMVh9xNQdRGXfk9vwjt
…lish audit @claude-flow/memory is a non-bundled leaf dependency of @claude-flow/cli that resolves from the npm registry rather than shipping with the release tarball. The "Plugin package install-safety" CI check's audit-leaf-package-publish.mjs correctly flagged that this PR changed v3/@claude-flow/memory/src/agentdb-backend.ts (publishable source) without bumping the package's version, which would mean the fix never reaches users on the next release. Bumped @claude-flow/memory to 3.0.6 (patch: bug fix only, no API change) and updated @claude-flow/cli's exact pin for it to match in both dependencies and optionalDependencies, per CLAUDE.md's Publishing Rules and the exact-pin discipline from #3414. All four related audit scripts (leaf-package-publish, plugin-packages, package-dep-overlap, wrapper-dep-ranges, umbrella-version-lockstep) now pass locally. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01KmvMMVh9xNQdRGXfk9vwjt
|
CI: "Plugin package install-safety" was failing — fixed, pushed. The Fix pushed in 73c0bc6: bumped Generated by Claude Code |
The previous commit bumped @claude-flow/memory's version and @claude-flow/cli's pin for it in package.json, but didn't update v3/pnpm-lock.yaml, so every CI job running `pnpm install --frozen-lockfile` failed with ERR_PNPM_OUTDATED_LOCKFILE (ci.yml's "Smoke (no better-sqlite3)" x2, "Plugin hooks smoke", "statusline generator delegation smoke", "Memory import smoke"). Hand-edited the single `specifier: 3.0.5 -> 3.0.6` line for @claude-flow/memory under @claude-flow/cli's optionalDependencies in v3/pnpm-lock.yaml, rather than regenerating the whole lockfile via `pnpm install` — a full regeneration in this environment pulled in ~175 lines of unrelated incidental drift (ruvector 0.2.27->0.2.41, @opentelemetry/api 1.9.1->1.9.0, stale `dev: false` annotation cleanup) that has nothing to do with this PR, since @claude-flow/memory is a workspace `link:` dependency with no real registry resolution to re-fetch. Verified `pnpm install --frozen-lockfile` now succeeds from a clean v3/ checkout with only this one line changed. Co-Authored-By: RuFlo <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01KmvMMVh9xNQdRGXfk9vwjt
|
CI: 5 more checks were failing — 4 fixed (this PR's), 1 is not this PR's. Fixed, pushed in 88b90fd: "Smoke (no better-sqlite3)" (Node 22 & 24), "Plugin hooks smoke", "statusline generator delegation smoke", and "Memory import smoke" were all failing with Fixed by hand-editing the single Not this PR's — Generated by Claude Code |
1. Hypothesis
Given
AgentDBBackend'sstoreInAgentDB(),searchWithAgentDB(), andgetStats()(v3/@claude-flow/memory/src/agentdb-backend.ts) each callthis.agentdb.getController('hnsw')inside a try/catch, expecting an optional native HNSW controller from theagentdbnpm package, when the realagentdbpackage'sAgentDB.getController()(confirmed directly fromagentdb@3.0.0-alpha.20's installeddist/src/core/AgentDB.js) recognizes only'memory'/'reflexion','skills','causal'/'causalGraph'and throwsError('Unknown controller: hnsw')for anything else — then every store/search/getStats call pays for a thrown-and-caught exception for a controller that can never exist, andsearchWithAgentDBadditionally logs a misleadingconsole.error('HNSW search failed:', ...)on every search even though the code unconditionally falls back to the already-correctbruteForceSearch()either way. Removing the three dead call sites should (a) eliminate the false error log with zero change to search results, and (b) remove a real, isolated, measurable per-call exception-handling cost. Frozen before evaluation; not modified after.2. Candidate
v3/@claude-flow/memory/src/agentdb-backend.ts— removed the three deadthis.agentdb.getController('hnsw')call sites instoreInAgentDB(),searchWithAgentDB(), andgetStats().searchWithAgentDB()now callsbruteForceSearch()directly (the only path that ever actually ran);getStats()'shnswStatsis now explicitlyundefined(what it always evaluated to anyway);storeInAgentDB()drops the dead add-to-index block entirely. Diff: 2 files, 80 insertions / 62 deletions (142 changed lines, well under 300), no behavior change to search results.This is the third instance of a recurring pattern already documented twice in this repo's own ledger — aspirational "native acceleration" integration code checking for an export/controller that was never real in the installed dependency (2026-08-30
diskann-backend.tsremoval, 2026-10-10graph-analyzer.tsnative-acceleration correction).3. Evaluation Receipt
evaluated: accepted. Real evaluator: Vitest 4.1.8 against a real (non-mocked)
agentdb@3.0.0-alpha.20instance (:memory:db), after a fullpnpm install --filter @claude-flow/memory....console.errorcalled with["HNSW search failed:", {}]during a normalstore()+search()— the bug fires in practice.expected [] to have length 0 but got 1), passes on candidate.agentdbpackage'sgetController('hnsw')throws/Unknown controller/— so a futureagentdbrelease adding real HNSW support fails this test loudly instead of the dead code silently staying dead.agentdb-backend.test.ts: 21/21 (baseline) → 23/23 (candidate, +2 new), 0 regressions.@claude-flow/memorysuite: 614/616 both baseline and candidate — the 2 failures are pre-existing/unrelated (a read-only-file permission test that doesn't work running as root; an N=5000 perf test timing out at 34s vs. a 30s ceiling, neither touching this file; first confirmed failing identically on baseline via stash isolation).tsc --noEmit: 0 errors, both ways.hybrid-backend.test.ts+database-provider.test.ts41/41 pass on candidate.4. Baseline Comparison
console.error("HNSW search failed:", ...)on a normal searchbruteForceSearch()(same either way)bruteForceSearch()(same either way)getStats().hnswStatsundefined(via swallowed throw)undefined(explicit)agentdb-backend.test.ts@claude-flow/memorysuitetsc --noEmitIsolated microbenchmark (explicitly NOT an end-to-end
store()/search()timing claim — a synthetic probe of only the mechanism removed, flagged by the adversarial critic as needing this exact qualifier): a tight loop calling a throw-then-catch path 200k times vs. a direct-return path 200k times measured ~5.66µs/call vs. ~0.008µs/call, a ~700x difference — consistent with V8's documented stack-trace-capture cost for thrown exceptions. Real-worldstore()/search()wall-clock is dominated by SQLite I/O and embedding work untouched by this fix; this number supports "removes a real per-call cost," not an end-to-end speedup claim.5. Darwin Lineage
Skipped — binary dead-code-removal/log-suppression fix, no continuous or categorical parameter for Darwin's real interface (
npx ruvector harness darwin <config> --execute, confirmed available) to search over. Same skip class as nearly every accepted fix in this repo's history (#3929, #3977, #4053, #3395, ...).6. Flywheel Evidence
No
.claude-flow/flywheel/state exists in this checkout (same disclosed gap as every recent night). Evidence retained as: 2 new discriminating tests, the direct reproduction probe, the full-suite comparison, the isolated microbenchmark, issue #4249, the gist, and the witness below.7. Reward Hack Check
Clean. Independently re-verified by an adversarial-critic subagent (separate context, no authoring bias): no weakened assertions, no gold-data edits, no cherry-picking (full suite disclosed both ways), no evaluator exploitation, no cost hiding ($0, deterministic), no seed manipulation. The critic specifically chased a possible null-safety regression (whether the dynamically-imported
HNSWIndexglobal might be perpetually falsy, silently swallowing real results via the unrelatedif (!this.agentdb || !HNSWIndex)guard) and confirmed it is truthy and search still returns real results end-to-end — no guard the removed try/catch depended on.8. Security Review
Not security-sensitive — pure dead-code removal plus removing a misleading log line. No new I/O/network/credential/filesystem surface; no change to what gets stored, searched, or returned.
9. Regression Analysis
Zero regressions:
agentdb-backend.test.tsfull pass (+2 new), full@claude-flow/memorysuite byte-identical failure set (614/616 both ways, both failures pre-existing/unrelated),hybrid-backend.test.ts/database-provider.test.ts(consumers) 41/41,tsc --noEmitclean both ways.10. ADR
Not created — scoped dead-code-removal fix matching an already-decided repo convention (2026-08-30
diskann-backend.ts, 2026-10-10graph-analyzer.tscorrections — neither created an ADR either).11. Research Gist
docs/dream-cycle/dream-gist-2026-10-17.md(this branch) — full SOTA report: 4-role parallel research fan-out (deep researcher, 2 scan researchers, competitor analyst), 9-row competitor comparison (LangGraph/AutoGen/CrewAI/OpenAI Agents SDK + 5 vector DBs), 2026-dated sources (SLEUTH, "When Routing Collapses", cheap reward-hack detection, HNSW tombstone repair, SLoRA), and 3 specific future-night recommendations. No gist-publishing tool available this session (noghCLI, no gist-equivalent MCP tool) — same disclosed FALLBACK as every night since 2026-09-30; full report embedded in issue #4249 instead.12. Issue
Closes the evidence trail documented in #4249 (full 15-section report, including the capabilities-surface and memory-surface scan findings).
13. Witness
Verifier: fetch
docs/dream-cycle/dream-gist-2026-10-17.mdfrom this branch, SHA-256 it, concatenate with the session commit above, SHA-256 again — must equal the witness stamp.14. Merge Policy
Human review required. Do not self-merge. Do not autonomously promote Flywheel state. Independent adversarial critic verdict: CONFIRMED-WITH-CAVEATS (no blocking issues; caveat was solely to ensure the microbenchmark stays labeled as isolated, which it is above).
🤖 Generated with RuFlo
https://claude.ai/code/session_01KmvMMVh9xNQdRGXfk9vwjt
Generated by Claude Code