Skip to content

feat: Phase 2a — Agent Focus Mode (The Bigger IDE) - #104

Merged
nutt-adam merged 3 commits into
mainfrom
feat/phase-2-agent-focus
Mar 21, 2026
Merged

feat: Phase 2a — Agent Focus Mode (The Bigger IDE)#104
nutt-adam merged 3 commits into
mainfrom
feat/phase-2-agent-focus

Conversation

@nutt-adam

@nutt-adam nutt-adam commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Agent Focus Mode: click any stage card → full-screen drill-down with live terminal, usage stats, git diff, and prompt input. The Factorio zoom-in moment.
  • Single /focus endpoint: GET /v1/agents/{ws}/{agent}/focus returns terminal + usage + diff + context % in one response. Eliminates polling race conditions vs 3 separate endpoints.
  • Smart auto-scroll: terminal stays at bottom unless user scrolls up to read
  • Context % fill bar: color-coded (green ≤70%, amber 70-90%, red >90%) extracted from terminal pane output
  • Prompt send: type instructions to the agent, sent via existing /v1/actions/send with auto_up
  • "Start Agent" CTA: shown when agent is stopped, boots it via /v1/actions/up
  • Mobile: full-width terminal, swipeable sidebar tabs, fixed prompt bar
  • Accessibility: ARIA landmarks, keyboard nav (Escape exits), aria-live on terminal
  • Bug fix: dispatch panel checked json.status instead of json.ok

Design docs

  • Office hours: ~/.gstack/projects/nutthouse-tutti/adamnutt-main-design-20260321-185022.md
  • Eng review: CLEARED (0 unresolved, 0 critical gaps)
  • Design review: 9/10 (CLEARED)
  • Codex review: 12 findings addressed

Test plan

  • cargo test — 327 tests pass
  • cargo clippy + cargo fmt clean
  • Open dashboard/test.html — all JS tests pass
  • Visual: tt serve, click stage card → focus view with terminal
  • Visual: type prompt → agent receives it
  • Visual: back button → factory floor intact
  • Visual: mobile viewport → swipeable tabs, fixed prompt bar
  • Visual: stopped agent → "Start Agent" CTA

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added "Agent Focus Mode" — full-screen view for live agent monitoring (terminal, status, usage, diff, progress) with send/start controls and mobile/desktop layout.
  • Improvements

    • Auto-refreshing focus feed with stale-response protection for consistent updates.
    • API support to surface aggregated focus data to the UI.
  • Bug Fixes

    • Aligned dispatch success handling with updated response shape.
  • Tests

    • Added unit tests for token formatting, HTML escaping, focus-state transitions, and stale-response guarding.
  • Chores

    • Increased startup grace period for an agent step in workflow config.

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>
@coderabbitai

coderabbitai Bot commented Mar 21, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@nutt-adam has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1f809fe7-fadb-47b9-9f73-145351d54384

📥 Commits

Reviewing files that changed from the base of the PR and between 862db1e and f29b03b.

📒 Files selected for processing (3)
  • dashboard/app.js
  • dashboard/style.css
  • src/cli/serve.rs
📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Dashboard Focus Mode Logic
dashboard/app.js
Replaced stage-card click to call enterFocusMode(primary.workspace, primary.agent). Added focus-mode state (appState.view = "focus", appState.focusAgent, focusPollId), enterFocusMode()/exitFocusMode(), 2s pollFocus() with stale-flight guard, focus rendering (terminal, status, usage, diff, progress), Start Agent (POST /v1/actions/up) and Send Prompt (POST /v1/actions/send) flows, and adjusted dispatch success check from json.status === "ok" to json.ok.
Focus Mode UI Structure
dashboard/index.html
Added #focus-view overlay markup: top bar (#focus-back, #focus-agent-name, #focus-status, #focus-meta), main terminal/log (#focus-terminal), sidebar containers for stats/diff/progress (#focus-stats, #focus-diff, #focus-progress), and a prompt bar (#focus-prompt-input, #focus-send). Initially hidden.
Focus Mode Styling
dashboard/style.css
Inserted full CSS block for focus overlay: fixed #focus-view, .focus-grid layout, top bar, terminal styles, sidebar diff/usage/progress styles, control states (disabled/sending), and mobile @media (max-width:767px) adjustments.
Focus Mode Testing
dashboard/test.html
Added unit tests for formatTokens(n), escapeHtml(str), focus state transitions (enter/exit), and stale-response guard behavior in polling.
Agent Focus API Endpoint
src/cli/serve.rs
Added GET route matcher for /v1/agents/{workspace}/{agent}/focus (optional lines param). Implemented agent_focus_data(...) aggregating tmux session status & capture, context pct (snapshot::extract_context_pct_for_runtime), 7-day usage stats, and git diff text + parsed --stat summary. Added parse_diff_stat() helper and wired route to return aggregated JSON.
Snapshot Visibility
src/cli/snapshot.rs
Changed extract_context_pct_for_runtime(...) visibility from private to pub(crate) for use in focus data aggregation.
Workflow Config
tutti.toml
Added startup_grace_secs = 120 to the implement_code prompt step in sdlc-auto workflow.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped into focus, bright and sly,

Watching logs roll by and bytes that fly,
Polling heart at two-second beats,
Diff and stats in tidy seats,
One agent's story, soft and spry.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes comprehensive Summary and Design docs sections with clear features and review status, but the required Versioning section is completely missing (no version bump documented, no SemVer choice made, no CHANGELOG.md entry noted). Complete the Versioning section by selecting a SemVer choice (likely MINOR for new capability), updating Cargo.toml, adding CHANGELOG.md entry, and documenting version impact.
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically identifies the main feature being introduced: Agent Focus Mode as Phase 2a, with the metaphor 'The Bigger IDE' conveying the scope of this full-screen drill-down capability.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase-2-agent-focus

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 and usage tips.

@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: 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 testAppState and reimplement the helpers locally, so they still pass if enterFocusMode, exitFocusMode, the DOM toggling, or the real stale-response guard in dashboard/app.js breaks. Please drive the production functions against fixtures/mocked fetch instead 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

📥 Commits

Reviewing files that changed from the base of the PR and between 75bbff2 and c4f16af.

📒 Files selected for processing (6)
  • dashboard/app.js
  • dashboard/index.html
  • dashboard/style.css
  • dashboard/test.html
  • src/cli/serve.rs
  • src/cli/snapshot.rs

Comment thread dashboard/app.js Outdated
Comment thread dashboard/app.js
Comment thread dashboard/app.js
Comment thread dashboard/app.js
Comment thread dashboard/style.css Outdated
Comment thread src/cli/serve.rs
Comment thread src/cli/serve.rs
nutt-adam and others added 2 commits March 21, 2026 19:50
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>
@nutt-adam
nutt-adam merged commit 9c6e1be into main Mar 21, 2026
11 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 21, 2026
11 tasks
nutt-adam added a commit that referenced this pull request Mar 21, 2026
* 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>
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.

1 participant