Skip to content

fix: improve queue steering interactions - #669

Closed
wibus-wee wants to merge 16 commits into
mainfrom
fix/queue-steer-shortcut-drag
Closed

wibus-wee wants to merge 16 commits into
mainfrom
fix/queue-steer-shortcut-drag

Conversation

@wibus-wee

@wibus-wee wibus-wee commented Sep 13, 2026

Copy link
Copy Markdown
Member

Related issue

Internal maintainer request; no intake issue required.

Problem / pressure

Queue ordering and Steer are separate actions: selecting C must steer C without rearranging A/B. Native delivery also crosses durable storage and irreversible provider effects. Keeping a reserved row editable could silently discard another client's edit; machine-only authorization could expose private-project sessions.

Summary

  • Add composer-focused Mod+Shift+Enter for a one-shot inverse Queue/Steer choice, without changing the saved preference. Expand queue dragging to the number and message body; action buttons remain separate.
  • Require queueItemSteer v2 for every queued-row Steer button. Older daemons have no Queue Steer compatibility path, including the head. Composer routing is unchanged.
  • Keep the UI identity-only. QueueSteerService owns selection, validation, durable evidence, fallback and receipts; ActiveTurnSteerPort retains live-turn ownership, provider submission, handoff and Stop serialization. Effect 3.18.4 scopes local resources and typed failures, not reversible provider delivery.
  • Route revision-checked edit/remove/reorder through session/queue-mutate, sharing daemon reservation authority. Native Steer persists frozen history and queue removal before submission; conflicting edits fail visibly and retain their drafts. Failed mutation RPCs never fall back to direct writes.
  • Share source-side session visibility authorization between both queue RPCs, using authenticated machine/project snapshots and current user. Missing metadata or incomplete snapshots fail closed. Local sender failure cannot change the resolved plane or create a remote client.
  • Clear pre-history reservations without terminal receipts so the same C/T can retry after revalidation. Preserve durable receipts after completion and never replay indeterminate delivery. Native requester identity comes from the active invocation, not the queue row or a caller claim.

Contract and rationale: draft Spec, Effect ownership decision. Both language versions are updated; implementation does not approve the Spec.

Visual explanation

UI: Steer(session, C, expected T) / mutate(session, revision)
  → require queueItemSteer v2
  → resolve routing plane
      local → sender or local error; never Streams fallback
      cloud → shared session authorization → Streams
      unknown → routing error
  → daemon session serialization
      mutate → revision + reservation check → durable change
      Steer → QueueSteerService → ActiveTurnSteerPort

Native: reserve → durable pending_apply history → durable queue removal
        → write-ahead submission evidence → provider → durable receipt
Recovery:
  reserved + no history → clear marker; leave row; revalidate same C/T
  submitting/acknowledged without live owner → no replay

Without native capability, the daemon persists the selected ordinary follow-up before cancelling only T. Scope releases local ownership; it cannot undo provider acceptance.

Before / after

Before After
Queue selection and execution policy leak into renderer paths. Exact C leaves A/B ordered; daemon selects native or cancel-and-dispatch.
A shared editable row overlaps native operation ownership. Reservation excludes ordinary mutations; durable removal precedes provider submission.
Machine-only checks and unavailable-local-sender fallthrough. Session/project authorization and routing-plane isolation.
Pre-history recovery can cache a permanent error for C/T. No terminal receipt before that boundary; cleanup and revalidation permit retry.

Test plan

Latest boundary-fix verification at 446cd45c:

  • CLI: 152 tests passed across session-execution-service, doc-user-turn, and session-queue-steer-operation-store.
  • Components: 64 tests passed across workspace-machine-rpc-facade, workspace-writer, create-workspace-runtime-meta-recovery, and message-queue-row-editing.
  • Deterministic traces connect the real source facade, Streams client/server, LoroDoc and execution service over in-memory transport. Private-project denial and local sender absence produce zero appends, no operation marker, and unchanged queue/history/active turn. Authorized positive controls reach the daemon. Startup, in-request and failed-clear recovery exercise same-C/T retries.
  • Direct CLI/components typechecks, targeted Prettier, i18n, public/platform boundaries, Code Collab import guard and git diff --check passed. Targeted oxlint: zero errors, 11 warnings. Docs check: zero errors, 34 existing warnings.
  • Root pnpm check and pnpm format cannot start in this environment because corepack is missing; the checks above are not a full-workspace run.
  • No real-provider, installed-desktop, physical-touch, or process-kill/restart end-to-end verification. Synthetic traces are not production-user traces.

Context handoff

Instructions for reviewing agents

  • Review focus: Inspect shared session authorization and RuntimeProvider snapshot fencing, queue RPC plane selection, reservation versus mutation serialization, and pre-history receipt classification.
  • Decisions to challenge: Verify native submission follows durable removal; only proven non-delivery can fall back; local ownership remains behind ActiveTurnSteerPort; no old-daemon Queue Steer path survives.
  • Plausible failures / evidence gaps: Provider acceptance is not atomic with local persistence, old renderers can still author CRDT data outside v2 authority, and process-kill/provider-backed UI behavior remains untested.

Authoring context

  • User goal / directives: Steer any exact queued item, preserve native delivery, separate ordering controls, and close authorization, ownership, routing and retry boundaries.
  • Constraints / non-goals: No general workflow engine, new saga phases, caller-claimed identity authorization, or old-daemon Queue Steer compatibility.
  • Risk-bearing decisions: Source session visibility gates remote controls; machine-local markers and frozen history own recovery; indeterminate provider delivery forbids replay.
  • Destructive or irreversible behavior: Accepted Steer consumes the selected queue row; provider submission is irreversible. Durable history/marker evidence precedes native submission, and stale edits retain their drafts.
  • Deliberately not done or tested: No exactly-once guarantee without provider support; no full-workspace or installed-app/provider/process-kill verification in the current environment.
  • Unknowns / confidence: Deterministic failure-path coverage supports the local contracts; it does not establish behavior under arbitrary mixed-old-client writes or real process/network failures.

Add one-shot inverse queue/steer submission, allow steering any queued item, and expand each row's safe drag target.

Model: gpt-5
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T11:01:12.358990Z 48e21a9 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31de8acad2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 16c85a460a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/loro-streams-rpc/src/rpc.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31582d5354

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/lib/loro/doc.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: feeb1180cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/session/session-execution-service.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0ba37d550

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/session/session-execution-service.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e97db58bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

