Repository navigation
[Bug]: Orchestrator V2 shows a Claude workflow as a plain subagent; its phases and agents are dropped #15448
Description
Activity
juliusmarminge commented
on Oct 4, 2026 MemberMore actionsNote
Grok responding on behalf of Julius.
Triage
Thanks @developerAkX for the thorough write-up and the projection table. I confirmed it on current
main(737993303d). It's related to #15429 (subagent token usage) and #15214 (subagent effort), since they share the same gap inOrchestrationV2Subagent, but this one is about workflow structure: phases and member agents.What I found
A Claude
local_workflowis ingested as an ordinary subagent:isClaudeNonSubagentTaskonly treatslocal_bashas non-subagent work, sotask_startedforlocal_workflowcreates akind: "subagent"node titled with the description.- A model is only remembered for an
Agenttool call. A Workflow launch never goes throughrememberClaudeSubagentLaunch, so the row has no model. - The child thread's opening message is
task_started.prompt, written as an agent-authored message. For a workflow that's the script, and nothing else is appended because member agents don't emit on the parent stream. task_progresskeepsdescriptionand ignoresworkflow_progress, which isn't read anywhere underapps/orpackages/.task_notificationkeepssummary, which is theDynamic workflow "…" completedresult.OrchestrationV2Subagenthas no task kind, workflow name, phases or members, andprojectedSubagentsToRuntimealways fills thoseRuntimeSubagentfields withnull/[].orchestration.getWorkflowScriptstill exists and the client has a query atom for it, butrunHandles.scriptPathis never filled, so nothing can reach it.
The V1 workflow fold went away with Orchestrator V2 (#2829), and #8720 was closed expecting V2 to cover between-phase visibility. I didn't replay a live Claude workflow, but given the payloads in your report, this code path produces exactly the Lineage row, script-only child thread and one-line status you described.
Likely fix area
How much of the V1 roster to bring back is a product call. The live data is
task_progress.workflow_progress; member transcripts only exist on disk. Some options:- A size-capped optional workflow field on
OrchestrationV2Subagent(name, phases, per-agent label/phase/state/model/last tool/counts, withoutpromptPreview), filled byClaudeAdapterV2fromlocal_workflowtasks andworkflow_progress, as you suggested. - Lineage and the child view rendering a workflow as a workflow, with the roster instead of the script message.
- Filling
runHandles.scriptPathso the existing script endpoint becomes reachable. That's complementary, not a replacement for the roster.
This could land independently of the usage (#15429) and effort (#15214) work on the same record.
A maintainer will decide on the fix direction.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 4, 2026 Thanks for confirming it, @juliusmarminge.
Reproduced on a fresh V2 thread. I ran a test workflow on the nightly, built from
mainat65731f986b: 2 Warmup agents, 44 Sleep agents (Sonnet 5.5, low effort, each runningsleep 300) and 1 Summary agent. While 12 sleepers were running:- Lineage showed one subagent row: "Check how T3 Co… Running", with a one-line status (
Warmup: warmup 2). It showed no phases and no member agents. - Opening the row showed the whole workflow script (
export const meta = {...}) as a message "Sent by another agent", followed only by "Thinking". - Claude sent
task_progressframes with a fullworkflow_progressroster during that time; for example,Sleep: sleeper 08at 03:29:37Z. All of it was dropped.
I'll add the screenshots in a follow-up comment.
Direction: here's my vote, to make the product call easier.
- Server and contract first. Add an optional, size-capped
workflowfield toOrchestrationV2Subagent, filled byClaudeAdapterV2fromlocal_workflowtasks andtask_progress.workflow_progress. It would carry the name, phases, and per-agent label, phase, state, model, last tool and counts, withoutpromptPreview. Also fillrunHandles.scriptPath, so the existing script endpoint can be reached. - Then Lineage. Mark a workflow row as a workflow, show the running phase on it, and when it's opened show the member list instead of the script message.
Step 1 changes nothing visible on its own, so it could be reviewed and land separately from the UI decision.
One more small finding: V2 mistakes a workflow launch for a resumed subagent. A
Workflowtool call is an ordinary tool call, so it lands incontext.toolCalls, unlike anAgentlaunch. When the workflow'stask_startedarrives,recoverResumedClaudeSubagentreads "a task_started under a tracked tool call" as aSendMessageresume. It then runssubagentLaunchToolUseId, which reads the CLI's session storage for a launch record that doesn't exist. The provider log shows asubagent.lookupon every workflow launch. It returns null, so nothing breaks, but it's wasted I/O on each launch, and it shows the adapter has no notion of a workflow task. Skipping the lookup fortask_type: "local_workflow"would fix it.Overlap with #12598. I've since found #12598 ("show Claude workflow phases and members in Lineage"), which already covers most of this on V2: a
workflowfield on the subagent record,workflow_progressparsing inClaudeAdapterV2,runHandles.scriptPath, and a Lineage workflow row. It's currently marked conflicting withmain, and it still callsrecoverResumedClaudeSubagentfor workflow launches, so the finding above applies there too.Questions:
- Is anyone already working on this, beyond feat(web): show Claude workflow phases and members in Lineage #12598?
- Can I take it on? If feat(web): show Claude workflow phases and members in Lineage #12598 is the intended path, I'm happy to help it land instead of starting over. I could record the live-workflow run you asked for there (phase expansion, member navigation and the running timer, against the base version), or send the
local_workflowlookup fix as a small separate PR. If you'd rather have a fresh PR for step 1, I can do that too.
- Lineage showed one subagent row: "Check how T3 Co… Running", with a one-line status (
Can confirm. This is how a workflow shows in the main thread. The lineage entry is the currently running agent task(s) within the workflow:
clicking on it:
And the Lineage on the 2nd screen shows the previous agent task(s) from the workflow. I can click through each to go backwards, but see nothing useful on any of them.
The v1 workflow overview that listed all the tasks and their status would be an improvement.
Still happens for me on T3 Code (Nightly) 0.0.46-nightly.20261009.2861, Claude Code 2.1.294, macOS 27.0.1.
I ran two
Workflowruns one after another in the same Claude thread. The first had 3 agents doing three review rounds in sequence. The second was apipeline()over 3 items, each going through 5 stages (explore, sceptic, contracts, implement, review), with the items running in parallel, so 15 agents.In the thread I only saw two subagent rows. Nothing showed the 18 agents, or even that the second run had 15 of them. I only got the counts from the completion notifications (
agent_count3 and 15) and from each run'sjournal.jsonl.I think the per-agent roster matters even more for
pipeline()runs. Items are in different stages at the same time, so one "current phase" status line can't really describe what's going on. Showing each agent's phase and state, like proposed above, would cover it.Astral Goat posting on behalf of GoatXYZ.
Another production workflow reproduction on the October 10 nightly
I hit this with a real implementation workflow today. The UI showed one agent for the workflow, but the retained Claude records show 17 executed member agents across three phases: eight builders, eight reviewers, and one integration agent. All 17 reached a terminal result. The work ran; its phase/member structure and per-member progress were missing from T3.
There is a version distinction: the run was on
0.0.46-nightly.20261010.2908, based on the updater/startup history and versioned renderer telemetry. I updated to0.0.46-nightly.20261010.2935afterward. The current install is confirmed from its bundle metadata, and its adapter/runtime still have the same workflow metadata gap. I have not rerun the workflow on 2935.Environment: macOS 27.0.1, Claude Code 2.1.296, Orchestrator V2, Claude Opus 5.5 parent. The workflow ran from 16:06:18 to 16:40:49 UTC on October 10, about 34 minutes 31 seconds.
What the retained records show
Evidence Result Native workflow progress 476 task_progressmessages for the workflow task; 101 includedworkflow_progressNative roster 3 phase rows and 17 distinct member agent IDs across the run; the final roster marks all 17 doneWorkflow journal 17 spawns and 17 results, with matching member transcripts T3 parent projection 1 workflow wrapper stored as kind: subagent, withmodel: nulland no phase/member structureT3 workflow child 1 timeline item: the 34,984-character workflow JavaScript, represented as an agent-authored user_messageMember conversations in T3 0 nested subagent records beneath that wrapper Same-thread ordinary agents Three earlier native Agentchildren have 71, 81, and 51 timeline items, including tool calls and assistant messagesThat last comparison matters: this conversation successfully ingested ordinary native subagents, while the workflow's members remained absent. “One agent” refers to this workflow's single wrapper, not to the whole conversation; the parent has four native subagent rows in total. All 17 final roster entries identify
claude-haiku-5-5, independently corroborated by their member transcripts.The final workflow projection contains
prompt,title,model,status,progress,result, identity fields, and timestamps. It has no workflow name, phases, member roster, member usage, or member model selection. A current read through T3's own thread API returns the same script-only child withitemCount: 1andrunCount: 0, so reopening the completed history has not recovered the roster.Two useful details for regression coverage
The roster is present from launch, but member identity arrives incrementally. The first structured progress frame contains eight member rows and only one assigned
agentId; the next frame contains all eight IDs. A parser needs to retain indexed entries while IDs are pending and enrich them when the IDs arrive, without duplicating the members.Build and Review also overlap: each review starts when its paired builder finishes. The first review began at 16:08:28Z while other builders continued until 16:14:09Z; integration started after all reviews ended. This is a productive pipeline case where one current-phase label loses useful information.
The final structured frame contains all 17 member IDs and terminal states. The raw provider log marks none of the 101 structured frames as truncated. This gives a bounded, completed-run case for checking initial identity, phase/member updates, terminal state, and historical persistence.
Where the metadata is lost in this build
The October 10 execution tag resolves to
3e94afa7c26734f525a7d8a9cdb47896e8618cf8. In that source:task_started/task_progresshandling admits the workflow through the ordinary subagent path and retains the progress description, without parsingworkflow_nameorworkflow_progress.- Child creation seeds the child with the native task prompt, which is the workflow source here.
OrchestrationV2Subagenthas no workflow structure, and the shared runtime mapping supplieskind: "subagent",workflowName: null, andphases: [].
The currently installed 2935 bundle identifies its commit as
98beed1a226c. Its bundled adapter and runtime also lack that workflow parsing/mapping. This is source and bundle verification, not a new live reproduction on 2935.One additional discrepancy to keep separate
Claude delivered a successful native
task_notificationwith a nonempty completion summary. The wrapper projection retains that result, but its child still has only the launch script. The adapter source appears to have a path that should append a child assistant summary. The retained provider log contains native protocol events, not the canonical event stream, so these records do not establish whether that child-summary event was emitted, rejected, or otherwise lost. I would keep this as a separate trace question rather than attribute it to the roster parser without evidence.This run completed and woke the parent for follow-up work. It supports #15448's observability/persistence problem, not a reproduction of #16697's silent mid-run closure.
What would make this case pass
The same input should retain a workflow identity with three phases and 17 stable members, preserve the member records as IDs arrive, and expose their states and models during the run and in completed history. Opening the workflow should lead to its roster, with the source available as a separate script view. The three ordinary
Agentchildren should continue to behave as they do now.The useful regression input here is the captured roster sequence, including the initial missing IDs and final all-done roster. I have prepared an allowlisted, pseudonymized 103-frame fixture locally: launch, all 101 roster updates, and completion. It omits the 375 progress-only messages, private prompts, source code, paths, and identifying descriptions. Timestamps are relative and some required SDK fields are omitted, so it needs a small harness before adapter replay. It has not been replayed against either proposed fix, and no raw conversation or provider log is included in this comment.
The one-row UI behavior is my observation; the counts above come from read-only log, journal, transcript, database, and T3 API checks. No new visual replay was performed during this investigation.
Note
Grok responding on behalf of Julius.
Fixed by #12598, which just landed on
main. Lineage now expands Claude workflows into phase sections with member rows, status, counts and elapsed time, and each member opens its own chat with its prompt and final answer. It'll ship in the next nightly. If anything still looks off after updating, please open a new issue with the details.
Before submitting
Area
apps/server, packages/contracts or packages/shared, apps/web
Steps to reproduce
Workflowwith at least two phases. For example:phase('Warmup')with 2 agents, thenphase('Sleep')with 44 agents that each runsleep 300.Expected behavior
Lineage shows the run as a workflow, not as a subagent. #14871 lists "Subagents and background work you can see" as a V2 feature, and V1 showed this through 0.0.45 (#5219). At minimum:
Actual behavior
V2 treats a workflow exactly like an
Agentsubagent:export const meta = {...}) shown as a message "Sent by another agent", and nothing else, for the entire run. A real subagent's child thread fills with its tool calls.task_progress.description(for exampleRollout: rollout, areas, tickets, indexes · opus high). At the end the result isDynamic workflow "…" completed.From one of my threads (
orchestration_v2_projection_subagents, read from a copy ofstatev2.sqlite; titles anonymized):local_workflowlocal_workflowlocal_workflowlocal_workflowlocal_agentlocal_agentlocal_agentlocal_agentlocal_agentAll nine rows look the same in Lineage. The four workflows ran 5 to 44 agents each, and none of those agents is visible anywhere in T3 Code.
Cause
Claude Code sends everything needed. Each workflow's
task_startedcarriestask_type: "local_workflow"andworkflow_name. Eachtask_progresscarries aworkflow_progressarray, one entry per phase and per agent:{"type":"system","subtype":"task_progress","task_id":"wmca5k5dl","description":"Apply: features: … · opus high", "workflow_progress":[ {"type":"workflow_phase","index":1,"title":"Apply"}, {"type":"workflow_phase","index":2,"title":"Rollout"}, {"type":"workflow_agent","index":1,"label":"feature-switches.md · opus high","phaseIndex":1,"phaseTitle":"Apply", "agentId":"a4073ffb714011049","model":"claude-opus-5-5[1m]","state":"progress","attempt":1, "lastToolName":"Bash","tokens":100021,"toolCalls":11,"promptPreview":"…"} ]}A workflow's member agents never send messages on the parent's stream; their transcripts are written to
~/.claude/projects/<project>/<session>/subagents/workflows/wf_<id>/. Soworkflow_progressis the only live view T3 gets, and V2 drops it:task_started: the handlerClaudeAdapterV2.ts#L5847-L5895only separateslocal_bash. Every othertask_type,local_workflowincluded, becomes an ordinary subagent node.task_progress: the handlerClaudeAdapterV2.ts#L5900-L5918keeps onlydescription.workflow_progressisn't read anywhere inapps/server.OrchestrationV2Subagenthas no field for a task kind, workflow name, phases or member agents.projectedSubagentsToRuntimesetsworkflowName: null,phases: [],runHandles: null,phaseIndex: nullandparentAgentId: nullfor every row. ThoseRuntimeSubagentfields are left over from V1 and never filled.The V1 path (#5219:
ClaudeAdapter,ProviderRuntimeIngestion, workflow folding insubagentRuntime) was removed in #2829 with no V2 replacement. The "view workflow script" endpoint (orchestration-v2/workflowScriptQuery.ts) also survives, but no client gets ascriptPathto call it with.Possible fix
workflowfield toOrchestrationV2Subagent: name, phases, and per-agent label, phase, state, model, last tool and counts. Leave outpromptPreviewto keep WebSocket payloads small.ClaudeAdapterV2, marklocal_workflowtasks as workflows and update that field fromtask_progress.workflow_progress.Impact
Major degradation or frequent failure
People who use workflows can't tell from T3 Code whether a long run (often 30+ agents over hours) is progressing, stuck or dead. I ended up writing a terminal script that reads the workflow's journal to see what it was doing.
Version or commit
T3 Code (Nightly) desktop, V2, built from
mainat 65731f9Environment
macOS 27, Claude Code 2.1.289, Claude Opus 5.5 driving
Workflowruns with Opus and Sonnet 5.5 member agents.Related
OrchestrationV2Subagenthas no slot for the data,projectedSubagentsToRuntimewritesnull, and thetask_progresshandler drops the rest. This issue covers workflow structure.cancelled, and nothing says which agents had finished.Workaround
Read
~/.claude/projects/<project>/<session>/subagents/workflows/wf_<id>/journal.jsonland theagent-*.jsonltranscripts next to it from a terminal.Investigated with Claude Code (Claude Opus 5.5) in T3 Code.