Skip to content

feat(app): queue and steer follow-up prompts - #44683

Merged
Hona merged 1 commit into
anomalyco:v2from
Hona:queue-steer-prompts
Aug 25, 2026
Merged

Hona merged 1 commit into
anomalyco:v2from
Hona:queue-steer-prompts

Conversation

@Hona

@Hona Hona commented Aug 24, 2026 •

Copy link
Copy Markdown
Member

Implements the complete Figma queue/steer follow-up workflow using existing V2 prompt and inbox APIs.

Included

  • Running sessions support configurable follow-up delivery: Enter uses Queue or Steer, and Ctrl/Cmd+Enter uses the alternate. Steer remains the default.
  • Queue pullout above the composer with drag handles, working drag reordering, attachment indicators, overflow, steer/remove actions, and stopped-session Send behavior.
  • Inline queued-prompt editing preserves the prompt's queue position and restores the existing composer draft, structured references, attachments, and metadata.
  • Settings → General exposes Follow-up behavior.
  • Reverting a prompt discards pending user input.

No core, server, protocol, client-generation, migration, or TUI changes are included.

The existing inbox API cannot reorder rows directly, so drag reordering and position-preserving edits temporarily rewrite the changed queue suffix through existing prompt admission and cancellation APIs. Follow-up #44858 replaces that workaround with one durable, atomic server operation without introducing any new desktop UI.

Verification

  • App and E2E TypeScript checks pass.
  • Three focused Playwright cases verify keyboard delivery preferences, working drag reorder, and position-preserving queued-prompt editing with draft restoration.

@Hona
Hona requested a review from Brendonovich as a code owner August 24, 2026 11:44
Copilot AI lite review requested due to automatic review settings August 24, 2026 11:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Hona
Hona force-pushed the queue-steer-prompts branch 3 times, most recently from 97aed5c to f1732b6 Compare August 24, 2026 21:59
@Hona

Hona commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

Responses to the review findings — all addressed in the latest push except where server support is required (this PR is intentionally app-only):

[P1] Defer model/agent changes until queue delivery — Fixed. sendPrompt now applies switchAgent/switchModel only for steer deliveries; a queued follow-up no longer reconfigures the turn it waits behind and runs with the session selection at delivery time. Caveat: honoring a different selection per queued prompt at delivery time needs per-prompt selection in the admission API (server change, out of scope here); the intended selection stays recorded in the prompt metadata.

[P1] Make queue rewrites atomic — Mitigated client-side. Clones are now admitted under client-minted IDs, and when an original's cancellation conflicts (it delivered because the turn hit its idle boundary mid-rebuild), its clone is withdrawn instead — so the race degrades to "reorder didn't apply", never duplicate execution or loss. True atomicity needs a server-side reorder endpoint.

[P1] Do not cancel before admitting an edited steer — Fixed. The steer is admitted first; the original is cancelled only once the steer exists. A failed admission leaves the original queued and restores the edited text into the composer for retry.

[P2] Wait for optimistic admission before rebuilding an edit — Fixed. rebuildQueue merges locally known queued rows whose admission echo has not landed into the authoritative list; clone admissions chain behind the in-flight POST per session, so the subsequent cancel targets an admitted row.

[P2] Restore the complete draft state after editing — Fixed. The stash now captures mode and the retry marker; cancelEdit restores mode, prompt, then retry (both mode and prompt writes clear retry, hence the order).

[P2] Preserve review-comment instructions during edits — Fixed. payload.text is display text plus appended comment notes; an edit now replaces only the display part and re-appends the notes, so the model-visible instructions survive.

[P2] Disable reordering while editing — Fixed. Sortables are disabled while an edit is open (and while a rebuild is in flight), and reorder guards re-entry, so a re-minted ID can never orphan the edit stash.

[P2] Order revert cancellation against admissions — Improved within app-only constraints: revert now cancels the union of the authoritative inbox list and the local snapshot (still fire-and-forget so restore is not delayed). An admission still in flight when the list is fetched can survive the sweep; fully closing that requires a server-side revert-discards-inbox rule — flagged in the code comment as follow-up.

New coverage: "steering an edited prompt admits the steer before cancelling the original" asserts the admission/cancel ordering; the queue suite is now 8 e2e tests, full app suite still green.

@Hona

Hona commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

Round-two findings — all addressed in the latest push, within the app-only constraint:

[P1] Do not clone optimistic payloads — Fixed by removing the merge entirely. rebuildQueue now waits (settledPendingList, bounded retries) until every locally queued row is visible in the authoritative pending list or has left the local store, and rewrites against authoritative payloads only. A rewrite can no longer clone a lossy optimistic row or duplicate an in-flight original; if admissions never settle, the rewrite aborts with a toast and the edit stays open.

[P1] Only roll back clones after a verified delivery conflict — Fixed. cancelQueued classifies outcomes: success → cancelled; a tagged 409 ConflictError body → delivered; anything else (transport, lost response) → one retry, then "failed". Clones are withdrawn only on a verified delivery conflict, and only when they are unchanged copies — an edited clone stays because the edit is still wanted. Unverifiable cancellations keep both copies (duplicate is recoverable, silent loss is not) and surface a toast; the closing sync renders reality.

[P1] Preserve queued model and agent selection — Commands fixed to match prompts: sendCommand omits agent/model for queue deliveries, so a queued slash command no longer reconfigures the running turn server-side. Applying a recorded selection at promotion time genuinely needs the server to honor per-item selection during inbox promotion — out of scope for this app-only PR, flagged as the follow-up.

