v0.6.0 — Workflow visibility
Summary
Workflow visibility. When a dynamic workflow fans out into a fleet of subagents, the monitor no longer goes dark. A new "Workflows" section at the top of the session detail view surfaces each run as a per-phase agent tree, and the menu-bar count gently pulses purple while a workflow is running.
The problem
Dynamic workflows (e.g. ultracode orchestration) spawn agents under a directory the app never read. SubagentLoader scans the flat subagents/agent-*.jsonl, but workflow agents live one level deeper at subagents/workflows/{wf_id}/agent-*.jsonl. A workflow could spin up a dozen agents and burn hundreds of thousands of tokens with zero signal in the UI.
What you get
- Workflows section at the top of an expanded session — above the flat agent list, so the phase structure stays prominent.
- Phase tree per workflow: each phase shows its agents; a completed phase gets a check, an in-progress one a spinner. Running workflows are expanded by default; completed ones collapse to a one-line summary you can click to expand.
- Per-agent detail — click any workflow agent to expand its recent messages, modified files, and tool breakdown, exactly like flat subagents (the nested transcript path is handled transparently).
- Live progress — a progress bar (completed agents / total) and running token total, refreshed on the same 5s cadence as the rest of the app.
- Menu-bar signal — the active-session count tints purple and pulses while any workflow is running. A
/goalin progress still takes priority (blue), so an explicit goal is never masked.
How "running" is detected
The run-state file workflows/{wf_id}.json is written only at completion (startTime + durationMs == timestamp), so its status field can't tell you a workflow is currently running. Detection therefore reads two signals:
| Signal | Source | Meaning |
|---|---|---|
| Unfinished agent | journal.jsonl — a started event with no matching result |
Primary "still running" signal |
| Definitive done | workflows/{wf_id}.json status: "completed" |
Overrides everything — the file only exists at completion |
| Activity fallback | agent-dir mtime within 60s | Bridges brief gaps between journal writes |
This ordering closes a subtle bug where a just-finished workflow could otherwise stay pinned as a spinner forever (its directory mtime never changes again once complete).
Under the hood
- New:
WorkflowInfo/WorkflowPhase/WorkflowRunStatusmodels,WorkflowJournal(pure running-detection parser),WorkflowLoader(disk loader with per-wf_idmtime caching), and theWorkflowSectionview. SubagentScanwas extracted fromSubagentLoaderso the flat and workflow loaders share one identical JSONL scanner (DRY) — flat-subagent behavior is unchanged.SubagentInfo,AgentRow, andAgentDetailVieware reused for workflow-phase agents;AgentRow/loadAgentDetailgained an optionalworkflowIdto resolve the nested transcript path, with consistent cache keys across read, write, and the live-refresh loop.
Tests
WorkflowJournalTests (running-detection: complete / unfinished / malformed-line / empty / result-before-started / dedup) and WorkflowLoaderTests (workflow-name parsing, phase mapping) cover the pure logic.
Note: XCTest requires the full Xcode SDK; on Command Line Tools–only machines
swift buildandbash scripts/build-app.shsucceed, whileswift testruns on a machine with Xcode. The disk-loading path was additionally verified against real on-disk workflow transcripts.
Version
Info.plist: 0.5.0 → 0.6.0, build 8 → 9.
Full Changelog: v0.5.0...v0.6.0