Skip to content

fix(control): a Retry button that could never work, reported through the widget - #476

Merged
catomean merged 1 commit into
mainfrom
fix/desktop-tab-not-found
Sep 4, 2026
Merged

fix(control): a Retry button that could never work, reported through the widget#476
catomean merged 1 commit into
mainfrom
fix/desktop-tab-not-found

Conversation

@catomean

@catomean catomean commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Filed from /control through 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 this

Its 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:

"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 exactly what happened, across a boundary the note did not consider. Two codebases word the same condition differently:

Path Message Classified
Cloud no zellij tab with that name in any active session ✅ → Start session
Fleet Runner desktop (desktop/src/main/poller.ts:887,895) tab not found: <name> ❌ → Retry

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, 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 ControlRetry the element picker captured — because that concatenated text is what actually reaches the function. Plus casing, precedence against NO_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 verify exits 0.

…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.
@catomean
catomean merged commit 0c932c2 into main Sep 4, 2026
3 checks passed
@catomean
catomean deleted the fix/desktop-tab-not-found branch September 4, 2026 01:17
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>
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.

1 participant