Workflow viewer
Integrating Claude Code dynamic-workflow JSONL into kolu — surfacing fan-out runs on the tile chrome and an Inspector run-list with live timelines.
Analysis of a live do-wf run in drishti/.worktrees/hm, generalized to the
real case: a session holds many workflow runs at once — some running, some
finished. Two-phase proposal, revised after hickey + lowy.
Multiplicity is the normal case (verified)
| Session | Workflow runs | Fan-out |
|---|---|---|
agency/…/cc-workflow | 2 runs (wf_84c9119d, wf_e6f94ccf) | 4 agents, 14 agents |
kolu/…/modest-runner | 3 runs | — |
drishti/…/hm (live) | 1 run, in flight | 6 started / 5 done |
So the UI target is a list of runs per session, each with its own status and timeline — not a single badge value.
What a dynamic workflow writes, and where status lives
~/.claude/projects/<encoded-cwd>/<session>.jsonl # main transcript — launch markers + completion notifications
~/.claude/projects/<encoded-cwd>/<session>/
├── workflows/scripts/<name>-<runId>.js # persisted script — meta.name + meta.phases[]
└── subagents/workflows/
├── wf_84c9119d-127/ journal.jsonl + agent-*.jsonl # run 1
└── wf_e6f94ccf-a1f/ journal.jsonl + agent-*.jsonl # run 2 …
The three discrepancies vs. kolu’s reader at the time (all verified; since fixed by #1124 ):
| kolu assumes | On disk |
|---|---|
One journal at <session>/workflows/<runId>.json | Many at <session>/subagents/workflows/<runId>/journal.jsonl |
Snapshot object {workflowName,status,agentCount} | Event log: {type:"started",agentId} / {type:"result",agentId,result:{status,…}} |
| Name + status live in the journal | name only in the script meta; workflow-level status is NOT in the journal at all |
Proposal — two phases, each shippable alone
Phase 1 — repoint the reader (name + fan-out count appear) shipped
Shipped in
#1124 (merged 2026-06-02; hardening follow-ups in
#1130 and
#1157 ). Bug-fix-shaped, almost entirely
in core.ts; no UI change. They already consume
ClaudeWorkflow {name,status,agents} (agentDisplay.ts:42-44,
TerminalMeta.tsx:78-80, MetadataInspector.tsx:194-205):
- Point the journal path at
subagents/workflows/<runId>/journal.jsonl. - Read the event log:
agents= distinctstartedagentIds;namefrom the script meta. - Status comes from the transcript, not the journal. A run is
runningiff its taskId is still inoutstandingBackgroundTasks();completed/failedonce its terminal<task-notification>is seen. No new “all-agents-done” heuristic.
Phase 2 — a list of workflow runs with live timelines, in the Inspector tab
Rendered prototype — a session’s runs, newest first; running auto-expanded, finished collapsed to a summary you can open:
Wiring (shaped by the review pass below):
- On-demand fetch, returning a list. A new RPC
loadWorkflowRuns(session)enumeratessubagents/workflows/wf_*/and returnsWorkflowRun[]— each{runId, name, status, startedEpoch, endedEpoch, agents[]}. Mirrors the existingexportTranscriptHtml/loadClaudeCodeTranscriptpull pattern. - Live metadata stays scalar.
ClaudeCodeInfostill carries only the single running-run summary (plus optionally a small “N runs” count) — the full list is never on the firehose. - Per-agent transcript reuses the canonical seam.
agent-<id>.jsonlis a normal transcript →parseClaudeCodeJsonl()→TranscriptEvent[]→ existingkolu-transcript-htmlrenderer. No parallel event schema.
What the hickey + lowy pass changed
Cut line
Phase 1 shipped in
#1124 (2026-06-02): a small correction that
made the already-built surface work for the first time, showing the active run.
Phase 2 — the multi-run list + timelines in the Inspector — is what remains, and
is independently reviewable. When ready, drive it with /do.