Skip to content

fix(vscode): adopt a same-key pending lint runtime instead of restarting it - #35

Merged
fi3ework merged 1 commit into
mainfrom
worktree-binary-painting-tarjan
Aug 25, 2026
Merged

fix(vscode): adopt a same-key pending lint runtime instead of restarting it#35
fi3ework merged 1 commit into
mainfrom
worktree-binary-painting-tarjan

Conversation

@fi3ework

Copy link
Copy Markdown
Member

Summary

Fixes a startup teardown race reported from a real user log: at activation, the first lint worker was SIGTERMed ~1.1s after spawn — while the LSP initialize was still in flight — then an identical runtime (same core, same shim, same runtime key) started successfully right after. vscode-languageclient force-notifies "Server initialization failed" / "couldn't create connection to server" when its in-flight initialize is severed (verified against vscode-languageclient 9.0.1; those toasts cannot be suppressed via client options), so users saw scary error popups for what was a self-healing restart.

Root cause: RuntimeManager.reconcile() swept the document's pending runtime uses before resolving what the new pass would land on. The register-time reconcile, a detection pass and didOpen routinely land inside one worker startup window, so the second reconcile dropped the not-yet-bound entry's refcount to zero and tore the runtime down mid-initialize, even though it then resolved to the very same key.

Fix (both ahead-of-upstream, recorded in AGENTS.md to offer back on the next sync):

  • RuntimeManager.reconcile now resolves the document's core first (planDocumentCore); the pending-use sweep keeps a pending entry whose key matches the plan, adopting the in-flight start instead of restarting it. A key change still cancels the pending start immediately (a hung start cannot block the per-document tail), and a superseded operation leaves the entry's fate to the newest reconcile's sweep.
  • Rslint.close() gives a still-Starting client one bounded chance (2s, shared CLOSE_SETTLEMENT_TIMEOUT_MS) to settle before tearing down the transport, covering the legitimate mid-start closes (document closed during start, core key changed). The pre-teardown wait subsumes the old post-dispose start wait, so a hung start pays the bound once. Planned aborts (close/supersede) are no longer logged as errors.

Verification: new regression suite tests/stacks/lint/runtimeManager.test.ts (same-key adoption — red before the fix; changed-key immediate cancel; document-close release), pnpm lint (type-checked) clean, full unit suite green, lint E2E slice (13 suites) all passing.

Related Links

  • Upstream RuntimeManager (web-infra-dev/rslint packages/vscode-extension) carries the same sweep ordering; both fixes are candidates to send upstream on the next sync.

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

…ing it

At activation the register-time reconcile, a detection pass and didOpen
routinely land inside one lint worker startup window. reconcile() swept the
document's pending runtime uses before knowing what the new pass would
resolve to, so the second reconcile tore down the not-yet-bound runtime
mid-LSP-initialize (SIGTERM), vscode-languageclient force-notified "Server
initialization failed" / "couldn't create connection to server", and an
identical runtime was then started from scratch.

- RuntimeManager.reconcile now resolves the document's core first
  (planDocumentCore) and the pending-use sweep keeps a pending entry whose
  key matches the plan — the reconcile adopts the in-flight start. A key
  change still cancels the pending start immediately, and a superseded
  operation leaves the entry's fate to the newest reconcile's sweep.
- Rslint.close() gives a still-Starting client one bounded chance (2s) to
  settle before tearing down the transport, so legitimate mid-start closes
  (document closed during start, core key changed) stop cleanly instead of
  triggering the same forced toasts; planned aborts are no longer logged as
  errors. The pre-teardown wait subsumes the old post-dispose start wait.

Both are ahead-of-upstream fixes, recorded in AGENTS.md to be offered back
on the next sync. Regression-locked in tests/stacks/lint/runtimeManager.test.ts.
@fi3ework
fi3ework merged commit 594efda into main Aug 25, 2026
3 checks passed
@fi3ework
fi3ework deleted the worktree-binary-painting-tarjan branch August 25, 2026 07:59
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