Files
orca/docs/reference/wsl-probe-failure-semantics.md
Jinjing 61e010079f New agent dashboard (#18222)
* more obvious toggle

* more obvious toggle

* feat(activity): redesign thread rows and add child agent filtering

- Emphasize task title and last activity in row layout over metadata
- Add child agent toggle; hide orchestration workers by default
- Support collapsible groups and ungrouped view mode
- Improve orchestration worker message handling to surface replies
- Add sidebar search and filter controls for agent activity

* periodic checkin

* feat(activity): add "Clear completed" action and performance improvement

- Add "Clear completed" action for activity threads with undo window; clears completed and interrupted rows from view, persists across restart
- Virtualize activity thread list to render only viewport-bounded rows
- Cache activity thread search text to prevent recomputation on every keystroke
- Cache dashboard bucket counts per-worktree for selective invalidation on unrelated changes
- Use useDeferredValue for activity search filtering to keep input responsive
- Make compact mode the default display for activity threads
- Add activity-cleared-at persisted state tracking (per-pane cutoff timestamps)

* improve style

* minor change

* feat(activity): add persisted host and project filters to agents view

Agents scope filters are deliberately separate from workspace-nav filters so a monitoring surface never inherits workspace context silently. Filters survive restarts and always display an active-filter chips row with hidden count, making filtering visible and reversible.

* Graduate Agents view from experimental, refine activity handling

- Agents Dashboard moves from experimental to standard feature with showAgentsSidebar setting controlling visibility
- Add identity-checked cache eviction (dropPersisted IPC) to prevent newer runs from being evicted when UI clears older status, fixing clear-completed safety
- Extract ActivityThreadHoverCardSummary and ActivityThreadListToolbar components for better organization and reusability
- Implement mark-thread-read as separate action from select with clickable bell icon
- Add hasActivityThreadWorkspace helper for checking workspace availability across hosts (SSH/runtime targets)
- Preserve scope filter array identity during hydration for memo optimization
- Track manually-unread turns in auto-ack to prevent re-acknowledgement
- Clean up activity cleared-at cutoffs on pane retirement
- Remove activity-thread-hover-card max-lines lint override (code refactored below threshold)

* Refactor agent cache identity to use timing fields only

- Simplify AgentStatusCacheIdentity: keep only paneKey, receivedAt, stateStartedAt
- This fixes silent no-ops where renderer-enriched fields diverged from main's cache
- Add worktree-jump-navigation for navigating activity to workspaces
- Add manual mark-unread protection separate from auto-ack
- Optimize activity owner resolution with per-build memoization
- Optimize detected worktree lookup with indexed search

* Remove sticky header, add scroll position persistence

Replace the floating sticky header overlay with scroll position memory via
a ref. This preserves the user's scroll location when switching between
threads or remounting the agents list, improving UX without requiring
React state.

* Implement sticky group headers in activity thread list

Keep group headers visible at the top while scrolling when threads are grouped. Headers stick to the viewport while their section is in view, then unstick as the next header approaches.

* add blue flash

* update settings appearnce

* Extracted activity acknowledgement/clearance actions from the oversized UI slice.
  - Removed dead sidebar search/menu props and the unused search ref.
  - Removed the unnecessary sidebar visibility bitmask.
  - Replaced hardcoded sidebar toggle colors with design-system tokens.
  - Removed duplicate “mark all read / clear completed” controls in the sidebar.
  - Preserved manual-unread state correctly across pane retire, transfer, and drop.
  - Made clear-completed cutoffs monotonic so clock skew cannot resurrect old activity.
  - Fixed blank workspace names in hover cards with the existing fallback helper.
  - Added missing localization entries and stabilized hydrated filter array identity.
  - Updated misleading Agents setting copy to describe both sidebar surfaces.

* add onboarding guide for the new agents panel

* Add activity clearance tracking and synced agent view settings

Agent view filters and presentation settings now sync across paired clients.
Preserves per-pane activity clearance cutoffs in persistent state. Improves
activity thread row accessibility with proper ARIA roles, and preserves
terminal host ownership after pane teardown via retained terminal handle.

* rm html

* Graduate Agents from experimental and improve activity visibility

- Migrate `showAgentsSidebar` setting from legacy experimental flags; default new profiles to the agents sidebar
- Replace scoped-thread filtering with visible-thread filtering so bulk actions (mark all read, clear completed) only affect rendered rows
- Rewrite child agent classification as a set of visible pane keys to fix orphan promotion and parent-cycle handling
- Improve activity cleared-at cutoff lifecycle: preserve on row dismissal (pane may still be live) but clear on pane removal
- Add pagehide flush for pending clear-completed evictions so quit/reload cannot replay cleared activity
- Polish agents sidebar: unread count badge, expand button, onboarding intro for migrated/new users
- Extract shared time-ago formatting to a library module
- Fix scroll restoration to defer until content can contain the saved offset
- Improve stable message hold for compact agent rows using state instead of refs
- Add worktree filter-visibility check to distinguish collapsed-but-unfiltered from filtered-hidden

* Graduate Agents from experimental and improve activity visibility

- Remove the deprecated full-page Agents view; fix settings navigation fallback
- Refactor bulk action bindings and separate mark-all-read from visible threads
- Preserve sidebar collapse state across remounts; fix child-agent badge filtering
- Add safety window for scroll-restore and improve worktree host-qualified filtering

* Graduate Agents from experimental and add manual unread tracking

- Move Agents sidebar from experimental settings to standard feature with intro flow
- Add persistent manual unread turn tracking for activity feed
- Consolidate workspace activation through activateAndRevealWorkspace dispatcher
- Improve sidebar view toggle with radio semantics and arrow-key navigation

* Graduate Agents sidebar and separate dashboard experiment

The Agents tab now has its own `showAgentsSidebar` setting (defaults on) independent from the dashboard popout experiment. Activity unread counting is simplified to count all events uniformly without mode-specific filtering. Dashboard visibility is now controlled solely by `experimentalAgentDashboardPopout`, with its own UI in the Experimental settings pane. Migration path updated: only `experimentalActivity=true` graduates to the sidebar; the dashboard experiment remains separate.

* Add agent-session tab support to activity tracking

Build activity event contexts from structured agent-session tabs and
worktree-attributed status entries. When activating a thread, try
agent-session tab activation before falling back to terminal pane.

* • The workspace sidebar tab is now a static Spaces
  label—no grouping-based “Projects” label or hidden
  width-reservation span.

* Show unread count badge and prioritize attention-needing agent threads

Activity group order now surfaces threads needing attention (blocked,
waiting, interrupted) before working/done so they're never buried. The
Agents tab shows an unread count badge while viewing Spaces, since the
open Agents list already highlights unread rows.

Also improves UX text ("Hide Agents" vs "Maybe later"), accessibility
with proper ARIA labels, and handles edge cases: preserves read state
for retained panes on SSH reconnect and handles deleted worktrees
gracefully in navigation.

* Batch agent-status evictions and optimize activity pane rebuilds

- Add dropPersistedStatusEntries batch API; consolidate evictions into one persist
- Implement fallback timeout in clear-completed for unseen toast callbacks
- Project only activity-relevant tabs; memoize terminal tab derivations
- Stabilize activity virtualizer key to prevent unnecessary item measurements

* Remove unread count badge from Agents sidebar tab

Simplify useActivityUnreadCount by removing the enabled parameter and
conditional logic, as the badge is no longer displayed in the UI.

* Deduplicate activity unread counts across source overlaps

Live pane status is the primary source; retained and migration entries
serve as fallback caches that may briefly overlap it during lifecycle
transitions. Count each pane only once by tracking seen keys, prioritizing
the live status as the canonical source.

Also fix monitoring state display: it's a distinct agent state, not a
tool-running row state, so exclude it from tool preview checks.

* Update activity pane tests to remove unread badge assertions

- Remove ActivityPaneVisibility type and readActivityPaneVisibility() helper
- Update agentsSidebarButton selector to match badge-less state
- Simplify assertions to check pane focus instead of visibility isolation
- Remove test for unread badge acknowledgement flow

* Fix activity pane workspace resolution and localization handling

- Thread defaultHostId through activity operations for correct host resolution
- Add language-aware caching for standalone terminal names with cache invalidation
- Fix scroll restoration bounds calculation for tall viewports
- Add focus management to sidebar radio group keyboard navigation
- Refresh localized sidebar content on language changes
- Preserve activity state across heartbeats to prevent history loss
- Improve host-id strictness in worktree jump navigation

* Preserve activity view when settings fetch fails

A failed window.api.settings.get() leaves settings null, which was
incorrectly treated as opt-out. Add the missing null check so the
activity-view gate only applies when settings are available.

Includes tests for this scenario and related edge cases in keyboard
navigation, worktree jumping, and session state handling.
2026-09-02 11:00:24 -07:00

4.6 KiB

WSL probe failure semantics

A WSL probe answers a question about a distro: is git installed, what is $HOME, which distros are running. Every one of those probes can fail for a reason that has nothing to do with the answer — the distro is booting, wsl.exe is slow under load, the VM was just shut down.

The recurring bug in this subsystem is reporting that failure as a negative answer.

The shape

try {
  await execCommandInWslOrThrow(target, `${shellQuote(command)} --version`)
  return true
} catch {
  return false // "not installed" and "could not ask" are now the same value
}

Nothing downstream can tell those two apart, because by this point they aren't two things.

Why it keeps shipping

Swallowing on its own is survivable. An uncached caller asks again a moment later and the answer corrects itself, so the bug stays invisible in review and in manual testing.

It becomes user-visible when the swallowed value is cached or used to gate discovery. Then a distro that was busy for one second reports no git, or no agent sessions, until the app is relaunched. The failure is sticky, silent, and indistinguishable from the real thing.

Three instances so far:

Where What the user saw Status
Preflight CLI probes Caching the result would have pinned "git not installed" until relaunch Bounded entry (#17350)
glab auth status fallback into WSL Idle VM woken repeatedly for users who never touch GitLab Open (#8941)
listRunningWslDistrosAsync Fails closed to [] with no last-known-good, polled every 2s — a persistently broken wsl.exe makes every WSL session vanish app-wide Open (PR #17072 review)

What to do instead

Pick the cheapest option that fits the call site.

  1. Don't pin it. If the probe is cheap and uncached, swallowing is fine — the next call self-heals. This is what most of src/ legitimately does.
  2. Bound the entry. If you cache, give it a TTL so a transient failure expires instead of lasting the session. Cheap, no signature change, and what #17350 does.
  3. Keep last-known-good. If the probe gates discovery, fall back to the previous successful answer on failure rather than to empty. listWslDistrosAsync in src/main/wsl.ts already does this — listRunningWslDistrosAsync, added beside it, does not.
  4. Propagate the third state. The durable fix: return present | absent | unreachable instead of a boolean, so a caller cannot accidentally treat "could not ask" as "no". This reaches past WSL into shared exec code and hasn't been done.

Whichever you pick, say in a comment which one and why — that sentence is what the guard below is really asking for.

The guard

src/main/wsl/wsl-probe-failure-semantics.test.ts scans the WSL and preflight probe modules for catch { return false | [] | null } and holds the current set in an allowlist that only shrinks.

Its limits are worth being explicit about, because they decide how much it is worth trusting:

  • It cannot see the dangerous part. Whether a swallowed value is later cached or gates discovery is dataflow, not syntax. Every allowlisted entry is currently safe; the guard does not verify that and cannot.
  • It is scoped, not global. The same shape appears ~850 times across src/ and is usually correct, because for most callers a failure genuinely does mean absent. Enforcing it repo-wide would be noise. It only matters where the answer describes a WSL distro.
  • It catches a shape, not a mistake. Code can conflate failure and absence without ever writing catch { return false }.

So it does not prevent the bug. What it does is stop a new swallow site appearing in these modules without someone stating why the value is safe to pin — which is the review conversation that was missing all three times.