Files
PaperClipAI/server
DottaandClaude Opus 5.5 705c3e955d fix(chat): show one onboarding question card when onboarding moves to agent chat (#19512)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New users start with an onboarding first task. It has an opening
question card and, after "Interview me", an interview question card.
> - With the coordinator chat enabled, the agent's chat with the user
mirrors pending questions from that agent's direct tasks. The onboarding
task is a direct task.
> - A tester moved onboarding from the task into the chat. The agent
asked its interview in the chat. The chat then showed the chat card and
the mirrored onboarding-task card. Both were answerable.
> - This pull request expires the agent's pending onboarding-task
questions when the agent asks a question in its chat with that user.
> - The benefit is one interview card, answerable once, wherever
onboarding runs.

## Linked Issues or Issue Description

**What happened?**
Onboarding testing on Paperclip Cloud staging (Codex API path). The
steps were: enable the experimental chat and coordinator features, start
the "Interview me" onboarding task, switch to the agent chat, and ask
the chat to run the onboarding there. The chat agent started the
interview, and a duplicate question card appeared.

**Root cause**
`conversationDecisionReceiptsService.listPendingQuestions` projects
pending questions from direct tasks (created by the chat user, assigned
to the chat agent) into the chat. The onboarding first task matches. The
PAP-437 sibling-supersede rule in `issueThreadInteractionService.create`
only works on the same issue, and it is off for chat questions. So the
task's pending interview card and the new chat card both stayed pending
and visible in the chat.

**Expected behavior**
One interview question card in the chat, answerable once.

**Steps to reproduce**
1. Enable the experimental Agent Chat and coordinator features.
2. Create the onboarding first task and choose "Interview me" on its
opening card.
3. Open the chat with the same agent and ask it to run the onboarding
there instead of in the task.
4. The agent asks its interview in the chat. The chat shows two pending
question cards: its own card and the mirrored onboarding-task card.

**Paperclip version or commit**
`master` at 819420a60a (Paperclip Cloud staging build at the time of the
test).

**Deployment mode**
Paperclip Cloud (staging). The same logic runs in self-hosted mode.

## What Changed

- `server/src/services/issue-thread-interactions.ts`: when the chat
agent creates an `ask_user_questions` card in its chat with a user, the
server expires that agent's pending `ask_user_questions` cards on the
onboarding first task that the same user created and assigned to the
agent. The result is the existing `superseded_by_newer_interaction`
shape and points at the chat card. Pending chat publications for those
cards are cancelled and terminal publications are queued, the same as
the existing sibling supersede.
- The rule does not change questions on other direct tasks, handoff
tasks, or other chats. Confirmations and plan approvals on the
onboarding task are not changed.
- `server/src/__tests__/coordinator-handoff.test.ts`: a new red/green
test for the reported flow, and a control test that an ordinary
direct-task question stays pending when the agent asks something else in
chat.
- `server/src/onboarding-assets/first-task/README.md`: one paragraph
that documents the behavior.

## Verification

Red on `master` (819420a60a) with only the new tests added:

```
$ cd server && npx vitest run src/__tests__/coordinator-handoff.test.ts -t "onboarding interview|ordinary direct task"
 × moves a pending onboarding interview into the agent chat instead of showing two question cards
AssertionError: expected [ …(2) ] to deeply equal [ Array(1) ]
  [
    "26b1e6ae-e1e5-4c26-8475-ad7ad93a7754",
+   "2364fd68-a40f-42be-854d-89210bfc5811",
  ]
 ❯ src/__tests__/coordinator-handoff.test.ts:231:53
      Tests  1 failed | 1 passed | 23 skipped (25)
```

The chat showed its own interview card and the mirrored onboarding-task
interview card.

Green after the fix:

```
$ cd server && npx vitest run src/__tests__/coordinator-handoff.test.ts
 Test Files  1 passed (1)
      Tests  25 passed (25)
```

Neighbouring suites:

```
$ npx vitest run src/__tests__/issue-thread-interactions-service.test.ts src/__tests__/issue-onboarding-first-task-routes.test.ts src/__tests__/chat-interaction-publications.test.ts src/__tests__/coordinator-handoff.test.ts
 Test Files  4 passed (4)
      Tests  183 passed (183)
```

`tsc --noEmit -p server` shows no errors in the changed files. Other
errors in that local run come from the unbuilt
`@paperclipai/paperclip-runner` package. CI typecheck covers the full
build.

Not done: a live browser repro. Staging has no Codex company and no
agent chat with this flow. The evidence is the tester's notes and the
service-level test.

## Risks

- If a user ignores the onboarding card and the agent asks an unrelated
question in its chat, the onboarding task's pending question card
expires. The onboarding task stays open, and a message on the task still
wakes the agent. The chat already showed both cards to that user, so
this keeps one live ask from the agent.
- Race: if the onboarding task run posts its interview card after the
chat card, both stay pending. Agent Chat questions are intentionally not
expired by other cards, so this PR does not reverse the rule.
- No schema, API, or UI change.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, via Claude Code with
tool use and code execution.

## 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-06 13:10:45 -06:00
..