Skip to content

feat(pi-lsp): share Server Instances across sessions in one Pi process - #475

Merged
ian-pascoe merged 2 commits into
mainfrom
feat/465-share-server-instances
Oct 19, 2026
Merged

ian-pascoe merged 2 commits into
mainfrom
feat/465-share-server-instances

Conversation

@ian-pascoe

Copy link
Copy Markdown
Owner

Summary

Each Pi session built its own LspServerManager, so every Minimal Subagents child (and Advisor session) that touched code cold-started its own tsc --lsp (about 8–10 CPU-s, 650–900 MB RSS) and oxlint for the same workspace root as its parent. Now the sessions in one Pi process share Server Instances through a pi-lsp-private pool, and each session holds a Server Instance Lease on the instances it uses.

globalThis[Symbol.for("@ian-pascoe/pi-lsp/server-instance-pool.v1")]   # pi-lsp only; no other package imports it
  key = [pi-lsp version, serverId, root, command, args, env, initializationOptions,
         settings, lsp.timeouts, maxConcurrentDiagnostics]
  entry: client | shared startup, leases, failure state, stderr (tmp, 0700/0600), #464 limiter

session A ──lease──┐
session B ──lease──┼──▶ one process per key
child C   (no lease until first use)
 LspServerManager (per session: routing, enablement, known routes, previews, session files)
-  ensureClient → LspServerClient.start(…session callbacks…)
+  ensureClient → pool.acquire(key, holder, start)    # lazy lease; joins a running or starting instance
-  stopServer / restartServer → client.shutdown()
+  stopServer / restartServer → pool.stop(key)        # stops for every leaseholder; each restarts lazily
-  shutdown → stop every client
+  shutdown → release every lease                     # the last release stops the process
+  disable  → release this session's leases only
 workspace/applyEdit (always unsolicited)
+  one leaseholder → held preview in that session; two or more → applied:false (cannot attribute)
 lsp_status / /lsp
+  "… shared with N other sessions" + shared_with_sessions (omitted when N = 0)

Decisions: ADR-0005. Glossary: Server Instance redefined, Server Instance Lease added, and the behavior boundary updated. A session that holds no lease shows an instance as stopped, even while other sessions run it.

Closes #465

Evidence

  • Before: two pi-lsp sessions in one process with the same cwd and settings → 2 launches of the fake server for each Server Definition and root.
    After: test/lsp-shared-server-instances.test.ts (two extension instances, fake server logging launches):

    same cwd + settings                         → 1 launch
    different settings value / project override → separate launches
    concurrent cold routes; trigger session shuts down mid-start → 1 launch, other session gets running client
    child shutdown → parent keeps instance; last shutdown → process exits; unused session takes no lease
    /lsp stop in A → process dead for both; next use relaunches
    crash → unavailable in both; lsp_restart from either → recovered for both
    lsp_restart while initialize is delayed → fresh running instance, no failure state
    disable in A → B keeps using the process
    unsolicited applyEdit: 1 leaseholder → preview; 2 → server logs {applied:false, failureReason:"…cannot attribute…"}
    stderr survives starter shutdown, removed on stop; dir 0700, file 0600
    lsp_status → "shared with 1 other session"; single-session text byte-identical
    maxConcurrentDiagnostics: 1 across two sessions → peak 1 in flight
    stop with a missing definition → lease released, no orphan
    
  • Independent two-axis review (standards and spec): the possible bugs, test gaps and cleanups it found are fixed in 54f353b.

  • pnpm --filter @ian-pascoe/pi-lsp test: 523 passed. pnpm test:root: 15 passed. pnpm verify passes.

Merge Danger

Door: two-way

The pool lives in process memory and no persisted format changes. Reverting restores one manager per session.

Blast Radius: lsp-lifecycle

  • /lsp stop and lsp_restart now affect every session using the instance, and a crash in one session's request makes the instance unavailable in all of them.
  • Server-initiated edits are declined while two or more sessions share an instance.
  • Server stderr files move from the session directory to a temp directory for each Server Instance.
  • Two copies of pi-lsp at the same version but with different code, such as a linked dev checkout and a published copy, share instances. This limitation is accepted and recorded in ADR-0005.

Sessions in one Pi process that resolve the same Server Definition
configuration and workspace root now lease one language-server process
from a pi-lsp-private pool on globalThis, instead of each starting its
own. Leases are taken on first use and released at shutdown, on
disable, or when the instance stops; the last release stops it. Stop
and restart act for every leaseholder, failure state is shared,
server-initiated edits are previewed only for a sole leaseholder, and
stderr files belong to the instance. Status counts other leaseholders.

Closes #465
…out of failure state

- stopServer releases the session's lease even when the Server Definition
  is gone, instead of dropping it while it kept the instance alive.
- A startup a Stop or the last release aborts is no longer recorded as a
  startup error, so a request waiting on it reports the instance stopped
  rather than unavailable with the abort error.
- The pooled start closure captures only the instance's configuration and
  the start function, not the manager, so a shut-down session is not
  retained by a startup it triggered.
- Declare the server edit handler once and derive LspServerStartInput from
  LspPooledStartInput; derive every lease accessor from one state cascade.
- Cover the pool-owned stderr modes and the server's applyEdit replies;
  correct ADR-0005 on key ordering and same-version copies.

Refs #465
@ian-pascoe
ian-pascoe merged commit 47b232c into main Oct 19, 2026
3 checks passed
@ian-pascoe
ian-pascoe deleted the feat/465-share-server-instances branch October 19, 2026 16:10
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.

pi-lsp: share Server Instances across sessions in one Pi process

1 participant