platformVersion.mjs pins the SDK to protocol version 12:
// TODO: Remove this pin once dashpay/platform#3809 lands in the consumed
// @dashevo/evo-sdk release; unpinned clients should then auto-detect the
// network protocol version safely.
export const PLATFORM_VERSION_OVERRIDE = 12;
That value was last bumped in chore: bump platform version override to 12 (2026-06-18). Since then testnet has moved to protocol version 13, and the pin appears not to have been bumped along with it.
Testnet is on PV 13. Consensus block headers show the switch happening between 2026-07-25 and 2026-07-28:
| Block height |
version.app |
Block time |
| 460000 |
12 |
2026-07-24 |
| 466000 |
12 |
2026-07-25 |
| 472000 |
13 |
2026-07-28 |
| 483354 |
13 |
2026-08-03 |
/consensus_params on testnet currently reports version.app_version: "13", i.e. the value in force on-chain rather than a node's desired version.
A pin disables auto-detection rather than acting as a floor. In rs-sdk/src/sdk.rs, the ratchet returns immediately when a version is pinned:
fn maybe_update_protocol_version(&self, received_version: u32) {
if self.version_pinned {
return;
}
...
and Sdk::version() documents that "when pinned via SdkBuilder::with_version(), always returns the pinned version". So a pinned client stays at 12 silently — no error and no warning — even when every response carries protocol_version: 13.
Since createClient() in setupDashClient-core.mjs passes the constant to all three networks, this applies to testnetTrusted, mainnetTrusted and localTrusted alike, and the same constant is imported by the dashrate, dashnote and dashnote-starter example apps. (dashmint-lab's dashmint-lite.html doesn't import it, but carries a comment pointing back at the same pin, so it would likely want the same treatment.)
The TODO's removal condition already appears to be met. dashpay/platform#3809 merged on 2026-06-12, and it looks present in the consumed release: unpacking @dashevo/evo-sdk@4.1.0 and decompressing the inlined WASM, the binary contains the tracing string received unknown protocol version from network, which that PR introduced. So the "once #3809 lands in the consumed release" condition seems satisfied as of 4.0.0/4.1.0.
What the green CI does and doesn't cover. The read-only suite has been passing against a PV-13 testnet with the client pinned to 12, and since the tutorials use the *Trusted factories — which pre-fetch quorum keys for proof verification — that is real evidence that the read path stays compatible across this particular version gap. It is a genuine data point in favour of the pin being harmless, and I want to be clear that I am not reporting a known breakage.
The write path is the part with no coverage. test-tutorials.yml gates the read-write job on github.event_name == 'workflow_dispatch', so pushes and pull requests only ever run test:read-only. The two workflow_dispatch runs in the workflow's history are from 2026-03-12 and 2026-04-10 — both months before testnet moved to 13. So state transitions built by a version-12-pinned client have not been exercised against a PV-13 network in CI.
What I have not verified: whether a client pinned to 12 actually behaves any differently against a PV-13 network for state transitions. I did not run the write tutorials, so I am not claiming breakage. drive_state_transition_method_versions has a v3 entry associated with protocol version 13, which suggests the write surface is at least version-sensitive, but that is a reason to check rather than evidence of a fault.
Given that mainnet's PV-13 vote has already passed threshold, the same window will open for NETWORK=mainnet at the next epoch boundary, so it seemed worth reporting before then rather than after.
Suggested next steps, in whichever order makes sense:
- Either bump the pin to 13 or drop it per the TODO, since #3809 appears to be in the consumed SDK release.
- Trigger one
workflow_dispatch run of the read-write suite — that would settle the open question above directly.
- Optionally, consider whether the read-write suite should run on some schedule, since a version-sensitive regression is currently only observable by manual dispatch.
Reported with assistance from Claude (Anthropic).
platformVersion.mjspins the SDK to protocol version 12:That value was last bumped in
chore: bump platform version override to 12(2026-06-18). Since then testnet has moved to protocol version 13, and the pin appears not to have been bumped along with it.Testnet is on PV 13. Consensus block headers show the switch happening between 2026-07-25 and 2026-07-28:
version.app/consensus_paramson testnet currently reportsversion.app_version: "13", i.e. the value in force on-chain rather than a node's desired version.A pin disables auto-detection rather than acting as a floor. In
rs-sdk/src/sdk.rs, the ratchet returns immediately when a version is pinned:and
Sdk::version()documents that "when pinned viaSdkBuilder::with_version(), always returns the pinned version". So a pinned client stays at 12 silently — no error and no warning — even when every response carriesprotocol_version: 13.Since
createClient()insetupDashClient-core.mjspasses the constant to all three networks, this applies totestnetTrusted,mainnetTrustedandlocalTrustedalike, and the same constant is imported by thedashrate,dashnoteanddashnote-starterexample apps. (dashmint-lab'sdashmint-lite.htmldoesn't import it, but carries a comment pointing back at the same pin, so it would likely want the same treatment.)The TODO's removal condition already appears to be met. dashpay/platform#3809 merged on 2026-06-12, and it looks present in the consumed release: unpacking
@dashevo/evo-sdk@4.1.0and decompressing the inlined WASM, the binary contains the tracing stringreceived unknown protocol version from network, which that PR introduced. So the "once #3809 lands in the consumed release" condition seems satisfied as of 4.0.0/4.1.0.What the green CI does and doesn't cover. The read-only suite has been passing against a PV-13 testnet with the client pinned to 12, and since the tutorials use the
*Trustedfactories — which pre-fetch quorum keys for proof verification — that is real evidence that the read path stays compatible across this particular version gap. It is a genuine data point in favour of the pin being harmless, and I want to be clear that I am not reporting a known breakage.The write path is the part with no coverage.
test-tutorials.ymlgates the read-write job ongithub.event_name == 'workflow_dispatch', so pushes and pull requests only ever runtest:read-only. The twoworkflow_dispatchruns in the workflow's history are from 2026-03-12 and 2026-04-10 — both months before testnet moved to 13. So state transitions built by a version-12-pinned client have not been exercised against a PV-13 network in CI.What I have not verified: whether a client pinned to 12 actually behaves any differently against a PV-13 network for state transitions. I did not run the write tutorials, so I am not claiming breakage.
drive_state_transition_method_versionshas a v3 entry associated with protocol version 13, which suggests the write surface is at least version-sensitive, but that is a reason to check rather than evidence of a fault.Given that mainnet's PV-13 vote has already passed threshold, the same window will open for
NETWORK=mainnetat the next epoch boundary, so it seemed worth reporting before then rather than after.Suggested next steps, in whichever order makes sense:
workflow_dispatchrun of the read-write suite — that would settle the open question above directly.Reported with assistance from Claude (Anthropic).