transport: fix zero-state reconnect duplicate delivery; add P formal verification - #405
Open
cbrewster wants to merge 1 commit into
Open
transport: fix zero-state reconnect duplicate delivery; add P formal verification#405cbrewster wants to merge 1 commit into
cbrewster wants to merge 1 commit into
Conversation
Member
Author
|
Pull Request Stack Stack managed with jj-github |
cbrewster
requested review from
daweifeng-replit
and removed request for
a team
August 27, 2026 22:58
lhchavez
reviewed
Aug 27, 2026
| @@ -0,0 +1,156 @@ | |||
| package river.pobserve; | |||
Contributor
There was a problem hiding this comment.
@replit/zerg rewrite in Kotlin, make no mistakes
cbrewster
force-pushed
the
cbrewster/push-muuqnpsxpoom
branch
from
August 28, 2026 00:18
8c704b4 to
ef485ee
Compare
…verification Why: a client that sent messages but never received an ack is indistinguishable from a new session at handshake time, so reconnecting to a server that lost the session (restart or grace expiry) replayed the send buffer and re-executed handlers while the original calls hung forever. What changed: - Handshake requests now carry `expectedSessionState.isReconnect` (optional, wire-compatible; sessions track `hadConnection` across transitions), and the server rejects marked reconnects to unknown sessions with SESSION_STATE_MISMATCH, yielding the documented hard-reconnect semantics (UNEXPECTED_DISCONNECT, fresh session, no replay). Deterministic regression test in __tests__/zerostate.test.ts; PROTOCOL.md documents the field. - Adds verification/ (P language, Nix-packaged toolchain): a model-checked transport/session model with fault injection (which found this bug and reproduces d7c0ec9 as a must-fail regression), an inductive UCLID5 proof of the seq/ack sliding window, and PObserve runtime conformance checking of the hegel property-suite traces against the model. `npm run model:check` / `model:observe`; not wired into CI. Staging test: run two server instances behind a flaky link (or kill/restart one mid-call with heartbeats disabled); a client with an in-flight rpc against the restarted server must get UNEXPECTED_DISCONNECT and the handler must not run twice for the same request. Locally: `npx vitest run __tests__/zerostate.test.ts`, `nix develop .#verification --command verification/p/check.sh && verification/p/observe.sh`.
cbrewster
force-pushed
the
cbrewster/push-muuqnpsxpoom
branch
from
August 28, 2026 15:12
ef485ee to
1e77f1e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why: a client that sent messages but never received an ack is indistinguishable
from a new session at handshake time, so reconnecting to a server that lost the
session (restart or grace expiry) replayed the send buffer and re-executed
handlers while the original calls hung forever.
What changed:
expectedSessionState.isReconnect(optional,wire-compatible; sessions track
hadConnectionacross transitions), and theserver rejects marked reconnects to unknown sessions with
SESSION_STATE_MISMATCH, yielding the documented hard-reconnect semantics
(UNEXPECTED_DISCONNECT, fresh session, no replay). Deterministic regression
test in tests/zerostate.test.ts; PROTOCOL.md documents the field.
transport/session model with fault injection (which found this bug and
reproduces d7c0ec9 as a must-fail regression), an inductive UCLID5 proof of
the seq/ack sliding window, and PObserve runtime conformance checking of the
hegel property-suite traces against the model.
npm run model:check/model:observe; not wired into CI.Staging test: run two server instances behind a flaky link (or kill/restart one
mid-call with heartbeats disabled); a client with an in-flight rpc against the
restarted server must get UNEXPECTED_DISCONNECT and the handler must not run
twice for the same request. Locally:
npx vitest run __tests__/zerostate.test.ts,nix develop .#verification --command verification/p/check.sh && verification/p/observe.sh.