Skip to content

fix(pool): an [lsp] restart can no longer land on a superseded config - #701

Merged
atlas-from-plumb merged 6 commits into
mainfrom
atlas/pool-reconfig-windows
Oct 16, 2026
Merged

atlas-from-plumb merged 6 commits into
mainfrom
atlas/pool-reconfig-windows

Conversation

@atlas-from-plumb

Copy link
Copy Markdown
Collaborator

What

The three MINOR windows PLAN-539's review left open, plus PLAN-538's log wording:

  • (b) stale resolve. reconfigureEntry resolves its config outside p.mu, so a reconfigure delayed past a newer config's whole restart could restart the server back onto the old config. It now reads the pool's config generation (langsGen, bumped after every publish) before resolving and re-checks it under p.mu; one that moved means a newer config was published, and it re-resolves. A publish that lands between the check and the restart is still caught by the again-flag, because every publish is followed by a reconfigure pass.
  • (a) one resolution. entryConfig/resolveLanguages return the effective config and the project file's validity from one resolution, replacing projectConfigInvalid + cfgForWorkspace, which could straddle an edit.
  • (c) no wake for a superseded config. A round that ends with another already requested hibernates and installs, but does not wake (costly for jdtls). The next round wakes, or wakeHeld does at the end when no further restart is needed. wakeHeld leaves an entry a newer restart owns.
  • Wording. A resync's restart logs "for a resync after lost file events", not "for its new config".

Verification

  • pool_reconfigure_window_test.go:
    • one resolve per reconfigure;
    • a stale resolve after a newer restart finished (driven through a nil-in-production hook in the window) leaves the newer config;
    • a superseded round logs a deferred wake and ends running on the newest config;
    • a deferred wake is not lost when no restart follows;
    • wakeHeld leaves an entry a newer restart owns, with its control.
  • TestWatcherLost_TheResyncSaysItIsOne.
  • Mutation: 9 mutants (gen check, both wake paths, the wakeHeld guard, two resolves, invalid ignored, resync wording), all KILLED. Existing reconfigure and resync suites pass under -race.

🤖 Generated with Claude Code

atlas-from-plumb added a commit that referenced this pull request Oct 16, 2026
Review of #701: when the change that deferred a round's wake disabled the
language, nextRestart found nothing to restart (the entry was hibernated) and
wakeHeld woke the disabled server anyway. wakeHeld now resolves the config and
refuses a disabled language. Also: only a restart's first round carries a
resync's reason (later rounds are config changes), wakeHeld logs its wake, and
the langsGen comment documents its role as the reconfigure staleness token.
Tests pin every wakeHeld guard, a single round waking at once, the config
reason, and an invalid project file during a waiting round.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
atlas-from-plumb added a commit that referenced this pull request Oct 16, 2026
…gure does

Re-review of #701: wakeHeld resolved the config outside p.mu, so a disable
published in between could still be woken. It now re-resolves when the
generation moved, exactly as reconfigureEntry does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
atlas-from-plumb and others added 6 commits October 16, 2026 14:05
PLAN-539's three MINOR windows, and PLAN-538's log wording:
- reconfigureEntry checks the pool's config generation under p.mu after its
  unlocked resolve and re-resolves when it moved, so a stale want can no
  longer restart a server back onto an old config after a newer restart
  finished;
- the effective config and the project file's validity come from one
  resolution (resolveLanguages / entryConfig) instead of two that could
  straddle an edit;
- a restart round that ends with another already requested defers its wake to
  that round (wakeHeld wakes it at the end if none needs a restart), so a
  superseded config's server is never started;
- a resync's restart is logged as a resync, not as a config change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review of #701: when the change that deferred a round's wake disabled the
language, nextRestart found nothing to restart (the entry was hibernated) and
wakeHeld woke the disabled server anyway. wakeHeld now resolves the config and
refuses a disabled language. Also: only a restart's first round carries a
resync's reason (later rounds are config changes), wakeHeld logs its wake, and
the langsGen comment documents its role as the reconfigure staleness token.
Tests pin every wakeHeld guard, a single round waking at once, the config
reason, and an invalid project file during a waiting round.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…gure does

Re-review of #701: wakeHeld resolved the config outside p.mu, so a disable
published in between could still be woken. It now re-resolves when the
generation moved, exactly as reconfigureEntry does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@atlas-from-plumb
atlas-from-plumb force-pushed the atlas/pool-reconfig-windows branch from b8cfe6e to 16c50a5 Compare October 16, 2026 03:06
@atlas-from-plumb
atlas-from-plumb merged commit 145599f into main Oct 16, 2026
9 checks passed
@atlas-from-plumb
atlas-from-plumb deleted the atlas/pool-reconfig-windows branch October 16, 2026 03:24
atlas-from-plumb added a commit that referenced this pull request Oct 16, 2026
Hooks stay inside their session's namespace (#698); context-hint uptake credits
a hint's use on a connection its session alone declared (#699); the shared file
watcher leaves linked git worktrees out (#700); an [lsp] restart can no longer
land on a superseded config (#701).


Plumb-Session: forest-ridge

Co-authored-by: Claude Opus 5.5 <[email protected]>
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.

2 participants