Skip to content

feat(gateway): multi-sandbox approval inbox via streaming proposal updates #1612

@zredlined

Description

@zredlined

Problem Statement

The agent-driven policy approval loop landing in #1097 / #1528 ships a single-sandbox CLI/TUI review flow as the locked MVP. Reviewers operating across multiple sandboxes today must either watch the TUI per-sandbox or poll GetDraftPolicy per sandbox via gRPC.

This is workable for a developer running one or two sandboxes, but blocks two patterns we are already seeing from real customer adoption:

  • Platform teams running cohorts of agents (e.g., 20-team rollouts) where a small reviewer pool needs a single inbox view across the fleet instead of N tabs.
  • Web/Slack approval adapters built on top of the gRPC contract — without a streaming or aggregated surface they have to long-poll per sandbox to keep an inbox UI live.

The #1062 umbrella explicitly lists "Multi-sandbox approval inbox" under Follow-Up Themes and "Multi-sandbox push inbox / WatchProposals" as an MVP non-goal. This issue promotes that follow-up theme into a discrete tracking item.

Proposed Design

Add an aggregated proposal inbox surface to the existing gRPC service so reviewers see one prioritized stream across all sandboxes they have access to.

Two surfaces:

  1. ListProposalInbox — unary RPC. Aggregates pending (and optionally approved/rejected) draft chunks across sandboxes the caller has config:read for, with pagination and filtering (sandbox label selector, status, age, has-finding). Returns the same DraftChunk shape used by GetDraftPolicy plus a sandbox_ref field so the client can disambiguate.
  2. WatchProposalInbox — server-streaming RPC. Emits inbox-level deltas (chunk added, status changed, reloaded) so a long-lived UI does not have to poll. Resumable via an opaque cursor so disconnects do not require a full re-list.

Both RPCs reuse the existing RBAC model (caller's config:read set defines visibility; admin role required for write RPCs which remain per-chunk and per-sandbox). Both reuse the existing DraftChunk, validation_result, and audit shapes — no new schema beyond the inbox envelope and the cursor.

CLI surface: extend openshell rule get with --all-sandboxes and add openshell rule watch as a stream consumer for terminal use. TUI: extend the existing SandboxDraft pane into a Dashboard-scope inbox tab as a follow-up; out of scope for this issue beyond defining the contract.

Alternatives Considered

  • Per-sandbox long-polling from clients. Workable for small N but does not scale to fleet review and produces N times the gateway load. Forces every partner-built approval UI to reinvent aggregation.
  • Push to external webhook/Slack from the gateway. Couples the gateway to specific transport adapters and pushes auth/retry concerns into the core. Better implemented as a partner adapter on top of WatchProposalInbox.
  • In-place expansion of GetDraftPolicy to accept sandbox_id=*. Conflates the per-sandbox and fleet shapes, weakens RBAC scoping in a single RPC, and gives no path to streaming.

Agent Investigation

  • Confirmed the loop is single-sandbox by design today: `crates/openshell-server/src/grpc/policy.rs` exposes `GetDraftPolicy`, `ApproveDraftChunk`, etc., all scoped to one sandbox.
  • Confirmed in OpenShell Agent-Driven Policy Management #1062: "Multi-sandbox push inbox / `WatchProposals`" is an explicit MVP non-goal and "Multi-sandbox approval inbox" is listed under Follow-Up Themes.
  • TUI (`crates/openshell-tui/src/sandbox_draft.rs`) ties inbox state to a specific sandbox screen; no Dashboard-scope inbox exists.
  • gRPC service contract (`proto/openshell.proto`) is the natural extension point; streaming RPCs are already used elsewhere in the service.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:gatewayGateway server and control-plane workarea:policyPolicy engine and policy lifecycle workfeature request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions