Skip to content

Interrupt acceptance and turn exit are not atomic: a 202 can queue into an unrelated later turn #69

Description

@Broccolito

Summary

Interrupt acceptance and turn exit are not atomic. POST /interrupt can observe an active turn, await the agent lookup, and queue the message — while the agent loop performs its final empty-queue check and exits. The route returns 202 Accepted, but the message stays queued and is delivered to an unrelated later turn.

The window

  • crates/biorouter-server/src/routes/reply.rs:1011 — acceptance observes an active turn, then awaits.
  • crates/biorouter/src/agents/agent.rs:5415 — the loop's final empty-queue check and exit.

Between the observation and the enqueue, the turn can end. Nothing rechecks, and nothing tells the caller.

Why a second check does not fix it

Adding another active-state check before enqueueing only narrows the window; it cannot close it, because the check and the enqueue are still two steps against a state that changes underneath them. Any fix that is "check again, then queue" has the same shape as the bug.

What the real fix needs

Associate the queue with a turn generation or turn id, and make acceptance a single atomic operation — queue if and only if this turn is still accepting — paired with a final close-and-drain handshake when the loop exits, so a message either lands in the turn the caller was talking about or is refused.

That is an ownership and transition protocol, not a local patch: it decides what a caller is promised when it gets a 202, and what happens to a message that arrives during teardown. It wants a deliberate design rather than a sweep fix.

Consequence today

A user or an agent interrupts a turn, is told the interruption was accepted, and the text surfaces later in a different turn — or not at all. Silent misdelivery rather than data loss, but the 202 is a promise the system does not keep.

Provenance

Found by a Codex (gpt-5.6-sol, ultra) review of the BR-71 spine, Tasks 1–6. Four other defects from that review were fixed and landed; this one was deliberately left because the reviewer judged it needed human judgment about the protocol. That judgement is recorded here rather than resolved by whoever touches the file next.

Relevant to #30: BR-71's workspace_send_prompt with mode: "steer" routes through this same acceptance path, so the promise it makes to a caller inherits this race.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions