--- name: pure-lsp-status description: "Checks whether the Legend Pure LSP dev-loop bridge is running and its current state — initializing/ready/failed, live compiler progress text, symbol/repository counts. Use whenever the user asks if the Pure LSP or pure-lsp bridge is up/running/ready, what it's currently doing, why it seems stuck, or wants to confirm it's ready before using pure-lsp-check or pure-lsp-go." --- # Check the Pure LSP bridge's status Never `curl` the bridge — `pure-lsp` is the only reliable client (see `references/lsp-devloop-usage-rules.md`). ## Usage ```bash pure-lsp health # is the process even alive? pure-lsp status # what is it actually doing? ``` Add `--port ` / `PURE_LSP_PORT` to target a specific bridge; otherwise `pure-lsp` discovers a running one automatically (sidecar lookup, falling back to 8991). `pure-lsp status --wait ` polls up to N seconds and returns as soon as it reaches a terminal state (`ready`/`degraded`/`failed`) — useful right after starting it instead of guessing how long to sleep. If `health` fails to connect at all, nothing is running — use `pure-lsp-launch-engine` rather than trying to interpret a connection error as a Pure-side problem. ## Reading the status JSON ```json { "state": "ready", "repositoryCount": 143, "symbolCount": 41788, "message": "Ready in 143973ms", "recoveryAttempts": 0, "recoveryInProgress": false, "compiledRepositories": 0, "totalRepositories": 0, "connectedClientCount": 1, "port": 9100, "transport": "socket", "requestPoolSize": 12, "executionConcurrency": 6, "repoRoots": ["/home/developer/projects/legend-pure", "/home/developer/projects/legend-engine"], "jvmArgs": ["-Dlegend.test.server.host=127.0.0.1", "-Dlegend.test.server.port=9095"], "recentErrors": [], "lockContended": false, "recentDisconnectCount": 0 } ``` The bridge passes the server's `legend/status` payload through verbatim. - `state`: `created` → `initializing` → `ready`, or `failed`/`degraded` on the way. `reindexing` and `recovering` are transient states after a workspace file change or a crash-recovery attempt. - `message`: live, human-readable compiler progress (e.g. `"Loading 2854 sources..."`, `"Finished compiling platform_dsl_mapping in 00:00:03"`) while initializing — or the real failure reason (e.g. a `NoClassDefFoundError`) if `state` is `failed`. It updating between polls means it's making progress, not hanging. - `repositoryCount`/`symbolCount`: only count **workspace** repos (discovered via `*.definition.json` under `--repo-root`) and indexed symbols. A `--classpath-file` (engine-scale mode) adds a lot to `symbolCount` once ready but does **not** change `repositoryCount` — that stat is workspace-only, so seeing it "stuck" at a small number in engine-scale mode is expected, not a bug. - `compiledRepositories`/`totalRepositories`: only populate during a genuine from-source compile pass (the `"Compiling repositories in the following order: [...]"` phase). Sitting at `0/0` the rest of the time — including while `ready` — is normal, not a sign anything is broken. - **`port` is the LSP daemon's SOCKET port, not the bridge's HTTP port.** In socket mode this is the `--socket-port` value (9100 above), so it will not match the `--port` you pass to `pure-lsp`. Use `pure-lsp health` for the HTTP port. - `executionConcurrency` is how many Pure executions run at once — the real ceiling for `pure-lsp execute-parallel`, and the number to raise/lower (`-Dlegend.lsp.executionConcurrency`) rather than hand-chunking batches. Default `0.75 x cores`: execution is CPU-bound, so more threads than that cost throughput instead of adding it. - `requestPoolSize` is the pool for *every other* request (`status`, `check`, hover, …). It is deliberately larger and is NOT the execution ceiling — executions have their own pool precisely so a big batch cannot occupy every request thread and make the daemon look wedged. - `recoveryAttempts`/`recoveryInProgress` track crash-recovery; `MAX_RECOVERY_ATTEMPTS` is 3, after which the runtime latches to `failed` and only a restart clears it (see `pure-lsp-restart` below). - `recentErrors` is a list of `{"timestamp": , "message": "..."}`. - `lockContended` reflects whether the LSP's internal read/write lock is currently contended (e.g. a `check`/`go()` blocked behind a long-running compile). The richer detail arrives as an async `legend/lockContention` notification (`{active, lockType, reason, pendingCount}`) pushed while contention is ongoing — it is not a `/status` field, so don't poll for one. ## How long to expect - Plain repo-only mode: ready in roughly 10 seconds. - Engine-scale mode (`--classpath-file`): roughly 3 minutes, and it genuinely burns CPU the whole time (confirmed via `ps`/`jstack` — a healthy compile shows real, sustained CPU usage, not near- zero). If `status` is stuck on the exact same `message` for several minutes *and* the java process's CPU usage is near zero, that's a real hang, not slowness — check with: ```bash ps -eo pid,cmd | grep LegendPureLspServer | grep -v grep ps -p -o pid,etimes,time,%cpu ``` A hang like this is what motivated adding real failure reporting to the server — if it's actually broken (not just slow), `state` should transition to `failed` with a specific message rather than sitting silently in `initializing` forever. ## `recovering` or `failed`: restart the daemon, don't just poll A bad request (e.g. checking a non-`.pure` file, see `pure-lsp-check`'s note on `.legend` files) can knock the runtime into `recovering`. Recovery can silently fail — `status` may keep reporting `recovering` while the runtime is actually dead, or flip to `failed`, and either way the next `pure-lsp-go` call errors with `"Runtime not initialized"`. Don't wait it out — restart: ```bash pure-lsp-restart [--socket-port N] [--dry-run] ``` This finds the java daemon and its bridge for `--socket-port` (default **9100**), captures the bridge's exact original argv from `/proc//cmdline` before touching anything, kills the daemon (then the now-stale bridge), relaunches the bridge with that captured argv, and polls it until the runtime reports `ready`/`failed` or a timeout. `--dry-run` prints the same discovery (pids, captured argv, what would be killed/relaunched) without killing or relaunching anything — use it to sanity- check before touching a shared daemon. Exit codes: `0` restarted and ready (or, under `--dry-run`, a valid plan was found); `2` nothing is listening on `--socket-port` — nothing to restart; `3` a bridge was found but its daemon's pid couldn't be determined — aborts without killing anything; `4` a daemon is listening but no bridge is attached to it, so there's no captured argv to relaunch with (use `pure-lsp-connect` instead); `5` the daemon/bridge were killed and relaunched but never reached `ready` within the timeout (see stderr and the bridge log). The backend Server (`EngineServerForTest`, if one is wired in) is a separate process and normally survives this; no need to restart it too.