Skip to content

feat(upstream): catch up to cd04b21a, batch 3c — incremental-sync fixes - #600

Merged
bompus merged 5 commits into
fork/consolidatedfrom
merge/upstream-cd04b21a-b3c
Oct 31, 2026
Merged

bompus merged 5 commits into
fork/consolidatedfrom
merge/upstream-cd04b21a-b3c

Conversation

@bompus

@bompus bompus commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Summary

Ports the four upstream fix(sync) commits into the fork's retained TypeScript sync surfaces (the fork's src/codegraph.ts sync path uses the WAL-valve/closeKernel teardown; ports are semantic, not text).

  • 39a835cc — tsconfig/jsconfig/go.mod change re-resolves governed imports (80f6b505)
  • 494802b1 — barrel re-export retarget re-links importers (66449255)
  • e6b66d9c + 32714510 — lone-candidate call re-links after delete/rename (2d98c1b6; fixture adapted to an unannotated JS receiver, any-annotated receivers are terminal in the kernel) plus a docs hunk the earlier commit dropped
  • 71b65ae6 — a skipped file that now indexes re-links its importers (4b0f72b7)

Validation

  • npm run build: pass; tsc --noEmit: clean
  • Targeted: 4 new test files + all *sync* neighbors — 31 files, 322 tests pass; 80f6b505 verified fail-then-pass (4 fail on parent, 7/7 after)
  • Full suite: 957 files, 11,344 passed / 46 skipped / 0 failed
  • Golden dumps unchanged in suite; eval:precision not run — sync-trigger/invalidation changes, not resolution semantics
  • CHANGELOG: all four upstream bullets verbatim. README rows checked: feature table, parsing/resolution rows, fix paragraphs — none stale; merge pointer left at 60b0d7a9 per brief

…rts it governs

Ported from upstream 80f6b50.

Upstream's resolver read tsconfig / jsconfig `paths` and `baseUrl` and
go.mod's module path once and kept the maps for its lifetime; the fork's
resolver does the same, and hands those maps to the kernel resolver at
construction, so an edit to one kept resolving imports by the old
mapping until a re-index. The index now carries a digest of every config
file resolution read — every tsconfig / jsconfig the alias loaders
opened (`extends` chains and nested scoped configs included), every
go.mod, and the root's candidates whether present or not. Every sync
reads them again; when one differs the maps are dropped (with the
kernel, which took them at construction), every import the file governs
is re-opened for the orphan sweep, and synthesis is marked for a
rebuild. An index an older engine stamped records a snapshot without
re-opening anything.

Fork adaptations: `loadScopedAliases` (the fork's nested-config
mechanism, where upstream used per-directory alias loads) threads the
same `read` set through so scoped config files join the snapshot;
`resetModuleConfig` also drops the fork's `aliasScopes` and closes the
kernel resolver, as `refreshWorkspacePackages` already does; the sync
block runs under the fork's WAL-valve/kernel teardown pattern and marks
synthesis via `setSynthesisPending` (upstream's `synthesis_pending`
metadata).

Validation: the ported upstream test file passes 7/7; the 4
tsconfig-retarget cases failed before the port.
…orters

Ported from upstream 6644925.

`c.ts` imports `foo` through `index.ts`, which re-exports it from `a.ts`.
Change the clause to `./b` and sync, and `bar` kept calling `a.ts:foo`
where a fresh index gives `b.ts:foo`. The barrel declares nothing of
`foo`, so the definition delta has nothing to rebind; no file was added
for the walks below a barrel or the added-files pass; the binding never
failed, so the retries never see it; and the name-matched-import pass
reads only bindings a name match took.

The orchestrator now reports the modified ES modules whose `export …
from` statements changed, read off their import nodes (named by the
module, with the statement as signature — the same shape the kernel
emits) before and after the store. Their importers' bindings through
them go back for the sweep, up through the modules that pass them on in
turn, with the failures parked under them and the edges into the members
of what the bindings pointed at. It is the importer walk the
removed-files pass uses, run after the name-matched-import pass.

