Skip to content

integration: knowledge-graph reference edges (#82) - #89

Merged
IceRhymers merged 7 commits into
masterfrom
integration/knowledge-graph-reference-edges
Jul 24, 2026
Merged

integration: knowledge-graph reference edges (#82)#89
IceRhymers merged 7 commits into
masterfrom
integration/knowledge-graph-reference-edges

Conversation

@IceRhymers

Copy link
Copy Markdown
Owner

Knowledge-graph reference edges — integration branch

This draft integration PR tracks the staged delivery of #82. Child PRs may merge into integration/knowledge-graph-reference-edges; this PR remains draft until the complete epic is integrated, verified, and ready for human review into master.

Planned delivery

Guardrails

  • Raw edges only; no extraction-time cross-file symbol_id foreign keys.
  • All child PRs target this integration branch.
  • master remains protected and is never agent-merged.

Integration exit criteria

* db: raw reference-edge schema with lifecycle-safe grants (#83)

Adds the reference_edges table (migration 0005) for raw, unresolved
call/import edges extracted per file. Deliberately no FK to symbols --
resolution to a concrete symbol happens at query time by name-join in a
later child of epic #82. FKs to repos/files only, both ON DELETE CASCADE,
so the existing sweep/reconcile cascade paths in indexer/store.py cover
the new table without any behavior change; their docstrings are updated
to say so truthfully. Grant builders in app/db/grants.py are schema-wide
and need no code change to cover the new table.

* test: cover reference_edges schema, cascades, and grant lifecycle (#83)

Unit: source-level tripwires for migration 0005 (revision chain, no
symbols FK, no app import, no stray CREATE EXTENSION) plus an ORM-metadata
tripwire (test_reference_edge_model.py) so a future models.py edit can't
silently reintroduce a symbols FK or loosen a NOT NULL column. Extends the
durable-core-tables tripwire in test_db_client.py.

Integration: adds migrated_edges_capable, a fixture that reaches migration
head on stock dev Postgres too (by pre-seeding a stub chunks table before
0004, the same idempotency guard test_0004_guard_preserves_preexisting_chunks
already exercises) so the reference_edges shape/cascade/EXPLAIN/downgrade
tests and both ADP same-role/different-role grant-lifecycle proofs run
without a live Lakebase branch. Extends test_reconcile.py and test_store.py
to seed reference_edges rows and assert they cascade through the existing
sweep/reconcile paths.

* docs: add reference-edges runbook and update key-file tables (#83)

New docs/runbooks/reference-edges.md: schema summary, index-to-consumer
mapping, and the deploy/grant-coupling section (same shape as
multi-branch.md and semantic-enablement.md) with the ADP same-role/
different-role rule and the has_table_privilege verification query.
Links it from README's further-reading list. Updates app/db/AGENTS.md
and app/alembic/AGENTS.md key-file tables and the 0001->0005 chain.

* Address review pass 1: quote-agnostic FK tripwire, re-export ReferenceEdge, assert job role has no DDL

Fresh code-reviewer and security-reviewer passes both returned APPROVE with
no blockers/majors; three low-cost nits/informational findings addressed:
- test_0005_no_symbol_fk now matches symbols.id via regex instead of a
  quote-literal substring, so it survives formatter drift.
- app/db/__init__.py re-exports ReferenceEdge alongside the other models.
- The ADP same-role grant test now also asserts the job role's grants are
  DML-only (TRUNCATE raises InsufficientPrivilege), matching
  build_job_grants' least-privilege intent.
…on (#84) (#91)

extract_file() extends the existing tree-sitter symbol walk in
indexer/symbols.py to also emit call/import reference edges in the same
single pass (indexer/languages.py's new EDGE_NODE_KINDS map, Python-only
for now). Call targets resolve to the rightmost identifier of the callee;
import targets are the full dotted path as written, alias-insensitive,
with source-faithful relative-import and wildcard handling. Each edge
attributes to the innermost named enclosing definition on the walk stack,
computed in O(1) with no second walk.

indexer/store.py's index_repo writes reference_edges exactly like
symbols: an unconditional per-file delete followed by a bulk reinsert
inside the same per-(repo, branch) transaction, so a file whose edges
all vanish still sheds its stale rows. IndexCounts gains an edges count;
indexer/job.py switches to extract_file and logs it.

INDEX_SEMANTICS_VERSION bumps 2 -> 3 so every already-indexed branch
re-indexes once to backfill reference_edges.
…#92)

Adds per-language call/import extractors (JS/TS/TSX share one pair; Go,
Java, and Rust each get their own) and a dispatch table so extract_file
resolves the pair once per file instead of hardcoding Python. Rust use
declarations are handled by a recursive use-tree descent to cover
scoped, grouped, nested, self, and wildcard forms; Java constructor
targets resolve generic and scoped type names since object_creation_expression
has no simple name field. Bumps INDEX_SEMANTICS_VERSION 3 -> 4 so every
already-indexed branch re-indexes once and backfills the new languages.
* search: add query-time candidate-set resolver over reference_edges (#86)

Two-query resolver (edge sites, then a SQL-window-bounded candidate scan
per name) that resolves raw reference_edges.target_name to ranked symbols
candidates without a self-join, mirroring symbols.py's auto-correlation
avoidance. Ranking is membership-preserving so ambiguity is never
silently collapsed, and candidate_count always carries the true pre-cap
total even when the fetch is capped. Serve-only: no migration, no
extraction-time symbol FK, no indexer change.

* search: add find_references/list_imports payload builders (#86)

Additive app.service builders over the new resolver: find_references_payload
(corpus-wide, call edges) and list_imports_payload (repo-scoped, import
edges, structured repo_known miss). Repo-id-to-name resolution runs in a
separate post-leg transaction, mirroring search_code_payload's
_repo_name_map handling. MCP tool registration is a later child (#87).

* scripts: measure reference-edge resolution distribution (#86)

Offline AC4 measurement CLI reusing the resolver's own
build_candidate_count_select/classify_resolution so the reported
distribution agrees with the serve path by construction. call edges are
the headline metric compared against the epic's baseline; import edges
are reported separately as informational.

* docs: document query-time reference resolution in reference-edges runbook (#86)

Adds §4 covering the resolver's two-query design, candidate-set/resolution
semantics, the exact-dotted-match import decision, repo_known for
list_imports, branch-scoping parity with search_code/get_file, and the
measurement script with a recorded distribution from self-indexing this
repo's own tracked source.
Add find_references and list_imports as thin FastMCP tool registrations
backed only by app/service.py builders. find_references_payload is
unchanged; list_imports_payload gains an additive target/direction
extension (imports|imported_by) with deterministic pre-DB structured
validation. Extends the MCP e2e fixture, smoke checks, docstrings, and
docs for the new tools and the "what tests cover symbol X" composition
pattern.
* webui: add /api/references and /api/imports passthrough routes (#88)

Thin passthrough routes over the existing app.service find_references_payload/
list_imports_payload builders -- the same builders the MCP find_references/list_imports
tools wrap. DataError is the only exception mapped to 400; every other recoverable
condition (query_too_broad, unsupported_direction, missing_repo, missing_target,
repo_known=False, truncation) passes through as a 200 body unchanged.

* webui: add Graph tab for references and imports lookups (#88)

One GraphPage covering both /references and /imports modes (mode from the pathname),
sharing a SiteList candidate-set renderer -- both routes' sites use the identical
_site_payload shape. App.tsx mounts GraphPage with key={route.mode} so switching modes
fully remounts the page and re-arms the mount-time auto-run guard. Entry points: a refs
link next to each symbol match in search results, and an imports link on each repo row.

* webui: rebuild frontend dist for the Graph tab (#88)

make webui-build after the router/App/GraphPage/SiteList/client changes.

* test: add webui graph parity integration test (#88)

Extracts seed_reference_corpus out of test_mcp_server.py::seeded_schema as a pure,
behavior-preserving, connection-parameterized helper, then reuses it in a new parity
suite that proves /api/references and /api/imports return payloads byte-identical to a
direct app.service builder call (clamp_limit applied identically) over the same seeded
corpus. Transitively, since app/main.py's MCP tools are pure wrappers around the same
builders (independently pinned by test_reference_tools_streamable_http), this proves
webui and MCP serve identical payloads.

* docs: document webui graph tools and fix stale INDEX_SEMANTICS_VERSION notes (#88)

README and docs/runbooks/webui.md document the new Graph tab/routes and their parity
contract with the MCP tools. docs/runbooks/reference-edges.md and app/db/AGENTS.md
corrected from a stale INDEX_SEMANTICS_VERSION=3 to the current 4 (bumped 3->4 by #85).
@IceRhymers
IceRhymers marked this pull request as ready for review July 24, 2026 01:06
@IceRhymers
IceRhymers merged commit 6aa57c4 into master Jul 24, 2026
4 checks passed
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.

1 participant