Skip to content

Fleet Studio B2b · slice 4 — Control UI: enable, driving banner, fail-closed affordances #1544

Description

@jeonghun-jj-lee

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.

  • 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.
  • Drop AC "prompt flows freely" — the composer→peer send is Fleet Studio B2b · slice 2 — Observation write plane: control-gated remote writes #1542's scope, not this slice's.
  • Dependency: add Fleet Studio B2b · slice 1 — Grant foundation: Enroll seeds lifecycle-admin + self-owned control issuance #1541 explicitly (its grant states must exist to test the fail-closed matrix / revocation paths); Fleet Studio B2b · slice 2 — Observation write plane: control-gated remote writes #1542 alone (which can always return no-control-grant) is insufficient to exercise them.
    Full record: ADR 0034 D4/D5.

Acceptance Criteria

  • 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).

Source

Notes

UX-bearing → HITL. Consumes slice 3's live stream when present; without it, results show on re-fetch (the honest degradation).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    hitlNeeds human review before merge

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions