This issue comes out of a cross-SDK conformance audit comparing every Sendspin client/server implementation (aiosendspin, sendspin-cli, sendspin-cpp, sendspin-dotnet, sendspin-go, sendspin-js, sendspin-jvm, sendspin-rs, SendspinKit) against the spec. sendspin-cpp is treated as the reference implementation throughout — it most closely matches the spec's language and is the only SDK that runs on the constrained-embedded target the spec was originally written for.
Most SDKs implement the message envelopes correctly but only one
(SendspinKit) implements external_source as a client API, and
several SDKs default to the wrong goodbye reason in common situations.
For external_source, SendspinKit is the reference —
enterExternalSource() / exitExternalSource() route through
transitionOperationalState(to:) with rollback on
server-notification failure. For goodbye-reason plumbing,
sendspin-cpp is the reference (SendspinGoodbyeReason enum,
auto-another_server from ConnectionManager).
1. Pick a default goodbye reason and make it consistent
When a client disconnects without specifying a reason in its SDK API,
the SDK SHOULD send reason: 'restart', matching the server's
no-goodbye assumption.
Today defaults are unprincipled: sendspin-js → shutdown,
sendspin-dotnet → user_request, SendspinKit → shutdown. All
three suppress server-side auto-reconnect for a user who simply
closes a laptop lid — the opposite of what the spec implies.
2. Add a normative note about the external_source SDK API
Client SDKs SHOULD expose API methods for entering and leaving
external_source state and SHOULD roll back the local state on
server-notification failure.
SendspinKit's rollback pattern
(SendspinClient+Commands.swift:27-45) is the model. Eight of nine
SDKs have no embedder-facing handle today — so a Sonos-style "input
switched to TV" event cannot be relayed to the server, and the
server keeps streaming audio into silence.
3. Document or relax delta client/state merge behavior
The spec says: "for subsequent updates, only include fields that have
changed; the server will merge these updates into existing state".
No SDK actually sends delta updates — everyone sends the full
payload every time. Either:
- add normative test cases (e.g., "if the client previously sent
static_delay_ms: 100 and now sends {volume: 50}, the server's
view of static_delay_ms MUST remain 100"), or
- relax to "clients MAY send partial state updates; servers MUST
handle full updates and SHOULD handle partial updates".
Today there are zero tests for either case.
Source audit doc: docs/goodbye-and-operational-state.md
Full audit branch: claude/stream-sync-correction-sdks-AWoNC
Per-SDK digest: sdk-issues digest
This issue comes out of a cross-SDK conformance audit comparing every Sendspin client/server implementation (
aiosendspin,sendspin-cli,sendspin-cpp,sendspin-dotnet,sendspin-go,sendspin-js,sendspin-jvm,sendspin-rs,SendspinKit) against the spec.sendspin-cppis treated as the reference implementation throughout — it most closely matches the spec's language and is the only SDK that runs on the constrained-embedded target the spec was originally written for.Most SDKs implement the message envelopes correctly but only one
(
SendspinKit) implementsexternal_sourceas a client API, andseveral SDKs default to the wrong goodbye reason in common situations.
For
external_source,SendspinKitis the reference —enterExternalSource()/exitExternalSource()route throughtransitionOperationalState(to:)with rollback onserver-notification failure. For goodbye-reason plumbing,
sendspin-cppis the reference (SendspinGoodbyeReasonenum,auto-
another_serverfromConnectionManager).1. Pick a default goodbye reason and make it consistent
Today defaults are unprincipled:
sendspin-js→shutdown,sendspin-dotnet→user_request,SendspinKit→shutdown. Allthree suppress server-side auto-reconnect for a user who simply
closes a laptop lid — the opposite of what the spec implies.
2. Add a normative note about the
external_sourceSDK APISendspinKit's rollback pattern
(
SendspinClient+Commands.swift:27-45) is the model. Eight of nineSDKs have no embedder-facing handle today — so a Sonos-style "input
switched to TV" event cannot be relayed to the server, and the
server keeps streaming audio into silence.
3. Document or relax delta
client/statemerge behaviorThe spec says: "for subsequent updates, only include fields that have
changed; the server will merge these updates into existing state".
No SDK actually sends delta updates — everyone sends the full
payload every time. Either:
static_delay_ms: 100and now sends{volume: 50}, the server'sview of
static_delay_msMUST remain 100"), orhandle full updates and SHOULD handle partial updates".
Today there are zero tests for either case.
Source audit doc:
docs/goodbye-and-operational-state.mdFull audit branch:
claude/stream-sync-correction-sdks-AWoNCPer-SDK digest: sdk-issues digest