Skip to content

Commit 6e50d77

Browse files
feat(index): unresolved calls staging (SCHEMA 36) (#162)
* feat(index): two-phase call resolution with unresolved_calls staging Add SCHEMA_VERSION 36, call-resolver pass after bindings, incremental scoped re-resolve, and unresolved-call-sites / call-resolution-stats recipes. * fix(index): tighten call-resolve scope and meta semantics Use expandHeritageResolveScope for incremental call resolve, include all --files paths even when hash-unchanged, global unresolved_calls_residual meta, skip method-call false binds, resolve after deletion-only incremental, add changeset. * test: enrich golden and integration coverage for index substrate Add substrate pin-down goldens, index-table-stats aggregate, coverage-matrix guard, run-index call/deletion integration tests, and expanded agent-eval probes. Document the harness map in docs/testing-coverage.md. * test: formalize in-repo test bench and capability manifest Add fixtures/README.md, CAPABILITIES.json, and in-repo-test-bench plan so CI validates Codemap without external CODEMAP_ROOT. Bench corpus stays at fixtures/minimal; --corpus bench aliases minimal. Add method-call bench slice and golden; extend coverage-matrix guard for manifest files. * test(bench): Phase 2–3 shop-symbols golden and CLI smoke e2e Add shop-symbols-recipe golden, cmd-test-bench-e2e for show/snippet/impact/ validate/SARIF on fixtures/minimal, and an agent-eval probe. Mark plan phases 2–3 largely complete in in-repo-test-bench.md. * test(agent-eval): one probe per capability group on in-repo bench Expand probes to 18 golden-backed scenarios, add capability-probes.test.mjs guard, and fix live MCP smoke to assert find-call-sites by id not index. * docs(plans): mark in-repo test bench Phases 1–3 shipped * fix(index): align call resolve with binding kinds and restore comments Treat re-exported/same-file without symbol_id as resolved; document method calls in call-resolution-stats; restore bindings-engine comments moved to loadBindingIndexContext. * docs(roadmap): check off test bench and unresolved calls staging Align P2 backlog with shipped plans (PR #162); Phase 4 bench scale stays in plan only. * docs(plans): retire shipped unresolved-calls-staging plan Delete plan file per docs-governance; point roadmap and callback-dispatch deps at architecture.md (#162). * docs: fact-check call resolution and bench coverage docs Align architecture with deletion-only resolve scope and method-call NULL kinds; fix testing-coverage paths and agent-eval probe wording; sync CAPABILITIES.json substrate scenario ids with testing-coverage table. * docs: drop rot-prone inventory counts; fix stale refs Per docs/README Rule 6: remove hardcoded recipe/scenario/probe counts; point schema version at src/db.ts; fix agent-eval and substrate-extraction stale wording; label historical 3-probe benchmark sample. * docs: address PR review — Rule 2 roadmap, glossary call resolve Remove shipped P2 backlog lines; add unresolved_calls glossary entries; testing-coverage cross-ref in docs/README; bench tier points at shipped docs. * docs(test): align substrate matrix with coverage map Add calls/coverage to SUBSTRATE_SCENARIO_BY_TABLE; fix glossary architecture anchor; clarify CONTRIBUTING guard scope.
1 parent 1d9c09e commit 6e50d77

67 files changed

Lines changed: 2339 additions & 145 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@stainless-code/codemap": minor
3+
---
4+
5+
Add two-phase call resolution (`unresolved_calls` staging, `calls.callee_symbol_id` / `callee_resolution_kind`). Schema version 36 — existing indexes rebuild on next `codemap` run.

.github/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Then open a PR on GitHub into **`main`**.
3939
- **Public API** — Anything exported from the package entry (`src/index.ts``src/api.ts`, `config.ts`, shared types) should have **JSDoc** that reads well in hovers and in published typings.
4040
- **Layers** — Keep boundaries clear: [architecture.md](../docs/architecture.md) (`cli``application` → infrastructure). Don’t let CLI concerns leak into parsers or the DB layer.
4141
- **Before you open / update a PR**`bun run check` (or at least `bun run test` + `bun run typecheck` while iterating).
42-
- **Golden queries (Tier A)** — If you change `fixtures/minimal/` or schema/query behavior expected by [fixtures/golden/](../fixtures/golden/), run `bun scripts/query-golden.ts --update`, review diffs, and commit updated JSON under `fixtures/golden/minimal/`. Prefer **fixing the indexer** when output changes for the wrong reason; only refresh goldens when the new rows are correct. See [docs/golden-queries.md](../docs/golden-queries.md).
42+
- **Golden queries (Tier A)** — If you change `fixtures/minimal/` or schema/query behavior expected by [fixtures/golden/](../fixtures/golden/), run `bun scripts/query-golden.ts --update`, review diffs, and commit updated JSON under `fixtures/golden/minimal/`. Prefer **fixing the indexer** when output changes for the wrong reason; only refresh goldens when the new rows are correct. See [docs/golden-queries.md](../docs/golden-queries.md) and the coverage map [docs/testing-coverage.md](../docs/testing-coverage.md). New tables should add a substrate scenario (or extend `index-table-stats`); `bun run test:scripts` runs `query-golden-coverage-matrix.test.mjs` (every bundled recipe + `SUBSTRATE_SCENARIO_BY_TABLE` pin-downs).
4343
- **Golden queries (Tier B)** — Against a **local** clone, use `bun run test:golden:external` with `CODEMAP_ROOT` / `--root`. Copy [fixtures/golden/scenarios.external.example.json](../fixtures/golden/scenarios.external.example.json) to `scenarios.external.json` if you need custom scenarios; goldens under `fixtures/golden/external/` are gitignored — do not commit snapshots from proprietary trees.
4444
- **Style** — Match Oxfmt/Oxlint; prefer **straight-line code** and extracted helpers over long nested blocks.
4545

docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Each topic has exactly one canonical file. Other files cross-reference by relati
1616
| [agents.md](./agents.md) | **`codemap agents init`** — bundled **`templates/agents/`** (thin pointer files) → **`.agents/`** in consumer projects; full content served live by **`codemap skill`** / **`codemap rule`** + **`codemap://skill`** / **`codemap://rule`** from `templates/agent-content/`; section assembler + `*.gen.md` renderers, **[pointer protocol](./agents.md#pointer-protocol-and-staleness-detection)** + staleness nag, per-file IDE symlink/copy, **`--interactive`**, **`--mcp`**, **`<state-dir>/.gitignore`** reconciler (root `.gitignore` untouched). |
1717
| [benchmark.md](./benchmark.md) | [**Indexing another project**](./benchmark.md#indexing-another-project) · [**Benchmark script**](./benchmark.md#the-benchmark-script) · [**Query stdout (table vs JSON)**](./benchmark.md#query-stdout-table-vs-json-benchmarkquery) · [**Custom scenarios**](./benchmark.md#custom-scenarios-codemap_benchmark_config) (`CODEMAP_BENCHMARK_CONFIG`) · [**Agent eval harness**](./benchmark.md#agent-eval-harness) · [`fixtures/minimal/`](../fixtures/minimal/). |
1818
| [golden-queries.md](./golden-queries.md) | Golden `query` **design & policy** (Tier A/B, no proprietary trees); runner: [scripts/query-golden.ts](../scripts/query-golden.ts). |
19+
| [testing-coverage.md](./testing-coverage.md) | Maintainer map: harness layers, substrate golden scenarios, recipe/table guard (`query-golden-coverage-matrix.test.mjs`). |
1920
| [fixtures/golden/](../fixtures/golden/) | [scenarios.json](../fixtures/golden/scenarios.json) + [minimal/](../fixtures/golden/minimal/)**`bun run test:golden`**; Tier B: [scenarios.external.example.json](../fixtures/golden/scenarios.external.example.json) + **`bun run test:golden:external`** ([benchmark § Fixtures](./benchmark.md#fixtures)). |
2021
| [fixtures/benchmark/](../fixtures/benchmark/) | Tracked [scenarios.example.json](../fixtures/benchmark/scenarios.example.json) — copy to `*.local.json` (gitignored) for [`CODEMAP_BENCHMARK_CONFIG`](./benchmark.md#custom-scenarios-codemap_benchmark_config). |
2122
| [fixtures/qa/](../fixtures/qa/) | [prompts.external.template.md](../fixtures/qa/prompts.external.template.md) — optional chat QA prompts for an external index (`*.local.md` gitignored). |
@@ -62,6 +63,7 @@ Cross-cutting topics that span multiple files. Each has exactly one canonical ho
6263
| **`CLAUDE.md` / `AGENTS.md` / `GEMINI.md` / Copilot** — managed **`codemap-pointer`** sections, merge vs **`--force`** | [agents.md § Pointer files](./agents.md#pointer-files) | Link here; do not duplicate the situation table |
6364
| End-user CLI (index, **`query --json`**, **`query --recipe`**, **`query --recipes-json`**, **`query --print-sql`**, **`skill`**, **`rule`**, agents, flags, env) — query has no row cap; use SQL **`LIMIT`**; **`--json`** errors include SQL, DB open, and bootstrap failures; bundled `templates/agent-content/skill/*.md` examples default to **`--json`** | [../README.md § CLI](../README.md#cli) | [architecture § CLI usage](./architecture.md#cli-usage) summarizes and links back; [agents.md](./agents.md) |
6465
| Golden query regression (`test:golden`, `test:golden:external`, `--update`) | [golden-queries.md](./golden-queries.md) | CONTRIBUTING § Golden queries; [benchmark § Fixtures](./benchmark.md#fixtures) |
66+
| In-repo test bench harness map (layers, substrate pin-down scenarios, `CAPABILITIES.json`) | [testing-coverage.md](./testing-coverage.md) | [fixtures/README.md](../fixtures/README.md); optional scale: [plans/in-repo-test-bench.md](./plans/in-repo-test-bench.md) Phase 4 only |
6567
| Agent eval harness (`test:agent-eval`, `scripts/agent-eval/`) | [benchmark § Agent eval harness](./benchmark.md#agent-eval-harness) | Reuses golden scenarios via `goldenId`; probe + live + log structural cost A/B; exploratory MCP vs agent findings in [research/agent-eval-findings-2026-05.md](./research/agent-eval-findings-2026-05.md) |
6668
| **`CODEMAP_BENCHMARK_CONFIG`** (per-repo benchmark JSON) | [benchmark § Custom scenarios](./benchmark.md#custom-scenarios-codemap_benchmark_config) | [fixtures/benchmark/scenarios.example.json](../fixtures/benchmark/scenarios.example.json) only |
6769
| `bun run qa:external` — index + disk checks + `benchmark.ts` on **`CODEMAP_*`** | [.github/CONTRIBUTING.md](../.github/CONTRIBUTING.md) | [scripts/qa-external-repo.ts](../scripts/qa-external-repo.ts) (invocation only) |

docs/architecture.md

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Optional **`<state-dir>/config.{ts,js,json}`** (default `.codemap/config.*`; def
196196

197197
**Fresh database:** the default CLI **`codemap`** (incremental) calls **`createSchema()`** in **`runCodemapIndex`** before **`getChangedFiles()`**, so the **`meta`** table exists before **`getMeta(..., "last_indexed_commit")`** runs on an empty **`.codemap/index.db`**.
198198

199-
Current schema version: **35** — see [Schema Versioning](#schema-versioning) for details.
199+
Live schema version: `SCHEMA_VERSION` in [`src/db.ts`](../src/db.ts) — see [Schema Versioning](#schema-versioning) for bump policy.
200200

201201
All base tables use `STRICT` mode; **`source_fts`** is an FTS5 virtual table (no `STRICT`). Tables marked with `WITHOUT ROWID` store data directly in the primary key B-tree. PRAGMAs and index design: [SQLite Performance Configuration](#sqlite-performance-configuration).
202202

@@ -245,23 +245,42 @@ All base tables use `STRICT` mode; **`source_fts`** is an FTS5 virtual table (no
245245

246246
### `calls` — Function-scoped call edges, deduped per file (`STRICT`)
247247

248-
| Column | Type | Description |
249-
| ------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------- |
250-
| id | INTEGER PK | Auto-increment row id |
251-
| file_path | TEXT FK | References `files(path)` ON DELETE CASCADE |
252-
| caller_name | TEXT | Name of the calling function/method |
253-
| caller_scope | TEXT | Dot-joined scope path (e.g. `UserService.run`). Anonymous scopes encode as `$anon_<localId>` to avoid sibling-callback collisions |
254-
| callee_name | TEXT | Name of the called function, `obj.method` / `obj.foo.bar` for member chains (recursive flatten), `this.method` for self |
255-
| line_start | INTEGER | 1-based line of the callee identifier token (per [R.6]) |
256-
| column_start | INTEGER | 0-based byte column of the callee token |
257-
| column_end | INTEGER | One-past-last column |
258-
| args_count | INTEGER | Argument count; NULL when a spread argument is present |
259-
| is_method_call | INTEGER | 1 when callee is a member expression (`obj.method()`) |
260-
| is_constructor_call | INTEGER | 1 for `new Foo()` (`NewExpression`) |
261-
| is_optional_chain | INTEGER | 1 when the call uses optional chaining (`?.`) |
248+
| Column | Type | Description |
249+
| ---------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
250+
| id | INTEGER PK | Auto-increment row id |
251+
| file_path | TEXT FK | References `files(path)` ON DELETE CASCADE |
252+
| caller_name | TEXT | Name of the calling function/method |
253+
| caller_scope | TEXT | Dot-joined scope path (e.g. `UserService.run`). Anonymous scopes encode as `$anon_<localId>` to avoid sibling-callback collisions |
254+
| callee_name | TEXT | Name of the called function, `obj.method` / `obj.foo.bar` for member chains (recursive flatten), `this.method` for self |
255+
| line_start | INTEGER | 1-based line of the callee identifier token (per [R.6]) |
256+
| column_start | INTEGER | 0-based byte column of the callee token |
257+
| column_end | INTEGER | One-past-last column |
258+
| args_count | INTEGER | Argument count; NULL when a spread argument is present |
259+
| is_method_call | INTEGER | 1 when callee is a member expression (`obj.method()`) |
260+
| is_constructor_call | INTEGER | 1 for `new Foo()` (`NewExpression`) |
261+
| is_optional_chain | INTEGER | 1 when the call uses optional chaining (`?.`) |
262+
| callee_symbol_id | INTEGER FK | Resolved callee in `symbols` (NULL when unresolved / external global) |
263+
| callee_resolution_kind | TEXT | `same-file`, `imported`, `re-exported`, `global`, or `unresolved` — set by `resolveCalls` after bindings; NULL on method calls (`is_method_call = 1`, deferred) |
262264

263265
Edges are deduped per (caller_scope, callee, call vs constructor) per file: if `foo` calls `bar` three times in the same file, only one row is stored. `foo()` and `new Foo()` with the same callee name remain distinct rows. Same-named methods in different classes get distinct `caller_scope` values. Module-level calls (outside any function) are excluded — only function-scoped calls are tracked.
264266

267+
**Call resolution:** `src/application/call-resolver.ts` runs after bindings on full rebuild and after incremental file updates. Incremental scope is `expandHeritageResolveScope` over changed/requested paths **and** deletion-only paths (includes importers). Method calls (`is_method_call = 1`) are not name-bound yet (`callee_*` stay NULL; not queued). Unresolved sites are staged in `unresolved_calls`; `meta.unresolved_calls_residual` is the global queue `COUNT(*)`.
268+
269+
### `unresolved_calls` — Staging queue for unresolved call sites (`STRICT`)
270+
271+
| Column | Type | Description |
272+
| -------------- | ---------- | ---------------------------------------------- |
273+
| id | INTEGER PK | Auto-increment row id |
274+
| file_path | TEXT FK | References `files(path)` ON DELETE CASCADE |
275+
| caller_scope | TEXT | Caller scope path at the call site |
276+
| callee_name | TEXT | Callee identifier as stored on the `calls` row |
277+
| line_start | INTEGER | 1-based line of the call site |
278+
| column_start | INTEGER | 0-based column of the callee token (nullable) |
279+
| reference_kind | TEXT | Default `call` |
280+
| created_at | TEXT | ISO timestamp when the row was queued |
281+
282+
Bundled recipes: `unresolved-call-sites`, `call-resolution-stats`.
283+
265284
### `type_members` — Properties and methods of interfaces and object-literal types (`STRICT`)
266285

267286
| Column | Type | Description |

0 commit comments

Comments
 (0)