Skip to main content

Module pending_background_pids

Module pending_background_pids 

Source
Expand description

In-memory holding pen for a declared-background task’s OS pid when it arrives before db_background_tasks has a row to attach it to.

agentmux-bashwrap publishes its own pid over MPS essentially at process start (before the wrapped command even begins running) — see bash_wrap.rs. The db_background_tasks row, by contrast, is only created once the FRONTEND observes the tool call as an accepted background launch (requires the literal “Command running in background with ID:” tool-result prefix) and pushes docknodestatus, which is a full extra round-trip through the CLI’s own stream-json processing. The pid publish routinely wins that race.

background_task_set_pid (background_tasks.rs) is a bare UPDATE ... WHERE id = ? — it silently no-ops (Ok(false)) when the row doesn’t exist yet, and the pid would be lost forever with no retry that could ever succeed (retrying the same too-early write doesn’t help — the row still won’t exist).

set_or_stash and observe_and_apply are the two sides of the fix, called from websocket.rs‘s COMMAND_BACKGROUND_TASK_PID and COMMAND_DOCK_NODE_STATUS handlers respectively. Both run their DB call (via the try_set/apply closure) WHILE HOLDING this module’s own lock, not as a separate step after it — an earlier version of this module exposed plain stash/take methods instead, with each handler doing its own DB call first and the stash/take second, unlocked in between. That let the two handlers’ steps interleave (pid handler’s DB call sees no row → before it stashes, the observe handler’s own take() already ran and found nothing → observe handler’s row now exists but nothing will ever re-check it → pid handler’s stash lands after the one take() that would have consumed it, and sits unclaimed until the TTL drops it). Making the DB call part of the SAME critical section as the buffer mutation closes that window: whichever handler’s critical section runs second always observes the first one’s effect, DB row or stash, correctly. See docs/specs/SPEC_BACKGROUND_TASK_PID_CAPTURE_2026_08_20.md and the Codex/reagentx findings on PR #2681 this was written to address.

Structs§

PendingBackgroundPids
PendingEntry 🔒

Constants§

PENDING_PID_TTL_MS 🔒
How long a stashed pid waits for its row before being dropped. Generous relative to the observed race window (a network round-trip plus one SQLite write, normally well under a second) but still bounded — a declared-background call whose launch is never actually accepted as background client-side (e.g. the command finished fast enough that Claude’s harness returned synchronously despite run_in_background: true — see issue #2519) would otherwise leak an entry here forever, since background_task_observe would then never fire for that id.