Skip to content

feat(kg): memory↔code join surface — recall code-context (#206)#208

Merged
edheltzel merged 3 commits into
mainfrom
worktree-phase2b-code-context
Jun 28, 2026
Merged

feat(kg): memory↔code join surface — recall code-context (#206)#208
edheltzel merged 3 commits into
mainfrom
worktree-phase2b-code-context

Conversation

@edheltzel

Copy link
Copy Markdown
Owner

What & why

Phase 2b of the native code-KG epic (#196). Phase 1 merged the code KG itself (code_files/code_nodes/code_edges + the tree-sitter indexer + recall index). This PR adds the surface that makes building it native worth it: the join between code structure and memory that lives in the same recall.db, so no external code-intelligence tool can run it.

For a source file, it answers: "which decisions and learnings were made about the symbols defined here?" — by correlating code_nodes with decisions_fts/learnings_fts on the symbol name, scoped by project (design §5).

Changes

  • src/lib/code-context.ts
    • codeContextForFile(db, {project, path}) — design §5 query 1: the decisions/learnings whose text mentions a symbol defined in the file, with the matched symbol names reported so the caller sees why each record surfaced. The synthetic module node is excluded so we match real symbols, not the filename.
    • symbolSearchWithDiscussion(db, {project, query}) — design §5 query 2: symbol search fused with where each found symbol was discussed.
  • src/commands/code-context.ts + wired into src/index.ts** — recall code-context : project-scoped (auto-detected or --project`), repo-relative-path normalization, token-cheap grouped output.

Security — FTS5 injection guard (design §9-C5)

The symbol name is fed to MATCH as a quoted phrase ('"' || name || '"'), never raw. A symbol legitimately named OR, NEAR, or AND would otherwise be parsed as FTS5 query syntax. Binding alone is not sufficient — FTS5 parses a bound string as a query — so the quoting is the actual guard. A test proves it: the same join with the name fed raw to MATCH throws on a symbol named OR, while the quoted implementation does not and returns the right record.

Project scoping

Join key is project (the column every memory table and code_files/code_nodes carry). Edges inherit scope via src_id (§9-C2). No hard FK between code and memory — deferred per §5/Q3.

Validation

  • bun run build ✅ · bun run lint (tsc) ✅ · bun test1207 pass / 0 fail (5 new tests in tests/lib/code-context.test.ts).
  • New tests seed both indexed code (via the real indexCode) and real decisions/learnings against the production schema (CREATE_TABLES + CREATE_FTS + triggers — zero drift), then assert:
    • the join returns memories that mention a file's symbols, with matched-symbol provenance;
    • negative controls: another file's symbol and another project's records are excluded;
    • the adversarial OR symbol is injection-safe (raw MATCH throws, quoted does not);
    • the fused symbol-search path works, including for the OR symbol.
  • End-to-end CLI smoke test against a temp RECALL_DB_PATH (recall initindex srcadd decisioncode-context):
src/lib/code-context.ts — 4 symbol(s) indexed [smoke]

Decisions (1):
  [decisions#1] 2026-06-27 · matched: codeContextForFile
    codeContextForFile must stay project-scoped

Closes #206

Implements design §5: fuse the native code KG (code_files/code_nodes)
with the memory FTS tables (decisions_fts/learnings_fts) in the one
recall.db — a join no external code-intelligence tool can run.

- src/lib/code-context.ts: codeContextForFile (file → decisions/learnings
  that mention its symbols) + symbolSearchWithDiscussion (symbol search
  fused with where each symbol was discussed). Join key is `project`;
  no hard FK (deferred, §5/Q3).
- §9-C5 injection guard: the symbol name is fed to MATCH as a quoted
  phrase ('"' || name || '"'), so a symbol named OR/NEAR/AND parses as a
  literal phrase, never FTS5 syntax.
- recall code-context <relpath>: project-scoped, token-cheap output.
- tests seed BOTH indexed code AND decisions/learnings and assert the
  join, project scoping, an adversarial `OR` symbol (proving the raw
  unquoted MATCH throws while the quoted impl does not), and the fused
  symbol-search path.

Closes #206
…-context

* origin/main:
  feat(kg): add code graph query verbs (#209)

# Conflicts:
#	src/index.ts
@edheltzel edheltzel merged commit 41510bf into main Jun 28, 2026
2 checks passed
@edheltzel edheltzel deleted the worktree-phase2b-code-context branch June 28, 2026 13:00
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.

Phase 2b: memory↔code join surface (recall code-context)

1 participant