Skip to content

Fix: Label workflow subagents in the agentop timeline - #1757

Merged
huang195 merged 1 commit into
rossoctl:mainfrom
huang195:fix/workflow-subagent-labels
Nov 9, 2026
Merged

huang195 merged 1 commit into
rossoctl:mainfrom
huang195:fix/workflow-subagent-labels

Conversation

@huang195

@huang195 huang195 commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Problem

The agentop timeline pane labels each agent that Claude Code's Workflow tool starts as subagent <id>. Such an agent has no Agent tool call on the wire, so the server has no description for it. Fixes #1756.

Change

  1. Harvest (core/observe/claude/harvest.go):
    • readSubagents also reads subagents/workflows/<run>/agent-<id>.meta.json, where Claude Code writes the workflow agents' labels.
    • An incremental harvest that skips an unchanged transcript still re-reads that session's meta files. A workflow starts agents without a write to the main transcript, so the transcript mtime does not show new agents.
  2. Timeline pane (cmd/agentop/tui/timeline_pane.go):
    • An agent with no spawn call uses its harvested description. An agent that a spawn call names keeps its wire label.
    • When the pane shows an agent with no label, it asks for a harvest, at most once per 15 s.

Cost

Measured on a real ~/.claude tree with 1,424 sessions: an incremental harvest went from 33 ms to about 68 ms. It found 716 subagent labels, up from 679. The 37 new ones are workflow agents. The harvest runs in the background, not on the UI thread.

Tests

  • TestHarvest_ReadsWorkflowSubagentsWhenTranscriptIsUnchanged: a workflow agent that starts after the first harvest is found while the transcript is skipped. It fails without the harvest change.
  • TestTimeline_WorkflowAgentUsesHarvestedLabel: the harvested label replaces subagent <id>, a spawned agent keeps its wire label, and the harvest request stops when every agent has a label.
  • go vet and gofmt on both modules. scripts/dev/go-test-shards.sh -race cmd/agentop (975 tests) and the sub-packages with -race pass.

Known limit

A teammate or a non-Claude-Code agent never has a meta file. While such an agent is on the timeline, the pane asks for a harvest every 15 s. Each one costs about 70 ms in the background.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

Summary by CodeRabbit

  • New Features
    • Timeline agent labels now use harvested descriptions when available, while preserving existing labels for spawned agents.
    • Newly available workflow subagent metadata can appear without requiring the session transcript to change.
  • Bug Fixes
    • The timeline now requests metadata harvesting when unlabeled agents need descriptions, and avoids duplicate requests while a harvest is in progress.

The timeline pane labeled each agent that Claude Code's Workflow tool
starts as "subagent <id>". Such an agent has no Agent tool call on the
wire, so the server has no description for it.

Claude Code writes the label to disk, in
subagents/workflows/<run>/agent-<id>.meta.json. The harvest now reads
that folder. An incremental harvest also re-reads the meta files of a
session whose transcript did not change, because a workflow starts
agents without a write to the main transcript.

The timeline pane uses the harvested description for an agent with no
spawn call. It asks for a harvest when such an agent has no label, at
most once per 15 s.

Fixes rossoctl#1756

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
@huang195
huang195 requested a review from a team as a code owner November 9, 2026 21:18
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Explain Change →

Walkthrough

The change reads workflow subagent metadata during incremental session harvests. The timeline requests harvests for eligible unlabeled agents and displays harvested descriptions when available.

Changes: Workflow subagent timeline labels

Layer / File(s) Summary
Read workflow metadata during incremental harvest
core/observe/claude/harvest.go, core/observe/claude/harvest_test.go
Harvesting reads metadata from regular and workflow subagent directories. For unchanged transcripts, it returns a session entry when its subagent map changes. The test covers newly added workflow metadata while retaining existing labels and the session title.
Harvest and display timeline labels
cmd/agentop/tui/app.go, cmd/agentop/tui/timeline_pane.go, cmd/agentop/tui/timeline_pane_test.go
After a timeline loads, the app can start a harvest. The timeline requests harvests at most once every 15 seconds when eligible unlabeled agents are present. It uses harvested descriptions for agents without a SpawnTool and retains wire labels for spawned agents. The test covers harvest requests, label selection, and the no-harvest case.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Low

Possibly related issues

Possibly related PRs

  • rossoctl/cortex#1517: This PR introduced the Timeline pane and agent rows that this change updates to show harvested workflow subagent descriptions.

Suggested reviewers: evaline-ju, esnible

Merge Risk: Minimal · up to eafc9

The labeling change is mergeable after normal checks. The remaining observations describe bounded harvest behavior, not a demonstrated blocker.

Pre-merge checks: Passed 5

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the main change: labeling workflow subagents in the agentop timeline.
Linked Issues check Passed Issue #1756 requests workflow-agent metadata harvesting, metadata refresh when an unchanged transcript is skipped, harvested labels in the timeline when no spawn call exists, and a harvest request for…
Out of Scope Changes check Passed The changes in core/observe/claude/harvest.go, cmd/agentop/tui/timeline_pane.go, and cmd/agentop/tui/app.go implement the harvesting and timeline behavior requested by issue #1756. The added tes…
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Full details: Linked Issues check

Explanation

Issue #1756 requests workflow-agent metadata harvesting, metadata refresh when an unchanged transcript is skipped, harvested labels in the timeline when no spawn call exists, and a harvest request for unlabeled agents no more often than every 15 seconds. readSubagents now reads workflow-run metadata, and ReadSessions refreshes changed subagent metadata on the skipped-transcript path. The timeline uses harvested descriptions only when SpawnTool is empty and preserves spawn-call labels. harvestForTimeline applies the 15-second interval. The new harvest and timeline tests cover these behaviors.

Full details: Out of Scope Changes check

Explanation

The changes in core/observe/claude/harvest.go, cmd/agentop/tui/timeline_pane.go, and cmd/agentop/tui/app.go implement the harvesting and timeline behavior requested by issue #1756. The added tests verify those changes. No unrelated change is evident in the whole-PR diff.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 5 functions across 5 files.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

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

Additional comments: 2

cmd/agentop/tui/app.go: 1

1639-1639: Trivial · Maintainability & Code Quality · Low value

Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

The timeline harvest stamp can delay the sessions pane harvest.

harvestForTimeline writes m.lastHarvest. The sessions pane gate compares now.Sub(m.lastHarvest) against untitledBackoff, so a timeline harvest delays the next sessions harvest by up to the backoff. The delay is bounded and the existing comment documents this class of coupling. The effect is minor.

If this matters, add a separate lastTimelineHarvest field. The timeline gate would read that field, and the sessions gate would keep lastHarvest.

cmd/agentop/tui/timeline_pane.go: 1

789-801: Trivial · Performance & Scalability · Poor tradeoff

Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Agents without a meta file trigger a harvest every 15 seconds.

Teammates and non-Claude-Code agents have SpawnTool == "" and no meta file. harvestedLabel stays empty for them, so each timelineLoadedMsg after 15 seconds starts another full incremental harvest. The PR documents this limit.

A per-session set of agent IDs already harvested would stop the repeat. Add agent IDs to the set after a harvest completes, and skip them in the loop. This is a recommended follow-up, not a blocker.

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7a893459-2ff9-4657-97a6-1702713ec0fd
Commits

Reviewing files that changed from the base of the PR and between 045fff6 and eafc90b.

Files selected for processing: 5

  • cmd/agentop/tui/app.go
  • cmd/agentop/tui/timeline_pane.go
  • cmd/agentop/tui/timeline_pane_test.go
  • core/observe/claude/harvest.go
  • core/observe/claude/harvest_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

@clawgenti clawgenti left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

This is a clean, focused fix. The harvest now reads subagents/workflows/<run>/agent-<id>.meta.json. It also re-reads a session's meta files when the transcript skip fires, which closes the gap the old comment assumed away ("a session only gains a subagent by appending the Agent tool call"). The timeline uses the harvested description only for agents with no spawn call, so wire labels stay authoritative for every other agent. I checked that both new tests are load-bearing. With the !maps.Equal re-read disabled, TestHarvest_ReadsWorkflowSubagentsWhenTranscriptIsUnchanged fails (b2 is missing). core/observe/claude and the cmd/agentop/tui timeline tests pass locally on head. The harvest gate shares m.harvesting and lastHarvest with the other paths, so it can't stack scans, and harvestedMsg stays the only place that clears the flag. I also rendered a CJK + ANSI/newline description through agentLine. The row stays inside the pane width (159/160), and the label is sanitised and cut by width as before. Two non-blocking notes are inline.

Author: huang195 (MEMBER — maintainer)
Areas reviewed: Go (core/observe/claude harvest, cmd/agentop/tui timeline pane + app update loop), tests
Agent/IDE config (.claude/.vscode): none
Commits: 1 commit, all signed-off: yes
CI status: passing (Spellcheck skipped)

return nil
}
for _, a := range m.timeline.tl.Agents {
if a.SpawnTool == "" && a.ID != timeline.MainAgent && a.ID != timeline.UnseparatedAgent && m.harvestedLabel(a) == "" {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

suggestion: The gate keys on the description rather than on whether the harvest found the agent at all. A meta file that has agentType but no description has a Subagents[id] entry, so further harvests can't add anything for it. Even so, harvestedLabel returns "" for it. The row stays subagent <id>, and on a live session this asks for a harvest every 15 s for as long as the pane is open. That's the same cost as the teammate "known limit", but here it comes from an agent the harvest did find. subagentLabel in events_blocks.go already falls back from Description to AgentType. Two changes would fix this: have the gate test key presence (_, ok := m.sessionsData[s].Subagents[a.ID]), and have the label use the same Description → AgentType fallback, so both panes name such an agent the same way. Not blocking: the 15 s floor already bounds the cost.

// mtime above does not move while new agents start. The meta files are small.
if subs := readSubagents(filepath.Join(projDir, id, "subagents")); !maps.Equal(subs, since[id].Subagents) {
kept := since[id]
kept.Subagents = subs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: After this change, a skipped session can appear in the returned map. The ReadSessions doc comment above still says "Skipped sessions are absent from the returned map rather than copied into it", so it needs an update. A related effect: such a session now counts in both res.Skipped and res.Harvested. That's harmless today, because Kept is derived and the Incremental caller in main.go prints neither. But it no longer matches Harvested's doc ("entries this run read from transcripts"), so one sentence on each would keep the counters honest.

@huang195
huang195 merged commit 83037bc into rossoctl:main Nov 9, 2026
41 checks passed
@huang195
huang195 deleted the fix/workflow-subagent-labels branch November 9, 2026 21:49
@github-project-automation github-project-automation Bot moved this from New/ToDo to Done in Rossoctl Issue Prioritization Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

agentop timeline shows workflow subagents as 'subagent <id>'

2 participants