Skip to content

feat(ui): approval focus-swap for the full-screen app#13

Merged
lavindeep merged 1 commit into
ui-reworkfrom
feat/ui-approvals
Jun 28, 2026
Merged

feat(ui): approval focus-swap for the full-screen app#13
lavindeep merged 1 commit into
ui-reworkfrom
feat/ui-approvals

Conversation

@lavindeep

Copy link
Copy Markdown
Owner

Branch 7 of the full-screen TUI rework (UI v2, design §31.16).

What

ask_approval/ask_plan_approval return a value, so they can't be marshaled fire-and-forget like the other UI calls. This adds the focus-swap handshake (cli/app_approval.py, ApprovalGate): the worker thread blocks on a concurrent.futures.Future while the loop thread renders the prompt into the pane and the dock becomes the approval input, then resolves the Future.

  • The dock's Enter routes the typed line to gate.submit(...) when an approval is active (before the /exit check); Ctrl-C and Ctrl-D route to gate.cancel().
  • Three-way outcome unchanged from the REPL (§14.6): HIGH-risk command executes only on the literal run; every other request takes y/e/n; [e]dit enters a steer phase whose text becomes ApprovalReply.steer_text, re-entering the deterministic classifier; empty steer = plain decline.
  • Ctrl-C/EOF during an approval declines that action (the worker is blocked on the Future, not in a model-stream read, so the §31.15 model-cancel can't fire); the turn continues.
  • Fails closed: a render/echo sink raising on the loop thread resolves the Future (via set_exception) instead of leaving the worker blocked — the turn ends through the existing "Turn failed" path, never a silent approve.

The default (non-app) REPL is untouched. AppUI gains show_approval/show_plan_approval (the same render_diff/approval_info/approval_cwd/plan_panel sinks as TerminalUI); echoed input and the plan path are control-char sanitized.

Tests

Pure parse helpers (typed-run gate, y/e/n, plan re-prompt), the gate handshake across a real worker thread (approve / decline / edit-steer / empty-steer / cancel; plan yes / edit-revision / unrecognized-reprompt / cancel), fail-closed Future resolution on a raising sink, AppUI render content, and headless keybinding routing. Worker threads are daemon so a regression fails fast rather than wedging the suite.

Phase gate green: ruff + format clean, mypy --strict clean (79 files), 1390 passed.

DESIGN.md §31.16 added.

The worker thread blocks on a concurrent.futures.Future while the loop
thread renders the approval prompt into the pane and the dock becomes the
approval input. ApprovalGate carries the three-way y/e/n + HIGH typed-run
contract; [e]dit steers via steer_text re-entering the classifier; Ctrl-C/
EOF during an approval declines this action and the turn continues.

Adds DESIGN section 31.16.
@lavindeep lavindeep merged commit 532028e into ui-rework Jun 28, 2026
2 checks passed
@lavindeep lavindeep deleted the feat/ui-approvals branch June 28, 2026 09:18
lavindeep added a commit that referenced this pull request Jul 2, 2026
feat(ui): approval focus-swap for the full-screen app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant