Sync UTS specs with 6.1.0 server-side resumability - #511
Conversation
6f8fd80 to
3406716
Compare
|
@ttypic how are we tracking now the spec version that each library implements (especially those that have UTS-based test suites)? |
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16g3 is not yet in the published specification - ably/specification#511 adds it and tombstones RTN16g2. Implemented ahead of that merging because it is the direct consequence of RTN14h in the previous commit, and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js already behaves this way, from 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Feels like RTN27d could be in the scope of this PR as I believe it's a relic of the pre-RTN14h days, as theoretically an SDK could become "suspended" but the server may well still have the information it needs to resume when it comes back? That is also the basis on which ably-js implemented it. |
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 is not yet in the published specification - ably/specification#511 adds it and tombstones RTN16g2. Implemented ahead of that merging because it is the direct consequence of RTN14h in the previous commit, and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js already behaves this way, from 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 is not yet in the published specification - ably/specification#511 adds it and tombstones RTN16g2. Implemented ahead of that merging because it is the direct consequence of RTN14h in the previous commit, and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js already behaves this way, from 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Spec PR #488 ("Move resumability decisions from the client to the server", specification version 6.1.0) updated features.md and api-docstrings.md but did not update the UTS test-spec sources or address createRecoveryKey's behaviour in SUSPENDED. This brings both in line. features.md: - RTN16g2 is replaced by RTN16g3: createRecoveryKey() no longer returns Null in SUSPENDED. Since the client now retains its connectionKey and always attempts to resume (RTN14h), the connection remains recoverable while suspended. (RTN16g2 as written still listed SUSPENDED, which contradicts RTN8d/RTN9d retaining the key.) - tweak wording of RTN27d (which is one of those "spec points" that shouldn't really be a spec point because it's kindof flavour-text for an enum member rather than specifying behaviour, so I don't think it needs a break) UTS sources: - connection_id_key: RTN8c/RTN9c "id/key null in SUSPENDED" -> RTN8d/RTN9d "id/key retained in SUSPENDED" (connect first, then suspend). - connection_failures: RTN15g "no resume after connectionStateTtl" -> RTN14h "still attempts resume after the TTL", asserting every post-suspension reconnect carries resume=<connectionKey>. - connection_recovery: RTN16g2 -> RTN16g3; the SUSPENDED case now expects a non-null recovery key. - channel_properties: RTL15b1 "channelSerial cleared on suspended" -> RTL15b2 "retained in suspended"; detach/failed cases relabelled. - channel_attach: RTL4j "ATTACH_RESUME set on reattach" -> "not set". - proxy/connection_resume: Test 22 RTN15g/g2 -> RTN14h; the post-TTL reconnect now asserts the resume param is present. - completion-status: matrix updated for RTN8d/RTN9d, RTN14h, RTN16g3, RTL15b2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3406716 to
d0d1c02
Compare
|
oh, I forgot to merge this, sorry
Good point thx, I'll trim off the last clause. (it's one of those "spec points" that shouldn't really be a spec point because it's kindof flavour-text for an enum member rather than specifying behaviour, so I don't think it needs a break) |
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey parameter goes from SetDisconnectedStateCommand and SetSuspendedStateCommand. No caller sets it, and leaving it would let the violation back in unnoticed. SetConnectingStateCommand keeps its own, still used by ConnectionClosingState for RTN11b/RTN11d, where a clean connection is the intent. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey parameter goes from SetDisconnectedStateCommand and SetSuspendedStateCommand. No caller sets it, and leaving it would let the violation back in unnoticed. SetConnectingStateCommand keeps its own, still used by ConnectionClosingState for RTN11b/RTN11d, where a clean connection is the intent. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN14h - implemented. Replaces RTN15g as of specification 6.1.0. The client discarded its connection state once connectionStateTtl had passed and reconnected fresh, throwing away a resume the server would still have honoured. DF1a settles the scope - the ttl is no longer used to decide whether to resume at all - so the gate is general rather than SUSPENDED-only, which is how ably-js reads it too. RTN27c - fixed. DISCONNECTED is a state where "if the library was previously connected, the next connect attempt will be an RTN15b resume attempt". Clearing the key on every failed attempt made that false from the second attempt onwards. RTN8d, RTN9d - fixed twice over. Both list only CLOSED, CLOSING and FAILED, so SUSPENDED must keep the key and id; it cleared them. And all three of the states they do name cleared after SetState, which is what emits the state change - inline, with no SynchronizationContext installed - so the application was told it had reached a terminal state while Connection.Key still read as a resumable key. The clear now happens before the transition, which also stops it depending on a finally. Only a listener reading during the transition could observe this, so it was inherited rather than introduced here. RTN15g1, RTN15g2, RTN15g3 - deleted at 6.1.0. HasConnectionStateTtlPassed and its tests go with them. The reattach RTN15g3 asked for is already unconditional under RTL3d. RTL4j, RTL4j1, RTL4j2 - deleted at 6.1.0; SDKs need not set ATTACH_RESUME. Safe only because RTL4c1 already sends channelSerial on ATTACH and RTL15b2 keeps it across a suspend, so the reattach still carries a continuity signal. The Flag constant stays, per TR3f. The now-dead clearConnectionKey plumbing goes entirely - from SetDisconnectedStateCommand and SetSuspendedStateCommand, whose violation it was, and from SetConnectingStateCommand and HandleConnectingErrorCommand, where it no longer does anything. Leaving it would let the violation back in unnoticed, and leave a retired mechanism looking load-bearing. ConnectionClosingState was its last caller, for RTN11b/RTN11d's clean connection, and needs it no longer: entering CLOSING now clears key and id for RTN8d/RTN9d before the emit, and the single reader processes that before it can reach ClosingState.Connect(), so the following CONNECTED finds no id to match and restarts the serial sequence under RTN15c7 regardless. HandleConnectingErrorCommand's copy was never read by any handler. RTN27d - the 6.1.0 edit left it describing the old model. It called SUSPENDED a state whose "next connect attempt is a clean connection (not a resume attempt)", which RTN14h, RTN8d, RTN9d and DF1a between them contradict; the commit that made those changes did not touch RTN27 at all. ably/specification#511 has since amended the clause in place and dropped that sentence, so this commit follows RTN27d rather than deviating from it. Amending in place is the right shape for RTN27, which declares its states "mutually exclusive and exhaustive" - tombstoning the clause would have left SUSPENDED undescribed. ably-js retains the key in SUSPENDED too, citing RTN8d/RTN9d and RTN14h for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RTN16g3 - implemented. Replaces RTN16g2, which listed SUSPENDED among the states where createRecoveryKey returns null. RTN8d and RTN9d now keep the connectionKey through SUSPENDED because RTN14h always attempts a resume, so the connection is still recoverable there and the key has to be available to hand to another client. Withholding it left the SDK holding a usable recovery key it would not surface, in the one prolonged-outage state where handing recovery over is most useful. RTN16i - fixed alongside it. GetChannelSerials filtered on ChannelState.Attached, and RTL3c puts every channel into SUSPENDED when the connection suspends, so the key would have gone out with no channelSerials at all - connection continuity without message continuity, and nothing to tell the caller. Gated on the serial instead, as ably-js does; RTL15b2 already keeps it through SUSPENDED. RTN16g3 comes from ably/specification#511, which tombstones RTN16g2 and drops SUSPENDED from the states where createRecoveryKey returns null. Written here before that merged, because it is the direct consequence of RTN14h in the previous commit and shipping the two apart would mean two behaviour changes for callers instead of one. ably-js has behaved this way since 2.27.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Spec PR #488 ("Move resumability decisions from the client to the server") updated features.md and api-docstrings.md but did not update the UTS test-spec sources or address createRecoveryKey's behaviour in SUSPENDED. This brings both in line.
features.md:
UTS sources: