You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Problem — There is no UI to enable control, to show you're driving a peer, or to degrade honestly when control isn't held; peer session rows are read-only (B2a) with no write affordances. See #1540. Approach — On the session surface, add Enable control (self-owned) / Request control (shared), a persistent "driving <peer>" banner, and write affordances (composer→peer send, archive, delete) enabled only under control — with disabled + reason-chip states otherwise. Delete reuses the existing arm→confirm; archive takes no confirm; enable-control is a native modal. Grant management + pending requests live in the Fleet Manager tab. The sidebar stays read-only. Scope — in: session-surface control affordances + driving banner + fail-closed states; the enable-control native modal; the Fleet Manager grant-management panel. · out: the write plane (slice 2); the request→approve backend (slice 5); live-stream plumbing (consumes slice 3 when present). Assumptions — slice 2 provides the gated write endpoints + named states; slice 3, if landed, provides live events, else results appear on session re-fetch.
Warning
Deliberate-review corrections (2026-09-24) — these BIND over any stale text below.
A state channel is missing and must be specified (Data Contract). The reason/eligibility modules are pure functions in the extension; nothing projects them to the SolidJS app. Name the app-visible shape {controlState, reason, eligibility} and which endpoint/SSE frame carries it, projected from remote_session_state.ts — that module is the single source of truth for the fail-closed chip (the write gate collapses revocation-pending→grant-revoked, so do NOT read the raw gate reason). Enumerate ALL emitted reasons: no-control-grant, grant-revoked, revocation-pending, transport-down, insufficient-scope.
There is no remote-delete surface to "reuse". A peer row renders only open+badge (<Show when={!isRemote()}>); the arm→confirm delete exists only on archived rows and the timeline delete is a local SDK call keyed on sessionID (no owner routing). This slice must build the remote-delete affordance and route it by owner; "reuse" means reuse the arm→confirm interaction, not an existing wired action. Pick which surface it attaches to.
Driving banner: pin its host surface + persistence scope (which navigation it survives) with a data- hook, so "persistent" is assertable.
When control is not held, write affordances are disabled with a visible reason chip (no-control-grant / revocation-pending / transport-down) — never a live button that errors.
An Enable control affordance appears for an eligible self-owned peer; a Request control affordance for a shared peer.
Enabling control shows the native-modal confirm and lights the persistent "driving <peer>" banner.
archive takes no discrete confirm; delete uses the existing arm→confirm / modal (no second modal); prompt flows freely under control.
The sidebar fleet section remains read-only — control affordances live only on the session surface + Fleet Manager.
Testing Decisions
Extend the existing session-header and home dropdown component suites (they already cover the open/delete affordances and the B2a resolveDropdownOpenAction surface). Add driving-banner and fail-closed-state tests (affordance disabled + reason chip per state). Extends slice 2's (#1542) named-state contract — assert the UI reflects each state.
Key Decisions
Authorize-vs-confirm: the service authorizes (grant + reachable); the closest existing UI confirms — never double-confirm.
The driving banner is the ambient-safety mechanism (control is never silent).
Control management lives in Fleet Manager, not the read-only sidebar.
Constraints & Invariants
Sidebar stays read-only.
No double-confirm (delete reuses its existing confirm; archive none; prompt none under enabled control).
Honest disabled states — never a 500 button.
Prior Art
Read first (modules): session-header and home (the dropdown open/delete affordances + session-fleet-peers), the message-timeline delete flow (the arm→confirm / modal to reuse), the Fleet Manager tab, and the app-surface CONTEXT.md (sidebar read-only invariant).
Important
Problem — There is no UI to enable control, to show you're driving a peer, or to degrade honestly when control isn't held; peer session rows are read-only (B2a) with no write affordances. See #1540.
Approach — On the session surface, add Enable control (self-owned) / Request control (shared), a persistent "driving
<peer>" banner, and write affordances (composer→peer send, archive, delete) enabled only under control — with disabled + reason-chip states otherwise. Delete reuses the existing arm→confirm; archive takes no confirm; enable-control is a native modal. Grant management + pending requests live in the Fleet Manager tab. The sidebar stays read-only.Scope — in: session-surface control affordances + driving banner + fail-closed states; the enable-control native modal; the Fleet Manager grant-management panel. · out: the write plane (slice 2); the request→approve backend (slice 5); live-stream plumbing (consumes slice 3 when present).
Assumptions — slice 2 provides the gated write endpoints + named states; slice 3, if landed, provides live events, else results appear on session re-fetch.
Warning
Deliberate-review corrections (2026-09-24) — these BIND over any stale text below.
{controlState, reason, eligibility}and which endpoint/SSE frame carries it, projected fromremote_session_state.ts— that module is the single source of truth for the fail-closed chip (the write gate collapsesrevocation-pending→grant-revoked, so do NOT read the raw gate reason). Enumerate ALL emitted reasons:no-control-grant,grant-revoked,revocation-pending,transport-down,insufficient-scope.<Show when={!isRemote()}>); the arm→confirm delete exists only on archived rows and the timeline delete is a local SDK call keyed onsessionID(no owner routing). This slice must build the remote-delete affordance and route it by owner; "reuse" means reuse the arm→confirm interaction, not an existing wired action. Pick which surface it attaches to.data-hook, so "persistent" is assertable.no-control-grant) is insufficient to exercise them.Full record: ADR 0034 D4/D5.
Acceptance Criteria
no-control-grant/revocation-pending/transport-down) — never a live button that errors.<peer>" banner.Testing Decisions
Extend the existing session-header and home dropdown component suites (they already cover the open/delete affordances and the B2a
resolveDropdownOpenActionsurface). Add driving-banner and fail-closed-state tests (affordance disabled + reason chip per state). Extends slice 2's (#1542) named-state contract — assert the UI reflects each state.Key Decisions
Constraints & Invariants
Prior Art
Read first (modules):
session-headerandhome(the dropdown open/delete affordances +session-fleet-peers), themessage-timelinedelete flow (the arm→confirm / modal to reuse), the Fleet Manager tab, and the app-surfaceCONTEXT.md(sidebar read-only invariant).Source
docs/adr/0034-base-peer-studio-control.md), decisions D4/D5/D7.Notes
UX-bearing → HITL. Consumes slice 3's live stream when present; without it, results show on re-fetch (the honest degradation).