feat: Phase 2a — Agent Focus Mode (The Bigger IDE) - #104
Conversation
The Factorio zoom-in: click a stage card on the factory floor and the
dashboard transitions to a full-screen Agent Focus Mode showing:
- Live terminal output (polled every 2s via single /focus endpoint)
- Token usage stats (input, output, cache read/write)
- Context % with color-coded fill bar (green/amber/red)
- Git diff of agent's worktree changes (syntax highlighted)
- Workflow progress (step X/Y)
- Prompt input bar to send instructions to the agent
- "Agent not running — Start Agent" CTA when agent is stopped
Backend: single GET /v1/agents/{ws}/{agent}/focus endpoint combines
terminal capture, usage scanning, diff resolution, and context %
extraction in one response. Eliminates polling race conditions.
Design: follows DESIGN.md tokens throughout. Smart auto-scroll on
terminal (stays put if user scrolled up). Mobile layout with swipeable
tabs (<768px). Keyboard: Escape returns to factory floor.
Also fixes dispatch panel json.status bug (was checking json.status
instead of json.ok).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR adds an "Agent Focus Mode": a fullscreen dashboard UI driven by appState.view="focus" that polls a new server endpoint for agent terminal, status, usage, and git-diff data; it includes enter/exit controls, prompt/send and start-agent actions, stale-response guarding, and a matching backend GET route to aggregate focus data. Changes
Sequence DiagramsequenceDiagram
participant User
participant Dashboard
participant Server
participant Host
User->>Dashboard: Click stage card
Dashboard->>Dashboard: enterFocusMode(workspace, agent)
Dashboard->>Dashboard: Hide factory/drawer, show focus view, start poll loop
loop every 2s
Dashboard->>Server: GET /v1/agents/{workspace}/{agent}/focus?lines=200
Server->>Host: Check tmux session & capture pane
Server->>Host: Read worktree git diff & --stat
Server->>Host: Query usage stats + compute context pct
Host-->>Server: Return session, capture, diff, stats, context pct
Server-->>Dashboard: Return aggregated focus JSON
Dashboard->>Dashboard: If focused agent unchanged render terminal, stats, diff, progress
end
User->>Dashboard: Click focus-back or press Escape
Dashboard->>Dashboard: exitFocusMode(), clear poll, restore UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
dashboard/test.html (1)
484-533: Exercise the real focus-mode code instead of a hand-rolled model.These suites only mutate
testAppStateand reimplement the helpers locally, so they still pass ifenterFocusMode,exitFocusMode, the DOM toggling, or the real stale-response guard indashboard/app.jsbreaks. Please drive the production functions against fixtures/mockedfetchinstead of copying the logic here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dashboard/test.html` around lines 484 - 533, Replace the hand-rolled state manipulation and helper re-implementations with calls to the real production functions: import/require the focus-mode helpers from dashboard/app.js (enterFocusMode, exitFocusMode and the stale-response guard handler) and the real formatTokens and escapeHtml implementations, then drive them against fixtures by mocking fetch and DOM as needed; specifically, change the tests that currently set testAppState and local formatTokens/escapeHtml to instead call enterFocusMode(...) and assert DOM/class changes and app state, call exitFocusMode(...) and assert restoration, and simulate responses through the actual stale-response guard by mocking fetch results for agent A then agent B and asserting which responses are applied—use the real symbols enterFocusMode, exitFocusMode, formatTokens, escapeHtml and the production stale-response check so tests fail if production logic breaks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@dashboard/app.js`:
- Around line 265-268: The card div currently only handles mouse clicks so
keyboard users can't open focus mode; make the card a keyboard-activatable
trigger by adding tabindex="0" (or replace the div with a semantic button) and
attach a keydown handler that calls enterFocusMode(primary.workspace,
primary.agent) when Enter (key === "Enter") or Space (key === " " or "Spacebar")
is pressed, preventing default for Space to avoid scrolling; keep the existing
click listener (card.addEventListener("click", ...)) and ensure the handler
reuses the same closure/arguments as the click (the same primary.workspace and
primary.agent).
- Line 762: The statRow output is being concatenated into
focusProgress.innerHTML with unescaped values (e.g., run.workflowName), which
allows HTML injection; update the code so statRow either returns escaped HTML
or, better, build rows using DOM APIs or set text nodes (e.g., use
element.textContent or createElement/appendChild) instead of string
concatenation into focusProgress.innerHTML; locate the statRow function and all
call sites that feed into focusProgress.innerHTML (including the similar calls
around the other occurrences mentioned) and ensure every dynamic value
(workflowName and any other run.* fields) is properly escaped or inserted via
textContent to eliminate the XSS risk.
- Around line 639-641: The polling currently uses setInterval which allows
overlapping fetches in pollFocus (and the other polling block around the 665-673
area); change the logic so only one request is in flight at a time by either (A)
replacing setInterval with a "schedule next" setTimeout called after the fetch
completes inside pollFocus and the other poll function, or (B) add an inFlight
boolean/AbortController or a monotonically-increasing requestId/sequence token
that is captured by each fetch and validated before applying results so
out-of-order older responses are ignored; update pollFocus and the corresponding
polling function (and remove/replace focusPollId usage accordingly) to implement
one of these patterns and ensure responses are only applied if the requestId
matches or if inFlight is false.
- Around line 695-705: The click handler currently treats any completed fetch to
"/v1/actions/up" as success; update the fetch promise chain to inspect the HTTP
response and parse the JSON (like other calls): call fetch("/v1/actions/up",
...) then check res.ok (or parse res.json() and validate the returned success
field) before setting startBtn.textContent = "Started"; on non-ok responses or
parsed errors set textContent = "Failed" and re-enable startBtn
(startBtn.disabled = false), and handle network errors in catch similarly;
locate this logic around the startBtn click listener and use existing
appState.focusAgent (fa.agent, fa.workspace) references to keep the same request
payload.
In `@dashboard/style.css`:
- Around line 501-502: In the CSS rule that currently contains "white-space:
pre-wrap; word-break: break-word;" in dashboard/style.css, replace the
deprecated property "word-break: break-word" with "overflow-wrap: anywhere" so
the block becomes "white-space: pre-wrap;" plus "overflow-wrap: anywhere"; keep
the rest of the rule unchanged to preserve behavior consistent with other uses
of overflow-wrap in the codebase.
In `@src/cli/serve.rs`:
- Around line 593-597: The user-controlled lines value parsed from query (via
get("lines") ... unwrap_or(200)) must be clamped before being passed into
agent_focus_data to avoid huge tmux captures; after computing let lines: u32 =
... unwrap_or(200) clamp it to a sane maximum (and optionally a minimum) using
lines = lines.min(MAX_LINES) or using std::cmp::min / saturating methods (e.g.
ensure 1..=2000) and then call agent_focus_data(workspace, agent_name, lines,
targets) with the clamped value.
- Around line 1060-1085: Resolve the agent from the target config before using
its name to build focus data: locate the agent via
target.config.agents.iter().find(|a| a.name == agent_name) and return
TuttiError::AgentNotFound if missing; then derive the runtime from that resolved
agent (use agent.runtime.as_deref().or_else(||
target.config.default_runtime.as_deref()) or similar) and pass this
resolved_runtime into snapshot::extract_context_pct_for_runtime instead of
defaulting unconditionally to "claude-code"; also apply the same
resolution/validation logic where context_pct is computed later (the other block
using runtime/context_pct) so tmux/worktree probing only happens for declared
agents.
---
Nitpick comments:
In `@dashboard/test.html`:
- Around line 484-533: Replace the hand-rolled state manipulation and helper
re-implementations with calls to the real production functions: import/require
the focus-mode helpers from dashboard/app.js (enterFocusMode, exitFocusMode and
the stale-response guard handler) and the real formatTokens and escapeHtml
implementations, then drive them against fixtures by mocking fetch and DOM as
needed; specifically, change the tests that currently set testAppState and local
formatTokens/escapeHtml to instead call enterFocusMode(...) and assert DOM/class
changes and app state, call exitFocusMode(...) and assert restoration, and
simulate responses through the actual stale-response guard by mocking fetch
results for agent A then agent B and asserting which responses are applied—use
the real symbols enterFocusMode, exitFocusMode, formatTokens, escapeHtml and the
production stale-response check so tests fail if production logic breaks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f0fdbb83-84e0-484d-bf26-25913ddea575
📒 Files selected for processing (6)
dashboard/app.jsdashboard/index.htmldashboard/style.cssdashboard/test.htmlsrc/cli/serve.rssrc/cli/snapshot.rs
The default 30s grace period was too short — the implementer agent was being marked idle before it had time to read the codebase and start writing code, causing premature "no commit" failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make stage cards keyboard-activatable (Enter/Space keydown handler) - Prevent overlapping focus polls from racing (focusPolling guard flag) - Check /up response status before marking as successful - Escape statRow values via escapeHtml to prevent innerHTML injection - Replace deprecated word-break: break-word with overflow-wrap: anywhere - Clamp lines query param to [1, 10000] before pane capture - Validate agent exists in workspace config before building focus data Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: Phase 2a — Agent Focus Mode with live terminal, usage, and diff
The Factorio zoom-in: click a stage card on the factory floor and the
dashboard transitions to a full-screen Agent Focus Mode showing:
- Live terminal output (polled every 2s via single /focus endpoint)
- Token usage stats (input, output, cache read/write)
- Context % with color-coded fill bar (green/amber/red)
- Git diff of agent's worktree changes (syntax highlighted)
- Workflow progress (step X/Y)
- Prompt input bar to send instructions to the agent
- "Agent not running — Start Agent" CTA when agent is stopped
Backend: single GET /v1/agents/{ws}/{agent}/focus endpoint combines
terminal capture, usage scanning, diff resolution, and context %
extraction in one response. Eliminates polling race conditions.
Design: follows DESIGN.md tokens throughout. Smart auto-scroll on
terminal (stays put if user scrolled up). Mobile layout with swipeable
tabs (<768px). Keyboard: Escape returns to factory floor.
Also fixes dispatch panel json.status bug (was checking json.status
instead of json.ok).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: increase implement_code startup_grace to 120s
The default 30s grace period was too short — the implementer agent
was being marked idle before it had time to read the codebase and
start writing code, causing premature "no commit" failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address 7 CodeRabbit review comments on PR #104
- Make stage cards keyboard-activatable (Enter/Space keydown handler)
- Prevent overlapping focus polls from racing (focusPolling guard flag)
- Check /up response status before marking as successful
- Escape statRow values via escapeHtml to prevent innerHTML injection
- Replace deprecated word-break: break-word with overflow-wrap: anywhere
- Clamp lines query param to [1, 10000] before pane capture
- Validate agent exists in workspace config before building focus data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: instant focus mode render from cached health data
Pre-populate agent name, status, and session from appState.agents
on click instead of waiting for the first /focus poll. Reduces
perceived latency from ~2s to near-instant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: split usage scan from focus endpoint — 11s → 35ms
scan_workspace_usage() does expensive filesystem I/O (~11s on large
projects). Move it to a separate slow poll (?usage=1, every 30s)
so the main focus endpoint (terminal + diff + context) responds in
~35ms for instant terminal updates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update project documentation for v0.6.0
- README: dashboard section updated from "Planned" to "Built" with
Agent Focus Mode details, roadmap checkboxes updated
- CHANGELOG: added Unreleased section with Phase 2 features
- DESIGN.md: added Agent Focus Mode design specifications
- Cargo.toml: bumped version to 0.6.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: operator failure-debugging loop (closes #79)
Documents the repeatable workflow from failed run to root cause:
run inspection → step timeline → agent check → log analysis.
Covers provider, policy, and tool failure patterns with concrete
CLI examples. Links dashboard Focus Mode for visual debugging.
Closes #79
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove accidentally committed dirs
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
/focusendpoint:GET /v1/agents/{ws}/{agent}/focusreturns terminal + usage + diff + context % in one response. Eliminates polling race conditions vs 3 separate endpoints./v1/actions/sendwithauto_up/v1/actions/uparia-liveon terminaljson.statusinstead ofjson.okDesign docs
~/.gstack/projects/nutthouse-tutti/adamnutt-main-design-20260321-185022.mdTest plan
cargo test— 327 tests passcargo clippy+cargo fmtcleandashboard/test.html— all JS tests passtt serve, click stage card → focus view with terminal🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests
Chores