How Interactive Run Management Works in the No-Mistakes Terminal UI (TUI)
The No-Mistakes TUI translates keyboard inputs into daemon RPC calls through a Bubble Tea state machine, rendering pipeline runs as scrollable lists while handling aborts, reruns, and findings inspection via event-driven updates.
The No-Mistakes CLI ships a full-screen Terminal User Interface (TUI) that eliminates context switching when managing pipeline executions. Built on the Bubble Tea framework, this interactive layer in kunchenguid/no-mistakes serves as a thin front-end for the daemon’s run-management API. Every keystroke—from selecting runs to aborting pipelines—triggers RPC calls through a strict state machine that maintains consistency with the daemon’s internal safety checks.
Architecture Overview
The TUI architecture separates presentation from business logic through a clean client-server model. On startup, the app model in internal/tui/app.go initializes a daemon.Client and subscribes to the Run event stream via internal/tui/events.go.
The core model maintains three critical pieces of state:
runs []runInfo– A live snapshot of all pipeline runs received from the daemon.cursor int– The index of the currently highlighted run in the list.state enum– The current view state (statePipeline,stateRunDetail,stateAbortConfirm, etc.).
This state machine ensures that keyboard shortcuts only trigger actions valid for the current context, preventing illegal operations like aborting a completed run.
Subscribing to Daemon Events
Real-time synchronization begins in internal/tui/events.go, where the UI subscribes to the daemon’s Run stream. The daemon pushes updates—EventRunCreated, EventRunUpdated, EventRunFinished, and EventRunAborted—as they occur.
When the TUI receives an EventRunUpdated message, the model’s runs slice refreshes immediately. This event-driven approach eliminates polling overhead while ensuring the user always views the latest status, branch names, and execution summaries. The subscription logic handles connection drops gracefully, attempting reconnection without blocking the main UI thread.
Navigating the Run List
The primary interface, implemented in internal/tui/pipeline.go, renders the runs slice as a scrollable list. Users navigate using j and k keys, which invoke the moveCursor() method to adjust the cursor index.
Selection highlighting applies the primary-action style defined in internal/tui/review.go. When the user presses Enter or Space on a selected run, the state machine transitions to stateRunDetail, triggering a view switch in app.go that reveals detailed panes for logs, findings, and CI status.
Interacting with Individual Runs
Once inside a run’s detail view, the TUI exposes four distinct interaction surfaces, each mapped to specific daemon RPCs.
Viewing Details and Findings
The Review pane, rendered by internal/tui/review.go, displays static analysis findings as a scrollable list. The renderFindingsWithSelection() function accepts the raw JSON findings, current cursor position, and a selected map[string]bool to produce a viewport-aware string.
Users toggle individual findings between [ ] and [x] using the Space key. The rendering logic respects viewport boundaries, ensuring the cursor remains centered while scrolling through large result sets with j and k.
Aborting and Rerunning Pipelines
Actionable commands live in internal/tui/commands.go. When a run status indicates Running, pressing a triggers the abortRun() method:
func (m *model) abortRun() tea.Cmd {
if m.state != stateRunDetail || m.selectedRun == nil {
return nil
}
return func() tea.Msg {
err := m.client.AbortRun(context.Background(), m.selectedRun.ID)
if err != nil {
return errMsg{err}
}
return runAbortedMsg{runID: m.selectedRun.ID}
}
}
The UI immediately transitions to an Aborting state and waits for the daemon’s EventRunAborted confirmation. Similarly, pressing r on a failed run invokes client.StartRun() with the original branch and intent parameters, then returns the view to the pipeline list to display the new execution.
Branch Sync and CI Monitoring
The Branch-Sync pane (internal/tui/branch_sync.go) spawns short-lived daemon commands to execute axi sync without leaving the TUI. Output streams back into the run’s custody fields in real-time.
For CI feedback, internal/tui/ci.go uses renderLogTail() to stream remote job logs. The helper applies color coding to PASS/FAIL lines and auto-scrolls the viewport as new lines arrive, matching the behavior of local log tailing.
The Update Loop and State Machine
All user input flows through the Update(msg tea.Msg) method in internal/tui/app.go. This function pattern-matches against the current state and key pressed, returning a tea.Cmd that either performs an asynchronous RPC or schedules a UI refresh.
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "j":
m.moveCursor(1)
case "k":
m.moveCursor(-1)
case "a":
return m, m.abortRun()
case "r":
return m, m.rerun()
case "enter":
return m.enterRunDetail()
}
}
return m, nil
}
The UI never mutates daemon state directly. All modifications—aborts, reruns, syncs—traverse the public API defined in internal/daemon/manager.go, ensuring the same validation logic protects both CLI and interactive operations.
Summary
- Event-Driven Architecture: The TUI subscribes to daemon event streams via
internal/tui/events.go, receiving real-time updates without polling. - State Machine Safety:
internal/tui/app.goenforces valid state transitions, preventing context-inappropriate actions like aborting finished runs. - Viewport-Aware Rendering:
internal/tui/review.gohandles cursor-centric scrolling and finding selection throughrenderFindingsWithSelection(). - RPC-Only Mutations: All interactive commands in
internal/tui/commands.goinvoke daemon RPCs (AbortRun,StartRun), maintaining safety guarantees. - Graceful Shutdown: Pressing
Ctrl-Ctriggers aShutdownRPC and waits for active runs to finish before exiting.
Frequently Asked Questions
How does the TUI handle real-time updates from the daemon?
The TUI maintains a persistent gRPC stream subscription in internal/tui/events.go. As the daemon emits EventRunUpdated messages, the Bubble Tea framework routes these to the Update() method, which refreshes the runs slice and triggers a re-render. This push-based model ensures the run list reflects current status within milliseconds of daemon state changes.
Can users interact with runs while they are executing?
Yes. The TUI supports live interaction with running pipelines. Users can press a to send an AbortRun RPC or monitor CI logs in real-time via internal/tui/ci.go. The state machine in app.go tracks transitional states (like Aborting) to prevent duplicate commands while waiting for daemon confirmation.
What happens if the daemon connection drops during an interactive session?
The event subscription logic in internal/tui/events.go implements reconnection logic. If the stream breaks, the TUI attempts to re-establish the connection without blocking the main update loop. The local model retains the last known state of all runs, allowing users to continue viewing historical data even during temporary disconnections.
How are keyboard shortcuts mapped to specific run actions?
Key mappings reside in internal/tui/commands.go. The Update() method in app.go dispatches to command functions based on the current state enum. For example, the a key only triggers abortRun() when m.state == stateRunDetail and m.selectedRun is non-nil, ensuring shortcuts are context-sensitive and cannot invoke invalid operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →