Skip to content

fix(bridge): signal degraded positions and evict documents on the LRU cap - #502

Merged
bug-ops merged 3 commits into
mainfrom
fix/497-bridge-encoding-eviction
Sep 21, 2026
Merged

bug-ops merged 3 commits into
mainfrom
fix/497-bridge-encoding-eviction

Conversation

@bug-ops

@bug-ops bug-ops commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Summary

Two independent fixes in the bridge subsystem, both surfaced by continuous-improvement cycle 035 against v0.5.0 (commit bfe82b0).

  • Position-encoding conversion now sets a positions_degraded flag on every DTO whose fields come from position conversion (hover, definition, references, locations, rename, format, code actions, document/workspace symbols, call hierarchy, inlay hints, diagnostics) whenever a column could not be converted for encoding — disk-read budget exhausted, unresolvable server path, oversized file, invalid UTF-8, or a line past EOF. Previously the only signal was a tracing::warn! in mcpls's own log; a caller had no way to tell that the tail of a result set might carry silently wrong columns.
  • DocumentTracker now evicts the least-recently-used unlocked document (sending textDocument/didClose to the LSP server) when open() would exceed workspace.max_documents, instead of permanently refusing every new document for the rest of the process lifetime once the cap is reached. Eviction skips documents with an in-flight operation (tracked via path_locks) or unsaved local edits (disk().is_some() gate), falling back to the existing DocumentLimitExceeded error only when no evictable candidate remains.

Details

positions_degraded (#497) — the flag is set directly at the "passing server column through unconverted" decision point in to_lsp/to_mcp (not derived from the disk-read-budget bookkeeping, which only covers one of several causes), so it fires on the first degraded lookup in a response, not just after the budget is fully exhausted. Translator::merge_diagnostics ORs the flag across the pull and cached diagnostics contexts. All affected MCP tool descriptions and DTO doc comments were updated to describe the flag; tool_surface.json was regenerated accordingly.

Document eviction (#495) — DocumentTracker has no LspClient access, so evictions are queued as EvictedDocument { path, uri, synced_servers } and drained by Translator::notify_evicted_documents, which sends textDocument/didClose after every ensure_open call (including on that call's own error path, so a queued eviction from a prior lookup is never lost). DocumentState::last_accessed tracks LRU order, bumped on every ensure_open/update against a tracked document.

A narrow, non-default-config race is documented but deliberately not structurally closed: PathLockGuard covers ensure_open/update, not the LSP round-trip that follows, so at max_documents: 1-2 with concurrent calls, two in-flight documents could in principle evict each other. This is gated behind a config far below the default (100), and closing it structurally would require widening lock scope across every MCP handler — out of scope for this PR. A follow-up issue will track it.

Test plan

  • cargo +nightly fmt --all -- --check
  • cargo clippy --all-targets --all-features --workspace -- -D warnings
  • cargo nextest run --workspace --all-features --lib --bins (1016 passed, 1 skipped)
  • RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace
  • New regression tests: positions_degraded flips true end-to-end through a full mocked handler call and through the merge_diagnostics OR-merge; DocumentTracker LRU-vs-insertion-order distinction, lock-skip fallback to DocumentLimitExceeded, unsaved-edit eviction guard, and wire-level textDocument/didClose on eviction

Closes #497
Closes #495

@github-actions github-actions Bot added documentation Improvements or additions to documentation rust Rust code changes mcpls-core mcpls-core crate changes labels Sep 21, 2026
bug-ops added a commit that referenced this pull request Sep 21, 2026
… cap

Position-encoding conversion now sets a positions_degraded flag on every
DTO whose fields come from position conversion whenever a column could
not be converted (disk-read budget exhausted, unresolvable server path,
oversized file, invalid UTF-8, or a line past EOF), instead of silently
passing the raw column through and leaving the only signal in a log line.

DocumentTracker now evicts the least-recently-used unlocked document
(sending textDocument/didClose to the LSP server) when open() would
exceed workspace.max_documents, instead of permanently refusing every
new document for the rest of the process lifetime once the cap is
reached. Eviction skips documents with an in-flight operation or
unsaved local edits, falling back to the existing DocumentLimitExceeded
error only when no evictable candidate remains.

Closes #497
Closes #495
@bug-ops
bug-ops force-pushed the fix/497-bridge-encoding-eviction branch from 8df64fb to 1cf3854 Compare September 21, 2026 14:26
@bug-ops
bug-ops enabled auto-merge (squash) September 21, 2026 14:27
test_e2e_max_documents_config_enforced still asserted the pre-#495
reject-once-the-cap-is-reached behavior, so it failed deterministically
in CI once DocumentTracker started evicting the least-recently-used
document instead. This test lives under tests/e2e/, a separate binary
not covered by --lib --bins, which is why local verification missed it.

Now asserts every open beyond the configured limit still succeeds
(crossing the boundary twice) and that a document opened before the
first crossing remains re-openable once evicted.
@github-actions github-actions Bot added the testing Test-related changes label Sep 21, 2026
@bug-ops
bug-ops merged commit 52d7582 into main Sep 21, 2026
29 checks passed
@bug-ops
bug-ops deleted the fix/497-bridge-encoding-eviction branch September 21, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation mcpls-core mcpls-core crate changes rust Rust code changes testing Test-related changes

Projects

None yet

1 participant