Skip to content

fix(agent-isolation): disarm the touch overlay when a command never runs - #1316

Merged
potiuk merged 1 commit into
apache:mainfrom
potiuk:fix/overlay-disarm-on-unrun-commands
Sep 21, 2026
Merged

potiuk merged 1 commit into
apache:mainfrom
potiuk:fix/overlay-disarm-on-unrun-commands

Conversation

@potiuk

@potiuk potiuk commented Sep 21, 2026

Copy link
Copy Markdown
Member

Summary

  • The documented arm/disarm wiring leaks a watcher whenever a command doesn't run. PreToolUse fires before the permission prompt, so a command the operator rejects has already armed a watcher that PostToolUse never tears down.
  • It then lives until MAX_WAIT — ten minutes — and because signing_in_flight is a machine-wide pgrep for gpg / ssh-keygen rather than something scoped to the armed context, any unrelated signature inside that window raises the window with nothing of the operator's pending.
  • The visible symptom is exactly the one the overlay exists to prevent: a touch landing while no signature waits fires the YubiKey's OTP slot and types cccc… into whatever has focus. I hit it three times in one session before working out why.
  • Fix: wire disarm onto PermissionDenied and PostToolUseFailure — the two events that fire when a Bash call does not run — in the setup doc, the install skill's K.2, the verify skill's check 10a, and the sandbox spec.

No script change is needed. disarm is already idempotent and exits 0 with nothing armed (test_disarm_is_silent_when_nothing_is_armed), so the extra calls cost nothing. This is a wiring-and-docs fix.

Type of change

  • Skill change — isolated-setup-install (K.2), isolated-setup-verify (10a)
  • Tool / bridge contract (tools/<system>/*.md)
  • Python package (tools/*/ with pyproject.toml)
  • Groovy reference impl
  • Cross-cutting (RFC, AGENTS.md, sandbox, privacy-LLM)
  • Documentation (docs/, README.md, CONTRIBUTING.md)
  • Project template (projects/_template/)
  • CI / dev loop (prek, workflows, validators)

Test plan

  • prek run --all-files passes (32 hooks, exit 0)
  • Applied the same wiring to my own ~/.claude/settings.json and verified it: JSON valid, disarm present on all three events, arm still on PreToolUse, disarm exits 0 with nothing armed, no watchers alive and no leftover registrations afterwards.
  • Not verified: that PermissionDenied actually fires. It only triggers when a human rejects a tool call, which the agent cannot do to itself. The event name is in the settings schema and the wiring validates, but the behaviour is unconfirmed. If it turns out not to fire, the fallback is shortening MAX_WAIT — worth a reviewer with a spare moment rejecting one git commit and checking pgrep -f 'gpg-touch-overlay.*_watch' comes back empty.
  • No eval-fixture change: neither skill edit alters a graded step's output shape (10a is a sub-check under the existing check 10, and the step-1-classify schema is n: 1-10).

RFC-AI-0004 compliance

  • HITL — strictly narrows when the overlay interrupts; adds no autonomous action.
  • Sandbox — no new host access; the change is hook wiring plus prose.

Linked issues

None.

Notes for reviewers (optional)

  • The deeper issue is left alone deliberately. signing_in_flight being machine-wide is what turns a leaked watcher into a wrong window rather than merely a wasted process. Scoping it to the armed context would be the stronger fix, but it is a behaviour change to the watcher with its own test surface, and this PR is meant to be the small correct one. Happy to follow up if you'd rather have that.
  • Every adopter has this leak today, since the wiring is what the install skill produces — that is why this is a docs+skill fix rather than a local settings tweak.

🤖 Generated with Claude Code

The documented arm/disarm wiring assumed every armed command
eventually executes. `PreToolUse` fires before the permission prompt,
so a command the operator rejects has already armed a watcher that
`PostToolUse` never tears down. It then lives until `MAX_WAIT` — ten
minutes — and because `signing_in_flight` is a machine-wide `pgrep`
for `gpg` / `ssh-keygen` rather than something scoped to the armed
context, any unrelated signature inside that window raises the window
with nothing of the operator's pending.

The visible symptom is the one the overlay exists to prevent: a touch
landing while no signature waits fires the key's OTP slot and types
`cccc…` into whatever has focus. Observed three times in one session.

Wires `disarm` onto `PermissionDenied` and `PostToolUseFailure` — the
events that fire when a Bash call does not run — in the setup doc, the
install skill's K.2, the verify skill's check 10a, and the sandbox
spec. `disarm` is already idempotent and exits 0 with nothing armed,
so the extra calls cost nothing and no script change is needed.

Generated-by: Claude Opus 5
@potiuk potiuk added the family:setup setup-* skills label Sep 21, 2026
@potiuk
potiuk merged commit c36dedf into apache:main Sep 21, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

family:setup setup-* skills

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant