Remove redundant binaryAvailable guard from TailscaleWidget.qml #11

Closed
opened 2026-05-19 01:14:54 +00:00 by vybe · 5 comments
Collaborator

binaryAvailable (line 14) is a boolean property that starts true and only flips to false when statusCheck exits with code 127 (command not found). It's used as a preemptive guard in toggleTailscale() and setExitNode() before running a Process.

Problems:

  1. Stale state — The property is only evaluated during statusCheck runs, which fire every 5 seconds via a timer. If the binary is uninstalled, moved, or becomes broken between checks, the guard still thinks it's available and allows the user to click through to toggleTailscale() or setExitNode(). The guard provides a false sense of safety for up to 5 seconds after the condition actually changes. Even at the moment of the check, there's a race: the binary could disappear between the statusCheck confirming it exists and the user clicking a button that invokes toggleProcess or exitNodeProcess.

  2. No recovery — Once binaryAvailable flips to false, there's no mechanism to reset it back to true. The periodic timer continues firing, but statusCheck will keep failing (since the binary is gone), so the property is stuck. Even if the user reinstalls Tailscale while the widget is running, the widget remains in the "not available" state until the plugin is reloaded or Quickshell is restarted. This means the guard is a one-way trip that degrades the user experience permanently for the session.

  3. Redundant — Each Process already has an onExited handler that shows an error toast on non-zero exit codes. The actual failure path is already covered whether the binary is missing, permission-denied, or crashes.

  4. Unnecessary UI state — The StyledText at line 160 only shows when binaryAvailable is false, adding dead UI branches and layout calculations for a state that's both unreliable and unrecoverable.

binaryAvailable (line 14) is a boolean property that starts true and only flips to false when statusCheck exits with code 127 (command not found). It's used as a preemptive guard in toggleTailscale() and setExitNode() before running a Process. **Problems:** 1. **Stale state** — The property is only evaluated during statusCheck runs, which fire every 5 seconds via a timer. If the binary is uninstalled, moved, or becomes broken between checks, the guard still thinks it's available and allows the user to click through to toggleTailscale() or setExitNode(). The guard provides a false sense of safety for up to 5 seconds after the condition actually changes. Even at the moment of the check, there's a race: the binary could disappear between the statusCheck confirming it exists and the user clicking a button that invokes toggleProcess or exitNodeProcess. 2. **No recovery** — Once binaryAvailable flips to false, there's no mechanism to reset it back to true. The periodic timer continues firing, but statusCheck will keep failing (since the binary is gone), so the property is stuck. Even if the user reinstalls Tailscale while the widget is running, the widget remains in the "not available" state until the plugin is reloaded or Quickshell is restarted. This means the guard is a one-way trip that degrades the user experience permanently for the session. 3. **Redundant** — Each Process already has an onExited handler that shows an error toast on non-zero exit codes. The actual failure path is already covered whether the binary is missing, permission-denied, or crashes. 4. **Unnecessary UI state** — The StyledText at line 160 only shows when binaryAvailable is false, adding dead UI branches and layout calculations for a state that's both unreliable and unrecoverable.
Author
Collaborator

FYI from cross-model audit (claude-sonnet-4-6):

When binaryAvailable = false, the centered "Tailscale not available" StyledText renders, but statusRow and peerList lack visible: root.binaryAvailable guards. They continue to draw underneath/over the notice, producing overlapping/confusing UI.

If the decision is to keep or evolve the binaryAvailable state instead of removing it entirely, this visibility bug must be fixed so the error state actually hides the normal content.

FYI from cross-model audit (claude-sonnet-4-6): When `binaryAvailable = false`, the centered "Tailscale not available" StyledText renders, but `statusRow` and `peerList` lack `visible: root.binaryAvailable` guards. They continue to draw underneath/over the notice, producing overlapping/confusing UI. If the decision is to keep or evolve the binaryAvailable state instead of removing it entirely, this visibility bug must be fixed so the error state actually hides the normal content.
Author
Collaborator

FYI from cross-model audit (claude-sonnet-4-6):

binaryAvailable is initialized to true. On startup the widget therefore shows the normal status row and peer list for up to one full 5-second timer interval before the first statusCheck can possibly set it to false. If the binary is absent, users briefly see a fully functional UI that then flips to the error state.

This is another concrete failure mode of the current binaryAvailable guard logic (in addition to the stale-state and one-way-trip problems already noted in this issue).

FYI from cross-model audit (claude-sonnet-4-6): `binaryAvailable` is initialized to `true`. On startup the widget therefore shows the normal status row and peer list for up to one full 5-second timer interval before the first `statusCheck` can possibly set it to `false`. If the binary is absent, users briefly see a fully functional UI that then flips to the error state. This is another concrete failure mode of the current `binaryAvailable` guard logic (in addition to the stale-state and one-way-trip problems already noted in this issue).
Author
Collaborator

Related: #13 (duplicate error toasts on statusCheck), #14 (stale isConnected), #30 (multiple concurrent statusCheck triggers). All stem from the same 5 s polling + state model.

Related: #13 (duplicate error toasts on statusCheck), #14 (stale isConnected), #30 (multiple concurrent statusCheck triggers). All stem from the same 5 s polling + state model.
Author
Collaborator

Resolved by removing binaryAvailable property, its setter, guards in toggle/setExitNode, and the dead 'not available' UI text. All error paths now rely on Process onExited handlers which already exist. Changes merged to vibes branch. No human review needed for verification as it's a pure removal of dead/redundant code with no behavior change for normal operation. Written by AI agent working for @jtmorris. Model: Grok 4.3.

Resolved by removing binaryAvailable property, its setter, guards in toggle/setExitNode, and the dead 'not available' UI text. All error paths now rely on Process onExited handlers which already exist. Changes merged to vibes branch. No human review needed for verification as it's a pure removal of dead/redundant code with no behavior change for normal operation. Written by AI agent working for @jtmorris. Model: Grok 4.3.
Author
Collaborator

Resolved via merge ac1669f (feature_remove_binaryavailable_guard). The plugin is confirmed working by @jtmorris. Human code review required before merging to testing.

Resolved via merge ac1669f (feature_remove_binaryavailable_guard). The plugin is confirmed working by @jtmorris. Human code review required before merging to testing.
vybe closed this issue 2026-05-21 07:16:43 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: jtmorris/dms_tailscalectl#11
No description provided.