Files
bahdotsh 31cf534625 feat(tui): live step view with streamed stdout/stderr
The execution tab used to stare back at you with a single progress
bar while the executor did... whatever it was doing. You found out
what happened when the whole workflow was over, and only then. Not
great for a tool whose entire point is running workflows locally.

The reason was structural. `execute_workflow` handed back one
`Vec<JobResult>` at the very end, `StepStatus` only had terminal
variants (Success/Failure/Skipped — no Running, no Pending), and
the container runtimes read logs *after* the container exited via
`wait_container` followed by `logs().collect()`. The TUI was doing
the best it could with a request/response it couldn't observe.

So: thread an optional event stream through the executor. New
`ExecutionEvent` enum (`JobStarted`, `StepStarted`, `StepLogChunk`,
`StepCompleted`, `JobCompleted`, plus `Execution*` bookends) emitted
via an `EventSink` that bundles an unbounded sender with a per-run
`JobId` allocator so matrix combinations get distinct ids. The TUI
spawns a receiver, drains it each tick, and paints the detail view
live — spinner on the running step, ✓/✖/⊘/○ everywhere else,
per-step duration, and a bottom pane that tails the step's log
buffer as bytes come off stdout/stderr. `f` re-engages auto-follow;
any manual j/k pins you where you are.

Log streaming required rewriting all four runtimes. Emulation,
secure_emulation and podman move to `tokio::process::Command` with
piped stdio drained via `AsyncBufReadExt::lines()` in parallel
tasks. Docker is the fun one: `logs(follow: true)` now runs
concurrently with `wait_container` under `tokio::join!`, instead of
the old sequential wait-then-read. Chunks mirror into the sink
*and* accumulate into the final `ContainerOutput` so both the live
view and the post-mortem `StepResult.output` stay truthful.

CLI path is unchanged — `ExecutionConfig.event_sink` defaults to
`None` and the whole thing short-circuits. Reusable workflows and
composite actions collapse to a single synthetic step in the event
stream for v1; nested forwarding is a separate problem for another
day.

`StepStatus` gained `Pending` and `Running` variants with
`#[non_exhaustive]` so we can add more without breaking callers
again. Per-step log buffer is capped at 10k lines with a truncation
marker; the Step Output pane pins to the bottom so newest-first is
always visible without caring about scroll state.

Two new integration tests assert event ordering (JobStarted before
any of its StepStarted, StepCompleted before JobCompleted, pairs
match by (job_id, step_idx)) and that \`StepLogChunk\` arrives
*between* \`StepStarted\` and \`StepCompleted\`. The sink-absent
path stays behaviorally identical.

Please don't put back the "collect everything then show it" pattern
when the executor grows new capabilities. The streaming seams are
there now; use them.
2026-04-20 00:14:33 +05:30
..

wrkflw-runtime

Runtime abstractions for executing steps in containers or emulation.

  • Container management primitives used by the executor
  • Emulation mode helpers (run on host without containers)

Example

// This crate is primarily consumed by `wrkflw-executor`.
// Prefer using the executor API instead of calling runtime directly.