--- created_at: 2026-10-28T16:08:46Z generated_by: harness: opencode provider: openai model: openai/gpt-6.1-sol git_ref: 7242dcb9 scope: code substantive: true raw_file: 20261028T160846Z_7242dcb9_websocket_close.raw.md --- ## Prompt The human reported repeated live Binance perp crashes after correct worktree startup: the real trio-websocket reader attempted a Pong while wsproto was LOCAL_CLOSING. They asked whether an existing downstream websocket fix should be reused, then requested: > ok add the fix now, follow by a /commit-plan and then i'll test > before saving it ya? They approved a real websocket reader/close regression covering recovery and intentional shutdown, and separately authorized reconciling this worktree's legacy workflow bookkeeping while preserving existing archives. ## Response summary The older `3d83b61f3f7b982e84a4230dba5777f1407c78f1` wrapper suppressed escaped communication failures. The landing replay `97eb8347` of `d50ab6124cd0e1646c82fa4a85d7c13bfff82611` added propagation; it did not restart a reader-context failure. Existing `LocalProtocolError` membership in NoBsWs.recon_errors only protected the receive relay, not reader-nursery context exit. Classify these errors around the complete websocket context and retry instead of restoring outer suppression. Place collapse_eg outside that context, invalidate stale connection readiness and socket references, and leave the retry wait cancellable. Preserve unexpected failures and mixed exception groups for propagation. After review, use RuntimeError rather than ValueError for the test-only unexpected-failure sentinel. Its injected exception has no underlying cause. Production recovery logs retain the original traceback and now name the URL; unexpected exceptions use bare raise to preserve identity, existing causes and notes. The deterministic seam controls protocol-state ordering in the real client Ping handler. It does not replace the websocket transport, reader, context manager, reconnect task or wsproto exception. ## Files changed - `piker/data/_web_bs.py`: reader-context failure recovery. - `tests/test_websocket.py`: loopback recovery and two shutdown variants; retain unexpected-failure propagation control. - This provenance pair: live report, lineage and test scope. ## Verification Baseline uses new tests against original HEAD websocket code in an isolated exact-tree checkout. The recovery case fails with the reported Pong-in-LOCAL_CLOSING LocalProtocolError. Fixed code passes 13 cases across tests/test_websocket.py and tests/test_binance.py. Exact-boundary verification and native-library configuration are retained in the ignored commit-plan package. Live testing remains human-owned and pending. ## Human edits No direct manual source edits observed in this follow-up. The human supplied live failures, corrected the earlier daemon/worktree setup, set the narrow landing scope, requested source-fix reuse, and will test before reviewing and committing. Existing commits are retained. The human requested the RuntimeError sentinel and preservation of underlying error context. The raw partner retains the initial output.