Park guest permission requests until an approver answers - #21
Open
QuicksilverSlick wants to merge 2 commits into
Open
Park guest permission requests until an approver answers#21QuicksilverSlick wants to merge 2 commits into
QuicksilverSlick wants to merge 2 commits into
Conversation
Slice A of the approval loop. A guest turn can now park a
session/request_permission instead of refusing it: nothing is written to
the agent until a verdict arrives on that request's own oneshot, or the
wait expires to reject_once. Nothing routes requests to the owner yet
(slice B), and shared agents still run in bypassPermissions (slice C), so
behaviour in production is unchanged.
- approval_gate.rs (new, pure): the parking rules and limits (600 s wait,
4 parked per connection, 6 asks per turn, a 30 s minimum window, a 10 s
margin inside the hard deadline); RequestDigest, which binds each
verdict to its request; DenyReason; AllowVia, with standing grants
refunded against the per-turn cap; and TurnClock. While a request
waits, the idle deadline is suspended and the hard deadline is credited
for the wait, up to 3 timeouts.
- pending_permissions.rs: parked asks, retirement, and a lazy verdict poll.
- acp.rs: the read loop polls verdicts first under `biased`, expires
overdue asks (closing the channel before writing the refusal), and
answers every held or owed request on exit and on cancel.
- Review fixes, each with a test that fails without it:
- the prompt stops counting as in flight once its result or error is
read, before the exit drain;
- a new prompt first answers ids an earlier turn left owed;
- the dropped-future cancel path resets authority to Guest;
- an expired ask is closed before its refusal is written.
Windows: 941 pass. The 32 failures are the 29 known timing failures plus
the 3 wire tests that need Unix bash.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With the slice A review fix, the prompt stops counting as in flight as soon as its response is read, and that includes a JSON-RPC error. If a steer, interrupt or stop then drops the prompt future during its exit drain, the pool finds no prompt in flight and takes its "completed before the control signal" branch. That branch reported every such turn as a success: the batch was recorded as delivered and never retried. The client now keeps the error it read until the prompt call returns, and that branch takes it. A failed turn is reported as the error it was, with no delivery record and an Error metric. Its batch follows the signal, as a cancel's would: a steer or interrupt re-prompts it, and a stop or rotate drops it. The copy is cleared when the prompt call returns and again when the next prompt starts, so it can never be reported against a later turn. Each test fails with its part of the fix undone: - a_prompt_error_read_before_a_drop_is_kept_for_the_pool - an_error_the_prompt_returned_is_not_kept_for_the_pool - test_classify_completed_before_control_signal_reports_a_read_error Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
QuicksilverSlick
force-pushed
the
feat/approval-park
branch
from
September 11, 2026 17:27
217ed43 to
d083507
Compare
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.
What this does
Slice A of the approval loop. A guest turn can now park a
session/request_permissioninstead of refusing it outright. Nothing is written back to the agent until a verdict arrives on that request's own oneshot, or until the wait expires toreject_once.Production behaviour is unchanged, for three reasons:
pool.rsstill passes no approval link, so nothing parks.bypassPermissions(slice C).Commits
approval_gate.rs(new, pure, unit-tested on every platform):RequestDigest, which binds each verdict to its request.DenyReason.AllowVia. Standing grants are refunded against the per-turn cap.TurnClock. While a request waits, the idle deadline is suspended and the hard deadline is credited for the wait, capped at 3 timeouts.pending_permissions.rs: parked asks, retirement, and a lazy verdict poll.acp.rs: the read loop polls verdicts first underbiasedand expires overdue asks, closing the channel before writing the refusal. It answers every held or owed request on exit and on cancel.PromptOutcome::Error, with no delivery record. The batch follows the signal, as it would after a cancel (classify_completed_before_control_signal, table-tested).Known limits (for slice B)
last_prompt_idis set, which the Race 1 invariant requires. A drop during those writes ends asCancelDrainTimeoutafter the 5 s grace. That is a safe failure, never a false success.an_expired_ask_is_closed_while_its_refusal_is_still_being_writtencannot tellclose()fromdrop(). A verdict that lands betweentry_recvandclose()is not covered deterministically.initial_message,pool.rs) is sent asTurnAuthority::Owner. Before slice C turns the gate on, confirm that nothing a guest writes can reach it.Tests
cargo test -p buzz-acp --lib: 944 passed, 32 failed. By name, the 32 are the 29 known Windows timing failures plus the 3 wire tests from Stop steered guest messages and session setup inheriting the owner's trust #19 that need Unixbash.cargo fmt --checkandcargo clippy -p buzz-acp --all-targetsare clean.catandsleep.🤖 Generated with Claude Code