Skip to content

fix: refuse the host's approval prompt for a shell command while a build or drain runs - #1116

Merged
REPPL merged 11 commits into
mainfrom
fix/autonomous-permission-ask-denied
Oct 26, 2026
Merged

REPPL merged 11 commits into
mainfrom
fix/autonomous-permission-ask-denied

Conversation

@REPPL

@REPPL REPPL commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

A host approval prompt for a shell command stalls an autonomous run. The host asks the person to approve a command it cannot check, and with nobody at the terminal the run waits until someone comes back.

What changes

  • The hook. The host's PermissionRequest event for the shell tool now reaches abcd: hooks/permission-request.sh runs abcd hook permission-request.
  • When it refuses. While a build or drain runs for the session's checkout, or the session works in one of a running build's lanes, the prompt is refused. The agent is told to rewrite the command into plain steps abcd can check, or to skip the step and say why.
    • A build counts while it is unfinished, waits on no person, and its state was written within the reservation lease (two hours).
    • A drain counts while it has not stopped and is within the same window.
    • A finished or discarded lane's worktree no longer counts.
  • What it never does. It never grants a prompt. Every other session, a tool other than the shell, and any error leave the host's prompt as it is.
  • The notice. build, build next and drain say once, on stderr, as they start, that refusals are on.
  • Lanes it does not reach. A lane started through the claude runner runs with the host's hooks off. Its prompts are answered by the guard (abcd guard approve), as the build and drain pages now say.
  • A dead run. A run that dies after its last write keeps counting until its lease runs out. The refusal says so, so an agent with a person at the terminal can tell them.

Records

  • The fix resolves a major issue, iss-2610171110154902.
  • It files two minor follow-ups:
    • iss-2610171200535090: a person-run switch to end refusal early;
    • iss-2610171200549757: a sandbox in place of the refusal.
  • The review deferred one more minor, iss-2610222047465878: the path match is case-sensitive on a case-insensitive file system, which fails open.

A session wrote this work on 2026-10-17 and died before committing it. It was recovered after the 2026-10-22 crash, committed as found, merged with main, and then reviewed. An Opus and Sonnet pair reviewed it, and both rounds' findings are folded in:

  • The docs overclaimed for runner lanes, and are now corrected.
  • The build next notice had no test; it now has one.
  • A finished lane counted as live, and no longer does.
  • The dead-run wording was added to the refusal.
  • Command names were removed from the brief's prose.
    Each new test was watched failing first.

Resolves: iss-2610171110154902
Refs: iss-2610171200535090
Refs: iss-2610171200549757
Refs: iss-2610222047465878

Assisted-by: Claude:claude-opus-5-5

REPPL added 11 commits October 22, 2026 21:20
…ild or drain runs

A host approval prompt for a shell command stalls an autonomous run: the
host asks the person to approve a command it cannot check, and with
nobody at the terminal the run waits. The host's PermissionRequest event
for the shell tool now reaches abcd (hooks/permission-request.sh, `abcd
hook permission-request`). While a build or drain runs for the session's
checkout, or the session is one of a running build's lanes, the prompt is
refused with a message telling the agent to rewrite the command into
plain steps abcd can check, or skip the step and say why. abcd never
grants a prompt. A run counts while it is unfinished, waits on no
person, and its state was written within the reservation lease; every
other session, tool and error leaves the host's prompt as it is. build
and drain say once as they start that refusals are on.

The session that wrote this died before committing it (last edit
2026-10-17 13:38); it is committed as found after the 2026-10-22 crash
triage, with the records it wrote: the major it fixes, and two minor
follow-ups it leaves open (a person-run switch for refusal outside a
run, and a sandbox in place of the refusal).

Resolves: iss-2610171110154902
Refs: iss-2610171200535090
Refs: iss-2610171200549757

Assisted-by: Claude:claude-opus-5-5
…guard answers the rest

The build and drain pages, their briefs and the start notice said every
lane's prompts were refused and that abcd never approves a prompt. A
lane started through the claude runner runs with the host's hooks off
(--bare), so the PermissionRequest hook never sees its prompts; the
guard answers them as that runner's permission prompt answerer
(`abcd guard approve`), which approves a command it reads whole and
clears. The claim is now the hook's: it never grants a prompt, and a
runner lane's prompts are the guard's.

Refs: iss-2610171110154902

Assisted-by: Claude:claude-opus-5-5
…used

The notice a pick prints as it starts a run had no test: deleting it
left the suite green. The test checks a pick's stderr carries it once
and its --json stdout stays whole; it fails with the notice removed.

Refs: iss-2610171110154902

Assisted-by: Claude:claude-opus-5-5
The lane match counted any lane whose worktree is the session's, whatever
its stage, so a person opening a done or discarded lane's worktree to look
at it had every approval prompt refused while a sibling lane kept the run
unfinished. A lane now counts only while it has work left. The test fails
on the old match.

Refs: iss-2610171110154902

Assisted-by: Claude:claude-opus-5-5
A run that died after its last write still counts for two hours, so an
attended session in that checkout has its prompts refused behind a
message saying a run is going. The refusal now says that the refusal
ends two hours after the run last wrote its state, so an agent with a
person at the terminal can tell them. A switch to end it early is
iss-2610171200535090.

Refs: iss-2610171110154902
Refs: iss-2610171200535090

Assisted-by: Claude:claude-opus-5-5
The review of the prompt refusal found the loop's samePath compares
resolved strings, so two spellings of one directory differing in case
compare unequal on a case-insensitive file system. For the refusal it
fails open; six other loop call sites share the helper, so changing it
is its own change.

Refs: iss-2610222047465878

Assisted-by: Claude:claude-opus-5-5
… command

The brief's chapter prose states no command shape (the appendix does),
and the narrowed claim about runner lanes named the guard's answering
verb inline, which the brief's own prose test refuses.

Refs: iss-2610171110154902

Assisted-by: Claude:claude-opus-5-5
Four conflicts, each resolved as the union of both sides: the hook
manifest now wires ten event types (PermissionRequest beside Stop and
StopFailure), the sweep's warning ceiling counts both new launchers'
warns, and the CLI registers both hook commands and their timeouts.

Assisted-by: Claude:claude-opus-5-5
@REPPL
REPPL enabled auto-merge October 26, 2026 12:03
@REPPL
REPPL added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 8de4bcd Oct 26, 2026
14 checks passed
@REPPL
REPPL deleted the fix/autonomous-permission-ask-denied branch October 26, 2026 13:08
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