Harden subscription stage tests and outcome handling
Address multiple review findings that landed across Phase 5 cycles 1-4
in one atomic change so bisect history stays clean:
- subscription stage: split live-tail reconnect into a shared helper,
add LiveTailOutcome::ConnectFailed to surface second-connect failures
as NetworkError rather than a spurious CleanHold row, and introduce
BackfillOutcome::StreamClosedDuringBackfill so a mid-stream close is
never silently reported as an idle-gap completion.
- subscription stage: on a mid-stream transport error, do not reset
last_frame_at, so the idle-gap timer still fires from the last
successful frame.
- FakeWebSocketClient: give new() strict "panic if no script queued"
semantics for subscription tests, while empty() now returns a silent
empty-stream placeholder so identity tests that only need to satisfy
the pipeline's subscription stage do not have to script it.
- FakeFrameStream: drop the dead transport_error field; replace the
mid_stream_transport_error_after counter with a single mid_stream_error
flag that yields one Transport error after all frames are drained and
then closes, so the stage's idle-gap timer can fire under paused clock.
- Tests: exercise the budget-exceeded path with realistic frame spacing
(20 × 150ms > 2s budget); use a properly length-prefixed malformed
frame fixture so the decode-failure snapshot contains a [FAIL] row;
add live_tail_connect_failure_emits_network_error and
mid_stream_transport_error_does_not_reset_idle_gap regression tests;
anchor gen_fixtures paths to CARGO_MANIFEST_DIR.
- .gitignore: exclude insta pending-snapshot markers from the tree.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>