[P1] Handle failure when cancelling an edited steer's original — Fixed. The steer's original is cancelled through cancelQueued: verified conflict means the original already ran (accepted mid-edit-delivery case); an unverifiable failure raises a toast and leaves the original visible in the panel for manual removal instead of being silently ignored.

[P1] Make revert and inbox cancellation one server-side boundary — Agreed that the complete fix is server-side; noted in the code as the follow-up. Client-side the sweep now uses a timeCreated cutoff captured at stage success, so the asynchronous list response can no longer cancel a prompt admitted after the revert; the remaining gap is an old admission still in flight when the list is fetched.

[P2] Keep the edit stash until confirmation succeeds — Fixed by inverting the flow: the stash is restored only after the confirmation lands. A failed confirmation keeps the edit open with the edited content still in the composer (correct mode included), so nothing is overwritten or lost and Enter retries. The recoverText overwrite is gone.

[P2] Preserve or disable rich content during queue edits — Preserved. Confirming an edit now builds a real prompt request from the composer content (buildPromptRequest): images added during the edit are uploaded as data URIs, mentions are parsed structurally, the original's stored attachments are re-attached, and the model-visible comment notes survive as before. Ambient composer context (open review comments) deliberately stays out of a queued edit — it belongs to the next fresh prompt.

[P2] Do not merge a stale local queue row over an authoritative steer — Resolved by construction: with the merge removed, a row another client steered shows up in the settled authoritative list with steer delivery and simply drops out of the rewrite; it is never cloned or cancelled.

Full queue suite (8 e2e) green, composer unit tests green, full app suite re-run green.

@Hona
Hona force-pushed the queue-steer-prompts branch 2 times, most recently from 947e380 to 0cbde34 Compare August 24, 2026 23:10
@Hona

Hona commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

Round-three triage. This round splits into deterministic bugs (fixed) and race-window enumerations that a client cannot close against these APIs (declined or noted as server-side follow-ups) — further client-side compensation machinery is what created several of these windows in the first place.

Fixed (deterministic):

  • Preserve original agent/skill attachments — editing degrades structured mentions to plain text; original agent and skill references now survive the edit as long as their mention text still appears in the edited prompt, alongside newly typed structured mentions.
  • Image-only edited prompts — confirmation now treats a prompt with images as non-empty instead of silently discarding the edit.
  • Command-configured selection — solved with one guard: slash commands always steer (delivery: "steer"), which is exactly the pre-PR v2 behavior. Commands can carry server-applied configuration at admission, so they are simply never queued.
  • Cancellation retry deleting the only prompt — the retry (added last round) created this window; it is removed. One attempt, classified, done.

Declined / follow-ups:

  • Idempotent edit-confirmation retries — requires a lost success response followed by a manual retry; the closing sync renders server reality between attempts. Accepted as vanishingly rare rather than adding client-side idempotency bookkeeping.
  • Apply queued selection at delivery and revert/inbox boundary — both genuinely need server support (per-item selection at promotion; a revert-discards-inbox rule). Flagged as server-side follow-ups; this PR is intentionally app-only.
  • Concurrently steered item cancelled by a rebuild — requires a second client steering the same item inside the rewrite's snapshot-to-cancel window. Accepted.

Also rebased onto latest v2 (conflict resolved) so CI can run. Queue suite 8/8, composer units green, typecheck clean.

@Hona
Hona force-pushed the queue-steer-prompts branch from 0cbde34 to 125f6c8 Compare August 24, 2026 23:30
@Hona

Hona commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

Round-four triage — final position for this PR.

Taken: queued move/compaction/synthetic items are now treated as ordering barriers: a rewrite aborts and leaves the queue untouched instead of reordering prompts around a control item (2-line guard).

Everything else is declined for this PR, deliberately. Six of the ten findings re-raise items already resolved as declined or as server-side follow-ups (per-item selection at promotion, revert-inbox boundary, lost-response idempotency, concurrent-client races, withdrawal verification). The command finding reverses the previous round's: round three flagged that queueing a command reconfigures the running turn, this round flags the fix for that — and correctly notes the actual answer is server-side command handling. There is no client-side resolution to choose from, only two imperfect behaviors; commands-always-steer matches pre-PR v2 exactly and keeps the running turn intact, so it stays.

The remaining windows share one shape: they simulate atomicity the inbox API does not offer, and each client-side compensation layer manufactures the next review round's findings (this round's withdrawal-verification finding is a hole in round two's compensation, which was itself a fix for round one's). The durable fix is a small server surface — inbox reorder/edit endpoints, per-item selection honored at promotion, and a revert-discards-inbox rule — at which point most of the client scaffolding here (settledPendingList, cancelQueued, clone withdrawal) gets deleted, not extended. That is the follow-up worth doing; more client machinery is not.

Feature has been manually tested end to end; queue suite is 8/8 e2e plus storybook visual states matched against the Figma comps.

@Hona
Hona force-pushed the queue-steer-prompts branch from b872fd1 to a1a9511 Compare August 25, 2026 02:47
@Hona
Hona enabled auto-merge (squash) August 25, 2026 02:59
@Hona
Hona disabled auto-merge August 25, 2026 03:00
@Hona
Hona merged commit a02a2f5 into anomalyco:v2 Aug 25, 2026
7 of 9 checks passed
jinhuang712 pushed a commit to jinhuang712/opencode that referenced this pull request Sep 26, 2026
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.

2 participants