orca/docs/terminal-close-confirmation.md

115 lines
7.5 KiB
Markdown

# Terminal Close Confirmation
## Problem
- `CloseTerminalDialog` repeats the action users already requested with "Close Terminal?" and a generic "process will be killed" warning (`src/renderer/src/components/terminal-pane/CloseTerminalDialog.tsx:30`).
- `Cmd/Ctrl+W` closes only the focused split pane, or the tab when it is the last pane; the guard exists so tab-level close does not kill every pane by accident (`src/renderer/src/components/terminal-pane/keyboard-handlers.ts:364`).
- The running-process guard probes the PTY over the active runtime/SSH path and shows the dialog only when child processes exist (`src/renderer/src/components/terminal-pane/TerminalPane.tsx:781`).
- There is no way for power users to say "I understand, close it next time" even though similar destructive workflows persist skip-confirm settings (`src/shared/types.ts:2522`, `src/renderer/src/components/settings/GeneralWorkspaceSettingsSection.tsx:58`).
## Goal
Make the terminal close confirmation communicate the consequence, respect focused-pane scope, and allow users to disable future running-process close confirmations from the dialog or Settings.
## Non-goals
- Do not change window-close behavior; whole-window shutdown intentionally bypasses the child-process dialog.
- Do not change idle-shell behavior; idle shells still close immediately.
- Do not add bulk-close UI for this change.
- Do not introduce provider-specific agent kill logic; closing still uses the existing terminal close path.
- Do not add telemetry.
## Design
1. Add a persisted `skipCloseTerminalWithRunningProcessConfirm` boolean to `GlobalSettings`, defaulting to `false`.
2. Keep the existing child-process probe. If the new setting is true, close immediately after the probe reports child processes instead of showing the dialog.
3. Track the pending close as `{ paneId, copyKind }`, where `copyKind` is `agent` only when `agentStatusByPaneKey[makePaneKey(tabId, leafId)]` has a live non-unknown `agentType`; otherwise it is `command`.
4. Update dialog copy:
- command: title `Stop running command?`, body `Closing this terminal will stop the command running inside it.`, destructive button `Stop and Close`.
- agent: title `Stop this agent?`, body `Closing this terminal will stop the agent's current work.`, destructive button `Stop Agent`.
5. Add a checkbox: `Don't ask again for running terminals`. When checked and confirmed, persist `skipCloseTerminalWithRunningProcessConfirm: true` before closing the pane.
6. Add a Terminal Interaction settings switch: `Ask Before Closing Running Terminals`, checked when the skip flag is false.
7. Add the new setting to terminal settings search so "confirm", "close", "running", "agent", and "command" find it.
## Data flow
- `Cmd/Ctrl+W` or pane close action
- `TerminalPane.handleRequestClosePane(paneId)`
- Get `ptyId`; no PTY closes immediately
- `inspectRuntimeTerminalProcess(settings, ptyId)`
- No child processes closes immediately
- Child processes + skip setting closes immediately
- Child processes + confirmation enabled opens `CloseTerminalDialog(copyKind)`
- Confirm optionally persists skip flag, then calls `executeClosePane(paneId)`
## Edge cases
- If process inspection rejects, preserve the existing fallback: close the pane instead of trapping the shortcut.
- If the pane is removed before the dialog confirms, `executeClosePane` already no-ops when the manager cannot close it.
- For split panes, only the active pane gets the prompt and closes.
- For last-pane tabs, confirming still delegates to `onCloseTab`.
- Agent copy appears only from live pane status. Freshly launched agents that have not emitted hooks yet may use command copy; that is acceptable because the consequence is still accurate.
- SSH/runtime-host terminals still use the existing runtime process inspection; the setting lives in global renderer settings and is passed through the same update path.
- The skip flag affects only terminal running-process close confirmations, not workspace deletion, automation deletion, window close, or future bulk-close prompts.
## Test plan
- Unit/component:
- `CloseTerminalDialog` renders command copy, agent copy, checkbox, and reports the checked state on confirm.
- Settings search includes the running-terminal confirmation entry.
- Default settings include `skipCloseTerminalWithRunningProcessConfirm: false`.
- Integration/lightweight:
- Verify `TerminalPane` opens agent copy when live pane status has `agentType` and command copy otherwise.
- Verify checked confirm persists the skip flag before closing.
- Electron:
- Running command + default setting shows command confirmation.
- Running command + checkbox checked confirms and future close skips the dialog.
- Agent pane with live status shows agent confirmation copy.
- Idle shell closes without confirmation.
## UI quality bar
- Dialog uses existing shadcn `Dialog` and `Button` primitives, token colors, and current compact modal sizing.
- Copy names the destructive consequence first and avoids implying every terminal/tab/window will close.
- Checkbox is visually subordinate to the message and aligned with existing dense dialog spacing.
- Settings row matches neighboring Terminal Interaction switch rows and is searchable.
- No layout shift, clipping, or button text overflow at the current modal width.
## Review screenshots
1. Running-command confirmation dialog.
2. Running-agent confirmation dialog.
3. Terminal Interaction settings row for `Ask Before Closing Running Terminals`.
## Rollout
1. Add shared setting type/default.
2. Add dialog copy modes and checkbox.
3. Wire `TerminalPane` to derive copy kind, honor skip flag, and persist "don't ask again" on confirm.
4. Add Terminal settings row and search entry.
5. Add targeted tests.
6. Force-add this design doc when staging because root `.gitignore` treats new `docs/**` files as local-only by default.
## Lightweight Eng Review
- Scope: kept focused on the existing running-process confirmation; no new close routing, bulk-close behavior, or native window-close changes.
- Architecture/data flow: renderer-only UI setting rides the existing settings persistence path; process detection remains owned by `inspectRuntimeTerminalProcess` so SSH/runtime compatibility does not fork.
- Failure modes covered:
- process-inspection rejection preserves current close fallback
- stale pane between prompt and confirm no-ops through existing manager guard
- split-pane close remains pane-scoped
- missing/stale agent status falls back to generic command copy
- skip flag is scoped to terminal running-process confirmations only
- Test coverage required:
- component test for `CloseTerminalDialog` copy/checkbox
- shared default/type coverage via existing typecheck plus default-setting assertion
- settings search test for new discoverable entry
- focused TerminalPane behavior test if practical; otherwise Electron validation covers prompt routing
- Performance/blast radius: no polling, IPC, startup, or renderer-jank impact; only an extra settings boolean read during an already user-triggered close path.
- UI quality bar: Electron validation should judge the modal and Terminal settings row against `docs/STYLEGUIDE.md`, existing `Dialog`/`Button`/`SettingsSwitchRow`, and adjacent Terminal Interaction density.
- Required review screenshots:
1. Running-command confirmation dialog
2. Running-agent confirmation dialog
3. Terminal Interaction settings row
- Residual risks: agent-specific copy depends on live hook status, so newly launched or manually run agents can still receive generic command copy; the design doc is ignored by default and must be force-staged for the PR.