Conversation
Proposes FEATURE_LSP (bit 8, opcode block 0x60): the server hosts warm language-server processes the way it hosts PTYs, acts as each one's sole LSP client, and projects LSP into blit-native records — server phase and capabilities as coalesced state snapshots, diagnostics as per-file replacement sets replayed in full to fresh subscribers, and definition/references/hover/symbols/rename-as-data as nonce pulls through a single query opcode with skippable record kinds. Positions are 0-based lines with UTF-8 byte columns in both directions; the server transcodes to each backend's negotiated encoding against canonical bytes, so no client ever learns UTF-16 exists. Disk is the only document truth and document sync is server-driven: the fssync shared-root watcher feeds didChangeWatchedFiles, and a dirty-fed LRU open set serves open-doc-only servers. Read-only by construction — rename returns its edit plan as data, applyEdit answers applied:false, and every write-shaped feature is gated on the anticipated mutation/buffer RFC. Zero-config discovery walks per-server root policies over a compiled marker table; commands come only from the table or user config, never repository contents. Terminating LSP server-side dissolves the documented multiplexer failure modes (id rewriting, capability intersection, dropped server->client requests, didChange version corruption) by making N=1 by construction, and keeps every client — browser, CLI one-shots, SDK — a thin apply-records-and-ack consumer sharing one warm backend per workspace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coverage
|
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Server-side implementation of FEATURE_LSP (bit 8, opcode block 0x60): warm language-server backends, daemon-owned and keyed by (canonical_root, server_id), shared across connections and surviving client disconnects. blit is the sole LSP client of each child and projects LSP into blit-native records — clients carry no JSON-RPC, no UTF-16, no capability negotiation. New crate blit-lsp: discovery (per-server root policies over a compiled marker table, verbatim-JSON config pass-through), a supervised JSON-RPC stdio client on a non-blocking writer thread, and a projection engine per backend that owns initialize, the dirty-fed LRU open set, the last-per-file diagnostics cache with FULL replay-on-subscribe, and UTF-8-byte <-> negotiated-encoding position transcoding. Server->client requests terminate in blit (workspace/configuration from config, applyEdit refused and counted, progress to phase). A scripted in-process fake LSP server drives the engine tests; verified end to end against clangd (definition, references, hover, symbols, rename-as-data, the edit->diagnose loop, stop-and-respawn). - crates/remote/src/lsp.rs + js/core/src/lsp.ts: wire codecs and the LspStateMirror / LspDiagMirror reducers, byte fixtures pinned across both implementations - crates/server: dispatch, per-connection attachments over the shared registry, feature gating, refusal tests - crates/cli: blit lsp def|refs|hover|symbols|diagnostics|rename|wait| list|stop, NDJSON, 0/1/2 exit codes, learn.md section - js/core: openLsp on BlitConnection with paced state/diag mirrors Positions are 0-based lines with UTF-8 byte columns both directions; LOCATION/DIAG_FILE carry BLAKE3-128 content hashes that compose with FS_SYNC for staleness. Discovery degrades silently when a server is absent, naming it in LSP_OPENED so a client learns what to install. Read-only by construction; write-shaped features gated on the mutation RFC. Applies the fixes from the adversarial review of this branch: drain queued queries on engine stop so no nonce leaks; LSP_STOP drops the SERVER record and a later query respawns; non-blocking child writes; kill-escalation and selective PTY-pid reaping so an LSP child leaves no parked status to collide with a recycled pid; defer didOpen replay past initialize; honor publishDiagnostics version; duplicate-nonce guards; FULL-diag byte budget; sweeper TOCTOU; query-only attachments count against idle; MAX_DOCS floor; diag --wait no longer spins or hangs on a failed backend; INCOMPLETE flag when a rename plan drops file operations; reset LSP attachments on transport drop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🔗 Preview: https://blit-k1sz204nj-indent.vercel.app |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft RFC for a third protocol family (
FEATURE_LSP, bit 8, opcode block0x60) exposing language intelligence to blit clients: docs/design/lsp.md.Architecture: terminate-and-project. The server hosts warm language-server processes like PTYs — daemon-owned, keyed by
(canonical_root, server_id), surviving disconnects — and is each one's sole LSP client. Clients speak blit records only: no JSON-RPC, no capability negotiation, no UTF-16. This is the shape Zed / JetBrains Gateway / Live Share converged on; every raw-LSP multiplexer (lspmux, lspd, ra-multiplex) documents the failure modes (id rewriting, capability intersection, dropped server→client requests, didChange version corruption) that N=1-by-construction dissolves.Wire, along the family grain:
LSP_STATE— server phase/progress/capabilities/RSS as coalesced whole snapshots (GIT_STATEpacing)LSP_DIAG— per-file replacement sets from a server-held last-per-file cache,FULLreplay on every subscribe (a one-shot CLI or late-joining tab never sees a blank gutter)LSP_QUERY— one opcode, kind byte (definition / references / hover / doc & workspace symbols / rename-as-data), skippable response recordsLOCATION/DIAG_FILEcarry BLAKE3-128 content hashes, composing withFS_SYNCfor staleness.Stances taken: disk is the only document truth (sync is server-driven; dirty-fed LRU open set for open-doc-only servers like tsserver/pyright/clangd); read-only by construction (applyEdit refused, rename returns its plan as data; write-shaped features gated on the mutation/buffer RFC); zero-config discovery via per-server root policies over a compiled marker table, commands never defined by repository contents; budgets that degrade (
WARMING, truncation flags, spawn-rate/restart caps); rollout carries an agent-adoption kill gate after the CLI ships.Stacked on #85 — the doc links
fs-watch.md/git.mdand reuses the fssync shared-root watcher, gix root discovery, and family conventions introduced there. Retarget tomainonce #85 merges. Doc-only change; no code.🤖 Generated with Claude Code