Skip to content

docs(spec): section 7.6, what a reconnect costs - #3

Merged
tactino merged 3 commits into
mainfrom
docs/spec-reconnect-state
Sep 11, 2026
Merged

tactino merged 3 commits into
mainfrom
docs/spec-reconnect-state

Conversation

@tactino

@tactino tactino commented Sep 11, 2026

Copy link
Copy Markdown
Member

Section 7.2 already said the server's per-environment state goes with a closed connection, but it said it inside the resync case, as though resync were the only way a connection ends mid-run.

It is not, and the consequence does not depend on the cause. The previous observation, the policy step state and the done flags are gone, so a feedback whose action arrived on an earlier connection describes a transition nothing can complete.

§7.6 adds:

  • the general statement, not tied to resync;
  • the client's obligation to drop held feedback across a reconnect;
  • a SHOULD that the server say something when feedback arrives for an environment it has no step state for;
  • a note that reconnects come from outside the protocol — a suspended laptop, a flaky link, a restarted server — which is why the rule is written in terms of the reconnect rather than any particular cause.

Also a conformance checklist line, and a pointer from §7.5, which said a departing client costs nothing. True of one that is finished, not of one that means to come back.

One thing this PR got wrong and then removed

An earlier revision told servers not to sever a healthy connection for a liveness reason §7.3 already covers, named WebSocket keepalive pings as the trap, and added a fifth Gap describing the price of turning them off.

All three rested on the claim that a CPU-bound learn step holds the event loop past the ping timeout. It does not. plugrl-server runs learn through asyncio.to_thread, and five learn steps of 177–190 s each — nine times the timeout — closed nothing. The incident that prompted the section was a machine suspending for 1 h 53 min, and the timeout that fired came from the client library, not the server's.

Measurement: experiments/e8-keepalive-hypothesis/ in PlugRL/plugrl-server#4.

The rest of §7.6 is unaffected, because it was read out of the server's handler and reproduced in a unit test rather than inferred from the incident.

Client half in PlugRL/plugrl-env-client#5; site tour in PlugRL/plugrl.github.io#4.

🤖 Generated with Claude Code

Section 7.2 already said the server's per-environment state goes with a
closed connection, but it said it inside the resync case, as if resync were
the only way a connection ends mid-run. It is not, and the consequence is
the same however the connection died: the previous observation, the policy
step state and the terminated/truncated flags are gone, so a feedback whose
action arrived on an earlier connection describes a transition that can no
longer be completed.

Section 7.6 states that generally, adds the obligation on the client to drop
held feedback across a reconnect, and adds the obligation on the server not
to sever a healthy connection for a liveness reason section 7.3 already
covers - WebSocket keepalive pings being the specific trap, since a
CPU-bound learn step cannot answer one.

Also a conformance checklist line, and a pointer from 7.5, which said a
departing client costs nothing - true of one that is finished, not of one
that means to come back.

This is written up from a real failure: plugrl-server left the websockets
default 20 s ping in place, and on a CPU-only machine the quickstart dropped
twice in six minutes, feeding the learner an empty transition each time.
Fixed in plugrl-server#4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gotham-Zolio and others added 2 commits September 11, 2026 14:40
Section 7.6 told servers not to sever a healthy connection for a liveness
reason section 7.3 already covers. It did not say that 7.3 does not cover
everything, which made the advice look free.

It is not free. The feedback timeout bounds the wait between an action and
its feedback, and nothing bounds the wait for the next infer, so a peer that
dies without closing its socket is now noticed only when TCP gives up. That
is a fifth Gap and it belongs beside the other four.

The alternative was a long ping timeout rather than none, and the reason it
was rejected belongs in the record too: the value would have to exceed both
the longest learn step and the longest legitimate pause before a client's
first infer, and neither is the server's to know.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section told servers not to sever a healthy connection for a liveness
reason 7.3 already covers, named WebSocket keepalive pings as the trap, and
added a fifth Gap describing what turning them off gives up.

All three rested on the claim that a CPU-bound learn step holds the event
loop past the ping timeout. It does not. plugrl-server runs learn through
asyncio.to_thread, so the loop stays free, and five learn steps of about 190
seconds each - nine times the timeout - closed nothing. The incident that
prompted the section was a machine suspending for nearly two hours, and the
timeout that fired was the client library's, not the server's.

What is measured and survives is the rest of the section: connection state is
per connection, a client must drop held feedback across a reconnect, and a
server should say something when feedback arrives with no step state. Those
came from reading the handler and were reproduced in a unit test, not
inferred from the incident, which is why they are unaffected.

The corollary and the fifth Gap are replaced by a note saying reconnects
come from outside the protocol and the rule is written in terms of the
reconnect for that reason. The historical note now records what actually
happened, including the diagnosis that measurement overturned.

Measurement in plugrl-server experiments/e8-keepalive-hypothesis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tactino
tactino merged commit 683d2ff into main Sep 11, 2026
3 checks passed
@tactino
tactino deleted the docs/spec-reconnect-state branch September 11, 2026 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant