Deferred from #1 (v0.3.0). Feed two updates as concurrent tasks (aiogram's handle_as_tasks=True shape) so tests can surface double-tap / double-submit races — e.g. two taps on the same "confirm" button racing to place one order.
Why it wasn't in #1
MockBot.dispatch attributes captured requests to a step by slicing mock_session.requests[start:]. Under true concurrency those slices interleave, so per-step attribution is unreliable. Doing it faithfully is a cross-cutting change deserving its own PR + tests, not a naïve gather() returning mis-sliced results.
Design sketch
- Set a
ContextVar[str] (the update id) in a thin wrapper before feed_update; aiogram propagates contextvars into handler tasks.
- In
MockedSession.make_request, stamp each captured method with the current update id.
MockBot.dispatch_as_tasks(*objs) → asyncio.gather the dispatches and split captured requests by stamp, returning one DispatchResult per update.
- Test: two taps on the same button racing → assert exactly one order/charge.
Interim workaround: drive updates sequentially.
Full design in docs/superpowers/specs/2026-07-18-fidelity-callback-message-lifecycle-design.md §5.
Deferred from #1 (v0.3.0). Feed two updates as concurrent tasks (aiogram's
handle_as_tasks=Trueshape) so tests can surface double-tap / double-submit races — e.g. two taps on the same "confirm" button racing to place one order.Why it wasn't in #1
MockBot.dispatchattributes captured requests to a step by slicingmock_session.requests[start:]. Under true concurrency those slices interleave, so per-step attribution is unreliable. Doing it faithfully is a cross-cutting change deserving its own PR + tests, not a naïvegather()returning mis-sliced results.Design sketch
ContextVar[str](the update id) in a thin wrapper beforefeed_update; aiogram propagates contextvars into handler tasks.MockedSession.make_request, stamp each captured method with the current update id.MockBot.dispatch_as_tasks(*objs)→asyncio.gatherthe dispatches and split captured requests by stamp, returning oneDispatchResultper update.Interim workaround: drive updates sequentially.
Full design in
docs/superpowers/specs/2026-07-18-fidelity-callback-message-lifecycle-design.md§5.