Skip to content

feat(workers-dev): introducing dev tui for easier dev experience - #311

Merged
sergiofilhowz merged 2 commits into
mainfrom
feat/dev-tui
Jun 23, 2026
Merged

feat(workers-dev): introducing dev tui for easier dev experience#311
sergiofilhowz merged 2 commits into
mainfrom
feat/dev-tui

Conversation

@sergiofilhowz

@sergiofilhowz sergiofilhowz commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Introducing a Dev TUI to be used to improve quality of life while developing workers on this repository

image

Summary by CodeRabbit

Release Notes

  • New Features
    • Introduced workers-dev, a new local development tool for managing worker processes.
    • Interactive terminal dashboard for real-time monitoring and controlling workers with status, logs, and lifecycle management.
    • CLI commands: status (view worker status), logs (stream/follow logs), start/stop/restart (manage workers).
    • Automatic worker discovery and dependency resolution.
    • Configuration file and environment variable support with color output control.

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment Jun 23, 2026 6:05pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8801022-802c-49e9-8436-2a5e4ec3ff79

📥 Commits

Reviewing files that changed from the base of the PR and between 0e793a7 and 06f97f1.

⛔ Files ignored due to path filters (1)
  • harness/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • workers-dev/src/commands/mod.rs
  • workers-dev/src/config.rs
  • workers-dev/src/graph.rs
  • workers-dev/src/main.rs
  • workers-dev/src/orchestrator.rs
  • workers-dev/src/status.rs
  • workers-dev/src/tui/mod.rs
  • workers-dev/src/tui/theme.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • workers-dev/src/main.rs
  • workers-dev/src/tui/theme.rs
  • workers-dev/src/status.rs
  • workers-dev/src/tui/mod.rs
  • workers-dev/src/graph.rs
  • workers-dev/src/commands/mod.rs
  • workers-dev/src/config.rs
  • workers-dev/src/orchestrator.rs

📝 Walkthrough

Walkthrough

Introduces a complete workers-dev Rust binary crate: it discovers workers from iii.worker.yaml files, builds a dependency graph for topological start/stop/restart ordering, spawns cargo run processes, streams their logs into ring buffers, polls an external iii engine for connectivity, and presents a ratatui TUI dashboard alongside a CLI with up/start/stop/restart/logs/status subcommands.

Changes

workers-dev crate

