fix(web): degrade the connected header to the catalog name for an empty reported serverInfo name (#1774) - #1775
Conversation
…ty reported serverInfo name (#1774) A server that reports serverInfo with an empty name string (an empty-but-present name) left ViewHeader rendering a nameless title. App's `??` fallback only fires when the whole serverInfo object is absent (the #1772 modern-omitted case), so a reported `{ name: "" }` slipped through with `serverInfo.name === ""`. Resolve the header's display name in InspectorView: fall back to the active server's catalog name when the reported name is blank. This is a display-only fallback — the Connection Info modal stays faithful to the raw report because it reads App's untouched `initializeResult` + `serverInfoReported`, so `serverInfoReported` keeps meaning "the server sent a serverInfo object" (a reported-but-empty name still reads as "—" in the modal, and any reported version stays intact — driving the flag off name emptiness would wrongly mask the version). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
@claude review |
|
Claude finished @cliffhall's task in 2m 36s —— View job Review:
|
…tests + cross-reference (#1774) Review follow-ups on PR #1775: - resolveHeaderServerInfo now guards with `serverInfo.name.trim()`, so a whitespace-only reported name (" ") — the same non-conforming class as an empty string — also degrades to the catalog name instead of rendering a blank-looking header title. - Strengthen the no-catalog-match test: assert the connected header chrome (Disconnect control) actually renders inside role="banner", making it a real regression guard rather than a vacuous absent-string check. - Add a whitespace-only header-fallback test. - Add a back-reference in App's initializeResult memo comment pointing down to InspectorView's resolveHeaderServerInfo, closing the loop between the two fallbacks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Thanks for the review — addressed the three actionable points in 1. Whitespace-only name slips through — fixed. 2. Vacuous test assertion — fixed. The no-catalog-match test now asserts the connected header chrome actually renders — the Disconnect control is present inside the banner — so it's a real regression guard for "the header still renders for a nameless server," while still covering the 3. Undocumented from App's side — fixed. Added a back-reference in App's 4.
@claude review |
|
Claude finished @cliffhall's task in 1m 58s —— View job Re-review:
|
…n Info modal too (#1774) Second-review follow-up on PR #1775. The header now degrades a whitespace-only reported name; the modal had the same gap — `serverInfo.name || "—"` treats " " as truthy and renders a visually blank row, the exact outcome the `||` exists to prevent. Trim both the name and version rows before the fallback so a blank-but-present value reads as "—". Uses optional chaining (`?.trim()`) to preserve the prior tolerance of a non-conforming runtime-absent field (the type is non-null, but a server can omit it — covered by the existing "version missing" test). Stays faithful: the fallback is still "—", never a borrowed catalog name. Applied the same `?.` hardening to the header's resolveHeaderServerInfo guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Good catch — took the modal fix; it's genuinely in scope (the header and the modal are the only two consumers of that field, and it'd be odd to tighten one and leave the other). Fixed in Modal whitespace gap — fixed. One wrinkle worth flagging: I used Point 4 (
@claude review |
|
Claude finished @cliffhall's task in 2m 13s —— View job Re-review:
|
…1774) Third-review follow-up on PR #1775. Commit 2f508ff added `serverInfo.name?.trim()` in both ConnectionInfoContent and resolveHeaderServerInfo, but only the version `?.` short-circuit was exercised (by the pre-existing "version missing" test) — the symmetric name `?.` was uncovered. Add two tests passing a serverInfo that omits `name` (`{ version: "1.0.0" } as never`): the modal Name row reads "—", and the header degrades to the catalog name. Pins the documented tolerance and makes the two new branches explicit rather than relying on gate headroom. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Fixed in Untested
Both are now real guards for the documented tolerance, not just coverage padding. I left the modal comment as-is — the redundancy you flagged is mild and, as you said, not worth a churn commit on its own. Everything else (point 4, the borrowed-name faithfulness, consumer completeness) I'm leaving per your notes.
@claude review |
|
Claude finished @cliffhall's task in 1m 19s —— View job Re-review:
|
|
Thanks — appreciate the thorough passes. Agreed on leaving the |
Closes #1774
Problem
Follow-up from the #1773 review. A server that reports
serverInfo: { name: "" }(an empty-but-present name) leftViewHeaderrendering a nameless title, while the tab bar worked fine. App's object-level??fallback only fires when the wholeserverInfoobject is absent (the #1772 modern-omitted case), so a reported{ name: "" }yieldsserverInfo.name === ""and reaches the header blank.The Connection Info modal already degrades gracefully here (
serverInfo.name || "—", #1773); only the header did not.Decision
serverInfoReported: left asserverInfo !== undefined(unchanged). This is the deliberate call the issue asked for — the flag tracks presence of the object, not name usability. Driving it off name emptiness would wrongly mask a reported version (a server could send{ name: "", version: "1.2" }), and would make the modal's "not reported by server" label untruthful. The modal therefore stays faithful: a reported-but-empty name still reads as—, any reported version intact.Implementation
The fallback is a pure display concern, so it lives in the view layer (
InspectorView), which has both the reported name and the catalog list —resolveHeaderServerInfo(...). App'sinitializeResultand the modal path are untouched and remain faithful to the raw report.Tests
Added two
InspectorViewtests (scoped to the header viarole="banner"to exclude the ServerCard, which shows the catalog name unconditionally):npm run cipasses locally (validate, coverage gate, smoke, Storybook).🤖 Generated with Claude Code
https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5