Source ingest flow - current source_id protocol

A source connection declares one source, Loop accepts it with a runtime source_id, and every post-registration data/read/open path uses that id.

SDK source process

Sends one declaration and then emits lifecycle/data messages for the accepted source.

Foundation Source Bus

Reconciles declaration metadata, owns connectors, and routes samples by source_id.

Recorder / consumer

Opens and subscribes using runtime ids returned by the source list.

1
SDK -> Bus
ClientEvent{ SourceDeclared{ source_key, robot|camera|tactile|marker } }
source_key is declaration/reconciliation/display metadata only.
2
Bus -> SDK
RecorderCommand{ RegistrationAccepted{ source_id } }
The SDK stores this server-assigned id before it handles open, data, state, or errors.
3
Consumer -> Bus
open(source_id) -> RecorderCommand{ OpenCommand{ session_id, params } }
The control stream is already source-scoped, so Open and Close do not carry source lists.
4
SDK -> Bus
SampleBatch{ session_id, source_id, samples[ Sample{ timestamp_us, sequence, RobotPayload{ state } } ] }
RobotPayload.state is a map from robot signal key to typed RobotValue. The data plane does not route by the declaration key.
5
Bus internal
_connections_by_source_id[source_id].submit_batch(batch)
Unknown or closed ids accept zero samples while the stream is drained.
6
Reader -> Bus
SubscribeRequest{ source_id, session_id } -> SubscribeEvent{ sample | state }
ReadStreamStateChanged.source_id carries lifecycle/error state for read subscribers.
Clean contract summary: SourceDeclared.source_key is registration metadata; RegistrationAccepted.source_id is the runtime identity; SampleBatch.source_id, SubscribeRequest.source_id, and ReadStreamStateChanged.source_id are the post-registration routing contract.