Skip to content

[Story 4.3] Serialize approval dialogs for parallel tools #377

Open

Description

@edelauna

Part of #359 (Epic 4: Parallel Tool Execution).

Depends on: Story 4.2b (Core parallel dispatch).

Context

cline.ask() writes to clineMessages and waits for a response. If two parallel tools both call ask() concurrently, their writes interleave and produce broken UI state — the second approval prompt would overwrite the first before it's answered. This story adds a write-through promise chain to ask() so concurrent approval requests are serialized transparently.

Developer Notes

In src/core/task/Task.ts:

  • Add private approvalQueue = pLimit(1) — p-limit is already in deps at ^6.2.0. Using pLimit(1) instead of a hand-rolled promise chain provides better error isolation — a rejected promise in a raw chain breaks the queue unless errors are explicitly caught, whereas p-limit handles this correctly.
  • Wrap ask() so concurrent calls serialize: return this.approvalQueue(() => actualAsk(...)).
  • Non-approval paths (auto-approved tools that never call ask()) are unaffected.
  • Abort drain behavior: When abortTask(true) fires while approvals are queued, pending approvals resolve with denial (not rejection) — producing valid tool_denied results. The queue settles cleanly with no unhandled rejections.

Files: src/core/task/Task.ts

Tests (extend src/core/assistant-message/__tests__/presentAssistantMessageParallel.spec.ts):

  • Two parallel tools both requiring approval: second prompt never appears before first resolves. Use controlled promises for ask() mock.
  • A third tool with auto-approval runs without waiting for the approval queue.
  • Abort drain: trigger abortTask(true) while two approvals are queued → both resolve with denial → userMessageContent has valid tool_denied results for both with no unhandled rejections.

Acceptance Criteria

  • No concurrent ask() calls produce overlapping UI state.
  • Sequential tool execution (flag off) is unaffected.
  • Abort during queued approvals produces valid results — no unhandled rejections, no missing tool_result entries.

Activity

  1. zoomote commented on Aug 21, 2026

    @zoomote
    Contributor

    Cross-linking #1308: serializing approval prompts is necessary but should also preserve this invariant: only explicit approval events resolve approval. Queued free-form messages are feedback (messageResponse), not implicit approve/deny actions. This should remain true when approvals are queued under parallel dispatch.

  2. zoomote commented on Aug 21, 2026

    @zoomote
    Contributor

    Coordination note for #1223: queued user input can currently be dequeued and lost when finishTask is auto-approved, even with parallel execution disabled. The #1223 fix should establish user-input-over-auto-approval precedence plus non-destructive claim/ack queue handling. This story's serialized approval path should reuse that resolution policy rather than introducing a second queue/approval ordering rule.

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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions