Skip to content

feat(tui): add multi-file read_lints operation - #5524

Merged
Hmbown merged 5 commits into
Hmbown:mainfrom
wuisabel-gif:wuisabel/4070-read-lints
Aug 23, 2026
Merged

feat(tui): add multi-file read_lints operation#5524
Hmbown merged 5 commits into
Hmbown:mainfrom
wuisabel-gif:wuisabel/4070-read-lints

Conversation

@wuisabel-gif

@wuisabel-gif wuisabel-gif commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Addresses the approved scope of #4070.

The existing model-visible lsp tool now supports a read_lints operation for multiple existing workspace-relative files. It reuses the session LspManager and its transport pool instead of creating another language-server lifecycle.

The operation returns bounded structured diagnostics with:

  • file path;
  • 1-based line and column;
  • severity;
  • diagnostic message;
  • empty result blocks for files with no diagnostics.

Paths are newline-separated through the existing lsp.path string field. This preserves the repository's frozen model-visible tool catalog identity. The output is capped at 16 paths, 100 diagnostics, 512 characters per message, and 12,000 serialized characters.

Failure behavior

  • LSP disabled returns a clear error.
  • An unavailable language server returns the affected file in the error.
  • Absolute paths, parent traversal, symlink escapes, missing files, and non-file paths fail closed.
  • No provider credentials, network access, or real language server are required by the tests.

Generated facts

The committed website facts were regenerated and remain at the real 75-tool catalog count. The private read_lints helper is not registered as a second model-visible tool.

Tests

Passed:

  • cargo fmt --all -- --check
  • git diff --check
  • npm run check:facts in web/
  • cargo test -p codewhale-tui --lib 'tools::lsp' --locked (5 passed)
  • CARGO_INCREMENTAL=0 cargo test -p codewhale-tui --lib --locked (full TUI library suite passed)
  • python3 scripts/check-runtime-contract-budget.py (55 ceilings respected)

The repository release helper still reports a baseline set of missing release-note receipts on current main; none refer to #4070 or this diff.

No-Issue: This PR addresses the approved #4070 slice without closing the broader issue automatically.

@wuisabel-gif
wuisabel-gif requested a review from Hmbown as a code owner August 20, 2026 09:15
@github-actions

Copy link
Copy Markdown
Contributor

Thanks @wuisabel-gif for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

Expose bounded, workspace-relative LSP diagnostics for multiple existing files through the model-visible lsp tool. Reuse the shared transport pool, fail clearly when LSP is unavailable, and preserve the frozen tool catalog budget for Hmbown#4070.
Regenerate the committed website facts after exposing read_lints through the existing lsp tool.
Avoid counting the private diagnostics helper as a second model-visible tool and refresh generated web facts back to the real 75-tool catalog.
@wuisabel-gif
wuisabel-gif force-pushed the wuisabel/4070-read-lints branch from 5fd5c6c to 1708a3f Compare August 21, 2026 04:16
Apply mechanical Clippy 1.98 compatibility fixes in unrelated baseline paths so the required PR lint gate can run cleanly alongside the Hmbown#4070 read_lints change.
@wuisabel-gif

Copy link
Copy Markdown
Contributor Author

CI note: the required Clippy job caught five Clippy 1.98 diagnostics in existing baseline paths outside the original #4070 diff:

  • commands/groups/core/voice.rs
  • llm_client/mod.rs
  • tui/ui/event_loop.rs
  • tui/views/status_picker.rs (two findings)

I added them in a separate mechanical cleanup commit, 97dfeaa50, so the required lint gate can run cleanly. These changes do not alter the read_lints behavior or its tool-catalog design. Focused tests for the affected areas and all-target Clippy pass locally.

@Lstarsky0 Lstarsky0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things about what the model actually gets back.

The failure modes collapse into "clean file". diagnostics_for_paths catches the no-server case up front, but everything past that goes through diagnostics_for, which returns None on a failed file read (crates/tui/src/lsp/mod.rs:200), on a server error (:226) and on a timed-out poll (:230) — and the unwrap_or_else at :526 turns all three into an empty DiagnosticBlock. The model then sees "diagnostics": [], "diagnostic_count": 0, "truncated": false and calls the file clean. The timeout is the one that will bite: the budget is poll_after_edit_ms, 5s by default, and read_lints exists to be called without a preceding edit, which is exactly when rust-analyzer is still indexing. read_lints_preserves_files_with_empty_diagnostics pins the one case where an empty block is honest — the transport returns Ok(vec![]) — and the timeout and error paths produce a byte-identical block that nothing covers.

