Files
PaperClipAI/server/src/services
DottaandClaude Opus 5.5 6dd8611a2f fix(queue): keep tool action outcomes out of the steering queue (#20582)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents ask a person to approve a governed tool action (for example,
create a Notebook page) before the tool runs
> - Approve means run: the tool runs as soon as the person approves it,
and the approval card then shows Executed, Failed or Declined
> - After the tool runs, the server wakes the agent with the recorded
result. When another turn is active, that wake waits behind it
> - The composer queue lists waiting interaction receipts as messages.
The result wake carries the approval's interaction id and status, so the
queue shows "Accepted: Approve tool action" with an Interrupt control,
after the tool already ran
> - People read this row as an approval that is still not done, and
Interrupt stops their current turn only to deliver a tool result
> - This pull request keeps tool action outcome wakes out of the
composer queue and out of its steer, interrupt and discard actions
> - The benefit is a steering pane that shows only messages the person
can act on, while the agent still receives the tool result after the
current turn

## Linked Issues or Issue Description

**What happened?**
After a person approves a governed tool action, the tool runs and its
card shows Succeeded. When the person then sends a new message, the
composer queue shows a row "Accepted: Approve tool action" with
Interrupt and Discard controls. The row stays until the active turn
ends.

**What did you expect?**
The steering pane does not show the tool result as a queued message. The
approval card already shows the outcome. The agent receives the result
after the current turn.

**Steps to reproduce**
1. Let an agent request approval for a governed tool action
(`request_confirmation` with `payload.toolAction`).
2. Approve it. The tool runs and the card shows Succeeded.
3. Send a new message so a new turn starts before the result wake runs.
4. See "Accepted: Approve tool action" in the composer queue.

## What Changed

- Add `isToolActionOutcomeReceipt` in
`server/src/services/queued-interaction-response.ts`. It identifies an
interaction receipt wake that carries a tool action (`toolAction`
identity or `toolActionRequestIds`).
- `findQueuedCommentWake` in `server/src/routes/issues.ts` skips these
wakes, so `GET /issues/:id/queued-comments` does not list them. The
payload projection adds only the tool action's `actionRequestId`,
`toolName` and `toolActionRequestIds`, never its result text.
- The locked by-id queue lookup (steer, interrupt, discard) rejects
these wakes with `queued_comment_not_pending`, so a stale client cannot
interrupt a turn for a tool result.
- Delivery, stranded-queue recovery and the handback hold
(`readQueuedInteractionResponse`, `hasQueuedInteractionResponse`) do not
change. The agent still receives the result.
- Add route tests for accepted and declined tool action outcomes. They
check that the queue is empty, the wake stays
`deferred_issue_execution`, and the issue still has owed interaction
input.

## Verification

- `cd server && npx vitest run
src/__tests__/issue-queued-comments-routes.test.ts
src/services/issue-queued-comment-queue.test.ts`: 137 of 137 tests pass.
The new tests fail without the fix (the queue lists the receipt).
- On a heavily loaded machine (load average about 25), the embedded
Postgres `afterAll` teardown timed out once at 30 s after all tests
passed. This is not related to the change.
- `cd server && npx tsc --noEmit -p .`: clean.

## Risks

- Low risk. The change only filters the user-facing queue view and its
by-id actions.
- A tool action outcome wake no longer appears as an Interrupt target.
Before, Interrupt could deliver a tool result into a new turn early. Now
the result arrives when the current turn ends, as for other system
deliveries.
- #13160 refactors the same steering path. One of the two PRs may need a
small rebase.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use
(shell, file edits, browser inspection of staging).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 07:39:37 -06:00
..
…
…
…
…