fix(acp): stop a hydrating steer from riding the next turn
Attachment hydration is the one await `submit_feedback_native` puts between the `turn_in_flight` admission check and the enqueue, and it runs for as long as reading the uploads takes. The loop's idle arm covers "the turn ended" — it replies `NoActiveTurn` and the composer queues the whole draft — but it cannot cover "the next turn started in the meantime": the flag reads true either way, so the loop is in its active arm and injects the note into a turn the user never aimed at, recorded `Delivered` while the composer clears. `turn_in_flight` says only that some turn is running, never which one, so give the state a turn identity: `SessionState.turns_completed`, bumped next to the `turn_in_flight` clear in the `TurnComplete` handler — the single production site that ends a turn. A steer captures it during admission and re-checks it after hydration; a change means the admitted turn is over and the note takes the caller's queue fallback, which re-routes the whole draft, attachment included. A counter rather than the existing `pending_user_message_started_at`: that stamp only exists once a turn has published a user message, and `user_message` is `None` for delegation children and unbound conversations, so those turns would have carried no identity at all. The counter is monotonic rather than an exact turn count — `TurnComplete` has three emitters and a repeat can land on a settled turn — and only inequality is ever read.
X
xintaofei committed
3c4bddd796574cd41d44962ec9c01a373e59f336
Parent: 87ee4b9