.agents/skills/verify-signaltty/features/approvals.md
Approval
Sub-features
Discovery, preserved decision on focus/attach, one-time choice delivery.
How to get to it (user POV)
An agent requests approval. Click the attention count or use Ctrl+Shift+J, then click Allow once in a native permission bar (Allow in the structured QA fixture).
Driving it with GTK/IPC
python3 scripts/qa-gui-reliability.py is designed to create a Codex-hinted cat
fixture, send a production PermissionRequest hook, invoke next-attention, and
test the same decision/gate by clicking the AT-SPI Allow action and observing
decision consumption. The recorded AT-SPI run failed during desktop enumeration
before its first assertion because the installed Python binding does not make
Atspi.Accessible iterable, so the Allow-and-consumption result remains
unverified (see specs/016-agent-event-recovery/verification.md).
decision_* server tests additionally verify delivered bytes and stale answers.
qa-ui-scenes.py --width 360 --long-choice --output /tmp/approval.png captures
complete wrapped choices. The ignored approval_question_and_choices_fit_a_narrow_pane
test checks actual 320px GTK allocations, choice IDs and persistent VTE identity.
keyboard_focus_renders_an_inactive_panes_controls compares real rendered pixels;
reduced_motion_removes_rendered_press_scaling inspects native render transforms,
including pointer, reduced-motion and keyboard-focused activation.
Gotchas
The cat fixture echoes terminal input; it is not a live provider session. Reading an approval preserves attention until an answer or agent transition resolves it.
Native permission route
Run cargo test -p signaltty-server --test native_permissions. These tests install
Claude/Codex reporters into temporary provider roots and execute their generated
commands in real managed PTYs using documented PermissionRequest inputs. They
observe exact Allow/Deny JSON on reporter stdout and no terminal input. Disconnect,
deadline, supersession, session changes, exit, restart, concurrent answers and
replayed render data cannot deliver a grant. Restored requests have no live channel.
This proves the installed reporter/API contract, not a paid model's native TUI. Hooks still require the provider's normal trust configuration.