Files
Gokul 499a4e53a3 fix(ui): show real timestamps for remaining bare TUI log pushes (#119)
* fix(ui): timestamp the log pushes that #116 missed

PR #116 claimed to fix the ??:??:?? timestamps for "all TUI log
messages". It turns out "all" meant "most": four bare
self.logs.push() sites survived — the diff-filter event rotation,
the copy-curl recipe lines, the "Dispatching ..." line, and the
dispatch outcome lines. All of them still rendered ??:??:?? in
the Execution and Logs tabs.

The renderer derives the timestamp column *only* from a
[HH:MM:SS] prefix on the line itself, so any push that skips
add_timestamped_log() is silently unparseable. Route the four
stragglers through add_timestamped_log(). Since that helper
already trims the buffer, the hand-rolled trim_logs_to_cap()
calls next to each push (and the `drained` counter that existed
only to gate one of them) are now dead weight. Gone.

The new tests don't re-feed literal strings to the parser —
that proves nothing about the call sites. They call the actual
methods and run the actual appended lines through the actual
parser, which needed nothing more than a pub(crate) on
process_log_entry. The "Dispatching ..." site is the one
exception: it tokio::spawns a real dispatch, so it's covered by
pattern parity, not a test. I can live with that.

* fix(ui): privatize the log buffer and fix change detection at the cap

The previous commits converted the worst offenders, but a dozen
call sites were still pushing raw strings into `app.logs`. Those
lines render as ??:??:?? in the Execution/Logs tabs (no [HH:MM:SS]
prefix to parse) and skip the buffer-cap trim until some unrelated
code path happens to run it.

Make the field private so the compiler does the policing: every
mutation now has to go through add_log / add_timestamped_log,
which timestamp, trim, and notify in one place. The hand-rolled
timestamp formatting and the defensive trim calls sprinkled
around the codebase all go away.

It turns out that leaning on the choke point exposed a second
bug: the background log processor detects changes by comparing
logs.len(). Once the buffer hits LOG_BUFFER_CAP, every push
drains one old entry, the length is pinned at 5000 forever, and
the Logs tab silently stops updating. Same story for search:
typing in the search box never changed the count, so results only
refreshed when an unrelated log line happened to arrive.

Replace the length with a revision counter bumped on every
log/search/filter change. The system-log count can stay a plain
len() — that buffer is unbounded, for better or worse.

* formatted
2026-07-03 10:46:18 +05:30
..

Wrkflw Crates

This directory contains the Rust crates that make up the wrkflw workspace.

Crate Structure

Crate Purpose
wrkflw CLI binary and library entry point
executor Workflow execution engine (Docker, Podman, emulation, secure emulation); ${{ }} expression evaluator; artifact/cache/inter-job-output stores
parser Workflow file parsing and JSON Schema validation
evaluator Structural evaluation of workflow files
validators Validation rules for jobs, steps, triggers, matrix
runtime Container management and emulation runtime
trigger-filter Parses on: blocks and matches them against simulated event context and changed-file sets
watcher File watcher with trigger-aware re-execution for wrkflw watch
ui Terminal user interface (ratatui-based)
models Shared data structures (ValidationResult, GitLab models)
matrix Matrix expansion (include, exclude, fail-fast)
secrets Secrets management with multiple providers and AES-256-GCM encryption
github GitHub API integration (list/trigger workflows)
gitlab GitLab API integration (trigger pipelines)
logging Thread-safe in-memory logging for TUI/CLI
utils Workflow file detection and fd redirection helpers

Building

# Build everything
cargo build

# Build a specific crate
cargo build -p wrkflw-executor

Testing

# Run all tests
cargo test

# Run tests for a specific crate
cargo test -p wrkflw-executor

Each crate has its own Cargo.toml with dependencies managed through workspace inheritance in the root Cargo.toml.