Skip to content

fix(bridge): carry external rewrites of open files through the reused tsserver program (#74) - #75

Merged
johnsoncodehk merged 3 commits into
masterfrom
cursor/issue-74-query-sync-external-changes-a06b
Sep 26, 2026
Merged

johnsoncodehk merged 3 commits into
masterfrom
cursor/issue-74-query-sync-external-changes-a06b

Conversation

@johnsoncodehk

@johnsoncodehk johnsoncodehk commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Fixes #74.

Root cause

Under tsserver, __tnbIsProgramUptoDate keeps the thin program across updateOpen and reload. createTsgoProgram's overlay collect therefore never reruns, and the only sync is the query-path pushHostOverlayToTsgo. That function never drained the #49 external-change set (_pendingExternalChangePaths). When an external tool rewrote an open file and the editor re-synced the buffer to the new disk text:

  • the file matched disk, so shouldSendHostOverlay skipped it,
  • no fileChanges.changed was sent, so the no-change fast path returned with no updateSnapshot,
  • Go kept the text it had read first.

As a result, semantic classifications were returned at the old file's offsets, and diagnostics were stale too. For example, a new const bad: string = 1 error was missing.

The reload command had a second gap: TextStorage.reloadWithFileText never raised the signal at all. Only reloadForOpen and editContent did.

The existing #49 tsserver witness missed this because it re-sends through openFiles, which goes through reloadForOpen. It never exercised changedFiles or reload.

Change

  • New drainExternalFileChanges() is the single place that decides the transport. Both updateSnapshot sites call it: the createTsgoProgram collect and pushHostOverlayToTsgo. Host==disk files ride fileChanges.changed. The fast path no longer skips when there are external changes, and the per-file JS caches (tsgoSfCache, node indexes, host SourceFile) are invalidated for those files.
  • reloadWithFileText calls tnbNoteExternalFileChange when the text changed.
  • triage-external-edits tsserver case: runs once per client re-sync (reopen, changedFiles, reload) and compares the rewritten file's encodedSemanticClassifications-full spans with stock, plus the dependent-file diagnostic.
  • AGENTS.md: the accepted-tradeoff entry names the new drain point.

No Go-side change, so no README ledger row.

Verification

Local runs used the fork bundle built from this branch plus the published 6.0.3-bridge.17 bridge.node and vendor/. The Go side is unchanged, and this VM has Go 1.22, not 1.26.

  • Issue repro. The issue's run3.mjs (updateOpen / reload) plus a change control:
    • bridge.17: stale spans (12 6 "文中文 上傳", …).
    • This branch: spans byte-identical to stock typescript@6.0.3, and semanticDiagnosticsSync reports the new TS2322 like stock.
  • triage-external-edits.
    • tsserver: fails on bridge.17 for changedFiles and reload; passes on this branch in all three modes.
    • stablecache: passes.
    • tscwatch: times out at watch block 2 on this VM for both bridge.17 and this branch, so it's environmental, not a regression.
  • Witness matrix. All 85 witnesses across wg0–wg6 pass. VOLAR_ROOT was a local volar checkout at VOLAR_SHA, and sweep-ls-throws reported 0 correlated throw hits.
  • check:sim-nav. diffs baseline=1747 current=1747, new=0, fixed=0. The probed unit count went from 25122 to 25123, and the extra unit is a match.
  • Other gates.
    • volar npm test: 211 passed, 4 skipped.
    • check:sourcefile-guard, check:lib, check:enums, ci-witness-groups.mjs all: pass.
Open in Web Open in Cursor 

cursoragent and others added 3 commits September 26, 2026 02:02
… tsserver program (#74)

tsserver keeps a thin program across updateOpen/reload (__tnbIsProgramUptoDate),
so its steady-state sync is pushHostOverlayToTsgo, which never drained the #49
external-change set: a host==disk rewrite skipped the overlay push and Go kept
its first-read text. Semantic classifications landed at the old offsets and
diagnostics went stale.

Both updateSnapshot sites now drain through drainExternalFileChanges and send
host==disk files as fileChanges.changed, and TextStorage.reloadWithFileText
(tsserver reload) raises the same signal as reloadForOpen.

Co-authored-by: Johnson Chu <johnsoncodehk@users.noreply.github.com>
…n triage-external-edits (#74)

The tsserver case now runs once per client re-sync (reopen, changedFiles,
reload) and compares the rewritten file's semantic classifications with stock,
alongside the dependent-file diagnostic. On 6.0.3-bridge.17, changedFiles and
reload fail.

Co-authored-by: Johnson Chu <johnsoncodehk@users.noreply.github.com>
…NB at the commit under test

Rewriting volar's typescript override to link: the workspace made pnpm
re-resolve every `latest` specifier from scratch. vitest 5.0.2 (published
2026-09-25) pulls why-is-node-running@3.2.2, which pnpm 11's trustPolicy
rejects as a trust downgrade, so every cache-miss build failed at
'Install + build volar' — the v4 cache had been evicted, so this hit every run.

tools/install-volar.sh is now the one recipe (ci.yml, nightly.yml, AGENTS.md):
frozen install of volar's vetted lockfile, the released TNB's store entries
symlinked to this repo, and the release's platform bridges deleted — they
resolve ahead of <repo>/native and would pair this commit's JS with the
pinned release's bridge.node. Cache keys bumped (v5, nightly-v3) for the new
tree layout.

Co-authored-by: Johnson Chu <johnsoncodehk@users.noreply.github.com>
@johnsoncodehk
johnsoncodehk marked this pull request as ready for review September 26, 2026 03:31
@johnsoncodehk
johnsoncodehk merged commit b42cc58 into master Sep 26, 2026
16 checks passed
@johnsoncodehk
johnsoncodehk deleted the cursor/issue-74-query-sync-external-changes-a06b branch September 26, 2026 03:31
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.

tsserver: external file rewrite leaves semantic classifications (and likely diagnostics) stale — residual of #49 on updateOpen/reload path

2 participants