Add deadline-based timeouts and request ID tracking to prevent stale responses
from freezing the checks panel. Include abort signal propagation throughout the
request chain and provide retry UI for failed check details loads.
* perf(renderer): index GitHub resume lookups
* perf(renderer): skip the resume cache sweep publication when nothing changed
Every eviction helper already returns its input reference untouched when it
evicts nothing, so the opening sweep in refreshAllGitHub can compare identities
and return the previous state. Window resume then stops waking every store
subscriber on a no-op sweep.
Folds in the remaining unique optimization from #13711, which this PR
supersedes on the lookup-indexing side.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): preserve unchanged remote mirror resources
* test(renderer): restore layout-value coverage and record the workspaceId dependency
Notes why browser page identity reuse is safe: browserPageEqual must keep
comparing workspaceId, since the removed-workspace page-list cleanup gates on
it. Also re-asserts the effective active leaf that the patch-only assertion
stopped covering.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): keep remote snapshot batches linear
* test(renderer): cover the prefix-colliding sibling worktree
The sibling-mapping test used a worktree id that shares no prefix with WT,
so it passed against the prefix-scan it was meant to pin. Use an id prefixed
by WT's and drain the new mapping index in the leak counters.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* fix(dashboard): keep agent map rings open while the pointer works inside them
The project and workspace rings drove their hover styling from `:hover` on the
ring element itself. A ring is a sibling of everything drawn inside it, so the
style dropped the moment the pointer crossed onto a workspace or an agent, and
a pan drag killed it outright — pointer capture retargets `:hover` to the
`<svg>` for the whole gesture. Both read as the ring pulsing open and shut.
Move the trigger to the containing group, wrapped in `:where()` so the existing
`.is-open` / `.is-selected` / status rules keep winning the same properties, and
add `.is-held` for the pan, driven by which rings the press landed in. The hold
releases on the first move after the button comes up, when the browser
recomputes `:hover` in that same event, so the two signals never both read off.
Hovering a workspace now also always shows its name and agent count. That label
was gated behind the declutter pass, which suppresses any label overlapping an
agent — at fleet zoom that is nearly all of them, so hover revealed nothing. It
now bypasses declutter and draws in a layer above every project, so a
neighbouring ring cannot wash it out.
* fix(dashboard): complete agent map ring interaction lifecycle