Closed general

A shell grant should resolve every pending prompt of its class on that machine and retract their mirrored cards

John Lauer · 2mo ago ·closed by John Lauer

Reported by John on 2026-09-11 from ah on AdomLapper, looking at the mirrored permission stack for ConfRoomROG (ab 2.1.99 on both machines).

What he saw: a stack of identical "Command needs approval" cards for ConfRoomROG (two visible, "+6 older waiting"), all shell-class, all from one AI thread polling a build done-file every 30 s while the box had no grant. Each poll raised a new prompt and a new mirrored card. He clicked "1 hour" on one. Expectation: a 1-hour grant covers the whole shell class on that machine, so every other pending shell card for ConfRoomROG is answered by that click and should disappear everywhere (ah on the laptop, ab on the origin). What happened: the other cards stayed.

Two halves, both ab's:

  1. On the origin box, when a grant of any duration beyond "once" lands (1 h, 24 h, Auto), resolve every other pending prompt of the same class as approved, and let the callers that are still waiting proceed. Today each was rejected or left pending on its own.
  2. Fan the resolution out the same way the prompt was fanned out: retract the mirrored cards on every same-account peer by id (the mirror already carries the one-shot nonce and the origin machine), so the stack collapses on the deciding box and on the others. ah renders what the mirror says, so if ab retracts, ah clears.

Related: ab holds at most one pending shell approval at a time (anti-spam), yet the mirror produced a card per rejected re-request; the retraction above also cleans that up. A once-only grant should keep the other prompts pending, as now.

2 Replies

John Lauer · 2mo ago

Confirmed and fixing in 2.1.102 (building). Root cause is narrower than 'the grant did not fan out': every generic grant prompt was mirrored with id "" (mirror_generic_grant_prompt), so (a) each refusal of a polling caller produced a NEW card on every peer and in Hydrogen, since receivers key cards by id and "" is not a stable key on the ah side, (b) the one-shot resolve nonce for "" was overwritten by each new prompt, and (c) the grant's fan-out could only ever say 'dismiss ""' once. Fix, both halves on ab: each generic prompt now carries its own id (grant-), at most ONE is live per box (a second refusal while one is pending does not mirror another card - the same anti-spam the local dialog already had), and a window grant (1 h / 24 h / Auto) or a decline drains every pending generic id, dismissing each on the origin's own stack, on every same-account peer and in Hydrogen; Once keeps the others pending, as now. Receivers also treat a legacy empty-id 'granted' from an older origin as 'every shell-class card from that peer'. Proof will raise a stack of refusals on winvm from a polling caller, decide one card from AdomLapper by verb, and show the stack collapse on winvm, on the laptop and in the events.

John Lauer · 2mo ago

Closed in 2.1.103 (insiders; 2.1.102 shipped the first half).

Root cause, measured on 2.1.102 (winvm, embedded under Hydrogen, no grant): every refused poll mirrored its own card. 2.1.102 had fixed the generic prompt (grant-<uuid> ids, one live per box, drain on grant/decline), but on an embedded box the stack came from consult_embedded_gate, which mirrored a shell-approval card carrying the call's own message id on every non-held refusal: 16:24:46 delivered type=shell-approval id=77033afb, 16:24:54 ... id=083c0d4b, one card per poll.

2.1.103: a non-held refusal (Tier-2 / run_script / WSL, the ones that only ask for a window) now goes through the same one-live-per-box registry as the generic prompt and honours the 5-minute decline pause; a held request (a command waiting on Once/Deny) keeps one card per command. The generic body also carries held:false, so no surface offers Once for a window.

Proof on winvm 2.1.103, three run_script refusals fired together, origin log:

[17:07:01.176] PERMISSION-MIRROR: delivered type=shell-approval id=44352f6a-693 to ah
[17:07:01.177] GRANT-PROMPT: 44352f6a... is still pending on this box - not mirroring the run_script refusal 6a033881-bdf as another card
[17:07:01.187] GRANT-PROMPT: 44352f6a... is still pending on this box - not mirroring the run_script refusal e47632fb-a19 as another card
[17:07:07.474] PERMISSION-MIRROR: dismiss id=44352f6a-693 (allow_1h) delivered to ah
[17:07:07.475] PERMISSION-MIRROR: dismiss id= (granted) delivered to ah        <- drains the whole shell class on every peer

notify_events: shell_granted, source: decided-on:AdomLapper, expiresInSec 3600 (John's 1 h click on the laptop). Three refusals, one card, one click clears it everywhere, the next poll passes.

For Hydrogen: nothing to change. Ids are grant-<uuid> (generic) or the refused call's id (shell-approval); at most one live per box; a dismiss with an empty id and decision granted means every shell-class card from that origin.

John Lauer closed this · 2mo ago

Log in to reply.