Skip to content

feat: polish subagent activity workspace - #936

Merged
chuks-qua merged 15 commits into
mainfrom
feat/916-subagent-activity-workspace
Jul 24, 2026
Merged

chuks-qua merged 15 commits into
mainfrom
feat/916-subagent-activity-workspace

Conversation

@chuks-qua

@chuks-qua chuks-qua commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

What

Polish the thread-scoped subagent experience across chat, Thread Overview, and the maximized Subagents workspace.

Why

Delegated work needs a clear lifecycle in the parent thread and a focused place to inspect each subagent without mixing its internal events into the main conversation.

Key Changes

  • Persist and hydrate provider-supplied subagent display names.
  • Show compact started, updated, and finished lifecycle rows in chat.
  • Add a conditional Subagents summary below Usage in Thread Overview.
  • Present active and finished subagents on one page with identity, status, duration, and readable final output.
  • Open subagent detail from chat and render child activity only in the dedicated workspace.
  • Add focused projection, persistence, navigation, accessibility, and presentation coverage.

Verification

  • Focused UI tests: 15 passed.
  • Typecheck: 5 packages passed.
  • Live verification: correct Overview order, 14px response text, no narrow overflow, and no console errors.
  • Full bun run verify exceeded the local 60-second command window and remains for CI.

Related to #916


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • New Features

    • Added a Subagents panel with active/completed activity, elapsed time, model/reasoning details, transcripts, and outputs.
    • Introduced compact lifecycle badges and consistent identity glyphs, with quick navigation from the thread overview and narrative timeline.
    • Enabled scoped diff review for subagent workspace changes, including scoped file lists and change stats.
  • Bug Fixes

    • Improved subagent grouping, lifecycle ordering, identity consistency, and resilience for nested/parallel activity.
    • Persisted and hydrated subagent metadata more reliably across sessions, including bounded display-name and metadata handling.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bfa5f39b-d65e-4820-a67d-da7f73030029

📥 Commits

Reviewing files that changed from the base of the PR and between 12b9ae6 and 410ac50.

📒 Files selected for processing (2)
  • apps/web/src/__tests__/diffStore.test.ts
  • apps/web/src/stores/diffStore.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/tests/diffStore.test.ts
  • apps/web/src/stores/diffStore.ts

📝 Walkthrough

Walkthrough

This PR adds native Codex subagent lifecycle handling, persists bounded subagent metadata, models live and hydrated lifecycle narratives, introduces a detailed Subagents panel, and supports subagent-scoped cumulative diffs and navigation.

Changes

Native subagent lifecycle and persistence

Layer / File(s) Summary
Codex event and metadata flow
apps/server/src/providers/codex/*
Codex native subagent activity, child-thread settings, deduplication, parent linkage, stale-turn protection, and related tests are added.
Tool-call metadata storage
packages/contracts/..., apps/server/src/store/*, apps/server/src/repositories/*, apps/server/drizzle/*
Tool-call records persist bounded display name, provider key, model, and reasoning-effort metadata through schemas, migrations, patches, repositories, and tests.
Narrative attribution
apps/server/src/services/narrative-store.ts, apps/server/src/services/__tests__/narrative-store.test.ts
Agent metadata and explicit parent attribution are preserved during buffering and deferred persistence.

Web subagent experience

Layer / File(s) Summary
Lifecycle-aware narratives
apps/web/src/components/chat/narrative/*
Live and persisted narratives emit started, updated, and finished subagent rows with participant relationships and compact identity controls.
Projection and detail data
apps/web/src/components/subagents/*
Subagent projection aggregates bounded activity, transcripts, identities, logical groups, statuses, and workspace file effects.
Roster and navigation
apps/web/src/components/panels/*, apps/web/src/lib/*, apps/web/src/stores/diffStore.ts
The Subagents panel supports active/done rosters, detail views, metadata, output, transcript, diff summaries, selection state, and per-thread navigation.
Scoped review diffs
apps/web/src/components/diff/*
Selected subagent paths and statistics are applied to cumulative diffs and Files navigation.
Overview identity display
apps/web/src/components/chat/ThreadOverview.tsx, apps/web/src/index.css
Thread Overview shows subagent counts and deterministic identity glyphs with light and dark theme colors.

Guidance updates

Layer / File(s) Summary
Verification and status documentation
AGENTS.md, DESIGN.md, docs/guides/ui-components.md
UI verification artifact locations, browser fallback procedures, and ambient status-dot placement rules are documented.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

Poem

A rabbit watched the agents run,
With lifecycle dots beneath the sun.
Metadata hopped into the store,
While scoped diffs opened one door.
“Started, updated, finished!” they say—
Then bounded transcripts tucked away.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change to subagent activity workspace polish.
Description check ✅ Passed The description covers What, Why, Key Changes, and Verification clearly; the optional Config Changes section is the only omission.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chuks-qua
chuks-qua marked this pull request as ready for review July 24, 2026 01:19

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/stores/diffStore.ts (1)

591-609: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the subagent review scope when "changes" is activated for the same thread.

setRightPanelTab deletes subagentReviewScopeByThread[threadId] whenever "changes" is activated, including after a refocus. The subagent panel code sets the scope and then calls setRightPanelTab(..., "changes"), so opening/switching back to the Changes tab can silently clear the scope that was just selected. Keep the stored scope intact instead of deleting it on "changes" activation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/stores/diffStore.ts` around lines 591 - 609, Update
setRightPanelTab to stop deleting subagentReviewScopeByThread[threadId] when the
"changes" tab is activated. Preserve the stored review scope across opening and
refocusing the Changes tab, while leaving the existing openTabs and activeTab
updates unchanged.
🧹 Nitpick comments (3)
apps/web/src/components/chat/narrative/build-narrative.ts (1)

292-308: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Participants/children/hooks recomputed per lifecycle row instead of once per agent.

For an agent with multiple lifecycle events (started + N "updated" markers + finished), subagentLifecycleParticipants (an O(n) .find over toolCalls), the children filter, and the hooks filter are all recomputed identically for every row of the same evt.call. Since children/hooks are currently unused by SubagentRow (it only reads participants/lifecycle), this work is pure waste; participants is used but is identical for every row of the same agent handoff.

Hoist these three values into a per-agent lookup (e.g. compute once when first encountering each evt.call.id, cache in a Map) and reuse across all lifecycle rows for that agent.

As per coding guidelines, "Validate and normalize once at process or trust boundaries, then rely on established invariants inside hot paths and loops; do not repeatedly re-parse, re-check, or re-clamp values" and "Bound all work and retention."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/chat/narrative/build-narrative.ts` around lines 292 -
308, In the narrative-building flow around the subagent event branch, cache the
derived participants, children, and hooks per evt.call.id in a bounded per-agent
Map, computing them only on the first lifecycle row for each agent. Reuse the
cached values for subsequent rows while preserving the existing filtering and
subagentLifecycleParticipants behavior.

Source: Coding guidelines

apps/web/src/components/chat/narrative/build-persisted-narrative.ts (2)

219-264: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

recordToToolCall is invoked redundantly for the same records.

For every top-level Agent record, recordToToolCall(t) runs in the topLevel loop (Line 219) and its result is discarded when t.tool_name === AGENT_TOOL_NAME; it then runs again for the same record inside the agentRecords loop (Line 226), and a third time via allToolCalls = tools.map(recordToToolCall) (Line 264) for every tool record including these Agent records. Consider building one Map<string, ToolCall> (or a single pass) and reusing it for topLevel, agentRecords, lifecycleMarkers, and allToolCalls instead of recomputing.

As per coding guidelines, "Validate and normalize once at process or trust boundaries, then rely on established invariants inside hot paths and loops; do not repeatedly re-parse, re-check, or re-clamp values."

♻️ Sketch of the fix
-  for (const t of topLevel) {
-    const call = recordToToolCall(t);
-    if (t.tool_name !== AGENT_TOOL_NAME) {
-      timeline.push({ kind: "tool", call, sortOrder: t.sort_order });
-    }
-  }
-  const agentRecords = tools.filter((tool) => tool.tool_name === AGENT_TOOL_NAME);
-  for (const t of agentRecords) {
-    const call = recordToToolCall(t);
+  const callById = new Map(tools.map((t) => [t.id, recordToToolCall(t)]));
+  for (const t of topLevel) {
+    if (t.tool_name !== AGENT_TOOL_NAME) {
+      timeline.push({ kind: "tool", call: callById.get(t.id)!, sortOrder: t.sort_order });
+    }
+  }
+  const agentRecords = tools.filter((tool) => tool.tool_name === AGENT_TOOL_NAME);
+  for (const t of agentRecords) {
+    const call = callById.get(t.id)!;

Then reuse callById for marker: recordToToolCall(marker) and replace const allToolCalls = tools.map(recordToToolCall); with [...callById.values()].

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/chat/narrative/build-persisted-narrative.ts` around
lines 219 - 264, Eliminate redundant recordToToolCall conversions in the
narrative-building flow by creating a single callById Map for all tool records
before the topLevel and agentRecords loops. Reuse the cached ToolCall for
top-level tools, agent subagent entries, lifecycle marker fields, and replace
allToolCalls mapping with the cached values; preserve existing ordering and
filtering behavior.

Source: Coding guidelines


295-314: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Children/participants/hooks recomputed per lifecycle row instead of once per agent.

Same pattern as build-narrative.ts: for every lifecycle row of the same evt.call (started, each "updated" marker, finished), childRecords/children are re-filtered/sorted/mapped, subagentLifecycleParticipants re-scans allToolCalls, and hooks is re-filtered — all producing identical results per agent. children/hooks are currently unused by SubagentRow, so this is pure waste on the hot narrative-build path.

As per coding guidelines, "Bound all work and retention... do not repeatedly re-parse, re-check, or re-clamp values."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/chat/narrative/build-persisted-narrative.ts` around
lines 295 - 314, In the subagent handling within the narrative builder, compute
each agent’s children, lifecycle participants, and hooks once per unique
evt.call.id rather than recomputing them for every lifecycle row. Reuse the
cached derived values when constructing each subagent item, preserving the
existing filtering, sorting, mapping, and participant-selection behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/web/src/stores/diffStore.ts`:
- Around line 591-609: Update setRightPanelTab to stop deleting
subagentReviewScopeByThread[threadId] when the "changes" tab is activated.
Preserve the stored review scope across opening and refocusing the Changes tab,
while leaving the existing openTabs and activeTab updates unchanged.

---

Nitpick comments:
In `@apps/web/src/components/chat/narrative/build-narrative.ts`:
- Around line 292-308: In the narrative-building flow around the subagent event
branch, cache the derived participants, children, and hooks per evt.call.id in a
bounded per-agent Map, computing them only on the first lifecycle row for each
agent. Reuse the cached values for subsequent rows while preserving the existing
filtering and subagentLifecycleParticipants behavior.

In `@apps/web/src/components/chat/narrative/build-persisted-narrative.ts`:
- Around line 219-264: Eliminate redundant recordToToolCall conversions in the
narrative-building flow by creating a single callById Map for all tool records
before the topLevel and agentRecords loops. Reuse the cached ToolCall for
top-level tools, agent subagent entries, lifecycle marker fields, and replace
allToolCalls mapping with the cached values; preserve existing ordering and
filtering behavior.
- Around line 295-314: In the subagent handling within the narrative builder,
compute each agent’s children, lifecycle participants, and hooks once per unique
evt.call.id rather than recomputing them for every lifecycle row. Reuse the
cached derived values when constructing each subagent item, preserving the
existing filtering, sorting, mapping, and participant-selection behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 087d9b6c-2a20-4ea2-a761-559bdbe92e0a

📥 Commits

Reviewing files that changed from the base of the PR and between 6e20316 and 12b9ae6.

📒 Files selected for processing (55)
  • AGENTS.md
  • DESIGN.md
  • apps/server/drizzle/0024_smart_glorian.sql
  • apps/server/drizzle/0025_wise_night_nurse.sql
  • apps/server/drizzle/0026_furry_red_hulk.sql
  • apps/server/drizzle/meta/0024_snapshot.json
  • apps/server/drizzle/meta/0025_snapshot.json
  • apps/server/drizzle/meta/0026_snapshot.json
  • apps/server/drizzle/meta/_journal.json
  • apps/server/src/__tests__/database-schema-patches.test.ts
  • apps/server/src/__tests__/tool-call-record-repo.test.ts
  • apps/server/src/providers/codex/__tests__/codex-app-server-handshake.test.ts
  • apps/server/src/providers/codex/__tests__/codex-event-mapper.test.ts
  • apps/server/src/providers/codex/__tests__/codex-provider-subagent-turn.test.ts
  • apps/server/src/providers/codex/codex-app-server.ts
  • apps/server/src/providers/codex/codex-event-mapper.ts
  • apps/server/src/providers/codex/codex-provider.ts
  • apps/server/src/providers/codex/codex-types.ts
  • apps/server/src/repositories/tool-call-record-repo.ts
  • apps/server/src/services/__tests__/narrative-store.test.ts
  • apps/server/src/services/narrative-store.ts
  • apps/server/src/store/database.ts
  • apps/server/src/store/schema.ts
  • apps/web/src/__tests__/diffStore.test.ts
  • apps/web/src/components/chat/ThreadOverview.branchless-pr.test.tsx
  • apps/web/src/components/chat/ThreadOverview.tsx
  • apps/web/src/components/chat/narrative/NarrativeFlow.tsx
  • apps/web/src/components/chat/narrative/PersistedNarrative.tsx
  • apps/web/src/components/chat/narrative/SubagentRow.tsx
  • apps/web/src/components/chat/narrative/__tests__/SubagentRow.test.tsx
  • apps/web/src/components/chat/narrative/__tests__/build-narrative-counts.test.ts
  • apps/web/src/components/chat/narrative/__tests__/parallel-subagent-nesting.test.ts
  • apps/web/src/components/chat/narrative/build-narrative.ts
  • apps/web/src/components/chat/narrative/build-persisted-narrative.test.ts
  • apps/web/src/components/chat/narrative/build-persisted-narrative.ts
  • apps/web/src/components/chat/narrative/subagent-lifecycle.ts
  • apps/web/src/components/chat/narrative/types.ts
  • apps/web/src/components/diff/CumulativeView.tsx
  • apps/web/src/components/diff/DiffPanel.tsx
  • apps/web/src/components/diff/__tests__/CumulativeView.test.tsx
  • apps/web/src/components/diff/__tests__/DiffPanel.files.test.tsx
  • apps/web/src/components/panels/SubagentChangeSummary.tsx
  • apps/web/src/components/panels/SubagentsPanel.tsx
  • apps/web/src/components/panels/__tests__/SubagentsPanel.test.tsx
  • apps/web/src/components/subagents/SubagentIdentityGlyph.tsx
  • apps/web/src/components/subagents/SubagentLifecycleStatus.tsx
  • apps/web/src/components/subagents/__tests__/subagent-projection.test.ts
  • apps/web/src/components/subagents/subagent-projection.ts
  • apps/web/src/index.css
  • apps/web/src/lib/__tests__/open-subagent-detail.test.ts
  • apps/web/src/lib/open-subagent-detail.ts
  • apps/web/src/stores/diffStore.ts
  • docs/guides/ui-components.md
  • packages/contracts/src/index.ts
  • packages/contracts/src/models/tool-call-record.ts

@chuks-qua
chuks-qua force-pushed the feat/916-subagent-activity-workspace branch from 60e7e57 to 410ac50 Compare July 24, 2026 02:09
@chuks-qua

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 3 minutes.

@chuks-qua

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chuks-qua
chuks-qua merged commit d82f932 into main Jul 24, 2026
10 checks passed
@chuks-qua
chuks-qua deleted the feat/916-subagent-activity-workspace branch July 24, 2026 14:15
@mze-bot mze-bot Bot mentioned this pull request Aug 9, 2026
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