Skip to content

fix(explore): three retrieval regressions found against v1.6.0 - #2057

Merged
colbymchenry merged 1 commit into
mainfrom
fix/explore-named-gap-fit
Sep 27, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
fix/explore-named-gap-fit

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Problem

The pre-release agent A/B (main vs v1.6.0, 7 README repos, 3-turn sessions, 3 runs per arm, Sonnet --effort high) had main slower on vscode (+9%) and django (+20%), with Reads in 2 of 3 vscode runs where v1.6.0 had none. Replaying every query the agents issued through both builds showed main returning 15% less source on vscode (and 1–8% less elsewhere) for identical queries. There were three causes:

  1. Named gaps (codegraph_explore: a trimmed file's elided symbols are never named, so the payload silently drops the answer and the footer's advice is circular #1711) displaced source. Every cluster fit measured the gap names, which on a long path run to hundreds of characters. A class shrunk into its file's room overran the room by the names alone and was dropped whole. vscode's rpcProtocol.ts emitted 222 of 5,496 funded chars (v1.6.0: 5,194), and the new fixture came back as an empty code fence. Fits now measure bare gaps; names are added at assembly from what the file's budget has left.
  2. Interface members (TypeScript: interface members are never indexed (method_signature/property_signature missing from the extractor) — every call through a .d.ts platform API is invisible #1638) corroborated English words. The NL-stopword guard lets a bare word seed a definition when another query word names a symbol in the same file. readonly host in an options interface made "main" seed main() in agentHostServerMain.ts, which took the named-first tier from the real answer files (9 symbols found instead of 32). Members declared inside an interface no longer count.
  3. The interface-dispatch note fired through any common base. "extension → runtime dispatch to 2706 types implementing Disposable" opened 9 of 31 vscode answers, because Disposable never declares extension. A supertype now has to declare the member: itself, an ancestor, or a same-named Swift extension. When that can't be judged (Swift protocol requirements aren't indexed), the note stays as before.

Validation

Replayed agent queries, total source lines (main → this PR, v1.6.0 in parentheses):

repo main this PR v1.6.0
vscode 10,744 12,577 12,617
excalidraw 13,065 13,698 13,764
django 8,420 8,803 8,954
tokio 13,618 14,151 14,600
okhttp 6,511 6,883 7,090
alamofire 7,019 7,249 7,120
gin 4,348 4,353 4,493

No query is below 60% of v1.6.0 (main had 2). Real dispatch notes are unchanged: Alamofire adapt/retry → RequestInterceptor and asURLRequest → URLRequestConvertible, django, okhttp. vscode's bogus Disposable notes are replaced by real declarers (send → IProtocolTransport, call → IServerChannel).

Tests: new explore-named-gap-budget.test.ts, plus cases in dynamic-boundaries.test.ts and explore-nl-stopword-collision.test.ts; 5 of the new cases fail on main. All 27 explore-related suites pass (348 tests). A confirming agent A/B of this PR together with #2055 against v1.6.0 is running; results will be posted here.

🤖 Generated with Claude Code

The pre-release agent A/B (main vs v1.6.0, 7 README repos, 3 runs each)
had main returning 15% less source on vscode for the same queries, with
Reads in 2 of 3 runs where v1.6.0 had none. Replaying every query both
builds' agents issued traced it to three causes:

1. Named gaps (#1711) displaced source. Every cluster fit measured the gap
   names, which on a long path run to hundreds of chars, so a class shrunk
   into its file's room overran it by the names alone and was dropped
   whole: rpcProtocol.ts emitted 222 of 5,496 funded chars (v1.6.0: 5,194),
   and the test fixture came back as an empty fence. Fits now measure bare
   gaps; names are added at assembly from what the file's budget has left.

2. Interface members (#1638) corroborated English words. The NL-stopword
   guard lets a bare word seed a definition when another query word names a
   symbol in the same file; `readonly host` in an options interface made
   "main" seed `main()` in agentHostServerMain.ts, which took the named-first
   tier from the answer files. Members declared inside an interface no
   longer count as corroboration.

3. The interface-dispatch note announced dispatch through any common base,
   declared or not: "`extension` -> runtime dispatch to 2706 types
   implementing Disposable" opened 9 of 31 vscode answers. A supertype now
   has to declare the member (it, an ancestor, or a same-named Swift
   extension). When absence can't be judged, because a protocol's
   requirements aren't indexed, the note stays as before.

Replayed queries, source lines vs v1.6.0 (main -> fixed): vscode 10,744 ->
12,577 (12,617), excalidraw 13,065 -> 13,698 (13,764), django 8,420 ->
8,803 (8,954), tokio 13,618 -> 14,151 (14,600), okhttp 6,511 -> 6,883
(7,090), alamofire 7,019 -> 7,249 (7,120); no query below 60% of v1.6.0.
Real dispatch notes (Alamofire adapt/retry/asURLRequest, django, okhttp)
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry
colbymchenry merged commit 66aebd1 into main Sep 27, 2026
@colbymchenry

Copy link
Copy Markdown
Owner Author

Confirming A/B (this PR + #2055 vs v1.6.0, same 7 README repos, 3-turn sessions, 3 runs per arm, Sonnet --effort high, CLI blocked, daemons pre-warmed):

repo Read (this / v1.6.0) Grep explore calls (median) cost (median)
vscode 0,0,0 / 0,0,0 0 / 0 6 / 5 $1.15 / $1.08
excalidraw 0,0,0 / 0,0,0 0 / 0 4 / 5 $1.03 / $1.13
django 0,0,1 / 0,0,0 0 / 0 3 / 4 $0.64 / $0.81
tokio 0,0,0 / 0,0,0 0 / 0 4 / 4 $1.06 / $0.92
okhttp 0,0,0 / 0,0,0 0 / 0 2 / 3 $0.60 / $0.84
gin 0,0,0 / 0,0,0 0 / 0 2 / 2 $0.47 / $0.62
alamofire 0,0,0 / 0,0,1 0 / 0 6 / 5 $0.96 / $0.94

main before these fixes had Reads in 2 of 3 vscode runs (5 and 2); with them it has 0 of 3.

Wall-clock from this pass is not usable: the machine was shared with another workload (load average ~20), and the slow arms' API time ballooned. Alamofire run 1 took 513s against 1,801s of API time, at the same cost as its 68s run. Explore latency measured directly on the same index is identical between main and this build (vscode median ~4.1s, alamofire ~110ms, interleaved runs), so this change adds no codegraph time. The first pass, on a quieter machine, had main 5% faster than v1.6.0 overall.

bompus added a commit to bompus/codegraph that referenced this pull request Sep 28, 2026
The fork keeps its kernel-only extraction and resolution, its synthesis
refresh on incremental sync, and its migrations v10-16. Upstream's TS
resolver files (name-matcher, js-builtins, cpp-constructor,
cpp-macro-visibility, synthesis-stage) and the Scala wasm grammar stay
deleted. The kernel walkers carry the ported upstream kernel changes:
NodeIdAllocator column suffixes, Python body docstrings, Scala objects as
modules, the C++ constructor and macro-name helpers, and the TS/JS
expression-receiver and curried-wrapper cases.

Taken from upstream:
- read-only CodeGraph.open/openSync (colbymchenry#1963), the shared project lease for
  MCP engines, and the query-pool and daemon fixes
- sync throws LockUnavailableError when the file lock is held (colbymchenry#2014);
  open() destroys the instance if its sync fails
- watcher lock-contention re-arm, explore miss diagnostics, the Scala
  grammar bump (colbymchenry#1823), and the vitest run-TMPDIR setup (colbymchenry#1879)
- pending-reference recovery counts on SyncResult and in `codegraph sync`
  output (colbymchenry#1360), ported onto the fork's orphan sweep
- `this.#field.method()` call edges (colbymchenry#1987), ported into the kernel's
  field-type matcher and method-call receiver pattern

Fork-side adjustments:
- edges keep the fork's `ORDER BY kind, id`. Upstream's
  `kind, target, line, col` tie-break (and a position-first variant)
  dropped CG-36's answer cluster and colbymchenry#1711's named gaps; the db-perf
  ordering test now checks kind precedence and insertion-order ties
- a gap marker that can't afford its full name list names fewer symbols
  plus `+N more` instead of going bare (colbymchenry#2057 budgeting kept)
- the lease catch-up sync defers synthesis, as the fork's engine did
- enableWatcherAfterHandover nudges the existing lease instead of
  acquiring a second one
- dropped idx_edges_synthesis_site from the bulk-load index list (the
  fork schema has no such index)
- Python and Go function refs stay bare-named in both the kernel and
  the generic extractor until colbymchenry#1820 is ported to both
- tests: the colbymchenry#1963 daemon test waits for attach before connecting; the
  Windows zip-archive cases run only where `zip` exists; the watcher and
  generated-flag tests keep their fork fixtures
- golden dumps re-baselined; torture-multilang now covers 42 files

Port follow-ups (tests skipped with a "kernel port pending" note):
- colbymchenry#1820 receiver-path function refs (Python, Go)
- colbymchenry#1838/colbymchenry#1839/colbymchenry#2035 C++ macro names and constructor calls
- colbymchenry#1932/colbymchenry#2034 alias-binding object member boundaries
- Scala companion resolution; colbymchenry#1857 Go bare calls; colbymchenry#1986 ABI evidence;
  expression receivers; HAR excluded imports; value+interface pairs
- colbymchenry#2033 synthesis_inputs convergence model (7 tests)
- colbymchenry#2043 stale-refusal banner policy needs a decision
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