feat(lsp): Phase 1 — bounded ingress, cancellation ownership, client gating#3976
feat(lsp): Phase 1 — bounded ingress, cancellation ownership, client gating#3976rossirpaulo wants to merge 2 commits into
Conversation
…gating Rust (stacked on the Phase 0 core): - IngressScheduler (transport-neutral admission/lifecycle/cancellation/ budget state machine) relocated into baml_lsp_server, with fixes: forced cancellation of protected methods can no longer be downgraded; permanent oversize is contained per-message (-32803) instead of closing the session; oversized $/cancelRequest no longer livelocks the control lane; pipelined `initialized` is admitted FIFO and accepted at dispatch. - New LspRuntime: stdio and browser LSP sessions dispatch through one scheduler; cancellation drained on the transport thread claims responses while handlers run ($/cancelRequest -> -32800); browser takeover revokes the old session's sink and replays document overlays; bounded outbound budgets with serialize-once shared frames. - Bounded stdio framing: 16 KiB header cap; oversized bodies discarded in chunks with a recoverable typed error; abnormal exit is nonzero. - bex_project kept minimal: LspError::RequestCanceled (-32800) at the one serialization boundary (I7); dispatch-scoped cancellation token checked at handler entry and after the source guard (never Salsa unwinding); connection-scoped LSP sessions (fresh encoding negotiation + workspace roots per connection, shared project registry). TypeScript: - D6 playground run-gating on is_bex_current: Run/test/preview disabled with a "Preparing current build..." state during rebuild windows; projectNotReady rejections render as that transient state and clear on the next current ProjectUpdate (no protocol version bump). - WebSocketRuntimePort reconnect correctness: fail-closed hello, bounded first-connect queue, session-scoped commands dropped on disconnect with synthesized rejections, desired-runtime lease resent after every hello. - Track A: projectRoots.ts canonical multi-root ownership + routing in the VS Code extension; closing the last document no longer stops the owner server; aggregate status preserves startup failures. Verification: baml_lsp_server 67/67, bex_project 45/45 (+1 pre-existing ignored), clippy -D warnings clean, wasm check clean; playground 125/125, extension 13/13, webview 19/19, all typechecks green. Harness (400 fns): $/cancelRequest now answers -32800; typing inlay p50 2.0 ms, p95 51.7 ms, zero errors, zero unanswered requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VEMxti5WQ8bCgJUuX9AQvo
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
⏭️ Performance benchmarks were skippedPerf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to To run them on this PR, do any of the following, then push a commit (or re-run CI):
|
Binary size checks failed❌ 1 violations · ✅ 6 passed
Details & how to fixViolations:
Add/update baselines:
[artifacts.bridge_wasm]
file_bytes = 14760377
gzip_bytes = 4184019Generated by |
Drive-less file:///tmp URIs fail Url::to_file_path on Windows, so canonical_document_identity_str returned None and the test panicked on the Windows CI runner only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VEMxti5WQ8bCgJUuX9AQvo
Stacked on #3969 (Phase 0). Do not merge before it; GitHub will retarget this to
canarywhen #3969 lands. Together the two PRs deliver the LSP latency/mutex design (docs/design/lsp-latency-and-mutex-fix.md) through Phase 1; Phase 2 (salsa snapshot reads) remains behind the design doc's spike gate.Summary
Rust — bounded ingress and cancellation (Track B2/D2/D3)
baml_lsp_serverwhere transport scheduling belongs): transport-neutral admission classes, lifecycle barriers, per-class byte/item budgets, response-ownership tokens, didChange tail-coalescing, browser takeover. Ported with fixes for every defect verified in review:NonCancellableWhenRunningoninitialize/shutdown/executeCommandcan no longer be downgraded by a caller policy (stronger_cancellation);-32803, notifications drop with a warn; the session and process stay alive;$/cancelRequestno longer livelocks the control lane;initializedduringInitializeRespondingis admitted FIFO and accepted at dispatch (found via the latency harness; the reference dropped it, breaking pipelining clients)./api/lspsessions dispatch through one scheduler behind a process-owned mutex;$/cancelRequestis drained on the transport thread so it can claim a response while a handler runs — queued requests answer-32800immediately, running cancellable requests observe a dispatch-scoped token (checked at handler entry and immediately after the source guard; never connected to salsa unwinding). Browser takeover atomically revokes the old session's sink (tombstone), purges its pending responses, and replays document overlays.Arc<[u8]>frames with a shared budget charge (accounting equals actual memory); per-session outbound budgets;Lagged/saturation close only the affected session; responses exceeding the reservation are replaced with a per-request-32803, never a session kill.LspError::RequestCanceled→-32800at the single serialization boundary (I7); connection-scoped LSP sessions (fresh position-encoding negotiation and workspace roots per connection, shared project registry) — fixes the global once-only encoding negotiation flagged in review.TypeScript — client gating (D6, D2 subset, Track A)
is_bex_currentis false (or a run/preview was refused withprojectNotReady), Run/test/preview actions disable with a "Preparing current build…" status and re-enable on the next currentProjectUpdate. No protocol version bump; closes the fail-closed D7 UX window refactor(lsp): Phase 0 latency and mutex containment #3969 introduces. Stale "using last successful build" copy removed.projectRoots.ts(canonical/symlink-aware root resolution, nested projects, multi-workspace) + routing in the extension — excluding two review-verified reference bugs: closing the last.bamldocument no longer stops the owner server (kept the playground webview alive), and aggregate status preserveserrorwhen every client start fails.Known limits (deliberate)
ensureProjectRuntime/releaseProjectRuntimeclient plumbing is dormant: typed, unit-tested, nothing sends them yet — the seam for the demand-gating server work (D5).didOpenin the same instant asinitialized(no ordering gap) can still have that notification dropped at admission — contract-conformant per the design doc; VS Code never does this.pendingTestTargetdeep-link auto-run is converted to the preparing state during a rebuild window but not auto-retried.Validation
cargo test --lib -p baml_lsp_server67/67 (22 scheduler, 9 runtime, 7 framing/budget, plus existing) ·-p bex_project45/45 (+1 pre-existing ignored)cargo clippy -p baml_lsp_server -p bex_project --all-targets -- -D warningsclean ·cargo check --target wasm32-unknown-unknown -p bridge_wasmclean ·cargo fmt --checkclean/tmp/baml-inlay-repro, 400 fns, release build of this branch):$/cancelRequestnow answers-32800(Phase 0: completed normally); typing inlay p50 2.0 ms / p95 51.7 ms; zero request errors; zero unanswered; 10-edit burst → 0 refresh storms.🤖 Generated with Claude Code
https://claude.ai/code/session_01VEMxti5WQ8bCgJUuX9AQvo