Consumer read protocol - source_id contract

External readers consume samples from the Source Bus by runtime source_id. The declaration source_key remains useful metadata, but it is not a read-plane route key.

Read RPC

service SourceReadService {
  rpc Subscribe(SubscribeRequest) returns (stream SubscribeEvent);
}

message SubscribeRequest {
  string source_id = 1;
  string session_id = 2;
}

Stream events

message SubscribeEvent {
  oneof event {
    Sample sample = 1;
    ReadStreamStateChanged state = 2;
  }
}

message ReadStreamStateChanged {
  string session_id = 1;
  string source_id = 2;
  StreamState state = 3;
  string detail = 4;
}
AreaCurrent contract
HandshakeProducers send one SourceDeclared; Loop replies with RegistrationAccepted.source_id.
Data planeProducers send SampleBatch.source_id; robot samples carry RobotPayload.state map entries.
Read planeConsumers subscribe with SubscribeRequest.source_id and receive Sample plus ReadStreamStateChanged.source_id.
Metadatasource_key can be shown to users or used for server reconciliation, but runtime actions use source_id.
Implementation shape: the gRPC read adapter is a thin server-streaming boundary over the existing source session subscription. It converts domain samples/events into protobuf events without adding source-key routing.