Files
ersatztv/docs/decisions/records/session/local-code-intelligence.md
T
4bead26326
Build ErsatzTV Image / Delimiter ban (release path) (push) Successful in 26s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 9m34s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 6m35s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (push) Successful in 6m22s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m12s
fix(777): repair both broken LSPs, and name the surface a subagent can actually reach (#793)
Both C#/TS language servers and the csharp-lsp MCP server were dead; all three are
fixed and each demonstrated with a real find-all-references call in this repo.

Root causes were one shape — a config naming a path this machine does not have,
with nothing checking. None returned a wrong answer; each refused to start:
- csharp-ls: MSBuildLocator needs a dotnet root owning host/fxr; Homebrew's bin
  has none, libexec does.
- typescript-language-server: the LSP workspace root is the repo root but
  `typescript` lives in web/node_modules, and the plugin cannot pass a tsserver
  path (v5 dropped --tsserver-path; lspServers cannot set initializationOptions).
- the csharp-lsp MCP server: .mcp.json named a dotnet install that no longer
  existed, while ~/.codex/config.toml's copy of the same server had been migrated.
  Both files are gitignored, so nothing could compare them.

Corrects defect-shapes-773.md §5.1: the "workflow agents must use csharp-lsp" note
names the MCP server's tools, which subagents DO reach — it was dead because the
server could not start, not because agents cannot call it. The LSP tool is the one
no subagent has been observed to resolve.

Six cold review rounds. Five false greens were found in this PR's own verification
code, each introduced by the fix for the previous one — extracted as #796.

fixes #777

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Timothy <timothy@noreply.gitea.tblindustries.be>
2026-08-14 15:55:20 +00:00

4.8 KiB

key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
key title status since supersedes superseded-by rule signals mechanics
session.local-code-intelligence 2026-08-14 — Brief subagents at the csharp-lsp MCP tools; no subagent has been observed to reach the LSP tool (#777) active 2026-08-14 none none C# and TypeScript find-all-references are available again; brief delegated agents to the `csharp-lsp` MCP tools (`csharp_references`, `csharp_diagnostics`, …) rather than the `LSP` tool, which no dispatched subagent has been observed to resolve (Claude Code 2.1.232, agent types `general-purpose` and `Explore`, 2026-08-14). Preconditions are machine-local — `env.DOTNET_ROOT` in `.claude/settings.local.json` and a root `node_modules/typescript` link — and checkable with `scripts/check-local-lsp.sh`. find all references, findReferences, csharp-ls, typescript-language-server, LSP tool, csharp-lsp MCP, libhostfxr, MSBuildLocator, DOTNET_ROOT, subagent tool availability · paths: `docs/local-lsp-tooling.md`, `scripts/check-local-lsp.sh`, `.claude/settings.local.json`, `.mcp.json` · issues: #777, #773, #403, #671 `docs/local-lsp-tooling.md` — surfaces, configuration, traps, verification

The capability that would address the residue of Family A was configured, enabled, and dead. The population that testing.guard-derives-population-from-source cannot reach is the one whose members are sites in code rather than values — #403 (5 of 6 dispatch sites) and #671 (4 of 10 media types). Find-all-references answers exactly that, and across 811 session transcripts the LSP tool was invoked zero times, against 23,661 Bash calls over the same corpus. Both C# surfaces failed to start; nothing reported it, because a language server that cannot initialize is indistinguishable from one nobody asked.

Two surfaces, and only one of them has been observed to cross the subagent boundary. A dispatched subagent's ToolSearch returns "No matching deferred tools found" for select:LSP while the same query resolves in the main session — measured on Claude Code 2.1.232, agent types general-purpose and Explore, 2026-08-14, plus the independent §5.1 observation. Three observations on one client version: design around it, but it is measured behaviour, not a guaranteed contract. The MCP side is positively evidenced — 326 MCP calls from inside subagent turns across four servers in the transcript corpus — though not yet for csharp-lsp specifically, which only became startable on 2026-08-14. So the standing note telling workflow agents to use C# code intelligence was unsatisfiable as written against the LSP tool, and is satisfiable once pointed at the MCP tools. Naming the surface is the load-bearing part: an agent that cannot resolve the tool substitutes Grep and does not say so.

Both failures were a config naming a path the machine does not have. MSBuildLocator needs a dotnet root owning host/fxr, which a Homebrew bin/dotnet does not have (libexec does); the MCP entry named a dotnet install that no longer existed, while ~/.codex/config.toml's copy of the same server had been migrated. Neither returned a wrong answer — each refused to start, which is the benign half of environment divergence and the reason it survived so long.

.mcp.json and .mcp/ are gitignored, so the working configuration is not recoverable from the repo. That is what let one client's copy drift from the other's with nothing to compare against. docs/local-lsp-tooling.md carries the entry verbatim so a second machine can reconstruct it, and scripts/check-local-lsp.sh turns every precondition into a check. The script is deliberately wired to no CI job: every dependency it tests is a developer-machine install, so a red on a runner would carry no information.

Rejected: making find-all-references a mandatory step before multi-site fixes. Not for availability reasons — C# is expected to reach delegated agents through the MCP server, so a mandate would likely be enforceable there (an inference from the MCP boundary generally, not a measurement of csharp-lsp from a subagent); only the TypeScript route is main-session-bound. The reason is readiness, and it bites each server separately: csharp-ls answers [] while ErsatzTV.sln loads (minutes), and typescript-language-server answers with the declaration alone while its own project graph loads — 20 references reported as 1, with nothing marking it incomplete. Mandating the step without a way to prove the server had settled would license treating that answer as a population — the precise failure the step exists to prevent, now carrying the authority of a rule. It is documented as available and recommended for the multi-site case, with the re-issue-and-confirm discipline attached, and the zero-invocation baseline above is what a future measurement should be compared against.