Skip to content

Goodbye and operational state: default reason, external_source API, delta updates #91

Description

@balloob

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-jsshutdown,
sendspin-dotnetuser_request, SendspinKitshutdown. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions