Summary
The daemon can push events to a subscribed IPC client, but nothing in the repository can consume them, and the only client that exists fails when it happens.
Command::Subscribe { topics: Vec<Topic> } and Event { version, sequence, topic, payload } are defined in crates/omachat-proto/src/ipc.rs, with topics Status, Conversations, Messages, Presence, Delivery.
serve_client in crates/omachatd/src/ipc_server.rs (the event = event_receiver.recv(), if subscribed arm) encodes Event lines onto the same stream that carries Response lines.
crates/omachat-ctl/src/lib.rs contains no Subscribe or Event handling. Client::request writes one line and reads exactly one line, deserializing it as Response and then checking response.id for correlation.
Event carries neither id nor outcome, so the first event pushed to a subscribed client fails serde_json::from_slice::<Response> and surfaces as ClientError::MalformedResponse. Response correlation is also offset by one line from that point on.
Why it matters
UiModel in omachat-tui already carries conversations, messages, unread counts and delivery state, but nothing populates them — Subscribe/Event is the only source that could. OC-024 (#25) is the first work that needs live topics and will meet this immediately.
Suggested extension
Give omachat-ctl a demultiplexing read path:
- separate the socket read loop from
request(), so every inbound line is classified once
- correlate
Response by id back to the pending request, and deliver Event to a receiver the caller owns
- keep the existing
MAX_LINE_BYTES bound and apply the version check to both shapes
- expose something along the lines of
Client::subscribe(topics) -> Receiver<Event>
Happy to implement this if the shape sounds right — I'd rather check the intended boundary first, since it touches the client contract.
Evidence
crates/omachat-proto/src/ipc.rs — Command::Subscribe, Topic, Event
crates/omachatd/src/ipc_server.rs:243 — the event write arm
crates/omachat-ctl/src/lib.rs — request() / read_line(), no event handling
crates/omachat-proto/tests/ipc.rs — covers the codec, not the client read path
Out of scope
Wiring the TUI to live topics (OC-024 / #25), and the OC-023 (#24) shell work itself.
Maintainer reconciliation — 2026-09-04
Next closure evidence/work: Implement a single bounded demultiplexing reader, response-ID correlation and event receiver; test interleaving, versions, disconnects and backpressure. Coordinate protocol changes with #226.
Baseline reviewed: main 87c3bcb7d65e3e2ab8ae317694b751f8efe9a8ed. Implementation presence is not a live conformance claim.
Summary
The daemon can push events to a subscribed IPC client, but nothing in the repository can consume them, and the only client that exists fails when it happens.
Command::Subscribe { topics: Vec<Topic> }andEvent { version, sequence, topic, payload }are defined incrates/omachat-proto/src/ipc.rs, with topicsStatus,Conversations,Messages,Presence,Delivery.serve_clientincrates/omachatd/src/ipc_server.rs(theevent = event_receiver.recv(), if subscribedarm) encodesEventlines onto the same stream that carriesResponselines.crates/omachat-ctl/src/lib.rscontains noSubscribeorEventhandling.Client::requestwrites one line and reads exactly one line, deserializing it asResponseand then checkingresponse.idfor correlation.Eventcarries neitheridnoroutcome, so the first event pushed to a subscribed client failsserde_json::from_slice::<Response>and surfaces asClientError::MalformedResponse. Response correlation is also offset by one line from that point on.Why it matters
UiModelinomachat-tuialready carries conversations, messages, unread counts and delivery state, but nothing populates them —Subscribe/Eventis the only source that could. OC-024 (#25) is the first work that needs live topics and will meet this immediately.Suggested extension
Give
omachat-ctla demultiplexing read path:request(), so every inbound line is classified onceResponsebyidback to the pending request, and deliverEventto a receiver the caller ownsMAX_LINE_BYTESbound and apply the version check to both shapesClient::subscribe(topics) -> Receiver<Event>Happy to implement this if the shape sounds right — I'd rather check the intended boundary first, since it touches the client contract.
Evidence
crates/omachat-proto/src/ipc.rs—Command::Subscribe,Topic,Eventcrates/omachatd/src/ipc_server.rs:243— the event write armcrates/omachat-ctl/src/lib.rs—request()/read_line(), no event handlingcrates/omachat-proto/tests/ipc.rs— covers the codec, not the client read pathOut of scope
Wiring the TUI to live topics (OC-024 / #25), and the OC-023 (#24) shell work itself.
Maintainer reconciliation — 2026-09-04
Next closure evidence/work: Implement a single bounded demultiplexing reader, response-ID correlation and event receiver; test interleaving, versions, disconnects and backpressure. Coordinate protocol changes with #226.
Baseline reviewed: main
87c3bcb7d65e3e2ab8ae317694b751f8efe9a8ed. Implementation presence is not a live conformance claim.