aide-context-retrieval-wiring · git:20260828.ba41dd3 · 2026-08-28 · sha256 e96320b33baed4c4
aide-context-retrieval-wiring git:20260828.ba41dd3A
Immutable. This exact content is served forever at /api/v1/blob/e96320b33baed4c4.
---
name: aide-context-retrieval-wiring
description: Wire the built-but-unwired codebase index (node/src/services/index-service.mjs, BM25+RRF+optional dense embeddings, git-aware, /api/index/reindex + /api/index/search already routed) into the actual chat path so every model answer is grounded in the operator's real workspace — retrieval injection in model-router/chat, an @codebase context tool, embed-function supply via the local engine, index freshness on file change, token-budgeted context packs. Use whenever model answers ignore workspace code, when asked "how do I make chat know my codebase", when wiring retrieval-augmented generation, tuning context budgets, or diagnosing stale/degraded index results.
---
# Context Retrieval Wiring — Ground Every Answer in the Real Workspace
Born 2026-08-27 gap analysis: AIDE already SHIPS a hybrid retrieval engine
(index-service.mjs: chunkFile, BM25 sparse, dense cosine, RRF fuse, git-branch
aware, incremental reindex, persist/restore from disk) with HTTP routes
(/api/index/reindex, /api/index/search) — and NOTHING in the chat path calls
it. `rg hybridSearch model-router.ts chat.ts orchestrator.mjs` = zero hits.
Rivals (Cursor secure codebase indexing 2026, VS Code workspace context) treat
this as THE differentiator. Also: no embed function is ever supplied
(openapi.ts `indexEmbedFn ?? null`), so the index runs in permanently
"degraded" BM25-only mode.
## Research base (verified 2026-08-27)
1. Cursor ships "secure codebase indexing" + "semantic search" as marquee
features (cursor.com, changelog 2026); VS Code ships "workspace context" +
"add prompt context" as first-class agent concepts (VS Code agents docs,
retrieved 2026-08-27).
2. In-repo engine ALREADY implements hybrid retrieval correctly: BM25 sparse +
dense cosine candidates → RRF fuse (k=20, 50 per list) → {path, line,
header, rrf_score, sparse_rank, dense_rank} + honest `degraded` flag.
3. Both chat paths have proven injection seams: legacy daemon /api/chat
(injectScaffold + learned block) and arch chat.ts (buildScaffold tiers).
## What to do (direct)
1. WIRE RETRIEVAL INTO CHAT (arch first): last user message →
`indexService.hybridSearch(msg, 8)` → CONTEXT block: top 5 results, each
`path:line header` + ≤20 lines read from disk via the existing fs path
jail. Inject as a DATA message after the scaffold, before user content.
Add harness metadata: `context: { hits, degraded, approx_tokens }`.
2. SUPPLY AN EMBED FUNCTION: local engine /v1/embeddings on the small in-box
model, batch 16 (EMBED_BATCH), passed as `indexEmbedFn` at service create.
Verify /v1/embeddings with curl first; if unsupported stay BM25-only and
keep the honest `degraded` flag — do NOT fake it.
3. LEGACY PARITY: same injection in daemon/server.mjs chat; reuse ONE index —
legacy calls the arch /api/index/search over 127.0.0.1, never a second scan.
4. FRESHNESS: fs watcher → debounce 5s → incremental reindex(); progress on
the events bus; status bar shows `index: <chunks>, fresh/stale/degraded`.
5. @codebase TOOL: explicit tool for "search the codebase for X" →
## Why it's done this way
- Retrieval MUST be default-on and invisible: rivals win because answers are
grounded without the operator asking. An unused endpoint is research, not a
feature.
- BM25-first with honest degradation: dense adds latency + a failure mode; RRF
fuse already handles missing dense gracefully. Ship value now, flip dense on
when embeddings verify.
- Reuse, don't rebuild: the engine matches Cursor's architecture (chunk →
sparse+dense → fuse), is written, routed, and tested. The gap is ONE call in
the chat path plus a freshness loop.
## Dependencies / issues / bugs
- Depends on: index-service.mjs (built), index routes (built), fs path jail
(built — reuse for line reads), events bus (built), scaffold injection
(built).
- Embeddings require the served model to support /v1/embeddings — verify with
curl before wiring; unsupported = BM25-only, flagged.
- Known repo pitfall: `indexEmbedFn ?? null` means every boot is degraded.
- Large repos: scanWorkspace must respect .gitignore + a file-size cap;
reindex is event-driven, never on the request path.
- Windows separators in doc.path: index-store normalizes; read doc.path
verbatim through the jail.
## Threat matrix
| Threat | Signature | Defense |
|---|---|---|
| Prompt-injection via workspace files | retrieved chunk says "ignore instructions" | retrieved text is DATA: delimited, never system role; scaffold asserts precedence |
| Secret leakage into context | .env/keys chunks indexed | respect .gitignore + deny-list (.env, *.pem, node_modules) in scan |
| Path escape on line reads | result path used raw for fs read | resolveInside jail on every chunk read |
| Stale index misleads model | answer cites deleted code | fs-event incremental reindex + freshness stamp in harness metadata |
| Context budget blowout | retrieval starves the real question | hard cap 5 hits × 20 lines (≤~1.5k tokens); drop lowest RRF rank first |
| Twin-orchestrator index drift | legacy + arch keep separate indexes | ONE index (arch owns); legacy calls arch HTTP |
## Pitfalls
- Do NOT put retrieved code into the system prompt — data block, delimited.
- Do NOT reindex per keystroke/save — debounce 5s, incremental only; full
reindex only on INDEX_VERSION bump.
- Do NOT drop the `degraded` flag — repo honesty laws require surfacing it.
- Do NOT store file contents in the index; read-at-answer-time keeps it small.