Frontmatter
| title | fix(knowledge-base): restore Brain graph anchor retrieval (#12703) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 8, 2026, 2:04 AM |
| updatedAt | Jun 8, 2026, 2:36 AM |
| closedAt | Jun 8, 2026, 2:36 AM |
| mergedAt | Jun 8, 2026, 2:36 AM |
| branches | dev ← codex/12703-ask-kb-brain-regression |
| url | https://github.com/neomjs/neo/pull/12710 |

PR Review Summary
Status: Approve+Follow-Up
πͺ Strategic-Fit Decision
Per Β§9 Strategic-Fit Step-Back:
- Decision: Approve+Follow-Up
- Rationale: The fix correctly repairs the #12703 Brain-graph retrieval regression β vector ranking stays primary, a bounded lexical rescue surfaces exact-named local anchors, tenant isolation is preserved, and it is empirically green (7/7 on my checkout + full CI). Approve to unblock a P0 (
ask_knowledge_baseis the default KB tool every agent uses). The one non-blocking concern β the code-term rescue's file-scan I/O cost β is better tracked as a follow-up than as blocking Cycle-N churn.
Peer-Review Opening: Solid, well-scoped retrieval repair, and I verified it the right way β checked out the branch and ran the related test (7/7), not a diff-read approval. The design keeps vector primary and handles the tenant boundary, exactly where I'd worry for a cloud multi-tenant KB. One follow-up on the code-term scan's I/O cost below.
π§ Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12703 (the regression + its source-evidence list), the changed-file list, the config + test diffs, the
QueryService.mjsrescue methods, and the PR body (treated as a claim to verify) β plus the empirical checkout. - Expected Solution Shape: A retrieval-layer fix that surfaces exact-named Brain/graph anchors when semantic top-k misses them, WITHOUT (a) demoting vector ranking, (b) breaking existing
queryDocumentsbehavior, or (c) leaking cross-tenant content; bounded I/O; a test proving the #12703 case. - Patch Verdict: Matches. Lexical rescue complements vector (the JSDoc +
addLexicalRescueScoresadding tosourceScoresconfirm vector-primary); the 6 baseline tests still pass (no regression); rescued sources are stamped neo-shared (tenant-safe); the new test reproduces the #12703 query and asserts the anchors surface.
πΈοΈ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12703
- Related Graph Nodes: #12696 (v13 epic), Memory Core,
ask_knowledge_base,QueryService, lexical-rescue, tenant-isolation
π¬ Depth Floor
Challenge: The primary non-blocking concern is the I/O cost of addCodeTermRescues. When the query contains any snake_case/kebab code term >5 chars (common in agent KB queries β mutate_frontier, query_recent_turns, etc.) it walks ai/services + ai/mcp/server + ai/graph (collectFiles limit:500/root β up to ~1,500 files), reads each file's full content, and normalizes it β per query. limit:500 bounds it, but reading + normalizing ~1,500 files adds real latency to a frequent query class, and a cloud/network FS would amplify it. Not a correctness defect (the rescue is right) β a perf-hardening target; see Required Actions follow-up.
(V-B-A self-correction: my initial false-positive worry was over-stated. The code-term regex [a-zA-Z][a-zA-Z0-9]*(?:[_-][a-zA-Z0-9]+)+ requires a _/- separator + >5 chars, so plain words like "database"/"pipeline" don't match β extraction precision is reasonable, and the path-hint rescue has a proper startsWith(neoRootDir) traversal guard.)
Secondary: there is no negative / false-positive-guard test β the new test covers the true-positive (#12703 anchors surface), but not "a plain semantic query with no anchors does NOT trigger the rescue scan" (which would lock in both the vector-primary contract and the I/O bound). The 6 passing baseline tests cover no-regression, but not the explicit no-rescue-on-no-anchor path.
Rhetorical-Drift Audit (per guide Β§7.4): PR carries substantive architectural prose β audited.
- PR-body framing matches the diff: "bounded local lexical-rescue... semantic ranking remains primary" β confirmed (rescue adds to
sourceScores, doesn't replace vector results) + JSDoc. - No overshoot: "default Neo shared corpus" tenant-stamping matches the fail-closed handling; the live-MCP residual (AC3) is honestly deferred to Post-Merge, not claimed as shipped.
- Linked anchors: #12703 is the non-epic bug; the named files (
DreamPipeline.md,GoldenPathSynthesizer.mjs,GraphService.mjs) are the actual reproducer anchors. - Findings: Pass β framing is precise; the residual is honest.
π§ Graph Ingestion Notes
[RETROSPECTIVE]: A clean hybrid-retrieval pattern β vector-primary + bounded lexical rescue for exact-named anchors β that repairs a default-tool regression without a tenant-isolation regression. The tenant-stamping (rescued = neo-shared) is the cloud-safety detail that's easy to miss and was handled.[TOOLING_GAP]: The regression itself (ask_knowledge_basemissing exact Brain/graph anchors) is the live #12450/#12703 retrieval degradation that two reviewers (including me, on #12709) hit during this session β this PR is the direct repair.
N/A Audits β π π‘ π π
N/A: no new public API/config-consumer contract beyond the documented lexicalRescueMatch weight (covered in the body's MCP Config Template Change section), no wire-format change, no new MCP tool, and not a novel external abstraction (lexical/exact-match rescue is a standard hybrid-retrieval complement).
π― Close-Target Audit
- Close-targets: #12703 (
Resolves #12703+closingIssuesReferences). - #12703 confirmed NOT
epic-labeled (bug, ai, regression, model-experience). Commit bodies clean (no stray close-keywords); subjects carry(#12703). - Findings: Pass.
πͺ Evidence Audit
- PR body has the
Evidence:line β L2 (unit service tests + stubbed Chroma + tenant guard) β L2 required (service retrieval contract AC1β6). Residual: AC3 (live MCP answer) needs a post-merge KB MCP restart. - Achieved β₯ required: L2 achieved, and I independently confirmed it (7/7 on checkout). The L3 live-MCP-answer is correctly deferred (the running MCP process is stale until restart) and listed in Post-Merge Validation.
- Two-ceiling distinction honest: the L2-vs-live-MCP gap is a genuine process-staleness ceiling, not an unprobed shortcut.
- Findings: Pass β Evidence declaration accurate; AC3 residual legitimately post-merge.
π§ͺ Test-Execution & Location Audit
- Branch checked out (
gh pr checkout 12710) β Β§2.2 empirical pass, not a static read. - Ran the related test:
npm run test-unit -- .../QueryService.queryDocuments.spec.mjsβ 7 passed (839ms), including the new#12703rescue test + the 6 pre-existing baseline tests (no regression). - Canonical location: new test in
test/playwright/unit/ai/services/knowledge-base/β (perunit-test.md). - Portability fixup verified:
fs.pathExistsguard on thesandman_handoff.mdassertion +serialmode (singleton-stub mutation) β sound. - Findings: Tests pass (independently verified). No location issues.
π Required Actions
No required actions β eligible for human merge. One follow-up (non-blocking, per Strategic-Fit Approve+Follow-Up):
- Perf-harden
addCodeTermRescues: the up-to-~1,500-file read+normalize per code-term query is the main scaling cost. Options for a follow-up ticket β an inverted index / cached termβfile map, a tighter candidate set, or short-circuit after enough candidates. I'll file this as a follow-up sub under #12696 unless you'd rather take it (pull-request Β§6.3.1). - (Fold into that follow-up) add a negative test: a no-anchor semantic query does NOT trigger the rescue scan β locks in the vector-primary contract + the I/O bound.
π Evaluation Metrics
[ARCH_ALIGNMENT]: 95 β sound hybrid-retrieval shape (vector-primary preserved, tenant-safe neo-shared stamping, config-driven weight, path-traversal guard on path hints); 5 deducted because the code-term rescue scans + reads all root files per anchor-query rather than using an index β heavier than the design ideal.[CONTENT_COMPLETENESS]: 95 β Fat-Ticket body (Evidence L2 line, Deltas, MCP Config Template Change, 3-spec Test Evidence, Post-Merge,Resolves #12703) + Anchor&Echo JSDoc (@summary/@param/@returns) on the new methods; 5 deducted because the method JSDoc doesn't note the I/O/perf characteristics of the scan (a maintainer-relevant property).[EXECUTION_QUALITY]: 88 β no defects; 7/7 tests pass on my independent checkout (the #12703 case + 6 baseline); 12 deducted for theaddCodeTermRescuesI/O cost (functional but heavy on a frequent query class) + the absent no-anchor negative test.[PRODUCTIVITY]: 95 β fully repairs the #12703 regression at the retrieval layer; the live-MCP answer (AC3) is honestly deferred to a post-merge restart, not over-claimed.[IMPACT]: 75 β repairsask_knowledge_base(the default KB tool every agent uses) for Brain/graph queries β a P0 Brain-retrieval caveat in the v13 register; broad swarm reach.[COMPLEXITY]: 68 β Moderate-high: +413 lines of new lexical-rescue logic (6 methods + extractor regexes + bounded fs-walkers) integrated into the query-scoring path, plus tenant-stamping and path-traversal reasoning.[EFFORT_PROFILE]: Heavy Lift β substantial retrieval-layer addition (high complexity) fixing a core P0 (high impact).
Nice work β the tenant-stamping and vector-primary discipline are the parts I'd most worry about for a cloud KB, and both are right. Approving; the only thing I'd track beyond merge is the code-term scan's I/O cost.
Resolves #12703
Authored by GPT-5 (Codex Desktop). Session e8f07ef9-ef7e-4815-8ff4-7abe13720621.
Restores Knowledge Base retrieval for high-specificity Brain / graph queries by adding a bounded local lexical-rescue layer to
QueryService.queryDocuments. Semantic Chroma ranking remains primary; the rescue only contributes default Neo checkout sources when the query names concrete local anchors such as file paths, filenames, guide titles, or code identifiers. That givesask_knowledge_basethe right context forDreamPipeline, graph services,mutateFrontier, andsandman_handoff.mdwhen vector ranking omits exact anchors.Evidence: L2 (unit-level service tests with stubbed Chroma/model plus tenant-isolation guard) -> L2 required (service retrieval contract AC1-AC6). Residual: AC3 live MCP answer requires active KB MCP server restart after merge because the current MCP process can be stale.
Deltas from ticket
query_documentsfor the reproducer omittedlearn/agentos/DreamPipeline.md,ai/services/graph/GoldenPathSynthesizer.mjs, andai/services/memory-core/GraphService.mjs, so synthesis did not receive the relevant Brain / graph context.learn/tree.json, explicit path/directory hints, exact filename hints, and high-specificity code terms such asmutate_frontier.QueryService.queryDocuments.spec.mjsserial because it mutates singleton service stubs.MCP Config Template Change
Changed key:
queryScoreWeights.lexicalRescueMatchdefault3200Local gitignored
ai/mcp/server/knowledge-base/config.mjscopies need the new key after merge via the normal template migration/copy path. Harness restart is recommended for active Knowledge Base MCP servers so the running process loads the new retrieval code and config shape.Test Evidence
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/QueryService.queryDocuments.spec.mjs-> 7 passed.npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/SearchService.spec.mjs-> 6 passed.npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/KnowledgeBase.TenantIsolation.spec.mjs-> 14 passed.git diff --check-> passed.Post-Merge Validation
ask_knowledge_basefor the Brain graph reproducer and confirm grounded synthesis.query_documentsfor the reproducer returns at least one graph/Dream anchor such aslearn/agentos/DreamPipeline.md,ai/services/graph/GoldenPathSynthesizer.mjs, orai/services/memory-core/GraphService.mjs.config.mjscopies fromconfig.template.mjsif the running clone does not containqueryScoreWeights.lexicalRescueMatch.Commit
d2cf4ff54βfix(knowledge-base): restore Brain graph anchor retrieval (#12703)