Layer / File(s) Summary
Project setup and docs
workers-dev/Cargo.toml, workers-dev/README.md
Cargo manifest with all dependencies (Tokio, ratatui, crossterm, clap, serde, etc.) and a complete README covering prerequisites, install, CLI usage, TUI keybindings, color conventions, config schema, and troubleshooting.
Core data models: color, worker discovery, and runtime state
workers-dev/src/color.rs, workers-dev/src/discover.rs, workers-dev/src/runtime.rs
ColorMode with stdout/TUI predicates; WorkerGroup, SpawnKind, WorkerSpec discovered from iii.worker.yaml; RingBuffer, ProcState, WorkerRuntime, and SharedRuntimes for in-process worker state.
Runtime configuration loading
workers-dev/src/config.rs
Config::load merges a YAML file, CLI flags, env vars, and candidate directory search to resolve repo root, discover workers, parse engine WebSocket URL into host/port, and select color mode; includes parse_engine_url unit tests.
Worker dependency graph and topo ordering
workers-dev/src/graph.rs
WorkerGraph loads per-worker YAML, builds an adjacency list, and provides Kahn-style topological start/stop ordering with cycle detection, reverse-dependent traversal, restart closure, and dependency-closure expansion; includes fixture-based unit tests.
Log normalization and rendering
workers-dev/src/logs.rs
normalize_log_line strips ANSI and handles \r overwrites; classify_log_line/LogKind categorize severity; log_line_to_ratatui renders width-aware ratatui spans with tracing timestamp splitting; print_colored_line outputs crossterm-colored terminal lines.
Engine status fetching and process orchestration
workers-dev/src/status.rs, workers-dev/src/orchestrator.rs
fetch_engine_workers shells out to iii engine::workers::list and deserializes JSON; Orchestrator spawns cargo run workers, streams stdout/stderr into ring buffers, polls engine connectivity via wait_connected, aggregates WorkerViews from engine + local state, and handles stop/restart via kill + topo ordering.
CLI entry point and command handlers
workers-dev/src/main.rs, workers-dev/src/commands/mod.rs
Cli/Command structs via clap drive Config+Orchestrator construction and dispatch; run_status, run_start, run_stop, run_restart, run_logs (tail + follow-broadcast), and run_up implement all subcommands with optional stop-on-exit cleanup.
TUI dashboard: theme, layout, and keyboard handling
workers-dev/src/tui/theme.rs, workers-dev/src/tui/mod.rs
theme.rs provides Style helpers keyed on WorkerGroup and status strings; tui/mod.rs implements the run event loop with raw-mode terminal, draw_ui (worker table, logs pane, overlay), handle_dashboard_key bindings, and background task spawners for orchestrator actions.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant main as main / CLI
  participant Config
  participant Orchestrator
  participant WorkerGraph
  participant CargoProcess as cargo run (worker)
  participant EngineIII as iii engine

  User->>main: workers-dev up (or subcommand)
  main->>Config: Config::load (YAML + flags + env)
  Config-->>main: Config { worker_specs, engine_url, ... }
  main->>Orchestrator: Orchestrator::new(config)
  Orchestrator->>WorkerGraph: WorkerGraph::load(repo_root, workers)
  WorkerGraph-->>Orchestrator: adjacency list + topo order
  main->>Orchestrator: start_harness_stack() then start_all_managed()
  loop each worker in topo order
    Orchestrator->>CargoProcess: spawn cargo run --manifest-path ...
    Orchestrator-->>Orchestrator: read_stream (stdout/stderr → RingBuffer + broadcast)
    Orchestrator->>EngineIII: fetch_engine_workers (poll until connected)
    EngineIII-->>Orchestrator: worker status: connected
  end
  main->>main: tui::run(orchestrator) OR commands::run_*(orchestrator)
  loop TUI poll_interval_ms
    main->>Orchestrator: worker_views()
    Orchestrator->>EngineIII: fetch_engine_workers
    EngineIII-->>Orchestrator: statuses
    Orchestrator-->>main: Vec<WorkerView>
    User->>main: keypress (s/S/r/l/q)
    main->>Orchestrator: start_workers / stop_workers / restart_worker
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Suggested reviewers

  • andersonleal

Poem

🐇 Hop, hop, a new crate appears!
Workers discovered, their YAML made clear,
Topo-sorted starts, ring buffers aglow,
A TUI dashboard puts on a show.
cargo run spawned, the engine pinged tight—
workers-dev leaps into the light!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'introducing dev tui for easier dev experience' accurately reflects the main feature added: a terminal UI for workers development management.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dev-tui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 25 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (1)
workers-dev/PLAN.md (1)

9-12: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add language identifiers to fenced code blocks to satisfy markdown linting.

The unnamed fenced blocks flagged at Line 9, Line 58, Line 98, Line 174, and Line 198 should use a language tag (for example, text, bash, or rust) to avoid MD040 warnings.

Also applies to: 58-76, 98-103, 174-183, 198-223

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workers-dev/PLAN.md` around lines 9 - 12, The PLAN.md file contains fenced
code blocks without language identifiers at lines 9, 58, 98, 174, and 198, which
violates markdown linting rules (MD040). To fix this, add an appropriate
language identifier to each unnamed fenced code block by specifying a language
tag after the opening triple backticks (such as text, bash, rust, etc. depending
on the content). For example, change triple backticks followed by a newline to
triple backticks followed by the language name, then the content. Apply this fix
to all five flagged locations to satisfy the markdown linter.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@workers-dev/PLAN.md`:
- Around line 19-27: The plan document inconsistently uses both `harness-dev`
and `workers-dev` as binary names, creating ambiguity with the actual crate and
README examples. Replace all occurrences of `harness-dev` with `workers-dev`
throughout the entire document, paying special attention to the sections
starting at line 19, lines 59-79, and lines 157-176 where this inconsistency is
most prevalent, to establish a single consistent binary name throughout the
plan.

