fix(control): a Retry button that could never work, reported through the widget - #476
Merged
Conversation
…the widget
Filed from /control via the feedback widget — "what is this? can you fix?"
against an element reading:
focus_tab → fleetcrown failed: tab not found: fleetcrown … Retry
focus-failure.ts already exists to prevent exactly this. Its own note says a
Retry beside "tab not found" is a lie, because retrying re-issues an
identical command against a target that is identically absent. It also
predicts how the protection would break:
"a message and a classifier that each spell the same sentence
independently are two definitions that drift the first time someone
rewords the copy — and the drift is silent, since a misclassified
failure still renders a plausible button."
That is what happened, across a boundary the note did not consider. Two
codebases word the SAME condition differently:
cloud "no zellij tab with that name in any active session" → matched
desktop "tab not found: <name>" (desktop/src/main/poller.ts) → NOT matched
So focusing a project through a local Fleet Runner fell through to RETRY and
drew a button that could never succeed.
Fixed in the classifier rather than only in the desktop app, deliberately:
Fleet Runner is versioned and released independently, so every operator on
an older build would keep sending the old wording. A classifier that only
understands the newest client is one that lies about older ones.
Tests use the reported string verbatim — including the trailing "4m agoOpen
on ControlRetry" the element picker captured, since that is what actually
reaches the function — plus casing, precedence against NO_TERMINAL, and two
near-misses ("tab closed by user") that must stay retryable. Proven by
mutation: removing the new match fails the assertion.
github-actions Bot
pushed a commit
that referenced
this pull request
Sep 4, 2026
… rendered (#477) Two defects, one class, found by sweeping for the neighbours of #476. 1. `no zellij session found` fell through to RETRY. This is how the Fleet Runner desktop app words the condition that the cloud words as "no zellij session is running on the connected computer" — poller.ts:881, three lines from the `tab not found:` string fixed in #476. Only one of the pair had been taught to the classifier, so fixing that one left this one shipping a Retry that can never succeed. 2. FAILURE_REMEDY.START_TERMINAL had ZERO consumers in AttentionBar. So fixing (1) alone would have made things worse, not better: a correctly classified failure would render no action at all, leaving the row with nothing but Dismiss. A remedy nothing renders is a classification that silently does nothing. Also names FOCUS_TIMED_OUT_DESKTOP (`did not gain focus`). That one already reached the right answer — a focus race IS retryable — but by accident, via a default that happened to suit it. An accidental right answer is indistinguishable from a wrong one until the default changes. The gate is the point. scripts/test/failure-message-pairing.ts reads the literals poller.ts actually throws and asserts each is recognised by a NAMED phrase, then asserts every remedy the classifier can return is rendered by AttentionBar. Producer and matcher are two definitions of the same sentence in two independently-released codebases; counting instances was never going to work. Reword either side, or add a remedy with no UI, and this fails first. Proven by mutation, both halves: - drop the NO_TERMINAL_DESKTOP matcher -> 14 passed, 1 failed - drop the START_TERMINAL render branch -> 14 passed, 1 failed npm run verify passes; the test runs inside test:unit by directory glob, so it is live in CI without wiring. Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P Co-authored-by: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Filed from
/controlthrough the feedback widget — "what is this? can you fix?" — against an element reading:focus-failure.tsalready exists to prevent thisIts own note argues a Retry beside "tab not found" is a lie: retrying re-issues an identical command against a target that is identically absent, so it fails identically, forever. It also predicts how the protection would break:
That is exactly what happened, across a boundary the note did not consider. Two codebases word the same condition differently:
no zellij tab with that name in any active sessiondesktop/src/main/poller.ts:887,895)tab not found: <name>So focusing a project through a local Fleet Runner fell through to
RETRYand drew a button that could never succeed.Fixed in the classifier, not only the desktop app
Deliberate: Fleet Runner is versioned and released independently, so every operator still on an older build would keep sending the old wording. A classifier that only understands the newest client is one that lies about older ones.
Tests
The reported string verbatim, including the trailing
4m agoOpen on ControlRetrythe element picker captured — because that concatenated text is what actually reaches the function. Plus casing, precedence againstNO_TERMINAL, and two near-misses (tab closed by user,could not find the window) that must stay retryable, so the new match cannot over-classify.Proven by mutation: removing the new match fails the assertion.
npm run verifyexits 0.