error: 'This machine does not support exact queued-message steering.',
};
}
if (await canUseLocalMachineRpc(machineId)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep local queue steering off the remote fallback

In Electron, when the router has already resolved this machine as local but the preload Machine RPC sender is unavailable, canUseLocalMachineRpc() returns false and this new control continues through the authorization check into getMachineRpcClient(). In dual-sync builds that sends a local session control over Streams—and in local-only builds it makes the action depend on unrelated authorization state—instead of reporting the failed local bridge. Branch on the resolved plane and return a local transport error rather than falling through.

AGENTS.md reference: packages/components/src/providers/AGENTS.md:L65-L67

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5758984a60

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +814 to +815
const authorizedMachineIds = deps.getAuthorizedMachineIds?.() ?? null;
if (!authorizedMachineIds?.has(machineId)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Enforce local-project access before queue Steer

Fresh evidence beyond the earlier requester-identity fix is that local-project authorization is a separate predicate: isSessionVisibleToUser requires visibleLocalProjectKeys, and the daemon's normal verifySessionMachineAccess forwards meta.project.localProjectId. Here the source check only tests whether the machine occurs in convexAuthorizedMachineIds, while the target steerQueuedMessage performs no access check. Consequently, a workspace member authorized for a team-shared machine but not one of its private local projects can submit known session/turn/queue IDs to consume that project's queued row and cancel its active turn. Gate this operation on the session's project access, or provide authenticated target-side authorization, rather than relying on machine visibility alone.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6964227412

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (nativeOperation && queuedTurn.id !== nativeOperation.userTurnId) return null;
return { ...queuedTurn, userId: authenticatedRequesterUserId };
},
{ publishDispatch: !useNativeSteer }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Transfer native reservations out of the editable queue

When native Steer is available, publishDispatch: false leaves the selected CRDT queue row present after its frozen pending_apply entry is created. While persistence, provider submission, and acknowledgement are awaited, another renderer can successfully edit or remove that same $cid; the native path still executes the frozen entry and later deletes the row, so the accepted edit is lost—or a removed message still runs. Reserve against ordinary mutations and durably remove the row before calling steerPrompt.

Useful? React with 👍 / 👎.

Separate queue delivery policy from live turn ownership, serialize revision-checked queue mutations, and persist removal before native submission. Remove legacy daemon steering paths and retain conflicting edit drafts.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4afc9f16d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +795 to +796
if (!deps.getAuthorizedMachineIds?.()?.has(machineId)) {
throw new Error('Source authorization for this machine is unavailable or denied.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Enforce project access for queue mutations

When a collaborator is authorized for a shared machine but not for one of its private local projects, this machine-only check still permits session/queue-mutate. The target handler forwards that RPC directly to mutateQueuedMessage, whose only ownership check is meta.machineId, while normal session visibility separately requires visibleLocalProjectKeys; a caller with known session and queue IDs can therefore edit, remove, or reorder messages in an inaccessible project. Gate the request on authenticated session/project visibility at the source, or add an authenticated target-side authorization boundary.

AGENTS.md reference: packages/loro-streams-rpc/AGENTS.md:L58-L59

Useful? React with 👍 / 👎.

Share source-side session authorization across queue controls, preserve routing planes on transport failure, and allow safe pre-history recovery retries.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 446cd45c62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +24 to +28
return isSessionVisibleToUser(
meta,
authorization.visibleMachineIds,
authorization.visibleLocalProjectKeys,
authorization.currentUserId

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Require current machine access for session owners

When a collaborator owns the cached Session but their access to its machine has been revoked, this delegates authorization to isSessionVisibleToUser, whose owner fast path returns true whenever meta.userId === currentUserId without consulting visibleMachineIds. The facade can therefore append session/queue-steer or session/queue-mutate using stale machine metadata even though the authenticated Convex snapshot no longer contains that machine; the target handlers perform no requester access check, so the revoked user can still consume, edit, or remove queued work and cancel an active turn. Require authenticated machine/project authorization independently of the Session-owner visibility fallback.

AGENTS.md reference: packages/components/src/providers/AGENTS.md:L59-L61

Useful? React with 👍 / 👎.

Make local/remote mutation outcomes and successful Effect guard completion explicit, and align the machine registration assertion with queueItemSteer v2.\n\nModel: gpt-5
Require current machine and local-project access for queue controls, remove the session-owner authorization fallback, and cover revoked-owner requests with end-to-end in-memory traces.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 64fc2f1d1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
yield* flush();
yield* removeReservedRow(doc, marker);
const submitting = yield* write(sessionId, { ...marker, phase: 'submitting' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Requeue failures before provider submission

Fresh evidence beyond the earlier rejected-delivery thread is that nativeSteerEffect can throw PersistenceFailure while building prompt blocks or applying configuration, before steerPrompt is called. This marker is nevertheless advanced to submitting, and the surrounding pipe falls back only for ProviderRejected and StaleTurn; the outer handler merely returns the persistence error. Recovery therefore treats a provably unsubmitted turn as indeterminate, fails its pending_apply history entry, and never dispatches the already-removed queue row. Keep the marker reserved through pre-submission preparation or explicitly route those failures through fallback.

AGENTS.md reference: apps/cli/src/session/AGENTS.md:L59-L61

Useful? React with 👍 / 👎.

Complete preparation before journaling submission, recover preparation failures as ordinary dispatch, and prevent replay after indeterminate provider failures.

Model: gpt-6

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48e21a9b21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (!meta || meta.machineId !== machineId || !authorization?.visibleMachineIds.has(machineId))
return false;
// Display ownership is not a control grant; revoked machine access always denies control.
if (meta.project?.kind === 'local') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Derive project authorization from an authenticated binding

When a collaborator can access the shared machine but not a private local project, this conditional trusts the shared Loro SessionMeta.project value and grants control for every absent or non-local value. Fresh evidence beyond the earlier project-access findings is that the new Spec itself states collaborators can write shared Session metadata, while getSessionMeta reads that metadata directly from the repo; changing or omitting project therefore bypasses the authoritative project-key check, and the target queue-steer/mutate handlers perform no requester/project authorization before consuming, editing, or removing queued work. Resolve the session-to-project binding from an authenticated authority or fail closed rather than trusting this mutable field.

AGENTS.md reference: packages/components/src/providers/AGENTS.md:L59-L61

Useful? React with 👍 / 👎.

Comment on lines +439 to +443
Effect.catchTag('ProviderRejected', (error) =>
fallback(
sessionId,
doc,
submitting,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve proven rejection when fallback persistence fails

When submitSteer returns ProviderRejected before injection and the first marker write inside fallback fails transiently, the durable marker remains submitting. Fresh evidence beyond the earlier failed-requeue thread is that recovery now treats that surviving phase as indeterminate and eventually fails the pending_apply turn instead of dispatching it, even though this tagged result proved the provider did not receive it; retries also cannot repair it while the old prompt remains owned. Preserve a durable/retryable non-delivery decision so failure to record the fallback transition cannot permanently drop the selected message.

AGENTS.md reference: apps/cli/src/session/AGENTS.md:L59-L61

Useful? React with 👍 / 👎.

@wibus-wee
wibus-wee marked this pull request as draft September 14, 2026 12:00
@wibus-wee wibus-wee closed this Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant