Skip to content

Show a session's subagents in the desktop chat - #1002

Merged
alexeyzimarev merged 16 commits into
mainfrom
capacitor/agent-e90d8dfa163045
Sep 18, 2026
Merged

alexeyzimarev merged 16 commits into
mainfrom
capacitor/agent-e90d8dfa163045

Conversation

@alexeyzimarev

@alexeyzimarev alexeyzimarev commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Part of #966 — AI-2840

What & why

A Claude session's subagents had no presence in the desktop app beyond a tool-call row reading "Ran an agent". The chat tab now shows a one-line "N subagents running" strip above the composer, and the work-context pane lists the session's subagents by type and description with their state and elapsed time. Both derive from the transcript alone: the Claude projection carries a tool-result line's toolUseResult in the claude_code extension (the shape kcap-server already persists), ClaudeChatRules turns Agent/Task calls, async_launched results, task-notifications and TaskStop results into vendor-neutral signals beside the chat rows, and one tracker per workspace folds them. Local and remote sessions alike; a remote session gets the strip alone, since its view has no pane. The daemon-side live count that keeps the rail honest while background subagents run is the second PR; this one carries the spec.

Where to look

SessionSubagents.Finish: a completion naming a call the tracker knows settles only that call and never falls back to the agent id, so a repeated notification from a first run cannot end a resumed second run. Session end is a view over the rows, not a transition, so a status landing before the first transcript read, and a remote row that comes back, both read right.

Verification

  • dotnet run --project test/Capacitor.App.Tests.Unit/Capacitor.App.Tests.Unit.csproj — 2485 total, 0 failed
  • dotnet run --project test/Capacitor.Cli.Core.Tests.Unit/Capacitor.Cli.Core.Tests.Unit.csproj — 3423 total, 9 skipped, 0 failed after one rerun of AgentAttachClientTests (a dispose-race test untouched here, 33/33 alone)
  • dotnet run --project test/Capacitor.Models.Transcripts.Tests.Unit/Capacitor.Models.Transcripts.Tests.Unit.csproj — 148 total, 0 failed
  • dotnet publish src/Capacitor.Cli/Capacitor.Cli.csproj -c Release 2>&1 | grep -E 'IL[23][01][0-9]{2}' — no output

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 18, 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-18T07:22:49.505892Z 9dc0309 PR opened
ℹ️ 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.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Show Claude subagents in desktop chat and work context

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Projects Claude subagent lifecycle signals from local and remote transcript records.
• Tracks detached, resumed, failed, stopped, and session-ended subagents per workspace.
• Shows running counts in chat and detailed status rows in work context.
Diagram

graph TD
    A["Claude Transcript"] --> B["Transcript Projector"] --> C["Claude Chat Rules"] --> D["Projection Result"] --> E["Session Tracker"] --> G["Chat Banner"]
    F["Remote Feed"] --> C
    E --> H["Context Pane"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Extend the wire envelope
  • ➕ Makes subagent facts available directly on each canonical event.
  • ➕ Could simplify consumers that already process AcpEventEnvelope instances.
  • ➖ Changes a server-mirrored compatibility-sensitive wire contract.
  • ➖ Leaks desktop display concerns into the canonical event schema.
  • ➖ Still requires vendor-specific lifecycle derivation somewhere.
2. Fetch subagents from a server API
  • ➕ Could centralize lifecycle tracking and expose richer remote-session data.
  • ➕ Avoids rebuilding state in each desktop client.
  • ➖ Requires server, endpoint, persistence, and synchronization changes.
  • ➖ Would not work uniformly for local sessions or previously ingested transcripts.
  • ➖ Adds network coupling for data already present in transcript records.

Recommendation: Keep the PR's transcript-derived side-band approach. It reuses evidence already available on both local and remote lanes, avoids changing the canonical wire contract or server, and keeps vendor-specific interpretation in Claude rules while exposing vendor-neutral signals to the app. A server-owned model is worth reconsidering only if future requirements need navigation, cross-client synchronization, or richer subagent metadata.

Files changed (38) +6321 / -44

Enhancement (17) +451 / -30
ChatTabViewModel.csFeed projected subagent activity into the chat +21/-3

Feed projected subagent activity into the chat

• Accepts a session tracker, applies projected signals, clears it with feed state, and reflects session completion. Exposes the running-subagent banner state and refreshes elapsed durations on chat ticks.

src/Capacitor.App/ViewModels/ChatTabViewModel.cs

RemoteSessionViewModel.csCreate a subagent tracker for remote chat +1/-1

Create a subagent tracker for remote chat

• Constructs a transcript-derived tracker for each remote session and supplies it to the chat tab.

src/Capacitor.App/ViewModels/RemoteSessionViewModel.cs

SessionSubagents.csTrack session subagent lifecycles +122/-0

Track session subagent lifecycles

• Adds the state machine that folds starts, asynchronous detachments, tool results, notifications, and stops into ordered rows. It guards resumed-agent matching, handles resets, computes running counts, and presents unfinished rows as stopped when a session ends.

src/Capacitor.App/ViewModels/SessionSubagents.cs

SubagentRow.csModel a displayed subagent row +73/-0

Model a displayed subagent row

• Adds reactive row state, background status, timestamps, elapsed-time formatting, and session-end presentation for running, done, failed, and stopped subagents.

src/Capacitor.App/ViewModels/SubagentRow.cs

SubagentState.csDefine displayed subagent states +3/-0

Define displayed subagent states

• Introduces the running, done, failed, and stopped states used by tracker rows and UI bindings.

src/Capacitor.App/ViewModels/SubagentState.cs

WorkContextViewModel.Projections.csAdd subagent section expansion controls +4/-0

Add subagent section expansion controls

• Adds expanded state and a toggle command for the new work-context subagent section.

src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs

WorkContextViewModel.csExpose tracked subagents to work context +24/-1

Expose tracked subagents to work context

• Shares the workspace tracker with the pane and exposes rows, visibility, and running-versus-total header text. It subscribes to tracker changes independently of the pane's server-read phase.

src/Capacitor.App/ViewModels/WorkContextViewModel.cs

WorkspaceViewModel.csShare one tracker across workspace views +3/-2

Share one tracker across workspace views

• Creates one SessionSubagents instance per workspace and passes it to both the chat tab and work-context pane.

src/Capacitor.App/ViewModels/WorkspaceViewModel.cs

ChatTabView.axamlAdd the running-subagents chat banner +15/-3

Add the running-subagents chat banner

• Adds a one-line pulsing banner above queued messages and the composer, visible while one or more subagents are running.

src/Capacitor.App/Views/ChatTabView.axaml

WorkContextView.axamlRender the work-context subagent list +50/-0

Render the work-context subagent list

• Adds an expandable SUBAGENTS section with counts, type, description, background marker, state text, elapsed time, and accessible state-specific indicators.

src/Capacitor.App/Views/WorkContextView.axaml

ChatProjectionResult.csCarry subagent signals beside chat rows +8/-2

Carry subagent signals beside chat rows

• Extends each projected chat result with a vendor-neutral collection of subagent lifecycle signals.

src/Capacitor.Cli.Core/ChatProjectionResult.cs

ClaudeChatRules.csDerive subagent signals from Claude events +76/-6

Derive subagent signals from Claude events

• Recognizes Agent and Task starts, asynchronous launches, task notifications, and successful TaskStop results. It also recognizes remote task notifications from their opening tag and prevents them from appearing as user input.

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs

SubagentOutcome.csDefine terminal subagent outcomes +3/-0

Define terminal subagent outcomes

• Introduces vendor-neutral done, failed, and stopped outcomes for lifecycle completion signals.

src/Capacitor.Cli.Core/SubagentOutcome.cs

SubagentSignal.csDefine vendor-neutral subagent signals +15/-0

Define vendor-neutral subagent signals

• Adds started, detached, and finished signal records carrying call identifiers, agent identifiers, display metadata, outcomes, and timestamps.

src/Capacitor.Cli.Core/SubagentSignal.cs

TranscriptChat.csAggregate subagent signals during projection +11/-4

Aggregate subagent signals during projection

• Adds a display-rules hook for deriving subagent facts from raw envelopes, even when rows are filtered. Aggregates signals across every canonical event projected from a transcript line.

src/Capacitor.Cli.Core/TranscriptChat.cs

ClaudeCodeExtension.csAdd the Claude tool-use-result extension field +10/-7

Add the Claude tool-use-result extension field

• Defines tool_use_result and allows an object-valued Claude tool result to be preserved in the canonical extension payload.

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

ClaudeTranscriptEvents.csPreserve single-result Claude launch metadata +12/-1

Preserve single-result Claude launch metadata

• Copies a line-level object-valued toolUseResult into the sole projected tool result. Multi-result lines intentionally receive no value because the root object cannot be attributed safely.

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

Bug fix (1) +1 / -1
RemoteTranscriptFeed.csPreserve signal-only remote projections +1/-1

Preserve signal-only remote projections

• Retains remote events that contain subagent signals even when they produce no visible chat row or submitted input.

src/Capacitor.App/ViewModels/RemoteTranscriptFeed.cs

Refactor (1) +1 / -1
IChatTranscriptProjection.csDefault projections to no subagent signals +1/-1

Default projections to no subagent signals

• Updates the default projection result construction to include an empty subagent signal collection.

src/Capacitor.Cli.Core/IChatTranscriptProjection.cs

Tests (14) +938 / -12
ChatAttachmentsTests.csSupply subagent tracking in attachment tests +1/-1

Supply subagent tracking in attachment tests

• Updates chat construction to provide the newly required session tracker.

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

ChatComposerTests.csSupply subagent tracking in composer tests +2/-2

Supply subagent tracking in composer tests

• Updates local chat test fixtures to construct and pass SessionSubagents.

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

ChatTabViewModelTests.csTest transcript-driven chat subagent state +112/-5

Test transcript-driven chat subagent state

• Covers singular and plural banner counts, finishes, feed resets and switches, terminal status before initial reads, and stale-generation protection. Existing construction sites are updated for the shared tracker.

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

ChatTabViewSmokeTests.csVerify the subagent banner layout and visibility +34/-1

Verify the subagent banner layout and visibility

• Checks that the banner appears only while subagents run, includes its pulse and summary, and sits between the activity note and queued-message banner.

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

RemoteSessionViewModelTests.csTest remote subagent banner behavior +72/-0

Test remote subagent banner behavior

• Exercises server-shaped launch and notification events, signal-only meta notifications, and restoration of running presentation when a removed remote row reappears.

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

RemoteTranscriptFeedTests.csTest signal-only remote feed entries +22/-0

Test signal-only remote feed entries

• Verifies that a hidden task notification still survives the remote feed with its completion signal and identifiers intact.

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

SessionSubagentsTests.csCover the subagent lifecycle state machine +314/-0

Cover the subagent lifecycle state machine

• Adds comprehensive tests for foreground and background execution, failures, stops, duplicates, resumed identifiers, stale evidence, resets, session-end presentation, ordering, and elapsed time.

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

WorkContextViewModelTests.csTest work-context subagent projections +81/-2

Test work-context subagent projections

• Verifies section visibility, running and total counts, availability across loading and failure phases, and expansion toggling.

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

WorkContextViewSmokeTests.csVerify rendered subagent rows and states +51/-1

Verify rendered subagent rows and states

• Checks row names, descriptions, background tags, elapsed text, failure styling, running animation, section visibility, and collapse behavior.

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

WorkspaceViewModelTests.csVerify chat and pane share subagent state +25/-0

Verify chat and pane share subagent state

• Confirms transcript activity read by the chat appears through the same tracker in the workspace's work-context pane.

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

ClaudeChatRulesTests.csTest Claude subagent signal derivation +106/-0

Test Claude subagent signal derivation

• Covers starts, detachments, task-notification completion and failure, text-only remote recognition, hidden notifications, sidechain exclusion, TaskStop handling, and probe rejection.

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

CodexChatRulesTests.csConfirm Codex emits no subagent signals +8/-0

Confirm Codex emits no subagent signals

• Pins the default behavior for an unsupported vendor even when its transcript includes an agent-spawning call.

test/Capacitor.Cli.Core.Tests.Unit/Harness/Codex/CodexChatRulesTests.cs

TranscriptChatCanonicalTests.csTest side-band signal propagation +57/-0

Test side-band signal propagation

• Verifies signals survive filtered rows, aggregate across events, default to empty for silent rules, and remain absent from journal projection.

test/Capacitor.Cli.Core.Tests.Unit/TranscriptChatCanonicalTests.cs

ClaudeTranscriptEventsTests.csTest Claude toolUseResult preservation +53/-0

Test Claude toolUseResult preservation

• Covers unchanged object projection, absent and invalid values, ambiguous multi-result lines, error flags, and stable canonical event identifiers.

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

Documentation (5) +4930 / -0
README.mdDocument desktop subagent visibility +2/-0

Document desktop subagent visibility

• Explains the running-subagent strip, work-context section, displayed states, and the remote-session limitation.

README.md

CHANGES.mdRecord subagent projection and tracking decisions +26/-0

Record subagent projection and tracking decisions

• Documents how transcript evidence becomes subagent lifecycle signals and why signals travel beside chat rows. It also explains completion matching, session-end presentation, and remote compatibility.

docs/CHANGES.md

2026-09-17-ai2840-subagents-display.mdAdd the desktop subagent display implementation plan +2279/-0

Add the desktop subagent display implementation plan

• Provides the detailed implementation, testing, verification, and commit plan for transcript projection, lifecycle tracking, and desktop UI display.

docs/superpowers/plans/2026-09-17-ai2840-subagents-display.md

2026-09-17-ai2840-subagents-live-count.mdPlan the follow-up daemon live-count work +2394/-0

Plan the follow-up daemon live-count work

• Specifies the separate follow-up for hook-based daemon counting, status propagation, expiry handling, and rail activity indicators. This PR carries the plan but does not implement that daemon-side count.

docs/superpowers/plans/2026-09-17-ai2840-subagents-live-count.md

2026-09-17-ai2840-desktop-subagents-design.mdDefine the end-to-end desktop subagent design +229/-0

Define the end-to-end desktop subagent design

• Captures scope, transcript evidence, signal contracts, tracker semantics, UI behavior, accepted limitations, delivery split, and testing strategy for both planned PRs.

docs/superpowers/specs/2026-09-17-ai2840-desktop-subagents-design.md

@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: 9dc0309031

ℹ️ 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 +65 to +66
if (SchemaExtensions.Text(result, "status") == "async_launched" && SchemaExtensions.Text(result, "agentId") is { Length: > 0 } agentId)
return [new SubagentSignal.Detached(callId, agentId)];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept snake_case agent IDs in launch results

When a valid async launch uses agent_id instead of agentId, this condition emits no Detached signal. SessionSubagents then treats the launch acknowledgement's ordinary ToolResult envelope as the terminal result, so the new row immediately appears done and the running banner never appears. The repository already accepts both spellings in SessionImporter.TryExtractFromAsyncLaunched and TryExtractFromToolUseResult, so this path should use the same fallback.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 9baa528: the rules now read the same shapes the server's parser accepts.

Comment on lines +92 to +94
// The root object describes one result and names none: on a line with several it is
// attributed to none of them.
var toolUseResult = CountResults(blocks) == 1 ? root.Obj("toolUseResult") : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Project standalone async launch result records

This only propagates launch metadata from a user record's root toolUseResult, while supported Claude transcripts can represent the same launch as {"type":"result","tool_use_id":...,"tool_result":{"status":"async_launched",...}} (the existing SessionImporterScanTests and TryExtractFromAsyncLaunched explicitly handle that shape). Because ClaudeTranscriptEvents.Project drops result records, these launches never produce Detached: their rows lack the background tag and agent-id binding, so a later TaskStop completion keyed only by agent ID is ignored and the row remains running until session end.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The projection mirrors kcap-server's ClaudeCodeNormalizer, which reads only user and assistant records, so a standalone type:"result" record produces no canonical event on either lane today — the live transcript shape carries the launch on the user line's root toolUseResult. Reading result records is a leaf-parity question (#775) rather than something this PR decides; a launch stored that way reads as it does today.

@qodo-code-review

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

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Remediation recommended

1. A test comment repeats its method ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The XML comment above The_chat_and_the_pane_share_one_subagent_tracker restates that the chat and
pane use one workspace tracker. The following method name and assertions already express that
relationship, leaving a duplicate description that can drift when the test changes.
Code

test/Capacitor.App.Tests.Unit/WorkspaceViewModelTests.cs[468]

+    /// One tracker per workspace: what the chat reads off the transcript is what the pane lists.
Relevance

●●● Strong

Recent repository precedents consistently remove comments that merely restate test names, coverage,
or assertions.

PR-#834
PR-#863

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 2762993 disallows comments that duplicate type or signature information. The comment
states the same chat-and-pane sharing behavior already named by the immediately following test and
verified by its assertions.

Rule 2762993: Restrict comments to documenting non-obvious, behavior‑critical constraints
test/Capacitor.App.Tests.Unit/WorkspaceViewModelTests.cs[468-484]

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

## Issue description
The added XML comment repeats the test method's name and assertions without documenting a non-obvious constraint or rationale.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/WorkspaceViewModelTests.cs[468-471]

## Recommended Fix
Remove the XML comment. Keep the descriptive method name and assertions as the test's explanation.

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


2. Stopped agents keep running ✓ Resolved 🔗 Cross-repo conflict ≡ Correctness
Description
The TaskStop branch requires message to begin with the exact case-sensitive text `Successfully
stopped task` and does not ignore leading whitespace. kcap-server trims the same field and compares
it case-insensitively, so stop results recognized by the server can leave the desktop subagent
running.
Code

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[R69-72]

+                if (SchemaExtensions.Text(result, "task_type") == "local_agent"
+                    && SchemaExtensions.Text(result, "task_id") is { Length: > 0 } taskId
+                    && (SchemaExtensions.Text(result, "message") ?? "").StartsWith(StoppedTaskMessage, StringComparison.Ordinal))
+                    return [new SubagentSignal.Finished(null, taskId, SubagentOutcome.Stopped, evt.Timestamp)];
Relevance

●●● Strong

Recent precedent accepts hardening exact parsing into trimmed, case-insensitive semantics for
interoperability.

PR-#736

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR uses a strict ordinal prefix check, while the server’s authoritative tool-result helper trims
leading whitespace and recognizes the same TaskStop marker case-insensitively.

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[64-72]
External repo: kurrent-io/kcap-server, src/Capacitor.Server.Core/ToolEventHelpers.cs [72-78]

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

## Issue description
The desktop recognizes fewer valid TaskStop result messages than kcap-server because its prefix check is case-sensitive and does not trim leading whitespace. A remotely stopped agent can therefore remain running in the desktop view.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[64-72]

## Recommended Fix
Apply `TrimStart()` to the stop message and use `StartsWith` with `StringComparison.OrdinalIgnoreCase`, matching kcap-server's completion helper.

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


3. Completed agents appear failed ✓ Resolved 🔗 Cross-repo conflict ≡ Correctness
Description
ClaudeChatRules.Subagents treats only the exact lowercase status completed as success and maps
every other spelling to Failed. kcap-server classifies completion statuses case-insensitively, so
a remote notification containing Completed is successful on the server but appears failed in the
desktop.
Code

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[80]

+                var outcome = Tag(TaskStatus(), text) == "completed" ? SubagentOutcome.Done : SubagentOutcome.Failed;
Relevance

●●● Strong

Recent precedent accepts case-insensitive normalization when exact status matching can misclassify
vendor results.

PR-#736

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed desktop branch uses exact string equality, while the server’s canonical
task-notification classifier explicitly compares completed using ordinal case-insensitive
semantics.

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[75-81]
External repo: kurrent-io/kcap-server, src/Capacitor.Server.Core/TaskNotificationParser.cs [63-72]

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

## Issue description
The desktop applies a case-sensitive completion-status comparison even though kcap-server's notification contract accepts completion statuses without regard to casing. This can display a server-successful subagent as failed.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[75-81]

## Recommended Fix
Trim the extracted status and compare it to `completed` with `StringComparison.OrdinalIgnoreCase`, preserving the existing failed fallback for other outcomes.

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


View medium (2)
4. Tagged prompts disappear from chat ✗ Dismissed 🐞 Bug ≡ Correctness
Description
IsTaskNotification treats any user message beginning with <task-notification> as a trusted
notification even when it has no notification origin marker. Such a prompt is removed from
submitted-input acknowledgement and normal chat display, while embedded identifiers and status tags
can also finish a matching tracked subagent.
Code

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[R89-91]

+    static bool IsTaskNotification(Struct? slug, AcpEventEnvelope raw) =>
+        SchemaExtensions.Text(slug, ClaudeCodeExtension.OriginKind) == "task-notification"
+        || (raw.Text ?? "").AsSpan().TrimStart().StartsWith("<task-notification>");
Relevance

●● Moderate

The fallback is intentional for server events, but false-positive filtering and tracker effects
require semantic judgment.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Ordinary Claude string-content user records are projected without requiring an origin marker, while
the added text fallback feeds the same classification into filtering, submitted-input
acknowledgement, and subagent completion parsing. The chat only acknowledges inputs present in
SubmittedInputs, and the tracker applies matching completion identifiers even when the source
envelope was hidden.

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[18-24]
src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[38-42]
src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[75-91]
src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs[72-77]
src/Capacitor.Cli.Core/TranscriptChat.cs[68-75]
src/Capacitor.App/ViewModels/SessionSubagents.cs[89-102]

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

## Issue description
Ordinary user prompts beginning with `<task-notification>` are classified as trusted task notifications, causing them to be hidden, left unacknowledged, and potentially interpreted as subagent completion signals.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[75-91]

## Recommended Fix
Require a trusted producer-supplied notification marker rather than classifying solely from message text. Preserve the existing local `origin_kind` check and arrange for remote normalized events to carry equivalent provenance before parsing task identifiers or suppressing the user message.

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


5. Truncated completions stay running ✓ Resolved 🔗 Cross-repo conflict ≡ Correctness
Description
TaskToolUseId and the shared Tag helper require a closing XML tag before they extract the call
identifier. When kcap-server serves a stored task notification truncated before that closing tag,
its parser still extracts the identifier but the desktop emits no finish signal, leaving the
corresponding subagent running.
Code

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[R143-144]

+    [GeneratedRegex(@"<tool-use-id>(.*?)</tool-use-id>", RegexOptions.Singleline)]
+    private static partial Regex TaskToolUseId();
Relevance

●● Moderate

Specific truncation behavior is plausible, but no closely matching repository precedent confirms the
expected fallback contract.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR’s regular expressions require explicit closing tags and its helper returns null otherwise,
whereas kcap-server documents and implements end-of-input fallback specifically for truncated stored
task notifications.

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[108-109]
src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[140-147]
External repo: kurrent-io/kcap-server, src/Capacitor.Server.Core/TaskNotificationParser.cs [118-135]

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

## Issue description
The desktop requires closing task-notification tags, while kcap-server explicitly supports stored notifications truncated before those tags close. Such remote completions do not settle their subagent rows.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[108-109]
- src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[140-147]

## Recommended Fix
Make the task ID, tool-use ID, and status extractors capture either through the closing tag or through end-of-input, matching kcap-server's TaskNotificationParser behavior.

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



Informational

6. Claude parsing spans two assemblies 📘 Rule violation ⌂ Architecture
Description
ClaudeChatRules.Subagents interprets the verbatim toolUseResult fields status and agentId in
the CLI Core assembly instead of receiving normalized subagent data from the transcript harness. The
same path also reads raw tool arguments and task-notification tags, so a Claude format change now
requires coordinated parsing changes outside the designated Models harness.
Code

src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[R64-66]

+            case AcpEventKind.ToolResult when raw.ToolCallId is { Length: > 0 } callId && ToolUseResult(slug) is { } result: {
+                if (SchemaExtensions.Text(result, "status") == "async_launched" && SchemaExtensions.Text(result, "agentId") is { Length: > 0 } agentId)
+                    return [new SubagentSignal.Detached(callId, agentId)];
Relevance

● Weak

Recent precedents reject moving vendor-specific logic solely to designated harness boundaries when
shared behavior remains intentional.

PR-#615
PR-#580

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Models harness copies toolUseResult verbatim into an extension, while the changed CLI Core
rules interpret its Claude-specific fields and produce subagent state. This places raw vendor-format
parsing outside the sole directory required by compliance rule 3114705.

Rule 3114705: Vendor transcript formats must be parsed only in designated harness directory
src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[60-81]
src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs[92-101]
src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeCodeExtension.cs[13-24]

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

## Issue description
Claude-specific transcript fields are copied verbatim by the Models transcript harness and then interpreted in the CLI Core assembly, splitting vendor-format parsing across assemblies.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs[60-81]
- src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeTranscriptEvents.cs[92-101]
- src/Capacitor.Models.Transcripts/Harness/Claude/ClaudeCodeExtension.cs[13-24]

## Recommended Fix
Interpret Claude tool inputs, tool-use results, and task-notification tags inside the Models Claude transcript harness. Expose normalized, vendor-neutral fields or events for `ClaudeChatRules` to consume without accessing raw Claude field names or structures.

ⓘ 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)
  Explored: repo: kurrent-io/kcap-server (sha: 8fda513c)
Review mode: 🧠 Deep: This is a high-density cross-layer change spanning transcript parsing, stateful subagent tracking, view models, UI, remote/local session behavior, and numerous independent lifecycle paths where redundant review can catch subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread test/Capacitor.App.Tests.Unit/WorkspaceViewModelTests.cs Outdated
Comment thread src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs
Comment thread src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs Outdated
Comment thread src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs Outdated
Comment thread src/Capacitor.Cli.Core/Harness/Claude/ClaudeChatRules.cs
alexeyzimarev and others added 14 commits September 18, 2026 10:54
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…#966)

Only a line with one tool_result block gets it: the root object names no result, so on a line with several it belongs to none.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The signal is read off the raw envelope for every event, so a row the chat hides still yields it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A task-notification is known by origin_kind or by its opening tag: the server's events carry no origin_kind, and the remote lane otherwise shows the notification as the user's own turn.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Session end is a view over the rows rather than a transition: a late first read, a final-drain finish and a remote row that comes back all read right without a reopen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The remote feed keeps a result that carries only signals: a notification marked meta yields no row, and dropping it would leave its subagent running forever.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The section sits with the session-local facts rather than inside the server-read card, so it renders while that read is loading or has failed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The section lives in the work-context pane, which the remote view lacks; the same commit drops a file-path banner comment and rewords a CHANGES.md line as a compatibility fact.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
agent_id beside agentId, case-insensitive status and stop messages, and tags a stored notification may cut off before they close: every shape kcap-server already accepts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@alexeyzimarev
alexeyzimarev force-pushed the capacitor/agent-e90d8dfa163045 branch from 80438ba to dc45a7d Compare September 18, 2026 09:00
alexeyzimarev and others added 2 commits September 18, 2026 12:26
The not-before-start guard compares a completion's time with the launch it would end, and a spooled batch stores an old completion after a newer launch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A horizontal StackPanel measures its children unbounded, so the name's ellipsis never engaged and a long subagent_type ran over the tag and the state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@alexeyzimarev
alexeyzimarev merged commit 0152d30 into main Sep 18, 2026
7 checks passed
@alexeyzimarev
alexeyzimarev deleted the capacitor/agent-e90d8dfa163045 branch September 18, 2026 10:45
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.

1 participant