Fork notes: the trigger is independent of the fork's `exportDelta`
(exported-name set), which flags the same edits but feeds the
name-matched pass only. Before the port, the ported test's
through-a-second-barrel case failed; the shallower cases were already
covered.

Validation: 9 tests; the second-barrel case failed before, all pass now.
…ename

x.go() with go in two classes stays unlinked while both exist. A sync that
deleted one of them, or renamed it away, never retried that call: the
failed-reference retry looks refs up by the names the changed files carry
now, which a deleted file or a renamed-away name no longer has, and a sync
that only deletes files skips the retry. Failed refs under the names a
sync's definitionDelta lost or gained now go back to pending for the
sweep, next to the CG-33 rebind that does the same for resolved edges,
under the same per-name ceiling. CODEGRAPH_NO_REBIND=1 turns both off.

The ported fixture drops the receiver's `any` annotation and the Python
case moves to JavaScript: the kernel claims an annotated receiver as
terminal unknown-receiver and never name-matches an untyped Python member
call, so only an unannotated receiver reaches the score-tie/unique-name
scan the upstream resolver ran for both.

Ported from upstream 2d98c1b.
A file over the source-size limit, or one whose parse failed, is tracked
with no nodes and its error recorded, so the imports of it fail. When an
edit lets it be indexed, sync stored it as a modified file, and only an
added file is looked up by what it can be imported as: an unchanged
importer's import of the file itself stayed failed, and so did what a
barrel passes on from it, until a full re-index. Named imports and calls
bound, since they wait by name.

A re-indexed file whose stored row had no nodes and recorded errors now
joins addedFilePaths once the store gives it nodes, so the added-file
retries and the importer and barrel walks run for it. It still counts as
modified. Deciding after the store leaves a file that changes but stays
over the limit, or still fails, alone, so it does not re-run those
retries on every sync.

Ported from upstream 4b0f72b.
Ported from upstream 2d98c1b — the docs hunk that e6b66d9 left out.
@bompus bompus added the author:cognition Pull request written by a cognition model label Oct 30, 2026

@bompus-ai-review bompus-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bompus AI Review (GPT-6.1 Sol, xhigh reasoning): 4 finding(s).

Comment thread src/resolution/module-importers.ts Resolved
Comment thread src/codegraph.ts Resolved
Comment thread src/codegraph.ts Resolved
Comment thread src/resolution/index.ts Resolved
@bompus

bompus commented Oct 31, 2026

Copy link
Copy Markdown
Owner Author

Disposition on the four medium findings — each describes a real edge case, but each is present in the upstream implementation this PR ports, so the port is faithful rather than regressive. Recorded here as follow-up candidates rather than changing parity mid-port:

  1. Inherited member calls after a barrel retarget — upstream 66449255 invalidates via getOutgoingEdgesFrom([...bound], ['contains']) only (direct members, no inherited walk). A extends BaseA / B extends BaseB swap keeps the stale BaseA.run edge upstream too.
  2. Cross-process cached aliases — upstream's changed check is the same disk-vs-getMetadata compare inside one process; no per-resolver fingerprint exists upstream (src/index.ts + resolution/index.ts at 80f6b505). A second process's stamp leaves another daemon's caches stale in upstream as well.
  3. Stamp TOCTOU — upstream stampModuleConfigSnapshotIfMoved() also snapshots disk state at stamp time (getModuleConfigSnapshot re-reads files when called), so a config changed mid-sync is recorded-but-unprocessed upstream too.
  4. Absent nested config candidates — upstream records only configs actually read: moduleConfigFiles gains a go.mod only if (found) and only files loadProjectAliases opens, so a nested config created later is equally invisible upstream.

Follow-up direction if pursued: report or fix upstream first (all four fixes would diverge the port otherwise); the smallest fork-side improvements are (4) recording probed-but-absent candidates and (1) extending member reopen to inherited members.

@bompus
bompus merged commit 962e657 into fork/consolidated Oct 31, 2026
3 checks passed
@bompus
bompus deleted the merge/upstream-cd04b21a-b3c branch October 31, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author:cognition Pull request written by a cognition model

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant