Skip to content

Clear chat queue rows once their prompt is being worked - #1191

Merged
alexeyzimarev merged 1 commit into
mainfrom
norton/ai-3234-chat-queue-tracking
Sep 29, 2026
Merged

alexeyzimarev merged 1 commit into
mainfrom
norton/ai-3234-chat-queue-tracking

Conversation

@nortonandreev

Copy link
Copy Markdown
Contributor

AI-3234 — no GitHub issue exists

What & why

The queued-messages strip drops a row only when the transcript echoes that prompt, and two echoes never matched. Claude Code records a prompt it takes up mid-turn only as a queued_command attachment (commandMode: prompt), which the Claude leaf ignored, so the row stayed "queued" while the agent worked on it. The leaf now projects that attachment as a user message, so the row clears and the prompt shows as a sent bubble. A prompt carrying an attachment trailer is echoed inside a <pasted_content> wrapper; stripping that wrapper is #1190's, and this PR relies on it for that case.

An attachment send also sat in the strip and the composer at once while the daemon fetched the files. The in-flight send is now tracked but kept out of the strip until the channel answers, so the composer (with its "Sending…" hint) is the only place it shows. Rejected and unconfirmed sends keep the draft, as before.

Where to look

ClaudeTranscriptEvents.QueuedCommand: servers will ingest these attachments as new UserMessageReceived events, keyed by the record's own uuid, so already-ingested ids are unchanged.

Verification

  • Screenshot session transcript: the lingering "there's a layout shift…" prompt exists only as a queued_command attachment (reason: absorbed_mid_turn).
  • The two new ChatTabViewModelTests fail on main and pass here.
  • App 2781/2781, Cli.Core 3923 passed / 0 failed, Models.Transcripts 155/155.
  • dotnet publish … -c Release | grep -cE 'IL[23][01][0-9]{2}' → 0

@linear-code

linear-code Bot commented Sep 28, 2026

Copy link
Copy Markdown

AI-3234

@nortonandreev nortonandreev self-assigned this Sep 28, 2026
@nortonandreev
nortonandreev force-pushed the norton/ai-3234-chat-queue-tracking branch from 4e383b0 to efad4b1 Compare September 28, 2026 09:24
@nortonandreev
nortonandreev changed the base branch from main to norton/ai-3235-chat-pasted-content-markup September 28, 2026 09:24
@nortonandreev
nortonandreev added this pull request to stack #1193 September 28, 2026 09:33
@nortonandreev
nortonandreev marked this pull request as ready for review September 28, 2026 11:22
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Clear queued chat prompts when Claude starts working on them

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Project mid-turn Claude prompt attachments as user messages so their transcript echoes clear
 queued rows.
• Keep sends out of the queue strip until the channel answers, avoiding duplicate composer display.
• Add regression tests for projection, queue acknowledgment, and attachment sends.
Diagram

graph TD
  C["Composer"] --> I["In-flight tracking"] --> S["Send channel"] -- "waiting" --> Q["Queue strip"]
  R["Claude attachment"] --> P["Claude projection"] --> F["Chat rules"] --> U["User turn"]
  F -- "acknowledges" --> I
  F -- "clears match" --> Q
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Treat server queue removal as delivery
  • ➕ Could retire queue rows without matching transcript text.
  • ➖ Queue removal cannot distinguish delivery from withdrawal.
  • ➖ It would not create the missing user turn in the transcript.

Recommendation: Keep transcript acknowledgment as the delivery signal. Projecting the attachment through the existing canonical-event path both displays the prompt and clears its queue row; server queue removal cannot reliably do both.

Files changed (5) +121 / -36

Bug fix (2) +47 / -31
ChatTabViewModel.csTrack channel-pending sends without showing premature queue rows +22/-12

Track channel-pending sends without showing premature queue rows

• Keeps a send outside the visible queue while awaiting the channel response, then queues it only if it still needs acknowledgment. Includes the in-flight send in server-queue matching, transcript matching, and feed rebasing so an early echo cannot requeue it.

src/Capacitor.App/ViewModels/ChatTabViewModel.cs

ClaudeTranscriptEvents.csProject queued-command prompt attachments as user messages +25/-19

Project queued-command prompt attachments as user messages

• Recognizes prompt-mode attachments alongside task notifications and emits a user-message event using the attachment record's ID. Supports string and text-block prompts while preserving origin metadata and ignoring unrelated attachments.

src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs

Tests (3) +74 / -5
ChatTabViewModelTests.csExercise queue visibility and mid-turn prompt acknowledgment +55/-1

Exercise queue visibility and mid-turn prompt acknowledgment

• Adds tests showing that an attachment send stays in the composer until accepted and that a mid-turn Claude attachment clears its queued row while appearing as a user turn. Adjusts an existing queue assertion to follow channel acceptance.

test/Capacitor.App.Tests.Unit/ChatTabViewModelTests.cs

ClaudeChatRulesTests.csVerify prompt attachments produce submitted inputs +2/-2

Verify prompt attachments produce submitted inputs

• Changes the prompt-attachment expectation from ignored to a visible user message and submitted input. Retains the check that it creates no subagent signal.

test/Capacitor.Cli.Core.Tests.Unit/Harness/Claude/ClaudeChatRulesTests.cs

ClaudeTranscriptEventsTests.csCover prompt-attachment projection and ignored attachment modes +17/-2

Cover prompt-attachment projection and ignored attachment modes

• Verifies prompt content, record-based event ID, origin metadata, and text-block extraction. Updates ignored-attachment cases to cover empty prompts and unsupported command modes.

test/Capacitor.Models.Transcripts.Tests.Unit/Harness/Claude/ClaudeTranscriptEventsTests.cs

@qodo-code-review

qodo-code-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Image-only prompts disappear locally ✗ Dismissed
Description
QueuedCommand requires nonempty text from a prompt array, so the new projection discards
image-only queued_command records. When Claude records such a mid-turn prompt, kcap-server's
ClaudeCodeNormalizer emits a UserMessageReceived with the image attachment, leaving the local
chat transcript inconsistent with the server session.
Code

src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs[R139-140]

+                var text = attachment.Str("prompt") ?? (attachment.Arr("prompt") is { } blocks ? JoinTextBlocks(blocks, "text") : null);
+                return text is { Length: > 0 } ? (text, attachment.Obj("origin")?.Str("kind")) : null;
Relevance

●●● Strong

Claude attachment projection bugs are accepted, especially when records disappear or fail to
preserve transcript semantics.

PR-#1045

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR's text-length guard excludes arrays containing only images. The server extracts images from
those arrays and emits a user message even when there are no text blocks; its test verifies that
case.

kcap-cli -> kcap-server
src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs[133-150]
src/Capacitor.Models.Transcripts/TranscriptText.cs[8-17]
External repo: kurrent-io/kcap-server, src/Capacitor.Server.Services/Sessions/Canonical/ClaudeCodeNormalizer.cs [266-301]
External repo: kurrent-io/kcap-server, src/Capacitor.Server.Services/Sessions/Canonical/ClaudeCodeNormalizer.cs [309-349]
External repo: kurrent-io/kcap-server, test/Capacitor.Server.Tests.Ingest/Normalizers/ClaudeCodeNormalizerTests.cs [584-599]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Image-only queued prompts are discarded locally, while kcap-server emits and stores them as user messages with attachments.

## Fix Focus Areas
- src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs[133-150]
- test/Capacitor.Models.Transcripts.Tests.Unit/Harness/Claude/ClaudeTranscriptEventsTests.cs[196-208]
- /cross_repos/kcap-server/src/Capacitor.Server.Services/Sessions/Canonical/ClaudeCodeNormalizer.cs[309-349]

## Recommended Fix
Handle image blocks in queued prompt arrays and project image-only input rather than requiring text. Add a test for an image-only prompt with `commandMode: "prompt"`, keeping its event and attachment representation consistent with server ingest.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Concurrent sends lose queue receipts ✗ Dismissed
Description
_sending holds only one message, and each send clears that shared slot in its finally block. If
a second execution starts before the first finishes, one pending message disappears from Tracked,
so a transcript echo arriving then cannot acknowledge it and a stale queue row can appear after the
channel answers.
Code

src/Capacitor.App/ViewModels/ChatTabViewModel.cs[573]

+            finally { _sending = null; }
Relevance

●●● Strong

Recent precedents accept concurrency races and queued-message acknowledgement failures in
ChatTabViewModel.

PR-#1069
PR-#889

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Tracked exposes only the single _sending value, while each execution assigns that value and
unconditionally clears it. The acknowledgement path searches Tracked; the repository also verifies
that ReactiveCommand.Execute() can run despite CanExecute being false, so the command gate alone
does not protect this state from overlapping direct executions.

src/Capacitor.App/ViewModels/ChatTabViewModel.cs[55-60]
src/Capacitor.App/ViewModels/ChatTabViewModel.cs[563-584]
src/Capacitor.App/ViewModels/ChatTabViewModel.cs[841-847]
src/Capacitor.App/ViewModels/ChatTabViewModel.cs[613-615]
test/Capacitor.App.Tests.Unit/HomeViewModelTests.cs[1495-1500]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Overlapping command executions share one `_sending` slot. One execution can clear the slot while another still needs transcript acknowledgement.
## Fix Focus Areas
- src/Capacitor.App/ViewModels/ChatTabViewModel.cs[55-60]
- src/Capacitor.App/ViewModels/ChatTabViewModel.cs[563-584]
- src/Capacitor.App/ViewModels/ChatTabViewModel.cs[841-847]
## Recommended Fix
Keep independently tracked in-flight messages and remove only the message belonging to the completed execution, or enforce a non-bypassable single-send guard inside the send operation. Add a test that overlaps direct command executions and delivers a transcript echo before both channels answer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 64 rules
✅ Cross-repo context — repo relationships
  Explored: repo: kurrent-io/kurrent-agents (sha: 8bf78d4f) — View relationship
  Explored: repo: kurrent-io/kcap-server (sha: acbd9635) — View relationship
Review mode: ⚖️ Balanced: This changes transcript projection and chat queue/send state across multiple code paths, with behavioral effects on message acknowledgment and attachment handling.

Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.App/ViewModels/ChatTabViewModel.cs
@alexeyzimarev
alexeyzimarev force-pushed the norton/ai-3234-chat-queue-tracking branch from efad4b1 to 853922c Compare September 28, 2026 13:18
alexeyzimarev
alexeyzimarev previously approved these changes Sep 28, 2026
alexeyzimarev
alexeyzimarev previously approved these changes Sep 29, 2026
@alexeyzimarev
alexeyzimarev force-pushed the norton/ai-3234-chat-queue-tracking branch from c57bbb7 to 454abf0 Compare September 29, 2026 14:36
Base automatically changed from norton/ai-3235-chat-pasted-content-markup to main September 29, 2026 14:37
Claude Code records a prompt taken up mid-turn only as a queued_command
attachment, so the leaf now projects it: servers will ingest these as new
user-message events keyed by the attachment's own uuid.
@alexeyzimarev
alexeyzimarev force-pushed the norton/ai-3234-chat-queue-tracking branch from 454abf0 to a2640d9 Compare September 29, 2026 14:37
@alexeyzimarev
alexeyzimarev merged commit 7355d94 into main Sep 29, 2026
1 check passed
@alexeyzimarev
alexeyzimarev deleted the norton/ai-3234-chat-queue-tracking branch September 29, 2026 14:37
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