Replace the popup-level overlay suppression with hit-testing truth. A card's
hover exit test now asks which component is topmost at that point instead of
only comparing bounds, so an overlay painted above the transcript counts as
having left the row. An overlay child can declare that it blocks the content
beneath it, and the layered panel then releases the hover of whatever the
pointer rests on when such a cover appears, moves, or hides — Swing delivers
no exit for that case on its own.
The connection banner is the first blocking overlay, so a card it covers no
longer stays lit and no longer keeps its hover popup open behind the banner.
Also repair the worktree icon palette test the mid-tone glyph change broke.
Gate the header hover popup on a suppression predicate so it neither opens
nor stays alive while the connection banner or modal blocker covers the
session, and dismiss any open popup when such an overlay appears.
Render the Agent Manager worktree list titles and section header in normal
weight and soften the idle worktree glyphs to a mid-tone neutral. Tint only
monochrome row icons to the selection foreground so colored status icons
(running/question/error) keep their hue. Prune a deleted session's lingering
question/error status locally so the session list, worktree list, and tab
attention dot re-evaluate instead of showing a stale badge.
Header popups now set the platform corner-to-pointer distance instead of moving the target point when a balloon body has to shift into the visible session. This keeps the arrow attached to the card or row the popup describes while still fitting the body in the viewport.
The geometry tests cover top and bottom shifts, collapsed cards, offscreen fallback, and the platform-legal pointer distance range so the balloon keeps its pointer.
ProvidersSettingsUiTest drove the real OAuth UI with a fixture URL of
https://auth.openai.com/device, and ProvidersSettingsUi calls
BrowserUtil.browse unstubbed. BrowserUtil is a static facade over the
BrowserLauncher application service, so every run of the frontend suite
launched an actual Chrome tab on the developer's machine.
Replace BrowserLauncher with a recording fake via replaceService instead
of adding a production seam, and assert the recorded URL so the browser
handoff is verified rather than performed. Install the same fake in the
GitHub/PR tests that reach other direct BrowserUtil call sites.
Header popups pointed at the edge of the whole session, so a balloon for a card in the middle of a wide transcript was flung to the far side of the session and read as belonging to whatever panel it landed on. The pointer now lands on the edge of the card the popup describes, which keeps it attached to that card.
Room is still measured against the window: cards are narrower than the session, so measuring inside the session would leave almost no width for a card in a split editor. Height still comes from the visible session, since a collapsed card header is only a couple of rows tall.
Tracking which sessions the user had looked at made the dot disappear on a tab round trip while a worktree was still failed or waiting, which is the opposite of what the signal is for. The dot mirrors the activity snapshot again: it stays up while any session in any worktree needs the user, and clears only when that state does.
activitySnapshot() only mapped busy statuses to RUNNING, so session and history rows had no source for the other kinds: a failed session showed no badge even though the backend already reported ERROR for it. The snapshot now derives from that activity map, with the busy statuses kept as a fallback for sessions whose directory the backend cannot resolve. Worktree rows and session rows therefore read the same source.
The dot also treated 'the Agent Manager is on screen' as read, which suppressed it for a session that failed while the user was working in a session editor. Reading now means the panel is focused, so the dot appears on failure and still clears once the user looks at the tab.
The dot mirrored the activity snapshot, and an error stays in that snapshot until its session runs again, so a failed session left the dot lit for good. AgentAttention now tracks which sessions the user has already seen: attention that is pending while the Agent Manager is on screen counts as read, because the rows carry the badge there. The dot is therefore re-evaluated on tab selection and tool window visibility as well as on activity, and a session that recovers and fails again lights it once more.
The leading icon dropped back to the resting branch glyph for an errored session, so a worktree whose session had just failed looked idle and the failure was only visible after opening the session. Error now takes the leading slot like the other kinds that need the user, and an operation on the row still outranks it.
Header popups budgeted their height against the IDE layered pane and anchored on the session panel's full bounds, so a session in a short tool window or an editor tab got balloons spanning the whole window. Placement now uses the session panel's visible rect for the height budget and the vertical clamp, mirroring SessionHoverCopyOverlay. The pane still decides only which side has horizontal room: clamping the width to the view rect would collapse the popup, since it deliberately sits beside the session.
A Stop makes the CLI publish session.error with MessageAbortedError, which the activity manager keeps in a sticky errors set that only session.turn.open cleared. kind() checked that set before busy, so a resumed session kept the resting glyph whenever the turn event was missed or arrived after the busy status: the status stream and the chat events are separate collectors, so their order is not guaranteed.
Busy now outranks a pending error and also clears it, and the per-directory aggregate prefers running over error so one stopped session cannot mask a sibling that is still working. An error that is never resumed still persists through idle as before.
New worktrees were appended, so the row the user just created landed at the bottom of a long list. The optimistic row is now inserted at index 0 for create, import-PR and move, reload keeps pending rows on top newest-first, and the backend records the created path at the head of the persisted order so the row stays there across reloads and restarts. Drag reorder still overrides it.
Creating a worktree with a prompt opens the new session and dispatches the prompt in the same EDT event, so the history load resolves afterwards with no messages and fired ViewChanged.ShowEmpty. That re-showed the account overlay and wedged the view state, because showSession() early-returns once model.showSession is set, so hideAccountOverlay() could never run again and the account chip stayed on top of a running session. setControllerViewState now ignores ShowEmpty once the transcript is shown.
Session rows in the worktree editor are all peers, so bolding every title added noise without conveying hierarchy. ActiveListConfig now carries a bold flag that defaults to on and the worktree session list opts out, leaving the worktree, history and settings lists unchanged.