[FEATURE] /workflows: attribute nested workflow() runs to the parent phase active at the call site

Open 💬 1 comment Opened Jul 7, 2026 by mpolatcan

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet (closest are #63982 — progress-visibility consolidation — and #24537 — hierarchy dashboard; neither covers nested workflow() phase attribution)
  • [x] This is a single feature request (not multiple features)

Problem Statement

When a dynamic workflow (Workflow tool) calls a nested child workflow via workflow({ scriptPath }), the child's agents render in /workflows as a separate top-level ▸ <child meta.name> group appended after the parent's phase list — never inside the parent phase that dispatched it.

The one-level nesting capability enables a great composition pattern: an orchestrator workflow owns the pipeline phases and delegates the work of a phase to a self-contained child workflow (e.g. a plugin-owned quality gate). Functionally this works well. Visually it breaks down:

  • The parent declares meta.phases — the panel shows the ordered phase list.
  • A phase whose work runs in a child workflow shows 0 agents of its own, so its row looks skipped/empty even while its work is running.
  • The actual agents appear in a ▸ child-name group at the bottom, numbered after the last declared phase, with no visual link to the phase that spawned it.

With 2–3 nested gates, the panel reads as "phases 6–8 did nothing, and three unrelated runs happened at the end", when in reality phases 6–8 each delegated to one of those runs.

Proposed Solution

Attribute the nested run's group to the parent phase active at the workflow() call site, so the child group renders nested under (or inside) that phase row — or allow explicit opt-in, e.g. workflow(ref, args, { phase: 'Coverage Gate' }).

Panel today:

Phases
 ✔ Build              1/1
 2 Coverage Gate      0/0   <- looks skipped while child runs
 3 Publish
 4 ▸ coverage-check   2/2   <- the real phase-2 work, orphaned at the bottom

Desired (either form):

 ✔ Build              1/1
 ✔ Coverage Gate      ▸ coverage-check 2/2
 3 Publish

Alternative Solutions

  • opts.phase on agent() only attributes within a single run; a child workflow's agents cannot join a parent phase group today, so there is no script-side workaround.
  • Cosmetic mitigation only: renaming parent phase titles to match child meta.names so a reader can pair the phase row with the trailing group manually.

Priority

Medium - Would be very helpful

Feature Category

Interactive mode (TUI)

Use Case Example

  1. An orchestrator pipeline workflow declares phases: Build → Coverage Gate → Publish.
  2. The Coverage Gate phase runs a plugin-owned child workflow:
export const meta = {
  name: 'pipeline',
  description: 'demo',
  phases: [{ title: 'Build' }, { title: 'Coverage Gate' }, { title: 'Publish' }],
}
phase('Build')
await agent('build the project')
phase('Coverage Gate')
// child renders as a separate trailing "▸ coverage-check" group;
// the "Coverage Gate" phase row stays at 0 agents.
const res = await workflow({ scriptPath: './coverage-check.workflow.js' })
phase('Publish')
await agent('open the PR')
  1. While the gate runs, the user watching /workflows sees "Coverage Gate 0/0" (looks skipped) and an unrelated-looking ▸ coverage-check group at the bottom.
  2. With phase attribution, the panel would show the gate's agents under the phase that owns them — the pipeline reads top-to-bottom as it actually executes.

Additional Context

  • Related to the broader progress-visibility consolidation asked in #63982, but this one is specific: parent-phase attribution for nested workflow() runs.
  • Claude Code 2.1.202, macOS (darwin 25.x).

As you can see code review and security review is a nested workflow but their phase looks empty:

<img width="778" height="379" alt="Image" src="https://github.com/user-attachments/assets/d632ad13-bb3d-4f3e-96ad-25e6deda9571" />

Additional detail: the child group also flattens the child's own phases.

Beyond the attribution problem above, there's a second layer of lost structure. Inside the ▸ <child-name> group, the child workflow's own meta.phases / phase() grouping isn't rendered at all — the group lists the child's agents flat by label, with no phase headers between them, even though the child declared and executed distinct phases.

A child that declares e.g. phases: [Gather, Analyze, Reduce] and whose agents span all three renders as:

▸ child-name   5 agents
  gather-ctx     ✔
  worker-a       ✔     ← child's "Analyze" phase (4 agents)
  worker-b       ▸
  worker-c       ✔
  reduce         ✔     ← child's "Reduce" phase

You can't tell where one child phase ends and the next begins — the child's phase axis is collapsed to a flat agent list. The phase() calls still execute (the grouping is in the child's own journal), it just never surfaces in /workflows.

Ideally the fix restores phase structure at both levels: (1) attribute the child group to the parent phase active at the call site (the original ask), and (2) render the child's own meta.phases as sub-groups within its group instead of a flat agent list.

<img width="783" height="379" alt="Image" src="https://github.com/user-attachments/assets/7535d60d-f5ae-40fe-b390-308d9bbe2468" />

This should be like this:

<img width="1392" height="478" alt="Image" src="https://github.com/user-attachments/assets/c656483c-8d49-433f-a381-b2d2fa666ae4" />

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