docs: describe broker preview subscription
- commit
- fff93b82cee24d19a8703b75ee6f2a7a47f48c84
- author
- brice · · 2026-09-22 19:23 UTC
- parent
- c56f581
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +14 -17
M docs/architecture.md +6 -10
| @@ -98,17 +98,13 @@ Interactive observation is a separate opt-in composition. `term.Terminal` remain | |||
| 98 | 98 | ||
| 99 | 99 | A client publishes exactly one of three presentations at a time. `picker` is the local picker: the client is usable and describable without a broker and without a daemon. `connecting` is an attachment attempt that has not committed its first frame. `attached` is a committed attachment, and only it carries the validated session identity, the committed output boundary, and the real actionable generation; `picker` and `connecting` carry the run's stable service handle and their status with every session field zeroed, so a capture can never present session metadata as an attachment and no caller can fence an input action against a generation no attachment committed. The former `detached`, `reconnecting`, and `transitioning` values are removed without alias: a reconnect is `connecting`, a detach returns to `picker`, and terminating closes the UI service instead of becoming a fourth persistent presentation. The presentation is published inside the terminal's UI output transaction by its owner: the supervisor publishes `connecting` before the attachment foreground is granted and `picker` after the run drained and revoked, while the admitted foreground publishes `attached` only after its first frame was written, flushed, and committed. | |
| 100 | 100 | ||
| 101 | - | Every attachment presents the navigation picker itself. The serving daemon publishes a typed interaction instead of installing an overlay: `PickerOffer` names the intent and the acquisition barrier, `PickerSnapshot` carries opaque row keys plus display text, `PickerSelection` commits a key at the displayed revision, `PickerClose` retires the interaction in either direction, and `PickerFailure` reports a bounded rejection. The daemon owns no presentation model: it keeps facts, eligibility, and mutations, and resolves committed keys to navigation, move, or kill targets. | |
| 101 | + | Session navigation is an autonomous client presentation, not an attachment interaction. While attached, Alt+Space opens the daemon command palette; `SSP` / `session-picker` ends the current attachment with `Detached{Reason: ReasonDetachToPicker}`. The session stays headless. The attachment worker maps this to `AttachmentDetachToPicker` settlement; the supervisor drains and revokes the foreground before giving its input lease and terminal writer to the local picker. Search, sorting, cursor movement, selection, and broker-backed attachment opening are client-owned. No picker bytes or navigation selections are sent to the serving daemon. | |
| 102 | 102 | ||
| 103 | - | Source eligibility travels with every row. `PickerLine.Focusable` is the source's authorisation to rest the cursor on that row, while `PickerLine.Actions` is what the row may commit; an action row is always focusable, a section header never is, and a focusable row without actions is published for inspection only. The client's cursor visits focusable rows, narrowed by its own search query, so it never lands on a row the source skipped and the footer never promises a commit the cursor cannot make. | |
| 103 | + | The selected row owns one client-scoped live preview subscription through `ports.BrokerService`. The broker opens sequential observation streams over its existing local or remote physical pool, sends bounded `RemotePreviewRequest` values, and publishes the newest fenced result. Cursor movement and resize replace the subscription with a strictly newer generation; broker loss, commit, picker close, and client exit cancel it. The client accepts a publication only when broker epoch, connection, generation, target, and dimensions still match, so a late capture cannot repaint another selection. Preview cells remain bounded, memory-only, and are never persisted or logged. | |
| 104 | 104 | ||
| 105 | - | The client owns who writes the terminal for the interaction lifetime, while the daemon keeps mutation authority and resolves keys. `pickerLease` in `internal/usecase/client` tracks the presentation states: a snapshot is admitted only once the client applied output through the barrier epoch/state it carries, daemon paints are then applied to the output shadow and acknowledged inside the bounded window without being written, and the interaction is released only by a full paint accepted after the close boundary. Locally composed frames and repaints go through the same single writer, UI transaction, and flush as daemon output, so a frame is never claimed before it is displayed. | |
| 105 | + | There is no forwarding or compatibility path through `PickerOffer`. The daemon does not dispatch `client picker request` or `picker control request`, cannot open a navigation interaction, and cannot resolve a `PickerActionNavigate` or `PickerActionKill` interaction selection. | |
| 106 | 106 | ||
| 107 | - | Input ownership is exclusive for the interaction lifetime. `pickerConsumer` in `internal/usecase/client` publishes the current owner to the stdin pump: while the picker owns input the pump decodes physical bytes and admitted automation batches into identified operations and the attach loop, its single model writer, applies them. Nothing falls back to the session pipeline: unknown, oversized, and paste-sized batches are consumed, a multi-key batch is never applied as a command, and an ambiguous escape prefix is bounded by a short deadline instead of being forwarded. The serving daemon mirrors the rule: while `pickerOpen` it drops raw key and mouse input for that attachment (`handleInput`/`handleInputForAttachment` return before the mouse scanner and the key router), so only typed `PickerSelection`, `PickerClose`, and `PickerPreviewRequest` can act. A terminal close — a resolved selection, a cancel, a stale-revision or retired-target rejection, or a superseding overlay — sends `PickerClose` when the client must retire the interaction itself, keeps consuming and dropping input until the daemon's authoritative repaint lands, and only then resumes normal routing. | |
| 108 | - | ||
| 109 | - | ### Row previews | |
| 110 | - | ||
| 111 | - | A preview is data, not a daemon paint: the client asks for the row its cursor rests on with a debounced `PickerPreviewRequest` (80 ms, one request in flight per interaction), and the serving daemon answers with a bounded styled-cell `PickerPreview`. The daemon captures local rows through the navigation preview facts it already owns (`previewTarget` plus `snapshotPickerPreview`), captures remote rows through its own remote viewport client and cache, and registers the requested row as that viewer's render subscription, so a row that keeps changing keeps publishing while a superseded request publishes nothing. Unchanged or retired rows answer with a status-only preview, and the client clears its viewport rather than showing another row's content. The client composes the viewport into its own modal; the daemon never writes it. | |
| 107 | + | Pane/tab move destination pickers are not forwarded through session navigation. The existing daemon interaction remains temporarily available only to the daemon command palette; it captures and revalidates the move source and accepts only move intents. The autonomous attachment worker does not consume that interaction. Explicit scripted move commands remain available. The command palette itself remains daemon-owned and is separate from session-picker presentation. | |
| 112 | 108 | ||
| 113 | 109 | ### Resize repaint fence | |
| 114 | 110 | ||
| @@ -116,7 +112,7 @@ A terminal resize reaches the client as one coalesced invalidation owned by the | |||
| 116 | 112 | ||
| 117 | 113 | ### Attention and notifications | |
| 118 | 114 | ||
| 119 | - | Attention is a daemon fact, not an overlay: a bell raised by a PTY, by a status-line notification, or by an agent run marks the owning tab, and the daemon publishes it in the frames its attachments receive. The status bar carries it as a pulsing glyph, the picker session header carries it inline, and `PickerLine.Attention` carries it on the tab row the client renders. Because a client-owned modal suppresses daemon paints, the attention pulse republishes the picker source for every attachment whose interaction the client presents, so a bell raised while the picker is open still reaches the displayed rows. | |
| 115 | + | Attention remains a daemon fact. The autonomous picker renders catalogue facts obtained through the broker; it does not subscribe to an attachment's legacy picker interaction. Legacy move-destination interactions retain their own attention refresh machinery. | |
| 120 | 116 | ||
| 121 | 117 | ## Broker-owned hosts and daemon composition | |
| 122 | 118 | ||
| @@ -160,7 +156,7 @@ Use cases exchange `protocol.ClientMessage` and `protocol.ServerMessage` values. | |||
| 160 | 156 | ## Broker wire core (Plan 001 P3.1, not activated) | |
| 161 | 157 | ||
| 162 | 158 | `internal/adapters/brokerwire` owns the broker's separate Protobuf | |
| 163 | - | conversation end to end: client tags 101-111 and server tags 201-209 are | |
| 159 | + | conversation end to end: client tags 101-113 and server tags 201-210 are | |
| 164 | 160 | disjoint from every session tag, and each direction is its own closed | |
| 165 | 161 | directional `oneof` union. A broker connection opens with exactly one | |
| 166 | 162 | broker preamble (roles 3/4) that carries the same magic, epoch, exact | |
| @@ -98,17 +98,13 @@ Interactive observation is a separate opt-in composition. `term.Terminal` remain | |||||
| 98 | 98 | ||||
| 99 | A client publishes exactly one of three presentations at a time. `picker` is the local picker: the client is usable and describable without a broker and without a daemon. `connecting` is an attachment attempt that has not committed its first frame. `attached` is a committed attachment, and only it carries the validated session identity, the committed output boundary, and the real actionable generation; `picker` and `connecting` carry the run's stable service handle and their status with every session field zeroed, so a capture can never present session metadata as an attachment and no caller can fence an input action against a generation no attachment committed. The former `detached`, `reconnecting`, and `transitioning` values are removed without alias: a reconnect is `connecting`, a detach returns to `picker`, and terminating closes the UI service instead of becoming a fourth persistent presentation. The presentation is published inside the terminal's UI output transaction by its owner: the supervisor publishes `connecting` before the attachment foreground is granted and `picker` after the run drained and revoked, while the admitted foreground publishes `attached` only after its first frame was written, flushed, and committed. | 99 | A client publishes exactly one of three presentations at a time. `picker` is the local picker: the client is usable and describable without a broker and without a daemon. `connecting` is an attachment attempt that has not committed its first frame. `attached` is a committed attachment, and only it carries the validated session identity, the committed output boundary, and the real actionable generation; `picker` and `connecting` carry the run's stable service handle and their status with every session field zeroed, so a capture can never present session metadata as an attachment and no caller can fence an input action against a generation no attachment committed. The former `detached`, `reconnecting`, and `transitioning` values are removed without alias: a reconnect is `connecting`, a detach returns to `picker`, and terminating closes the UI service instead of becoming a fourth persistent presentation. The presentation is published inside the terminal's UI output transaction by its owner: the supervisor publishes `connecting` before the attachment foreground is granted and `picker` after the run drained and revoked, while the admitted foreground publishes `attached` only after its first frame was written, flushed, and committed. | ||
| 100 | 100 | ||||
| 101 | - | Every attachment presents the navigation picker itself. The serving daemon publishes a typed interaction instead of installing an overlay: `PickerOffer` names the intent and the acquisition barrier, `PickerSnapshot` carries opaque row keys plus display text, `PickerSelection` commits a key at the displayed revision, `PickerClose` retires the interaction in either direction, and `PickerFailure` reports a bounded rejection. The daemon owns no presentation model: it keeps facts, eligibility, and mutations, and resolves committed keys to navigation, move, or kill targets. | 101 | + | Session navigation is an autonomous client presentation, not an attachment interaction. While attached, Alt+Space opens the daemon command palette; `SSP` / `session-picker` ends the current attachment with `Detached{Reason: ReasonDetachToPicker}`. The session stays headless. The attachment worker maps this to `AttachmentDetachToPicker` settlement; the supervisor drains and revokes the foreground before giving its input lease and terminal writer to the local picker. Search, sorting, cursor movement, selection, and broker-backed attachment opening are client-owned. No picker bytes or navigation selections are sent to the serving daemon. |
| 102 | 102 | ||||
| 103 | - | Source eligibility travels with every row. `PickerLine.Focusable` is the source's authorisation to rest the cursor on that row, while `PickerLine.Actions` is what the row may commit; an action row is always focusable, a section header never is, and a focusable row without actions is published for inspection only. The client's cursor visits focusable rows, narrowed by its own search query, so it never lands on a row the source skipped and the footer never promises a commit the cursor cannot make. | 103 | + | The selected row owns one client-scoped live preview subscription through `ports.BrokerService`. The broker opens sequential observation streams over its existing local or remote physical pool, sends bounded `RemotePreviewRequest` values, and publishes the newest fenced result. Cursor movement and resize replace the subscription with a strictly newer generation; broker loss, commit, picker close, and client exit cancel it. The client accepts a publication only when broker epoch, connection, generation, target, and dimensions still match, so a late capture cannot repaint another selection. Preview cells remain bounded, memory-only, and are never persisted or logged. |
| 104 | 104 | ||||
| 105 | - | The client owns who writes the terminal for the interaction lifetime, while the daemon keeps mutation authority and resolves keys. `pickerLease` in `internal/usecase/client` tracks the presentation states: a snapshot is admitted only once the client applied output through the barrier epoch/state it carries, daemon paints are then applied to the output shadow and acknowledged inside the bounded window without being written, and the interaction is released only by a full paint accepted after the close boundary. Locally composed frames and repaints go through the same single writer, UI transaction, and flush as daemon output, so a frame is never claimed before it is displayed. | 105 | + | There is no forwarding or compatibility path through `PickerOffer`. The daemon does not dispatch `client picker request` or `picker control request`, cannot open a navigation interaction, and cannot resolve a `PickerActionNavigate` or `PickerActionKill` interaction selection. |
| 106 | 106 | ||||
| 107 | - | Input ownership is exclusive for the interaction lifetime. `pickerConsumer` in `internal/usecase/client` publishes the current owner to the stdin pump: while the picker owns input the pump decodes physical bytes and admitted automation batches into identified operations and the attach loop, its single model writer, applies them. Nothing falls back to the session pipeline: unknown, oversized, and paste-sized batches are consumed, a multi-key batch is never applied as a command, and an ambiguous escape prefix is bounded by a short deadline instead of being forwarded. The serving daemon mirrors the rule: while `pickerOpen` it drops raw key and mouse input for that attachment (`handleInput`/`handleInputForAttachment` return before the mouse scanner and the key router), so only typed `PickerSelection`, `PickerClose`, and `PickerPreviewRequest` can act. A terminal close — a resolved selection, a cancel, a stale-revision or retired-target rejection, or a superseding overlay — sends `PickerClose` when the client must retire the interaction itself, keeps consuming and dropping input until the daemon's authoritative repaint lands, and only then resumes normal routing. | 107 | + | Pane/tab move destination pickers are not forwarded through session navigation. The existing daemon interaction remains temporarily available only to the daemon command palette; it captures and revalidates the move source and accepts only move intents. The autonomous attachment worker does not consume that interaction. Explicit scripted move commands remain available. The command palette itself remains daemon-owned and is separate from session-picker presentation. |
| 108 | - | ||||
| 109 | - | ### Row previews | |||
| 110 | - | ||||
| 111 | - | A preview is data, not a daemon paint: the client asks for the row its cursor rests on with a debounced `PickerPreviewRequest` (80 ms, one request in flight per interaction), and the serving daemon answers with a bounded styled-cell `PickerPreview`. The daemon captures local rows through the navigation preview facts it already owns (`previewTarget` plus `snapshotPickerPreview`), captures remote rows through its own remote viewport client and cache, and registers the requested row as that viewer's render subscription, so a row that keeps changing keeps publishing while a superseded request publishes nothing. Unchanged or retired rows answer with a status-only preview, and the client clears its viewport rather than showing another row's content. The client composes the viewport into its own modal; the daemon never writes it. | |||
| 112 | 108 | ||||
| 113 | ### Resize repaint fence | 109 | ### Resize repaint fence | ||
| 114 | 110 | ||||
| @@ -116,7 +112,7 @@ A terminal resize reaches the client as one coalesced invalidation owned by the | |||||
| 116 | 112 | ||||
| 117 | ### Attention and notifications | 113 | ### Attention and notifications | ||
| 118 | 114 | ||||
| 119 | - | Attention is a daemon fact, not an overlay: a bell raised by a PTY, by a status-line notification, or by an agent run marks the owning tab, and the daemon publishes it in the frames its attachments receive. The status bar carries it as a pulsing glyph, the picker session header carries it inline, and `PickerLine.Attention` carries it on the tab row the client renders. Because a client-owned modal suppresses daemon paints, the attention pulse republishes the picker source for every attachment whose interaction the client presents, so a bell raised while the picker is open still reaches the displayed rows. | 115 | + | Attention remains a daemon fact. The autonomous picker renders catalogue facts obtained through the broker; it does not subscribe to an attachment's legacy picker interaction. Legacy move-destination interactions retain their own attention refresh machinery. |
| 120 | 116 | ||||
| 121 | ## Broker-owned hosts and daemon composition | 117 | ## Broker-owned hosts and daemon composition | ||
| 122 | 118 | ||||
| @@ -160,7 +156,7 @@ Use cases exchange `protocol.ClientMessage` and `protocol.ServerMessage` values. | |||||
| 160 | ## Broker wire core (Plan 001 P3.1, not activated) | 156 | ## Broker wire core (Plan 001 P3.1, not activated) | ||
| 161 | 157 | ||||
| 162 | `internal/adapters/brokerwire` owns the broker's separate Protobuf | 158 | `internal/adapters/brokerwire` owns the broker's separate Protobuf | ||
| 163 | - | conversation end to end: client tags 101-111 and server tags 201-209 are | 159 | + | conversation end to end: client tags 101-113 and server tags 201-210 are |
| 164 | disjoint from every session tag, and each direction is its own closed | 160 | disjoint from every session tag, and each direction is its own closed | ||
| 165 | directional `oneof` union. A broker connection opens with exactly one | 161 | directional `oneof` union. A broker connection opens with exactly one | ||
| 166 | broker preamble (roles 3/4) that carries the same magic, epoch, exact | 162 | broker preamble (roles 3/4) that carries the same magic, epoch, exact | ||
M docs/configuration.md +2 -0
| @@ -126,6 +126,8 @@ rm -rf .dev/dev | |||
| 126 | 126 | ||
| 127 | 127 | The selected absolute root is retained by the detached daemon and inherited vev processes even when their working directory changes. A name isolates configuration, runtime files, durable sessions, hosts, snapshots, and logs from normal XDG locations and from other names. Invocations using the same name intentionally share a daemon and files; use separate names for side-by-side binaries. | |
| 128 | 128 | ||
| 129 | + | If the runtime path would exceed the portable Unix-socket limit (103 bytes, including `vev/broker/broker.sock`), sockets and lifecycle locks instead use `/tmp/vev-<uid>-<sha256>`; the hash identifies the complete original runtime directory. Configuration and durable state remain under `.dev/<name>/`. All roles resolve the same private runtime directory, and distinct roots remain isolated. Short paths keep their existing location. Stop processes using an older binary before switching versions if their runtime location changes; do not delete a live runtime directory or its lock files. | |
| 130 | + | ||
| 129 | 131 | This feature isolates vev-owned data only. It does not sandbox pane processes or provide OS, filesystem, or network isolation. | |
| 130 | 132 | ||
| 131 | 133 | ## Remote hosts | |
| @@ -126,6 +126,8 @@ rm -rf .dev/dev | |||||
| 126 | 126 | ||||
| 127 | The selected absolute root is retained by the detached daemon and inherited vev processes even when their working directory changes. A name isolates configuration, runtime files, durable sessions, hosts, snapshots, and logs from normal XDG locations and from other names. Invocations using the same name intentionally share a daemon and files; use separate names for side-by-side binaries. | 127 | The selected absolute root is retained by the detached daemon and inherited vev processes even when their working directory changes. A name isolates configuration, runtime files, durable sessions, hosts, snapshots, and logs from normal XDG locations and from other names. Invocations using the same name intentionally share a daemon and files; use separate names for side-by-side binaries. | ||
| 128 | 128 | ||||
| 129 | + | If the runtime path would exceed the portable Unix-socket limit (103 bytes, including `vev/broker/broker.sock`), sockets and lifecycle locks instead use `/tmp/vev-<uid>-<sha256>`; the hash identifies the complete original runtime directory. Configuration and durable state remain under `.dev/<name>/`. All roles resolve the same private runtime directory, and distinct roots remain isolated. Short paths keep their existing location. Stop processes using an older binary before switching versions if their runtime location changes; do not delete a live runtime directory or its lock files. | |||
| 130 | + | ||||
| 129 | This feature isolates vev-owned data only. It does not sandbox pane processes or provide OS, filesystem, or network isolation. | 131 | This feature isolates vev-owned data only. It does not sandbox pane processes or provide OS, filesystem, or network isolation. | ||
| 130 | 132 | ||||
| 131 | ## Remote hosts | 133 | ## Remote hosts | ||
M docs/protocol.md +6 -7
| @@ -17,8 +17,7 @@ Client → server: `Hello`, `Input`, `Resize`, `Detach`, `Ping`, `List`, | |||
| 17 | 17 | `RecentRouteSnapshot`, `RouteNavigationFailure`, | |
| 18 | 18 | `SessionCreationFailure`, `UIFence`, `NavigationInventoryRequest`, | |
| 19 | 19 | `NavigationInventoryPublication`, `NavigationInventoryFailure`, | |
| 20 | - | `PickerBegin`, `PickerClose`, `PickerSelection`, `PickerPreviewRequest`, | |
| 21 | - | `PickerControlRequest`. | |
| 20 | + | `PickerClose`, `PickerSelection`, `PickerPreviewRequest`. | |
| 22 | 21 | ||
| 23 | 22 | Server → client: `Welcome`, `ErrorMsg`, `Output`, `Detached`, `Pong`, | |
| 24 | 23 | `Sessions`, `CommandResult`, `AttachTarget`, `RemotePreview`, | |
| @@ -28,7 +27,7 @@ Server → client: `Welcome`, `ErrorMsg`, `Output`, `Detached`, `Pong`, | |||
| 28 | 27 | `NavigationInventoryResponse`, `NavigationInventoryDemand`, | |
| 29 | 28 | `NavigationInventorySelection`, `PickerOffer`, `PickerSnapshot`, | |
| 30 | 29 | `PickerClosed`, `PickerResult`, `PickerFailure`, `PickerPreview`, | |
| 31 | - | `PickerControlResponse`, `KillResult`. | |
| 30 | + | `KillResult`. | |
| 32 | 31 | ||
| 33 | 32 | `RouteNavigationFailure` is the one payload that appears in both unions. | |
| 34 | 33 | `sessionwire` conversion switches (`decodeProtoClient`/`decodeProtoServer`, | |
| @@ -68,7 +67,7 @@ negotiated ceiling. | |||
| 68 | 67 | ||
| 69 | 68 | First frame routing (`daemon.go:handleConn`): `Hello` → full attach; | |
| 70 | 69 | `List`, `CommandRequest`, `RemotePreviewRequest`, | |
| 71 | - | `NavigationInventoryRequest`, `PickerControlRequest`, `Kill` are one-shot | |
| 70 | + | `NavigationInventoryRequest`, `Kill` are one-shot | |
| 72 | 71 | control connections answered and closed. Anything else, including any | |
| 73 | 72 | malformed first frame, gets a typed compatibility response and no session | |
| 74 | 73 | mutation: | |
| @@ -84,9 +83,9 @@ mutation: | |||
| 84 | 83 | | other / unknown | `ErrorMsg{ErrInternal, "expected hello"}` | no | | |
| 85 | 84 | ||
| 86 | 85 | Live checks after decode: `Hello.Version`, `CommandRequest.Version`, | |
| 87 | - | `NavigationInventoryRequest.Version`, `PickerControlRequest.Version`, | |
| 88 | - | and `RemotePreviewRequest.Version` (independent catalogue-era schema | |
| 89 | - | version `1`) must equal the negotiated constant or the request fails | |
| 86 | + | `NavigationInventoryRequest.Version`, and `RemotePreviewRequest.Version` | |
| 87 | + | (independent catalogue-era schema version `1`) must equal the negotiated | |
| 88 | + | constant or the request fails | |
| 90 | 89 | with the version-mismatch response above. There is no multi-version | |
| 91 | 90 | negotiation: equality is mandatory. | |
| 92 | 91 | ||
| @@ -17,8 +17,7 @@ Client → server: `Hello`, `Input`, `Resize`, `Detach`, `Ping`, `List`, | |||||
| 17 | `RecentRouteSnapshot`, `RouteNavigationFailure`, | 17 | `RecentRouteSnapshot`, `RouteNavigationFailure`, | ||
| 18 | `SessionCreationFailure`, `UIFence`, `NavigationInventoryRequest`, | 18 | `SessionCreationFailure`, `UIFence`, `NavigationInventoryRequest`, | ||
| 19 | `NavigationInventoryPublication`, `NavigationInventoryFailure`, | 19 | `NavigationInventoryPublication`, `NavigationInventoryFailure`, | ||
| 20 | - | `PickerBegin`, `PickerClose`, `PickerSelection`, `PickerPreviewRequest`, | 20 | + | `PickerClose`, `PickerSelection`, `PickerPreviewRequest`. |
| 21 | - | `PickerControlRequest`. | |||
| 22 | 21 | ||||
| 23 | Server → client: `Welcome`, `ErrorMsg`, `Output`, `Detached`, `Pong`, | 22 | Server → client: `Welcome`, `ErrorMsg`, `Output`, `Detached`, `Pong`, | ||
| 24 | `Sessions`, `CommandResult`, `AttachTarget`, `RemotePreview`, | 23 | `Sessions`, `CommandResult`, `AttachTarget`, `RemotePreview`, | ||
| @@ -28,7 +27,7 @@ Server → client: `Welcome`, `ErrorMsg`, `Output`, `Detached`, `Pong`, | |||||
| 28 | `NavigationInventoryResponse`, `NavigationInventoryDemand`, | 27 | `NavigationInventoryResponse`, `NavigationInventoryDemand`, | ||
| 29 | `NavigationInventorySelection`, `PickerOffer`, `PickerSnapshot`, | 28 | `NavigationInventorySelection`, `PickerOffer`, `PickerSnapshot`, | ||
| 30 | `PickerClosed`, `PickerResult`, `PickerFailure`, `PickerPreview`, | 29 | `PickerClosed`, `PickerResult`, `PickerFailure`, `PickerPreview`, | ||
| 31 | - | `PickerControlResponse`, `KillResult`. | 30 | + | `KillResult`. |
| 32 | 31 | ||||
| 33 | `RouteNavigationFailure` is the one payload that appears in both unions. | 32 | `RouteNavigationFailure` is the one payload that appears in both unions. | ||
| 34 | `sessionwire` conversion switches (`decodeProtoClient`/`decodeProtoServer`, | 33 | `sessionwire` conversion switches (`decodeProtoClient`/`decodeProtoServer`, | ||
| @@ -68,7 +67,7 @@ negotiated ceiling. | |||||
| 68 | 67 | ||||
| 69 | First frame routing (`daemon.go:handleConn`): `Hello` → full attach; | 68 | First frame routing (`daemon.go:handleConn`): `Hello` → full attach; | ||
| 70 | `List`, `CommandRequest`, `RemotePreviewRequest`, | 69 | `List`, `CommandRequest`, `RemotePreviewRequest`, | ||
| 71 | - | `NavigationInventoryRequest`, `PickerControlRequest`, `Kill` are one-shot | 70 | + | `NavigationInventoryRequest`, `Kill` are one-shot |
| 72 | control connections answered and closed. Anything else, including any | 71 | control connections answered and closed. Anything else, including any | ||
| 73 | malformed first frame, gets a typed compatibility response and no session | 72 | malformed first frame, gets a typed compatibility response and no session | ||
| 74 | mutation: | 73 | mutation: | ||
| @@ -84,9 +83,9 @@ mutation: | |||||
| 84 | | other / unknown | `ErrorMsg{ErrInternal, "expected hello"}` | no | | 83 | | other / unknown | `ErrorMsg{ErrInternal, "expected hello"}` | no | | ||
| 85 | 84 | ||||
| 86 | Live checks after decode: `Hello.Version`, `CommandRequest.Version`, | 85 | Live checks after decode: `Hello.Version`, `CommandRequest.Version`, | ||
| 87 | - | `NavigationInventoryRequest.Version`, `PickerControlRequest.Version`, | 86 | + | `NavigationInventoryRequest.Version`, and `RemotePreviewRequest.Version` |
| 88 | - | and `RemotePreviewRequest.Version` (independent catalogue-era schema | 87 | + | (independent catalogue-era schema version `1`) must equal the negotiated |
| 89 | - | version `1`) must equal the negotiated constant or the request fails | 88 | + | constant or the request fails |
| 90 | with the version-mismatch response above. There is no multi-version | 89 | with the version-mismatch response above. There is no multi-version | ||
| 91 | negotiation: equality is mandatory. | 90 | negotiation: equality is mandatory. | ||
| 92 | 91 | ||||