In `@workers-dev/src/commands/mod.rs`:
- Around line 76-83: The error handler for RecvError::Closed in the worker log
subscription loop only exits when the worker status is "stopped", but does not
account for other terminal states like "crashed" or absent workers, which causes
infinite re-subscribe loops. Modify the condition in the any() iterator check to
also break on "crashed" status or when the worker is not found in the proc list,
ensuring that any terminal state of the worker causes the loop to exit instead
of retrying indefinitely.

In `@workers-dev/src/config.rs`:
- Around line 175-188: The parse_engine_url function fails when parsing
WebSocket URLs with paths or trailing slashes because split_once(':') captures
port_str as "49134/" instead of just "49134". After stripping the protocol in
the stripped variable, split on '/' first to isolate just the host:port portion
before then splitting on ':' to extract the port number, ensuring port_str only
contains the numeric port value when calling .parse() to convert it to u16.
- Around line 97-107: The precedence for the `release` field (line 97) and
`stop_on_exit` field (line 107) is reversed—they currently prioritize the file
configuration over CLI flags. Swap the precedence order so that CLI flag values
take priority over the file configuration values. For both fields, the CLI value
should be checked first, and only if it's not explicitly set should the file
configuration value be used as a fallback.

In `@workers-dev/src/graph.rs`:
- Around line 67-113: The topo_start_order method needs to validate and
deduplicate the subset parameter before building the topological graph.
Currently, the HashMap-based approach implicitly deduplicates entries, but the
final length comparison against subset.len() can produce false "dependency
cycle" errors when duplicates exist in the input, and unknown worker names are
silently accepted as valid nodes. Add validation at the start of the method to
ensure all names in subset correspond to valid workers in the graph, then
deduplicate the subset and use the deduplicated collection for the remaining
algorithm and the final length check to ensure correctness.

In `@workers-dev/src/logs.rs`:
- Around line 85-99: The code unconditionally adds a space separator after the
timestamp (the Span::raw(" ") on line 92), which causes a visual overflow when
the timestamp already consumes the full max_width due to truncation. Modify the
logic to only add the separator space when the timestamp does not consume the
full width. Check if the character count of the timestamp (calculated as
ts.chars().count().min(max_width)) is less than max_width before pushing the
Span::raw(" ") to the spans vector.

In `@workers-dev/src/main.rs`:
- Around line 128-130: The orchestrator.stop_workers call within the
stop_on_exit conditional block is currently using let _ to silently ignore any
errors that occur during shutdown. Instead of discarding the result with let _,
handle the error returned from orchestrator.stop_workers by either logging it or
printing it to the user so that shutdown failures are visible rather than
silently dropped. This ensures users are aware if workers fail to stop properly.

In `@workers-dev/src/orchestrator.rs`:
- Around line 155-159: The wait_for_exit task spawned in the tokio::spawn call
is bound only to the worker name (name_wait), not to a specific instance,
causing old waiters from previous restarts to attach to and monitor newer child
processes. This creates accumulating concurrent watchers for the same worker. To
fix this, add an instance-specific identifier (such as a generation ID, restart
counter, or unique instance handle) that gets passed to the wait_for_exit
function alongside name_wait and runtimes_wait. This ensures each waiter is tied
to its specific process instance and prevents old waiters from attaching to new
processes after a restart. Apply the same fix to the similar code at lines
366-409.

In `@workers-dev/src/status.rs`:
- Around line 54-76: The current implementation calls child.wait() before
reading from stdout and stderr, which creates a deadlock risk if the child
process output exceeds pipe buffer capacity—the process will block trying to
write while the parent waits for it to exit. Replace the manual spawn, wait, and
read pattern with Command::output() which safely handles reading pipes while the
process runs, or add a timeout to the wait operation to prevent indefinite hangs
in the polling loop that expects responses every ~500ms.

In `@workers-dev/src/tui/mod.rs`:
- Around line 183-190: The KeyCode::Char('l') handler blocks the entire
dashboard event loop by awaiting crate::commands::run_logs inline, preventing
input and refresh handling during log tailing. Move the log execution to run
asynchronously without blocking the event loop (consider spawning it as a
background task or using a non-blocking approach), and update or remove the
misleading "Ctrl+C to return" prompt text since the TUI is currently
unresponsive during log execution. This will allow the dashboard to remain
responsive while logs run.
- Around line 45-127: The run function has early return paths from error
propagation (? operators) within the while loop that bypass the terminal
restoration code at the end. Restructure the run function to guarantee that
disable_raw_mode() and the LeaveAlternateScreen execute are always called, even
when errors occur. Consider wrapping the main event loop logic in a separate
function or using a guard pattern that automatically restores terminal state
when exiting scope, ensuring cleanup happens regardless of which ? operator
returns an error.
- Around line 497-502: The overlay block unconditionally applies the
overlay_bg_style() regardless of whether colors are disabled. Modify the
.style(overlay_bg_style()) call to conditionally apply the overlay background
style only when colors are enabled. Check the appropriate color setting or flag
that respects the --color never option, and pass either overlay_bg_style() or a
neutral/default style based on that condition to the .style() method call.

---

Nitpick comments:
In `@workers-dev/PLAN.md`:
- Around line 9-12: The PLAN.md file contains fenced code blocks without
language identifiers at lines 9, 58, 98, 174, and 198, which violates markdown
linting rules (MD040). To fix this, add an appropriate language identifier to
each unnamed fenced code block by specifying a language tag after the opening
triple backticks (such as text, bash, rust, etc. depending on the content). For
example, change triple backticks followed by a newline to triple backticks
followed by the language name, then the content. Apply this fix to all five
flagged locations to satisfy the markdown linter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a51494e-bad7-4453-b55c-e0355ca01b3a

📥 Commits

Reviewing files that changed from the base of the PR and between 167ef95 and 0e793a7.

⛔ Files ignored due to path filters (1)
  • workers-dev/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • workers-dev/Cargo.toml
  • workers-dev/PLAN.md
  • workers-dev/README.md
  • workers-dev/src/color.rs
  • workers-dev/src/commands/mod.rs
  • workers-dev/src/config.rs
  • workers-dev/src/discover.rs
  • workers-dev/src/graph.rs
  • workers-dev/src/logs.rs
  • workers-dev/src/main.rs
  • workers-dev/src/orchestrator.rs
  • workers-dev/src/runtime.rs
  • workers-dev/src/status.rs
  • workers-dev/src/tui/mod.rs
  • workers-dev/src/tui/theme.rs

Comment thread workers-dev/PLAN.md Outdated
Comment thread workers-dev/src/commands/mod.rs
Comment thread workers-dev/src/config.rs Outdated
Comment thread workers-dev/src/config.rs
Comment thread workers-dev/src/graph.rs
Comment thread workers-dev/src/orchestrator.rs
Comment thread workers-dev/src/status.rs Outdated
Comment thread workers-dev/src/tui/mod.rs Outdated
Comment on lines +183 to +190
KeyCode::Char('l') => {
if let Some(name) = worker_name {
disable_raw_mode()?;
execute!(io::stdout(), LeaveAlternateScreen)?;
println!("Following logs for {name} (Ctrl+C to return)…\n");
let _ = crate::commands::run_logs(orchestrator.clone(), name, true, 20).await;
enable_raw_mode()?;
execute!(io::stdout(), EnterAlternateScreen)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

l blocks the dashboard event loop until logs end.

Line 188 awaits follow mode inline, so the TUI stops handling input/refresh while log tailing runs. The prompt text (“Ctrl+C to return”) is also misleading in this flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workers-dev/src/tui/mod.rs` around lines 183 - 190, The KeyCode::Char('l')
handler blocks the entire dashboard event loop by awaiting
crate::commands::run_logs inline, preventing input and refresh handling during
log tailing. Move the log execution to run asynchronously without blocking the
event loop (consider spawning it as a background task or using a non-blocking
approach), and update or remove the misleading "Ctrl+C to return" prompt text
since the TUI is currently unresponsive during log execution. This will allow
the dashboard to remain responsive while logs run.

Comment thread workers-dev/src/tui/mod.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants