"Requests take turns, so a status read behind a running command waits instead of failing." — Tinker README
The First Version Said "Busy"
The engine runs one action at a time; that is a safety property, not a performance shortcut. The first app door inherited it bluntly: a request that arrived while another was running was refused as busy. In practice the commonest second request is harmless — Pippa applies the editing preset and, a moment later, reads status to see how it went. Refusing that read made her retry in a loop or report a failure that was not one. The review pass replaced "busy" with a queue.
A Queue Built From an Actor
BridgeWorker is a Swift actor, so its stored properties are only ever touched by one task at a time. It keeps a busy flag and an array of suspended continuations. enter() takes the turn immediately if nobody holds it; otherwise it suspends the calling task with withCheckedContinuation and appends the continuation to the line. leave() hands the turn directly to the first waiter by resuming its continuation — busy stays true, because the turn passed without ever being free. Each exchange is bracketed with enter() and a deferred leave(), so a thrown error still passes the turn on. First come, first served, and no polling.
Bounded by the Listener
An unbounded queue is a slow memory leak and a way to make a caller wait forever. The bound lives one layer out, in the loopback HTTP listener: at most four connections at once, a 16 KiB request body limit, and an 80-second total deadline per request. A read stuck behind a long command times out at the listener with a clear error rather than hanging. Inside, the worker's 75-second watchdog terminates a worker still running at 75 seconds and kills it one second later, so no single exchange holds the turn much past that.
Belt and Braces on the Python Side
The worker also takes an exclusive flock on an actions.lock file in Tinker's data directory before running any invocation. Inside one agent the actor already serialises everything, so the lock is never contended there. It guards the case the actor cannot see: two workers started by two different agents, which the resident lock is supposed to make impossible. When two independent mechanisms both say "one at a time", a bug in either one is not enough to break the rule.
And Inside the Engine
The door's queue sits in front of the engine's own guard. The engine refuses an overlapping apply or restore with "Another action is in progress." Settings and the menu do not queue either; they simply disable their preset buttons while a command is running, and a shortcut pressed then is dropped (track 5). A door request never meets the guard at all, because it only reaches the engine on its turn.