Skip to content

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

@balloob

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions