Repository navigation
fix(webui): a null deadline must not read as 'time is up', and a comment that denied its own liveness - #28
Merged
Conversation
added 2 commits
October 5, 2026 21:15
The header said the component is "Deliberately not a transport-level liveness probe: nothing in the client exposes one, so the honest signal is the turn stream's own phase". That was the reason the component stayed unmounted for as long as it did, and it is now false: #26 added `connection-health`, the long-lived `watchEvents` socket marking itself healthy on an ack and degraded on `close`, and the component merges the two signals. The projection function itself is untouched and still reads only the turn stream's phase. A reader who trusts this comment concludes the liveness signal cannot exist, which is exactly the conclusion that kept the component off screen for two releases. It now says which signal answers which question, and what the channel signal does not cover: no heartbeat, so detection is close-edge only and a half-open link is invisible; and a watcher that was never accepted stays "unknown", so a host that is already unreachable at load time still reads as connected until something else moves.
#25 tightened the goal auto-reply window to `purpose === 1 && expiresAt !== undefined`, which matches what the runtime actually acts on. The guard still let a non-numeric `expiresAt` through, and the countdown clamps to zero: `null` rendered as "⏱ 0s" and `NaN` rendered the literal string "NaNs" into the panel. Both claim a deadline the payload never carried. `goalAutoReplyDeadline` in the terminal client guards with `typeof expiresAt === "number" && Number.isFinite(expiresAt)`, and this panel's comment already points at that function as its reference. Take the same pair. Zero and a past timestamp stay valid — they are real deadlines that have passed. Also corrects the premise in the MCP status suite. `LocalMcpPublicServerStatus` does classify every server, but that is the MCP tool surface; the plugin page's `listConfiguredServers` → `configuredSummary` returns no `status` at all and hardcodes `configJson` to `"{}"`, so every real row reads 未知状态 and the badge is inert until that path carries the field. The suite now pins today's honest answer with the real server shape, so the day the field arrives the change is visible rather than silent.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two review findings on #25 / #26 / #27, and one comment that was telling the next reader the opposite of the truth.
1. The
ConnectionStatuscomment denied its own livenessIts header read:
That was the reason the component sat unmounted for two releases. It is now false — #26 added
connection-health, the long-livedwatchEventssocket marking itself healthy on an ack and degraded onclose, and the component merges the two signals. A reader who trusts that comment concludes the liveness signal cannot exist, which is exactly the conclusion that kept the component off screen.It now says which signal answers which question — the projection function is untouched and still reads only the turn stream's phase — and what the channel signal does not cover: no heartbeat, so detection is close-edge only and a half-open link is invisible; and a watcher that was never accepted stays "unknown", so a host already unreachable at load time still reads as connected.
2. A null or NaN deadline rendered as "time is up"
#25 tightened the goal auto-reply window to
purpose === 1 && expiresAt !== undefined, which is right about which payloads count. The guard still let a non-numeric value through, and the countdown clamps to zero:expiresAtnull⏱ 0s— "time is up"NaN⏱ NaNs— the literal string in the panel0⏱ 0s⏱ 0s(a real past deadline)⏱ 12s⏱ 12sgoalAutoReplyDeadlinein the terminal client guards withtypeof expiresAt === "number" && Number.isFinite(expiresAt), and this panel's comment already points at that function as its reference. It takes the same pair now.No current producer emits
nullorNaNthere, so this is a parity gap rather than a live regression. It is still the "missing data displayed as a valid state" class, andNaNsrendering into the panel is worth not shipping.3. The MCP status suite's premise was wrong, and hid a real gap
The suite header said the runtime "already classifies every server" and that the gap was only that the panel rendered none of it. That is not the plugin page's path.
LocalMcpPublicServerStatusdoes classify every server — but that is the MCP tool surface (LocalMcpPublicFacade). The plugin page callslistMcpServers→listConfiguredServers→configuredSummary, which returns onlyname / enabled / transport / description / endpoint / configJson, withconfigJsonhardcoded to"{}". Nostatus, noerror, not evenavailable. Fed the real shape, every row reads 未知状态 — the badge is inert.The tests all fed self-made
{status: "error", …}rows, which is why they were green.The real fix is upstream (
configuredSummaryhas to carry the connection state), so this does not pretend to close that gap. What it does is pin today's honest answer with the server's actual shape, so the day the field arrives, this test goes red — the change becomes visible instead of silent.The two
indexOfsource assertions stay: the rows load through an async effect, so no static render exercises them. The file now says plainly that they cannot prove the badge works, which is what the new contract test and the browser spec are for.Verification
node scripts/verify.mjs --profile fullon the committed tree: 20/20 gates, exit 0.The browser suite was not run locally for this change — port 4179 is hardcoded and was held by another agent's live run. CI's
test:webui-browseris linux-only and runs the whole suite there, so that is where the 88 specs get checked.