fix(tui): escape during approval prompt denies instead of quitting - #1760
Merged
Conversation
Pressing Escape while an approval prompt is up called RequestAppShutdown() and tore down the whole chat session, killing the pending interaction without approving or denying it (#1757). Escape now routes to the first-class Deny option when a pending interaction exists, matching Termina's default cancel semantics and the convention every other NetClaw page follows. Bare Escape only quits when no prompt is active; generation-cancel remains a separate TODO. Adds ChatViewModel.DenyPendingInteractionAsync() which resolves the ApprovalOptionKeys.Deny option and submits it via the existing interaction-response path. Regression tests cover: Escape denies + app stays alive, bare Escape still quits, and a no-deny-option interaction is a safe no-op.
Addresses the pr-review-specialist findings on #1760: - Reorder HandleKeyPress so HasPendingInteraction is checked BEFORE IsGenerating. A pending approval prompt always wins over the generation-cancel TODO branch, closing the stale-flag race where Escape got swallowed and the user had to press it twice. - Escape during generation now shows a status message instead of silently eating the key (generation cancel remains a separate TODO). - Add an in-flight guard in SubmitInteractionSelectionAsync so back-to-back Escape + Enter can't dispatch two responses for the same interaction (daemon already nacks expired prompts; this is defensive). - Narrow DenyPendingInteractionAsync from public to internal virtual. - Strengthen the no-deny-option test with a deny:called lifecycle marker so it positively proves the no-op branch ran instead of passing vacuously. Add tests for the reorder and the generation status message (16 ChatPageTests total, 1288 CLI tests green).
Aaronontheweb
force-pushed
the
fix/1757-escape-denies-approval
branch
from
August 5, 2026 14:44
4d77fd5 to
8f6774e
Compare
Collaborator
Author
|
Addressed the adversarial review findings in 8f6774e (rebased onto current dev): Fixed in this PR:
Deferred as design work (daemon/API side, out of scope for a TUI fix): generation cancellation as a first-class API capability, interaction lifecycle as pushed client events, session-history semantics for cancelled turns — the substrate a future web/desktop client (NetClaw HTTP + WebSocket API) would build on. Verification: 16/16 ChatPageTests, 1288/1288 full Netclaw.Cli.Tests, 0 warnings/0 errors. |
Idle Escape no longer calls RequestAppShutdown(). Escape now means cancel in every state: denies a pending approval, shows a status message while generating, and is a no-op when idle. The status bar already advertises [Ctrl+Q] Quit as the quit affordance. Updates Escape_WithNoPendingInteraction_StillQuits to assert Escape is a no-op and only an explicit Ctrl+Q shuts the app down.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1757.
Problem
Pressing Escape while an approval prompt is up in
netclaw chatcallsRequestAppShutdown()and tears down the whole session. The pending interaction dies with the process — neither approved nor denied.Fix
In
ChatPage.HandleKeyPress, whenHasPendingInteractionis true, Escape now routes to the first-classDenyoption instead of quitting:ChatViewModel.DenyPendingInteractionAsync()resolves theApprovalOptionKeys.Denyoption and submits it through the existing interaction-response path.This matches Termina's default "cancel" semantics and the convention every other NetClaw page follows (Escape = back/cancel, never quit).
Tests
Escape_WithPendingInteraction_DeniesInsteadOfQuitting— proves Escape submitsdenyand only the explicit Ctrl+Q shuts down (ordered event log).Escape_WithNoPendingInteraction_StillQuits— bare Escape keeps quitting.DenyPendingInteraction_NoDenyOption_DoesNotSubmit— a prompt without a deny option is a safe no-op, no quit, no bogus submit.Full
Netclaw.Cli.Testssuite: 1286/1286 passing. Build: 0 warnings, 0 errors.