Second, what comes back isn't lints, it's errors. diagnostics_for filters on include_warnings, which defaults to false (:100, :241), and the _ => false arm drops Information and Hint even when it's on. Then block.truncate(max_diagnostics_per_file) at :255 cuts to 20 by default without recording anything, so a file with 40 errors comes back as 20 with "truncated": false — only the tool's own caps set that flag. All of it is pre-existing LspManager behaviour and reusing the manager is the right call; the problem is that none of it is visible to the caller, and the model can't discount what it can't see.

Separate from both: the query description is gone from the schema entirely and character lost its documented default. Those descriptions are the only docs the model has for those fields.

Distinguish honest empty results from timed-out, failed, or server-less polls via per-file status and note fields; surface warnings_included and per-file config-cap truncation so capped lists are never mistaken for complete ones; restore query and character schema docs within the frozen catalog budget.
@wuisabel-gif

Copy link
Copy Markdown
Contributor Author

Thanks @Lstarsky0 — all three points addressed in 90ac629e4:

  1. Degradation no longer reads as clean. poll_diagnostics now returns a tri-state outcome (Ready/CleanEmpty/Unavailable) and diagnostics_for_paths carries a per-file status (ok / clean / unavailable) plus an actionable note. Timeouts name their budget ("timed out after Nms waiting for diagnostics"), transport errors carry the server message, unreadable files say so, and missing servers are per-file notes instead of a whole-batch failure. The post-edit hook's behavior is unchanged — it maps the new outcome back to its old Option shape.

  2. Filtering and truncation are visible. Output now includes top-level warnings_included (mirrors [lsp] include_warnings, default false) and per-file truncated: true whenever [lsp] max_diagnostics_per_file cut items, so 20-of-40 can't present as complete. New tests pin the timeout path (previously byte-identical to clean), the transport-error path, the config-cap path, and both warning-visibility modes.

  3. Schema docs restored. query has its description back and character documents its default again. To stay under the frozen catalog ceiling I trimmed two other lsp descriptions ("Intelligence operation to run." → "Operation to run."); the runtime-contract check passes at 69300 < 69304.

All 9 tools::lsp tests pass locally, along with cargo fmt, strict all-targets Clippy on the crate, and the budget script.

Lstarsky0 pushed a commit to Lstarsky0/CodeWhale that referenced this pull request Aug 23, 2026
feat(tui): add multi-file read_lints operation (Hmbown#5524, Isabel Wu).

Conflict was confined to the two changelog files, where our Unreleased
section had moved on past the PR's base; the read_lints entry is folded
into ours and the tui slice re-synced. Contributor commits stay intact
as merge parents.
Lstarsky0 pushed a commit to Lstarsky0/CodeWhale that referenced this pull request Aug 23, 2026
Record the integrated contributions behind PRs Hmbown#5523, Hmbown#5524, and Hmbown#5525, and acknowledge the independently reviewed overlap in Hmbown#5530 without counting it as a duplicate merge.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
@Hmbown

Hmbown commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Thank you @wuisabel-gif — this shipped in v0.9.11 🎉

Your multi-file read_lints operation is in the released tag v0.9.11 (96d13a0b): the feature commit 8cf8a6979 feat(tui): add multi-file read_lints operation plus 5fd5c6cc0 fix(tui): keep read_lints inside the lsp catalog, and you're credited in the CHANGELOG ("Thanks to Isabel Wu (@wuisabel-gif) for PR #5524").

It came in through the v0.9.11 release integration branch (which merged your commits with a couple of small fixups) rather than through this PR's button, so GitHub left this one open and now shows it as conflicting against the released tree. Closing it as shipped — not rejected. Your contribution is live and credited. Thanks for making Codewhale better, and sorry this PR was left hanging.

@Hmbown Hmbown closed this Aug 23, 2026
@Hmbown Hmbown reopened this Aug 23, 2026
@Hmbown
Hmbown merged commit 8b11853 into Hmbown:main Aug 23, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants