Skip to content

Commit 61a7f7d

Browse files
charliekclaude
andcommitted
docs: stop claiming events.subscribe delivers events
docs/reference/ipc.md said in four places that the server pushes event envelopes after events.subscribe; the op has always answered not-implemented (ops::EVENTS_SUBSCRIBE in roost-engine/src/ipc.rs). All four sites now document the real error reply, point callers at polling, and carry a forward note: HS-1 delivers events as atomic {revision, events[]} EventBatches (types landed in roost-ipc on this branch). The event catalog stays, reframed as the shape a future push will carry (plan 033 / HS-0, D7). Also sweeps the profile docs for the new Session profile: paths.md gains the reserved-profile row, the session path table (with the -dev split), and the ROOST_BUNDLE_PROFILE note; ipc.md points at it from the socket-path section. setup.md's profile table is deliberately untouched — it is scoped to UI profiles and roostctl targets, which Session is not. Docs-only commit; self-reviewed (paths and op behavior were code-verified earlier in this branch's commits). Gate: make check + make docs (strict). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XdmwyhPxKnPHFFiqAMyUMC
1 parent 4c4b8ea commit 61a7f7d

2 files changed

Lines changed: 63 additions & 23 deletions

File tree

docs/reference/ipc.md

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ than one is live, it reports the actual candidates and requires selection.
2424
(The `linux` profile also resolves on macOS, as `~/Library/Caches/Roost-linux/`,
2525
but nothing ships or launches it there.)
2626

