Skip to content

Render the open request family through the pending-interaction banner (WS3 layer 4) - #2209

Closed
SawyerHood wants to merge 1 commit into
ws3-l3-mobile-declarative-basefrom
ws3-l4-open-requests
Closed

Render the open request family through the pending-interaction banner (WS3 layer 4)#2209
SawyerHood wants to merge 1 commit into
ws3-l3-mobile-declarative-basefrom
ws3-l4-open-requests

Conversation

@SawyerHood

@SawyerHood SawyerHood commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

WS3 layer 4 of 5, stacked on #2208 (layer 3: mobile). Do not merge — the coordinator reviews, Sawyer merges the stack.

What was wrong

The contract PR defined the open request family (interactionRequestPayloadSchema: user_question, plan_review, "<pluginId>/<kind>") beside today's wire, but the client still dispatched on the old shape: a finished plan rendered as a permission approval (the plan approval subject), and a plugin request was special-cased at every mount point (isPluginPendingInteraction branches in the prompt area) instead of being a request the pendingInteraction slot renders. Per §4, userQuestion and planReview render with core renderers and "<pluginId>/<kind>" with the plugin — the UI side of that split did not exist.

What changed (UI only; WS5 owns producers)

  • apps/app/src/components/thread/pending-interactions/interaction-request.ts: classifyInteractionRequest maps an interaction to { family: "approval" } or { family: "request", kind }. It reads both wire shapes — today's PendingInteraction (plan review on the plan approval subject; plugin request as kind: "plugin" + origin) and the target InteractionRequestPayload (plan_review directly; the namespaced plugin kind split with parseExtensionKind) — so the UI is ready before the wire moves and unchanged after it does.
  • ThreadPendingInteractionBanner dispatches through the classifier: approvals keep the decision buttons; user_question keeps the answer form; plan_review gets PlanReviewRequestBanner ("Ready to code?", the plan Markdown, the plan file path, "Approve plan" / "Keep planning" — never the permission vocabulary), resolving through the approval resolution today's wire carries (resolvesAs: "approval"); a "<pluginId>/<kind>" request mounts PluginPendingInteractionComposer (the plugin's pendingInteraction slot, resolved by plugin id + renderer name) with the child-thread source link.
  • ThreadDetailPromptArea drops its plugin special-casing and always renders the banner.
  • No wire change, no server change.

How you verified

  • interaction-request.test.ts: approvals stay approvals; today's plan approval lifts into a plan_review request; the target plan_review and user_question payloads classify; a plugin request classifies identically from today's origin shape and from the namespaced kind.
  • ThreadPendingInteractionBanner.test.tsx: the plan review renders as a request (title, plan body, file path, plan-verdict buttons; "Allow once" absent) and resolves allow_once/deny through the approval resolution; a plugin request renders through the registered slot component keyed by secrets/secret-request.
  • turbo test --filter=@bb/app: 420 files, 3,214 passed (one prompt-area expectation updated: the stack item is the banner's placeholder now that the banner routes plugin requests).
  • turbo typecheck --filter=@bb/app clean.

AGENT GENERATED: by Claude Opus 5

… (WS3 layer 4)

UI only — WS5 owns the producers. The client now renders interactions by
the §4 split: approvals (command, fileChange, permissionGrant, toolUse)
keep their decision buttons; the open requests render with core renderers
(`user_question`, `plan_review`) or, for `"<pluginId>/<kind>"`, through the
plugin's `pendingInteraction` slot.

- `interaction-request.ts` classifies either wire shape: today's
  `PendingInteraction` (a plan review riding the `plan` approval subject, a
  plugin request as `kind: "plugin"` + origin) and the target
  `InteractionRequestPayload` (`plan_review`, the namespaced plugin kind), so
  the UI is ready before the wire moves and unchanged after.
- `PlanReviewRequestBanner`: "Ready to code?" with the plan Markdown and
  file path, plan-verdict actions ("Approve plan" / "Keep planning")
  resolved through the approval resolution today's wire carries.
- The banner routes plugin requests to `PluginPendingInteractionComposer`
  itself (child-thread source link included), so the prompt area no longer
  special-cases plugin interactions.

Tests: the classifier over both shapes; the banner rendering a plan review
as a request and a plugin request through the slot keyed by
`<pluginId>/<kind>`. `turbo test --filter=@bb/app`: 3,214 passed.
@SawyerHood
SawyerHood force-pushed the ws3-l4-open-requests branch from 4a104bb to e2814c2 Compare August 21, 2026 18:09
@SawyerHood SawyerHood closed this Aug 21, 2026
@SawyerHood
SawyerHood deleted the ws3-l4-open-requests branch August 21, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant