Skip to content

dream(intelligence): #4249 agentdb-backend.ts calls a native HNSW controller that has never existed in the real agentdb package (evaluated, ACCEPT) - #4250

Draft
ruvnet wants to merge 4 commits into
mainfrom
dream/2026-10-17-intelligence
Draft

ruvnet wants to merge 4 commits into
mainfrom
dream/2026-10-17-intelligence

Conversation

@ruvnet

@ruvnet ruvnet commented Oct 17, 2026

Copy link
Copy Markdown
Owner

1. Hypothesis

Given AgentDBBackend's storeInAgentDB(), searchWithAgentDB(), and getStats() (v3/@claude-flow/memory/src/agentdb-backend.ts) each call this.agentdb.getController('hnsw') inside a try/catch, expecting an optional native HNSW controller from the agentdb npm package, when the real agentdb package's AgentDB.getController() (confirmed directly from agentdb@3.0.0-alpha.20's installed dist/src/core/AgentDB.js) recognizes only 'memory'/'reflexion', 'skills', 'causal'/'causalGraph' and throws Error('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, and searchWithAgentDB additionally logs a misleading console.error('HNSW search failed:', ...) on every search even though the code unconditionally falls back to the already-correct bruteForceSearch() 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 dead this.agentdb.getController('hnsw') call sites in storeInAgentDB(), searchWithAgentDB(), and getStats(). searchWithAgentDB() now calls bruteForceSearch() directly (the only path that ever actually ran); getStats()'s hnswStats is now explicitly undefined (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.ts removal, 2026-10-10 graph-analyzer.ts native-acceleration correction).

3. Evaluation Receipt

evaluated: accepted. Real evaluator: Vitest 4.1.8 against a real (non-mocked) agentdb@3.0.0-alpha.20 instance (:memory: db), after a full pnpm install --filter @claude-flow/memory....

  • Direct reproduction against the unmodified source: console.error called with ["HNSW search failed:", {}] during a normal store() + search() — the bug fires in practice.
  • New test 1 (false-error regression guard): stash-isolated — fails on baseline (expected [] to have length 0 but got 1), passes on candidate.
  • New test 2 (tripwire): asserts the real agentdb package's getController('hnsw') throws /Unknown controller/ — so a future agentdb release 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.
  • Full @claude-flow/memory suite: 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.
  • Consumer regression check: hybrid-backend.test.ts + database-provider.test.ts 41/41 pass on candidate.

4. Baseline Comparison

Baseline Candidate
console.error("HNSW search failed:", ...) on a normal search fires every call never fires
Search results returned via bruteForceSearch() (same either way) via bruteForceSearch() (same either way)
getStats().hnswStats always undefined (via swallowed throw) always undefined (explicit)
agentdb-backend.test.ts 21/21 23/23 (+2 new)
Full @claude-flow/memory suite 614/616 614/616 (same 2 pre-existing failures)
tsc --noEmit clean clean

Isolated 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-world store()/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 HNSWIndex global might be perpetually falsy, silently swallowing real results via the unrelated if (!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.ts full pass (+2 new), full @claude-flow/memory suite 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 --noEmit clean 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-10 graph-analyzer.ts corrections — 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 (no gh CLI, 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

Session commit:  1882006a02487590c8c66a7eec8d7504f6af33c3
Gist SHA-256:    cbc84de295ffb01ce5ddbe2e6fa5a78b544228dabdda7129cdf18f8aaa19dba4
Witness stamp:   e2abed9831542617a74b3b0254ff4e775beea19199327a159dbd6138bf3d6914

Verifier: fetch docs/dream-cycle/dream-gist-2026-10-17.md from 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

claude and others added 3 commits October 17, 2026 06:29
…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
…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

ruvnet commented Oct 17, 2026

Copy link
Copy Markdown
Owner Author

CI: "Plugin package install-safety" was failing — fixed, pushed.

The audit-leaf-package-publish.mjs check correctly flagged that this PR changed publishable source in @claude-flow/memory (agentdb-backend.ts) — a non-bundled leaf dependency that resolves from the npm registry rather than shipping with the release tarball — without bumping its version, which would mean the fix never reaches users on the next release.

Fix pushed in 73c0bc6: bumped @claude-flow/memory 3.0.5 → 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. Verified locally: audit-leaf-package-publish.mjs, audit-plugin-packages.mjs, audit-package-dep-overlap.mjs, audit-wrapper-dep-ranges.mjs, and audit-umbrella-version-lockstep.mjs all pass against origin/main.


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

ruvnet commented Oct 17, 2026

Copy link
Copy Markdown
Owner Author

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 ERR_PNPM_OUTDATED_LOCKFILE — my previous commit (73c0bc6) bumped @claude-flow/memory's version in package.json but didn't sync v3/pnpm-lock.yaml's specifier field for it, so every job running pnpm install --frozen-lockfile failed immediately on install, before any of their actual test logic ran.

Fixed by hand-editing the single specifier: 3.0.5 -> 3.0.6 line under @claude-flow/cli's optionalDependencies for @claude-flow/memory 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 — the targeted edit is exactly what pnpm install --frozen-lockfile needs and nothing more. Verified locally: pnpm install --frozen-lockfile now succeeds from the checked-out v3/ workspace with only this one line changed.

Not this PR's — pin-drift (metaharness-pin-drift check): failing because @metaharness/darwin is pinned at ~0.10.2 in v3/@claude-flow/cli/package.json while npm's current latest is 0.12.0. This PR never touches any @metaharness/* pin — confirmed the last scheduled run of this same check on main (2026-10-12) passed, meaning @metaharness/darwin@0.12.0 was published sometime after that and nothing has re-run the check against main since; it would fail identically on main right now. Bumping that pin is out of scope for this intelligence/memory dead-code-removal PR — flagging it as a separate, pre-existing drift item rather than widening this PR to fix it.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants