pa-tui/pa-cli: dock panel exits restore the dock's own group, not the prompt bar (operator ruling 2026-09-26) - #2864
Conversation
Prime Agent performance — partialPR Benchmark execution did not complete successfully. Missing measurements are not performance wins. Failure diagnostics:
See the saved per-trial logs and terminal transcripts for details. Overall: 0 regressed · 0 improved · 0 no clear change · 42 unavailable.
Python runtime
Session transport
UI interactions
Sandbox cost: ~$0.0513 — no inference calls. Methodology and samplesMain resolved at 2026-09-27T06:07:50.523029+00:00. Harness
Failures:
|
f93f032 to
7f2b1a8
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7f2b1a8. Configure here.
21758cd to
2ad439f
Compare
… prompt bar (operator ruling 2026-09-26) ESC/left from any dock panel now returns to the chat view with the originating panel's dock item still selected: the heartbeats view, the bash (shells) view, and the goal panel hand the keyboard focus back to the dock on close (focus_activity_dock — the dock-owns-the-hand-off shape the editor's Down/Alt+A path already used per the arrows directive, minus the editor hand-off's tray gate), and the scoped agents view (the Subagents panel) carries scope_back on its outcome so the agents-view flow reopens the scope root's chat with restore_dock_focus: the reopened chat hands the dock its focus exactly once, at the first summary after the attach (the roster is seeded inside the attach, so the Subagents group rides the rendered row at its first paint; a dock that never mounts keeps the editor's focus, and a LATE roster can never yank the keyboard back mid-composition; every later attach's rebind reset drops the armed restore with the session it belonged to). The heartbeats view IS the dock's Heartbeates item on every entry path: open_heartbeats_view selects the Heartbeates group (the /heartbeats command would otherwise leave the dock's previous selection, so its panel exit would restore the wrong item — the Bugbot/Macroscope round). TS has no multi-group dock: its SubagentSummaryLine is the single subagents entry, the heartbeat manager closes back to the editor, and the TS scope_back reopen runs resetSubagentSummary — so the panel-exit restore is a sanctioned Rust divergence (cited in the pa-tui README's dock clause). Regression tests: the headless mock-supervisor battery (dock_panel_exit_focus_headless.rs — Heartbeates/Shells/Goal panels x ESC/left plus the /heartbeats command path; the navigation steps the rendered groups one press at a time per the arrows directive, and each barrier pins the panel's content), proven by the next Enter re-opening the SAME panel (which an Enter on the empty prompt bar never does), and the real-daemon e2e (dock_scope_exit_focus_e2e.rs — the Subagents panel x ESC/left: the scope-back reopen's bare Enter re-opens the scoped view, and the outcome carries scope_back). Every InteractiveOptions literal grows the restore_dock_focus field. Fold: the tip wave since the first fold (incl. the dock arrows directive #2861 — the selection now rides the rendered groups, empty ones included, one arrow one group) is reconciled here: focus_activity _dock keeps the arrows' dock-visible/rendered-group semantics, the scope-back restore rides dock.visible(), and the battery's navigation walks the rendered groups.
2ad439f to
d304155
Compare
…e the recall (#2864) The 2026-09-26 panel-exit ruling (this PR) hands the keyboard focus to the dock's own group when a dock panel closes: the Heartbeates view's Escape now lands on the dock's Heartbeates item (focus_activity_dock), never the prompt bar. #2862's alt_a coexistence test pinned the pre-ruling path — one Escape from the Heartbeates panel straight back to the editor, so one Up recalled — and under the ruling that Up is consumed releasing the dock's focus, the final wait_render("first prompt") barrier times out: the deterministic shard-2 red on rounds 9 (2ad439f) and 10 (d304155), identical failing step in both. The test now pins the sanctioned round trip: the panel's Escape closes onto the dock's own item, the cancel Escape hands the editor back, and only then the Up recalls — the recall still must survive the trip, so the protective intent is unchanged.
…us (#2864) The fold onto 7064d03 (#2865's click surfaces, #2863's info panel, #2872's heap returns) merged clean, but the tip's new InteractiveOptions construction sites predate this PR's restore_dock_focus field: four new interactive_daemon_e2e fixtures, the touch_click battery, and the info panel battery build the options literal without it. Every new site takes the field (false) at the same seam as the battery: after session_has_children, before client_settings. No behavior change — the new tests keep the editor's focus on entry, same as the pre-fold ones.
… tip (SS7 #2903 update/restart cut + #2907 agent_engine model split + #2900 session_ui queue split + #2864 dock panel) over the SS4 sessions cut - facade conflict resolved per the campaign pattern: the update/restart family (SS7's cut at tip) and the saved-session list family (my cut) both resolve OUT of the facade - one region, shared '}' tail dropped; keep-both mod decls hold (mod sessions; + mod update_restart; auto-merged alongside clients/adoption/routing/subscribers) - no tip-side changes landed inside the moved sessions spans (region-disjoint, per the conductor ruling): sessions.rs UNCHANGED in this fold; per-method byte-proof vs the a2b1224 tip blobs all IDENTICAL modulo my recorded pub bumps; facade vs tip = +7 structural (mod sessions; test-only import) / -828 (the sessions family only) - gates on the folded tree: cargo fmt --check clean; clippy -p pa-daemon --all-targets --locked -D warnings clean; env-stripped lib battery 769 passed / 2 failed, both the REGISTERED red-cache-rejects-mtime-fs-timing-20260927-1 family (same pair as the fold-2 differential; cited, not chased)
…n window, not after a quiet-wait The exit-path LEFT_RELEASE write sat after drain_until_quiet(10) — a >=250ms quiet wait during which a fast local exit (restore + child leave, well under the wait) completes, so the release arrived on a pty nobody reads: never in the ~50ms exit-drain window the case claims to cover, and a write to the master after the slave is gone can fail the harness outright. The release now rides immediately after the exit key — inside the exit path's own drain window — which the case's stream-hygiene asserts (no release echo after the alt-screen leave; the kitty flags pop is the stream's last mode byte) actually verify. Fold note: the merge of the 9b62f26 tip resolved the one add/add conflict (kitty_release_handoff_e2e.rs) by keeping the lane's restore_dock_focus fixture init — the #2916 squash on the tip predates the fixture fix (the tip's own InteractiveOptions carries the #2864 field, so the squash copy alone does not build). (Cursor thread PRRT_kwDOSXZbXs6mYBZZ: "Exit release injected after drain".)
… forward-only resolutions) The tree moved heavily since this lane was built (supervisor.rs split 7/8, session_ui.rs split 3/14, agent_engine 5/11, TU3's prompt channel into SessionUi::open). Resolutions, forward-only: - pa-cli/interactive_mode.rs: the tip's dock-focus + opened_cwd pre-open setup feeds the lane's run_interactive_agents_view_open route. - pa-daemon/supervisor.rs: the SS2 split owns handle_client (tip side taken); the lane's three #2391/#2515 hunks re-homed: the typed UpdateRestarting errorInfo on the update-prepare gate -> supervisor/clients.rs; the session-naming resident refusal + helper + test -> supervisor/update_restart.rs (the facade's auto-merged helper copy removed - dead after the re-home). - pa-tui/daemon_client.rs: the tip independently evolved RequestRejected.error_info - merged with the lane's fuller doc. - pa-tui/session_ui.rs: describe_session_open_failure passes the typed error_info through the decorated refusal (the common context already binds it). - pa-tui/interactive.rs: SessionOpenRoute + the tip's # Errors doc combined; the wait closure threads the tip's new prompt_tx; the tip's pre-open reader_dead binding dropped (the post-open re-home survives); the tip's opened_urls + the lane's wait notice combined. - TIP REPAIR (pre-existing break, verified on pristine 9b62f26): pa-cli/tests/kitty_release_handoff_e2e.rs missed InteractiveOptions.restore_dock_focus (#2864's squash updated 38 of 39 literals) - one line added; the target did not compile on the tip. Gates (Prime VM rust:1-bookworm, rustc 1.98.1): fmt EXIT 0; clippy --workspace --all-targets -D warnings EXIT 0; batteries: pa-types + pa-daemon + pa-tui 96 binaries green (uv + xdg-utils installed for the kernel-env and platform-opener tests), pa-cli 33 binaries green; the registered wedge (interactive_daemon_e2e, red-interactivedaemon-e2e- load-wedge-20260926-1) CITED, not rerun.
…live tip after the conductor's HOLD) The previous fold (1cf0b62) was built against the stale tip 9b62f26; the kitty E0063 found there was the #2916/#2864 mid-air break, healed on the live tip by #2927 (826f877) and settled by the 631fe82 dedupe. The one-line kitty repair rider from the previous fold is DROPPED: the merge auto-resolves to the healed single-line state (exactly one restore_dock_focus initializer — verified), the E0062 double-apply class stays closed. The 5-file resolution discipline from 1cf0b62 carries (all intact: the supervisor re-homes in clients.rs/update_restart.rs, the prompt_tx-threaded wait closure, the typed-error_info merge in daemon_client.rs, the decorated-refusal pass-through in session_ui.rs — the new tip's session_ui splits auto-merged around them).

Summary
The operator's 2026-09-26 ruling: leaving a dock panel with ESC or the left arrow returns to the chat view with the panel's own dock item still selected — the dock holds the keyboard focus, not the prompt bar. Today every panel exit lands on the prompt bar: the heartbeats picker's Close, the bash (Shells) view's Close, and the goal panel's close all leave
subagents_focusedfalse, and the Subagents panel is cross-run (the dock's Subagents group opens the scoped agents view — the session run exits; ESC/left there reopens the scope root's chat as a freshSessionUiwith the dock unfocused).The change
SessionUi::focus_activity_dock— the group-pick hand-off extracted fromfocus_subagents_summary(which keeps its TS tray-override gate and delegates). The originating dock group stays selected AND focused; the existing "focused selection must stay actionable" reconciliation inupdate_subagent_summarystill moves/releases the selection when a group went unselectable while the panel was open.AgentsViewOutcome::scope_backis set inopen_scope_root(the parent key with pop AND escape without pop; a plain row open keeps it false), the pa-cli agents-view flow passes it asInteractiveOptions::restore_dock_focus, and the reopened session parks it aspending_dock_focus_restoreuntilupdate_subagent_summarysees the roster make the Subagents group actionable — so a late roster cannot bounce a focused-but-unselectable dock straight back to the editor.InteractiveOptionsliteral growsrestore_dock_focus(the repo's own precedent for agents-view-passed flags:session_has_children,client_settings).open_heartbeats_viewdoc line ("closing the picker returns to the editor") now describes the current behavior; the pa-tui README's dock focus-clause gains one sentence recording the ruling and the TS divergence.Parity-diff evidence (AGENTS merge gate)
subagent-summary-line.tsis a single subagents entry (isSelectable= counts + openable),closeHeartbeatManagerhides the overlay and focus returns to the editor (interactive-mode.ts:11995), and the TSscope_backreopen runsresetSubagentSummary(editor focus). The unified Heartbeats/Subagents/Shells/Goal dock is the Rust-side operator redesign (pa-tui: the activity dock's selection reads as a slight green band, not accent text (operator 2026-09-26 directive) #2831, the direct-navigation redesign), so the panel-exit restore is a sanctioned Rust divergence, cited in the pa-tui README dock clause. The editor's Down/Alt+A hand-off keeps the TSfocusSubagentSummarygates (picker-open, selectability, the tray override label).heartbeats_list,list_kernel_bash, the roster, and the agents-view outcome shapes are unchanged;scope_backis a new client-side outcome field the wire never sees.Ownership compliance
InteractiveOptions::restore_dock_focusispublike its sibling open-metadata flags;AgentsViewOutcome::scope_backrides the existingpuboutcome the flow already reads. Dependency direction unchanged (pa-tui -> pa-types; pa-cli -> pa-tui); no crate-internal edits outside pa-tui/pa-cli.Regression pins
All fail before the fix:
crates/pa-tui/tests/dock_panel_exit_focus_headless.rs(mock supervisor): Heartbeats/Shells/Goal panels x ESC and left. Proof: after the panel closes, a bare Enter re-opens the SAME panel (an Enter on the empty prompt bar submits nothing) — final-frame assert plus a never-handoff assert; the entry path exercises the dock's left/right group navigation (Down, Right(s), Enter).crates/pa-cli/tests/dock_scope_exit_focus_e2e.rs(real daemon + ledger-seeded child): Subagents panel x ESC and left. Run 1 dock-opens the scoped view; run 2 exits it (assertsselection == Attach(parent)andscope_back); run 3 reopens the chat with the flag and asserts a bare Enter re-opens the scoped view (return_to_agents_viewtrue; with the bug the Enter is swallowed and the assert fails).Telemetry
No new event: the focus model of existing surfaces — no new user-visible surface or user action (the existing
activity_openedevents already count the panel opens).Fold note
Folded as the tip moved (the standing lane rule: the second lander takes the fold):
bc412c5f7(pa-tui: refuse supervisor requests on a reader that already died (the post-turn refresh stall) #2829-pa-tui: the agents-view search corpus is the SESSION column's own title (operator directive 2026-09-26) #2841 incl. pa-tui: the activity dock's selection reads as a slight green band, not accent text (operator 2026-09-26 directive) #2831's green-band dock selection, pa-core/pa-tui/pa-cli: persist the Ctrl+O conversation-detail level as the chatDetail setting (TS #2709 port) #2834's chatDetail persistence) — clean rebase; the tip's three new test files withInteractiveOptionsliterals grew the field.a93d0c401(incl. pa-tui: the dock's arrows visit every rendered group, empty ones included (operator directive 2026-09-26) #2861 — the dock's arrows visit every rendered group, empty ones included, one arrow one group) — a real reconciliation insession_ui.rs, keep-both:activity_selectableis gone in pa-tui: the dock's arrows visit every rendered group, empty ones included (operator directive 2026-09-26) #2861's world, sofocus_activity_dock(the close restore) adopts the arrows' own semantics (the dock owns the hand-off exactly while it renders; every rendered group is traversable; a selection whose group left the row steps back), the scope-back restore fires ondock.visible()at the first post-attach summary, and the battery's navigation walks the rendered groups one press at a time (the barriers now pin the panel content, not the navigation — the pa-tui: the dock's arrows visit every rendered group, empty ones included (operator directive 2026-09-26) #2861 arrows make the group order feed-independent). The tip's new test literals (dock_arrows_headless,effort_tray_label_headless, plus a newinteractive_daemon_e2eliteral) grew the field in the same fold. Folded head:17c6f9866.Cross-lane: #2861 landed first, so this PR takes the fold (the README's dock clause merges both directives' sentences). #2862 (prompt-history-nav) was still open at fold time; if it lands first, its owner folds this.
d5cd4b132(pa-tui: clickable links, click-to-open, the plain-key login-URL copy, and a selectable onboarding pane #2846 clickable links + pa-tui: the chat-open first frame - the dock folds with the attach, direct opens hold the previous surface, content opens suppress the brand splash (operator directive 2026-09-26) #2854 the chat-open first frame — the dock folds with the attach, + pa-daemon: per-worker stderr capture - not-ready launch errors carry a 4 KiB tail of the worker's log #2851/pa-ai: the anthropic oauth tests stage the registered port and skip when the runner holds it (EADDRINUSE flake hardening) #2867) — clean rebase; the tip's three new test literals (mouse_click_open_headless,chat_open_first_frame_headless,chat_open_first_frame_pty_e2e) grew the field in the same fold. pa-tui: the chat-open first frame - the dock folds with the attach, direct opens hold the previous surface, content opens suppress the brand splash (operator directive 2026-09-26) #2854's first-frame fold does not touch the restore's internals: the one-shot still fires at the first post-attach summary, and the folded first paint carries the restored dock. Folded head:881e6e8ec.7064d039a(pa-tui: mouse/touch clicks on the core surfaces - click-to-expand condensed runs and tool cards, click-to-place-caret in the prompt bar, click-to-open rows in the agents view, picker row select #2865 the click surfaces + perf(pa-tui): submit prompt off the render path - TS onSubmit parity (ordered FIFO worker; ack p50 1.8->0.2ms) #2868 submit-off-render + pa-tui: wrap overlong words in one pass (first-frame transcript load) #2870 word wrap + pa-tui: the read-only inline info panel - the /context-family commands render as docked popup panels (ESC closes, transcript untouched); the ? quick-shortcut guide removed (operator directives 2026-09-26) #2863 the info panel + pa-daemon: supervisor.rs split 1/8 - the routing concern moves out of src/supervisor.rs #2877/perf(pa-daemon/pa-cli/pa-tui): return session-load and attach heap to the OS - loaded-session settled RSS -24 to -31pct #2872/perf: memoize the kernel venv runtime-ready probe (managed path; custom override keeps the direct probe) #2857/perf(pa-daemon): bound the legacy-registry parent probe to the header line #2869) — clean merge; six construction-site seams fixed in the fold commit0c2805a05(fourinteractive_daemon_e2efixtures +touch_click_headless+info_panel_headlessliterals grewrestore_dock_focus), plus theprompt_history_nav_headlessalt_a two-Escape round-trip fix (fd79bf351).The post-test(pa-tui): the bang-during-streaming-turn e2e gates the mock's turn end on the ack bang, not a wall clock - every awaited state is causal or terminal, closing the two-channel load red (red-bang-stream-flush-20260926-1) #2904-unlock resume fold — THREE tip squashes folded as they landed mid-resume:
0038c3f72d59(test(pa-tui): the bang-during-streaming-turn e2e gates the mock's turn end on the ack bang, not a wall clock - every awaited state is causal or terminal, closing the two-channel load red (red-bang-stream-flush-20260926-1) #2904 — the bang-during-streaming-turn e2e is now ack-gated and deterministic, closing the registeredred-bang-stream-flush-20260926-1),61d92a206(pa-ai/pa-daemon: the faux repeat-last knob + the goal-recovery e2e opts in and pins the exhaustion race deterministically - closes red-goal-recovery-faux-exhaustion-20260926-1 #2902 — the faux repeat-last knob + the goal-recovery e2e determinized, closingred-goal-recovery-faux-exhaustion-20260926-1), andc94637534(pa-tui: the inline pickers' selection wash reads off the surface (operator directive) #2908 — the inline pickers' selection wash; rendering-only, line-disjoint from the restore). All three merges clean. One construction seam fixed in fold commit09bea71a1: this PR's owndock_scope_exit_focus_e2e.rsAgentsViewOptionsliteral grew the tip's newincident_notice_statefield (pa-types/pa-cli/pa-tui: daemon incident forensics — prime-agent incident CLI + agents-view incident notices (TS #2406 port) #2866). Whole-tree audits on the folded head: everyInteractiveOptionsliteral carries exactly onerestore_dock_focus(67/67); everyAgentsViewOptionsliteral carriesincident_notice_state(25/25). Lint-discipline pass (de73b1466): the e2e's#[allow(clippy::zombie_processes)]now states its reason (the repo'sinteractive_fork_e2eprecedent) and the supervisor readiness poll documents that the sleep is the poll interval, never a pass condition. Folded head:7c7672197.Advisory note (rulebook, non-blocking):
dock_panel_exit_focus_headless.rsis 530 LoC — one mock-harness battery of the seven regression tests (one harness, one per-close-path family), the same shape asskills_menu_headless; splitting would separate the shared harness from its tests without a responsibility seam.Note
Restore dock group focus on scoped panel exit in pa-tui/pa-cli sessions
restore_dock_focustoInteractiveOptionsin interactive.rs;run_agents_view_flowin interactive_mode.rs sets it from the newAgentsViewOutcome.scope_backflag. Direct CLI launches keep it false.SessionUiarms a one-shotpending_dock_focus_restoreon attach and focuses the selected activity-dock group on the first post-attach summary update. A newfocus_activity_dockhelper also restores focus when goal, bash, or heartbeat panels close; opening the heartbeats panel selects the Heartbeats dock group. See session_ui.rs.restore_dock_focus: falseacross all existing fixtures. Dock-arrow tests drop the repeated focus grab after panel exit.Macroscope summarized a12c284.
Note
Low Risk
UI focus/keyboard navigation only; no auth, wire protocol, or persistence changes beyond a client-side option flag.
Overview
Implements the 2026-09-26 panel-exit ruling: closing dock panels or backing out of the scoped Subagents view should land keyboard focus on that panel’s dock group, not the prompt bar.
Cross-run (Subagents panel):
AgentsViewOutcomegainsscope_back, set when the scoped view returns to the parent chat. The pa-cli agents-view loop maps it toInteractiveOptions::restore_dock_focus. The reopened chat arms a one-shotpending_dock_focus_restoreafter attach and focuses the dock on the first subagent summary update once the dock is visible.In-session panels: New
focus_activity_dockruns when Heartbeats, Shells (bash), or goal panels close (ESC/left), and opening/heartbeatsselects the Heartbeats dock group. Session rebind clears any pending scope-back restore.Tests add headless and daemon e2e coverage; existing fixtures set
restore_dock_focus: false. Dock-arrow tests drop extra focus grabs now that panel exit keeps the dock focused.Reviewed by Cursor Bugbot for commit a12c284. Bugbot is set up for automated code reviews on this repo. Configure here.