feat: document and enforce 5 SDK contracts for GUI consumers - #22
Merged
Conversation
Documents and tests the 5 contracts that the syncfield no-code GUI flow (in the sibling syncfield repo) implicitly depends on: 1. on_connect callback is non-blocking — slow user callbacks must not stall the connect path or stream loop. 2. expected_hz enables burst-aware capture_ns interpolation — when a single transport read returns N>1 samples, timestamps are spread across the read window, not clustered on the read instant. 3. Transient transport hiccups auto-reopen — adapters that own a transport (serial, BLE, USB) attempt up to 5 reopens with exponential backoff (≤30s total) before surfacing a stream error. Implemented in adapters/_generic.py:retry_open() and used by PollingSensorStream / PushSensorStream open paths. 4. SyncToneConfig.silent() MUST NOT register host_audio — adds suppress_host_audio flag set by the silent() factory; orchestrator honours it in both _maybe_preregister_host_audio and _maybe_inject_host_audio. Direct SyncToneConfig(enabled=False) keeps the legacy behaviour for backward compatibility. 5. Stream errors are isolated — a raise inside a stream's capture thread does not propagate to the SessionOrchestrator or transition the session out of RECORDING. 27 new tests in tests/unit/test_sdk_contracts.py, all passing. No breaking changes — all existing public API preserved, new fields default to backward-compatible values. See syncfield-sensor-onboarding-enhancements.md §5 for the contract rationale and how each guarantee underpins the GUI's no-code promise for Tier A/B sensors. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Documents and enforces the 5 SDK contracts that the no-code GUI flow (in the sibling
syncfieldrepo) implicitly depends on. Each contract is now stated in MUST-form on the relevant base class and exercised by a unit test. Where the SDK previously violated a contract, the violation is fixed.The 5 contracts:
on_connectcallback is non-blocking — slow user callbacks can't stall the connect path or the stream loop.expected_hztriggers burst-aware capture_ns interpolation — when one transport read returns N>1 samples, timestamps are spread across the read window instead of clustering on the read instant.adapters/_generic.py:retry_open()and used byPollingSensorStream/PushSensorStream.SyncToneConfig.silent()MUST NOT register host_audio — addssuppress_host_audioflag set by thesilent()factory. The orchestrator honours it in both_maybe_preregister_host_audioand_maybe_inject_host_audio. DirectSyncToneConfig(enabled=False)retains the legacy behaviour for backward compat. This is a real bug fix — flagged in the EMG report.SessionOrchestratoror transition the session out ofRECORDING.Why this matters
The sibling
syncfieldrepo is shipping a Tier A/B no-code onboarding flow (BLE / serial / network / camera, with Live Explorer + bundled recipes). That flow's reliability promise leaks abstraction without these guarantees:on_connectuser callbacks hang the viewer at "Connecting..." — Contract 1 documents the design intent.Without these contracts being explicit + tested, future SDK changes can silently break GUI assumptions.
Test plan
tests/unit/test_sdk_contracts.py— all pass locallySyncToneConfig(enabled=False)direct constructor retains old behaviour (only thesilent()factory triggers the new suppression flag)syncfieldrepo's existing usage ofSyncToneConfig.silent()benefits from Contract 4 — verify with a smoke session in the desktop appCompanion PR
This PR pairs with the sibling repo's sensor onboarding enhancements branch (
feat/sensor-onboarding-phase1-sse-schemainOpenGraphLabs/syncfield). The GUI flow there assumes these guarantees. Either can land first — both are non-breaking — but landing this one first means the GUI flow ships on a documented foundation instead of an implicit one.See
syncfield-sensor-onboarding-enhancements.md§5 (in the sibling repo) for the contract rationale.🤖 Generated with Claude Code