27+
A fifth socket path, `Session` (`~/Library/Caches/RoostSession/roost.sock`
28+
on macOS, `$XDG_RUNTIME_DIR/roost-session/roost.sock` on Linux), is
29+
**reserved** for the future `roost-session` daemon (HS-1). Nothing binds
30+
it yet, it is not a `roostctl --target` value, and `roostctl` never probes
31+
it — see [`paths.md`](paths.md#session-profile-reserved).
32+
2733
## Wire format
2834

2935
* **Framing:** newline-delimited JSON. One JSON object per line.
@@ -42,10 +48,11 @@ but nothing ships or launches it there.)
4248
`{"id": "<string>", "ok": true, "result": {...}}`.
4349
* **Response envelope (error):**
4450
`{"id": "<string>", "ok": false, "error": {"code": "<kebab>", "message": "<string>"}}`.
45-
* **Event envelope** (server-push, unsolicited, only sent after
46-
`events.subscribe`):
51+
* **Event envelope** (server-push, unsolicited**not delivered
52+
today**; see [`events.subscribe`](#eventssubscribe)):
4753
`{"event": "<dotted-name>", "data": {...}}` — no `id`, no response
48-
expected.
54+
expected. This is the shape a future push will use; the catalog in
55+
[Events](#events) below documents it in advance.
4956
* **Bytes payloads** (e.g. `tab.write.data`, and any future binary
5057
field): **base64-encoded strings** using the standard alphabet,
5158
no padding stripping. Tested for binary fidelity (`0x00..0xff`
@@ -1009,26 +1016,40 @@ state live on the UI side.
10091016

10101017
### `events.subscribe`
10111018

1012-
Opt-in to the event stream. After the response, the server pushes
1013-
`{"event": ..., "data": ...}` envelopes on the same connection until
1014-
the connection closes.
1015-
1016-
Request: `{"params": {"tab_id_filter": "0"}}`. A non-zero
1017-
`tab_id_filter` restricts the stream to events for that tab.
1018-
1019-
**M0 status:** stubbed. The server replies `{"ok": true, "result":
1020-
{}}` and never sends event envelopes on the connection. This is
1021-
intentional — `roostctl` does not need events for any current
1022-
subcommand, and clients that *do* want events will surface as
1023-
follow-ups against a working stub.
1024-
1025-
Response: `{}`.
1019+
**Not implemented.** The op exists on the wire but the server
1020+
rejects every call with `{"ok": false, "error": {"code":
1021+
"not-implemented", "message": "events.subscribe is not yet
1022+
implemented"}}` (`ops::EVENTS_SUBSCRIBE` in
1023+
`crates/roost-engine/src/ipc.rs`) rather than a false ACK — a client
1024+
that treated the reply as "subscribed" would wait forever, since no
1025+
event is ever pushed on the connection today. Callers that want
1026+
current state should poll `tab.list` / `project.list` /
1027+
`tab.dump` instead.
1028+
1029+
Request shape as designed: `{"params": {"tab_id_filter": "0"}}`. A
1030+
non-zero `tab_id_filter` would restrict the stream to events for
1031+
that tab.
1032+
1033+
**Forward note (HS-1):** push delivery lands with `roost-session`
1034+
(the host-sessions daemon), delivered as atomic `EventBatch`es —
1035+
`{"revision": <u64>, "events": [<EventEnvelope>, ...]}` — rather than
1036+
one envelope per line. Batching every envelope that shares a
1037+
`revision` lets a client detect loss with a simple gap check ("did I
1038+
skip a revision?") instead of reconstructing individual dropped
1039+
events; on a gap it re-pulls `tab.list` / `project.list`. Both
1040+
`EventBatch` and `EventEnvelope` are already defined in
1041+
`crates/roost-ipc/src/messages.rs` (landed ahead of the push
1042+
implementation) — see [Events](#events) below for the envelope
1043+
catalog they will carry.
10261044

10271045
## Events
10281046

1029-
Server-push only. Each is a line of the form `{"event": "<name>", "data":
1030-
{...}}`. The set below is the exhaustive list; no other event names are
1031-
emitted.
1047+
Server-push only, and **not delivered yet** — see
1048+
[`events.subscribe`](#eventssubscribe). Each event is documented here
1049+
in the envelope shape it will be pushed in once HS-1 implements
1050+
delivery: a line of the form `{"event": "<name>", "data": {...}}`
1051+
inside an `EventBatch`. The set below is the exhaustive list of event
1052+
*types* the workspace already models; no other event names exist.
10321053

10331054
* `tab.opened``{"tab": <Tab>}`.
10341055
* `tab.closed``{"tab_id": "<id>"}`.
@@ -1069,7 +1090,9 @@ makes them unnecessary:
10691090
* `ReportOsc`. OSC sequences are parsed in the UI; the UI updates
10701091
its own state directly. There is nobody to round-trip to.
10711092
* `WatchEvents` (legacy event stream RPC) is replaced by the
1072-
`events.subscribe` op + push envelopes on the same connection.
1093+
`events.subscribe` op + push envelopes on the same connection —
1094+
designed, wire-typed (`EventEnvelope` / `EventBatch`), but not
1095+
implemented yet; see [`events.subscribe`](#eventssubscribe).
10731096

10741097
Schema-only fields that survive but rename:
10751098

docs/reference/paths.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
Roost resolves all of its filesystem state once at startup. Other components read the paths from this resolution; nothing should derive its own.
44

5-
There are three bundle profiles — `Mac`, `Linux`, and `Iced` (slugs `mac`, `linux`, `iced`) — and each running UI resolves exactly one of them. There is no shared daemon; the profile a UI resolves determines the socket `roostctl` dials. The Rust definition lives in `crates/roost-ipc/src/paths.rs`; the Swift companion is `mac/Sources/Roost/BundleProfile.swift`. On macOS the two implementations are tested in lockstep.
5+
There are three UI bundle profiles — `Mac`, `Linux`, and `Iced` (slugs `mac`, `linux`, `iced`) — and each running UI resolves exactly one of them. There is no shared daemon; the profile a UI resolves determines the socket `roostctl` dials. A fourth profile, `Session`, exists in the same enum for the future `roost-session` daemon (HS-1) but is reserved: nothing launches it yet, and it is not a legal `roostctl` target or `ROOST_BUNDLE_PROFILE` value today (see [Session profile (reserved)](#session-profile-reserved) below). The Rust definition lives in `crates/roost-ipc/src/paths.rs`; the Swift companion is `mac/Sources/Roost/BundleProfile.swift`. On macOS the two implementations are tested in lockstep.
66

77
| Profile | Who resolves it | `app_label` | `app_id` |
88
|---|---|---|---|
99
| `Mac` | the Swift `Roost.app` | `Roost` | `ai.stridelabs.Roost` |
1010
| `Linux` | the packaged Linux UI (`/usr/bin/roost`) | `Roost-linux` | `ai.stridelabs.Roost` on Linux; `ai.stridelabs.Roost.linux` on macOS |
1111
| `Iced` | a dev build of `roost-iced`, and the experimental macOS `Roost-Iced.app` | `Roost-iced` | `ai.stridelabs.Roost.iced` |
12+
| `Session` (reserved) | nobody yet — HS-1's `roost-session` daemon | `RoostSession` (`RoostSessionDev` in debug builds) | `ai.stridelabs.Roost.session` |
1213

1314
`app_label` is fixed per profile on every platform — it is the string `identify` and `roostctl doctor` report, and on macOS it is also the directory component of the profile's paths. `app_id` is the only field that resolves per platform: on Linux the `Linux` profile shares `ai.stridelabs.Roost` with the `Mac` profile, which can never run there and already shares that platform's path namespace; on macOS every profile stays independently resolvable (so their paths and identities can never collide), even though nothing ships or launches the `Linux` one there.
1415

@@ -23,7 +24,7 @@ The profile defaults to:
2324
| `roost-iced` (dev build, any platform; macOS `Roost-Iced.app`) | `Iced` | `ROOST_BUNDLE_PROFILE=mac` / `=linux` to dial another profile's namespace |
2425
| `roostctl` (binary from the `roost-cli` crate) | auto-detect | `ROOST_BUNDLE_PROFILE` / `--socket` / `ROOST_SOCKET` / `--target {mac,linux,iced}` |
2526

26-
The two sides treat an **unrecognized** `ROOST_BUNDLE_PROFILE` value differently, on purpose. A UI logs a warning and falls back to its compiled-in default rather than refusing to launch; `roostctl` hard-errors with `unknown ROOST_BUNDLE_PROFILE value … (expected mac, linux, or iced)`. A stale `ROOST_BUNDLE_PROFILE=gtk` left over from an older install therefore starts the UI on its normal profile (with a warning in the log) but stops the CLI outright.
27+
The two sides treat an **unrecognized** `ROOST_BUNDLE_PROFILE` value differently, on purpose. A UI logs a warning and falls back to its compiled-in default rather than refusing to launch; `roostctl` hard-errors with `unknown ROOST_BUNDLE_PROFILE value … (expected mac, linux, or iced)`. A stale `ROOST_BUNDLE_PROFILE=gtk` left over from an older install therefore starts the UI on its normal profile (with a warning in the log) but stops the CLI outright. `session` gets the same `roostctl` rejection today even though it names a real profile kind internally — see [Session profile (reserved)](#session-profile-reserved) — because HS-1 hasn't defined how a session gets addressed yet.
2728

2829
## File locations
2930

@@ -98,6 +99,22 @@ keep launching.
9899

99100
The directories are created at first launch with mode `0700`.
100101

102+
### Session profile (reserved)
103+
104+
`Session` resolves paths the same way the three UI profiles do, but
105+
nothing launches it yet — it is reserved for the future headless
106+
`roost-session` daemon (HS-1). It is **not** a `roostctl` target and
107+
**not** a legal `ROOST_BUNDLE_PROFILE` value today; `roostctl --target
108+
session` and `ROOST_BUNDLE_PROFILE=session` are both rejected — see
109+
the `ROOST_BUNDLE_PROFILE` discussion above. Debug
110+
builds get their own `-dev`/`Dev`-suffixed directories so a dev
111+
session can never collide with a real one.
112+
113+
| Platform | Socket | State | Logs |
114+
|---|---|---|---|
115+
| macOS | `~/Library/Caches/RoostSession/roost.sock` (`RoostSessionDev` in debug builds) | `~/Library/Application Support/RoostSession/` | `~/Library/Logs/RoostSession/` |
116+
| Linux | `$XDG_RUNTIME_DIR/roost-session/roost.sock`, falling back to `/tmp/roost-session-<uid>/roost.sock` (`roost-session-dev` in debug builds) | `$XDG_DATA_HOME/roost-session/` | `$XDG_STATE_HOME/roost-session/` |
117+
101118
### Two single-instance locks
102119

103120
A running UI holds **two** flocks, because the two things a single

0 commit comments

Comments
 (0)