Skip to content

auto-run-goal-driven: 34 tasks across 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-01-Goal-Driven-Core-Engine, 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-02-Engine-Integration +3 more#1045

Open
pedramamini wants to merge 5 commits into
rcfrom
auto-run-goal-driven

Conversation

@pedramamini
Copy link
Copy Markdown
Collaborator

@pedramamini pedramamini commented May 26, 2026

Auto Run Summary

Documents processed:

  • 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-01-Goal-Driven-Core-Engine
  • 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-02-Engine-Integration
  • 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-03-Modal-UI-Tabs
  • 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-04-Progress-Display
  • 2026-05-25-Goal-Driven-Auto-Run-Mode/Phase-05-Stats-Help-Polish

Total tasks completed: 34

Changes

  • MAESTRO: Stats, help docs, and hardening for Goal-Driven Auto Run (Phase 05)
  • MAESTRO: Render goal progress in Auto Run UI (desktop + web/mobile + History)
  • MAESTRO: Add Goal-Driven tabs + GoalConfigPanel to Auto Run modal
  • MAESTRO: Wire Goal-Driven Auto Run loop into the batch engine
  • MAESTRO: Add Goal-Driven Auto Run core engine (types, marker parser, exit evaluator)
  • Show bookmarked agents in web side panel; default Adaptive Mode on; composer + CodeFence fixes
  • Update Custom AI Commands subheading copy
  • fix(shiki): allow WASM in CSP + auto-detect untagged code fences
  • fix(left-bar): reduce collapsed pill cap from 25 to 20 per row
  • Merge pull request Add Olive Nights theme #1038 from felipeggv/feat/olive-nights-theme
  • Add Olive Nights theme
  • Remove Claude Max Plan Usage panel from Usage Dashboard
  • CHANGES

  • Merge pull request refactor(FileExplorerPanel): decompose 2,920-line monolith into directory module #1035 from RunMaestro/cue-polish
  • Merge branch 'rc' of https://github.com/RunMaestro/Maestro into cue-polish
  • fix: keep file explorer auto-refresh failures recoverable
  • refactor: move BatchRunner "Agent thinking" pill to header, normalize Shiki imports
  • feat: BatchRunner task-selection toggle, file-explorer multi-delete, Shiki fix
  • fix(FileExplorerPanel): address CodeRabbit review feedback
  • fix(FileExplorerPanel): resolve all ESLint warnings in decomposed module
  • refactor(FileExplorerPanel): decompose 2,920-line monolith into directory module
  • Merge pull request refactor(input-area): split InputArea into focused modules #1034 from RunMaestro/cue-polish
  • fix(input-area): address review edge cases
  • fix(shiki): normalize highlight.js dynamic import
  • refactor(input-area): split coordinator into focused modules
  • fix(shiki): resolve TS lint error in languageDetect — highlight.js uses export= not ESM default
  • feat: FilePreview CodeMirror editor, Cue time.once + notify, session recovery, autorun per-task/document
  • feat: file-explorer multi-select, AutoRun gating polish, New Agent group picker
  • CHANGES

  • feat: About settings tab, absolute-path file open, tab kind icons
  • feat: Cmd+Shift+[/] cycles internal tabs in queue/tab-switcher modals
  • Merge pull request refactor(QuickActionsModal): decompose 2,528-line monolith into directory module #1029 from RunMaestro/cue-polish
  • fix(QuickActionsModal): address post-decomposition review findings
  • refactor(QuickActionsModal): decompose 2,528-line monolith into directory module
  • fix(tests): decouple claude usage startup test from build artifact
  • CHANGES

  • Merge PR Fix obvious RC Sentry field crashes #1011: Fix obvious RC Sentry field crashes
  • CHANGES

  • feat: annotator text bg + dashboard sort highlight + copilot SSH paging
  • feat(files-panel): auto-expand target folder on create + move
  • test(session-list): update drag-and-drop tests for compact ungroup zone
  • CHANGES - Added CLI discovery watchdog to republish missing/stale server info file 🔭 - Watchdog auto-starts at app launch for stronger maestro-cli reliability 🛡️ - Cleanly stops watchdog on quit to avoid post-exit file rewrites 🧹 - Watchdog can bootstrap the CLI server when none is active 🔌 - Ungrouped Agents header now hides when empty for cleaner session list 🧩 - New compact ungroup drop-zone appears when all sessions are grouped 🎯 - “New Group” button stays visible even without ungrouped sessions ➕ - Removed Quick Actions “Edit Prompt” entries for a slimmer command list ✂️ - Added comprehensive tests covering watchdog rewrite, noop, startup, and stop ✅

  • feat(annotator): text labels + assorted UX polish
  • fix(claude-code): disable background tasks by default across every spawn path
  • feat(files-panel): "New File" context menu option on folder rows
  • feat(files-panel): drag-to-move files and folders between folders
  • chore(drag-drop): nits on top of Drag and drop improvements #903
  • Merge PR Drag and drop improvements #903: Drag and drop improvements
  • feat(dashboard): agent card age badge + sort-by-created
  • CHANGES


This PR was automatically created by Maestro Auto Run.

Summary by CodeRabbit

  • New Features

    • Added Goal-Driven Auto Run mode as an alternative to task-based execution
    • Goal progress displays as percentage with iteration count and optional rationale
    • Configurable exit criteria: completion, deadlock, max iterations, or stall detection
    • Sessions remember goal configuration for quick re-running
  • Tests

    • Comprehensive test coverage for goal-driven execution logic and UI components
  • Documentation

    • Updated help modal with Goal-Driven Mode guide
    • Added goal-driven agent prompt template with marker syntax

Review Change Stack

…exit evaluator)

Phase 01 of Goal-Driven Auto Run mode — pure, dependency-free logic with tests.

- src/shared/goalDriven/types.ts: GoalRunConfig, GoalMarkers,
  GoalIterationRecord, GoalExitReason, GoalExitDecision, STALL_THRESHOLD (=3,
  mirrors MAX_CONSECUTIVE_NO_CHANGES in useBatchRunner.ts).
- src/shared/goalDriven/goalMarkers.ts: parseGoalMarkers() — whitespace-tolerant
  regex parsing of <!-- maestro:progress|goal-complete|deadlock --> markers
  (last occurrence wins; clamp 0-100; progress 100 implies complete).
- src/shared/goalDriven/goalExitEvaluator.ts: evaluateGoalExit() — priority
  order completion > deadlock > max-iterations > stall > continue; defensive
  missing-progress normalization for stall detection.
- Tests: 43 cases across marker parser, exit evaluator, and a narrative
  simulation that drives the control loop 0->45->70->100 and the stall/deadlock
  scenarios. Full suite green (29,592 passed).
Phase 02 of Goal-Driven Auto Run: makes a goal run executable end-to-end.

- types: add goalConfig? to BatchRunConfig (the goal-mode discriminator) and
  goal* progress fields to BatchRunState; seed DEFAULT_BATCH_STATE defaults.
  Thread goal fields through UpdateProgressPayload + the UPDATE_PROGRESS reducer
  case + the useBatchBroadcast debounce diff so goal state reaches the store/web.
- prompt: add src/prompts/autorun-goal.md (synopsis-first; pursue {{GOAL}},
  one iteration of real progress then EXIT, emit progress/goal-complete/deadlock
  markers matching parseGoalMarkers). Register autorun-goal in promptDefinitions.
  Add {{GOAL}} / {{GOAL_EXIT_CRITERIA}} template variables.
- engine: add useGoalRunner — mirrors useBatchRunner's lifecycle (time tracking,
  power inhibit, stats, history, broadcast, flush-state kill guard) but drives a
  goal loop: build prompt, spawn via onSpawnAgent (SSH preserved), parse markers,
  evaluateGoalExit, stop on completed/deadlock/max-iterations/stalled/user-stop.
- routing: useBatchProcessor instantiates both runners and routes startBatchRun
  on config.goalConfig; public signature unchanged. stop/kill already generic.
- tests: useGoalRunner.test.ts (8 tests) covering completion, stall, deadlock,
  max-iterations, lifecycle, mid-run stop, template substitution, disabled gate.
Phase 03 of Goal-Driven Auto Run — the visible half of the feature.

- GoalConfigPanel.tsx: Goal + Exit Criteria textareas and an iteration-limit
  control (Infinite/Limit segmented toggle + numeric input; Infinite => null,
  mirrors the loop infinite/max affordance).
- BatchRunnerModal.tsx: Spec-Driven / Goal-Driven ToggleButtonGroup beneath the
  Playbook row with a contrast description. Goal mode swaps DocumentsPanel for
  GoalConfigPanel, keeps the worktree section, hides the fresh-context selector +
  agent prompt. handleGo branches to build a goalConfig BatchRunConfig (empty
  documents, no taskSelectionMode); Go gates on a non-empty goal; header task
  badge becomes a Goal-Driven pill. Layout cleanups: header retitled to 'Auto
  Run', fresh-context block stands solo above the prompt, its description moved
  above the toggle and bumped 10px -> text-xs.
- Session: persist autoRunGoalConfig + autoRunDriveMode (named autoRunDriveMode,
  not autoRunMode, to avoid colliding with the existing edit/preview field) via
  updateSessionWith, debounced + flushed on launch/close; seeded on mount.
- Tests: new Goal-Driven Mode suite (8 cases); tightened Go-button queries to
  exact 'Go' since the new Goal-Driven tab matched the /Go/ regex.

Config flow verified intact onGo -> handleStartBatchRun -> startBatchRun ->
startGoalRun (no field stripping, no documents.length gate). Full suite green
(29,608 passed); lint, eslint, prettier clean.
…History)

Phase 04 of Goal-Driven Auto Run. Every progress surface that spoke in
"X of Y tasks" now renders the agent's self-reported goal percent,
iteration, and rationale when goalMode is true; spec/document runs are
unchanged.

- AutoRunBottomPanel: optional goal prop -> "Goal: N%" (accent->success),
  "iteration N", and truncated rationale; AutoRun.tsx derives + passes it
  and tracks the goal fields in its memo comparator.
- RightPanel + ThinkingStatusPill: drive the active-run bar/readout from
  goalProgress with a Goal label and rationale on hover.
- History: per-iteration summary now "Goal progress: N% - <rationale>";
  final entry keeps exit reason/detail + correct success flag.
- Web bridge: thread goalMode/goalProgress/goalRationale/goalIteration
  through the broadcast serializer and all five AutoRunState type defs;
  AutoRunIndicator/AutoRunInline show goal percent + iteration.
- Tests: goal-mode coverage for AutoRunBottomPanel and the mobile
  AutoRunIndicator; updated useGoalRunner final-summary helper.
…ase 05)

- Record goal runs in stats behind a 'Goal: <80 chars>' documentPath via new
  shared goalRunLabel helper; surface a 'Goal' tag + percent in LongestAutoRunsTable
- Add a Goal-Driven Mode section to AutoRunnerHelpModal (config inputs, progress
  marker, four exit conditions)
- Harden parseGoalMarkers (tolerate trailing %, fenced/backtick markers, smart
  punctuation); add GOAL_RUN_HARD_ITERATION_CAP safety net for infinite runs;
  trim exitCriteria on launch
- Add runner tests (marker-less -> stalled, oscillating -> hard cap), goalRunLabel
  unit tests, and an end-to-end integration test driving a goal run to completion
  and a stall through useBatchProcessor
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

This PR introduces Goal-Driven Auto Run mode alongside the existing spec-driven execution. Users can now define a free-text goal, exit criteria, and iteration limit, then watch as an AI agent iteratively makes progress toward that goal via structured HTML comment markers (progress percent, rationale, completion, deadlock). The system evaluates exit conditions in priority order (completed, deadlock, max-iterations, stalled) and tracks progress through a unified batch processing pipeline.

Changes

Goal-Driven Auto Run Feature

Layer / File(s) Summary
Shared types, constants, and marker parsing
src/shared/goalDriven/types.ts, src/shared/goalDriven/goalMarkers.ts, src/shared/goalDriven/goalRunLabel.ts
New type contracts (GoalRunConfig, GoalMarkers, GoalIterationRecord, GoalExitReason), regex-based marker parsing from agent responses, and utility functions for goal-run document labeling/detection.
Goal exit evaluation and stall detection
src/shared/goalDriven/goalExitEvaluator.ts, src/__tests__/shared/goalDriven/goalExitEvaluator.test.ts, src/__tests__/shared/goalDriven/goalMarkers.test.ts, src/__tests__/shared/goalDriven/goalRun.simulation.test.ts
Pure function evaluateGoalExit applying stop-condition priority (completion, deadlock, max-iterations, stall) to iteration history; comprehensive tests covering all exit paths and priority ordering.
Agent prompt template and template variables
src/prompts/autorun-goal.md, src/shared/promptDefinitions.ts, src/shared/templateVariables.ts
New agent prompt defining required response structure, marker formats, and multi-step iteration workflow; support for {{GOAL}} and {{GOAL_EXIT_CRITERIA}} template variables in per-iteration prompt substitution.
Goal runner engine and orchestration
src/renderer/hooks/batch/internal/useGoalRunner.ts, src/__tests__/renderer/hooks/batch/useGoalRunner.test.ts
useGoalRunner hook implementing the complete goal-driven iteration loop: agent spawning with marker parsing, progress/rationale updates, history accumulation, exit evaluation, stats tracking, and lifecycle callbacks; full test coverage including completion, stall, deadlock, max-iterations, and user-stop scenarios.
Batch processor and state integration
src/renderer/hooks/batch/useBatchProcessor.ts, src/renderer/hooks/batch/batchReducer.ts, src/renderer/hooks/batch/internal/useBatchBroadcast.ts, src/renderer/types/index.ts
Extend batch processor to discriminate between goal-driven (via config.goalConfig) and spec-driven modes; update reducer and broadcast layer to carry goal-driven state fields (goalMode, progress, rationale, iteration, exitReason) through batch lifecycle.
Goal configuration UI and modal refactor
src/renderer/components/GoalConfigPanel.tsx, src/renderer/components/BatchRunnerModal.tsx, src/__tests__/renderer/components/BatchRunnerModal.test.tsx, src/renderer/components/AutoRun/AutoRunnerHelpModal.tsx
New GoalConfigPanel component for goal/exit-criteria/iteration-limit inputs; major refactor of BatchRunnerModal to add spec-driven/goal-driven toggle tabs, conditional rendering, mode-dependent Go button gating, and persisted session goal config; help modal documentation for goal-driven mode.
Progress display updates across components
src/renderer/components/AutoRun/AutoRun.tsx, src/renderer/components/AutoRun/AutoRunBottomPanel.tsx, src/renderer/components/RightPanel.tsx, src/renderer/components/ThinkingStatusPill.tsx, src/__tests__/renderer/components/AutoRun/AutoRunBottomPanel.test.tsx
Update renderer components to display goal-driven progress (percent, iteration, rationale) instead of task counts when goal mode is active; conditional styling (accent vs success colors) and optional iteration labels; tests validating goal UI rendering and precedence over task readouts.
Web/mobile progress display and integration
src/web/mobile/AutoRunIndicator.tsx, src/web/mobile/AutoRunInline.tsx, src/web/hooks/useWebSocket.ts, src/renderer/components/UsageDashboard/LongestAutoRunsTable.tsx, src/__tests__/web/mobile/AutoRunIndicator.test.tsx, src/renderer/global.d.ts, src/main/preload/web.ts, src/main/web-server/types.ts, src/main/ipc/handlers/web.ts
Extend web/mobile AutoRunIndicator to render goal progress (percent, iteration, rationale) instead of task counts; update AutoRunInline to show goal-run banners; modify LongestAutoRunsTable to detect and label goal runs with percent completion; extend AutoRunState broadcast type across web hooks and IPC handlers.
Integration and end-to-end tests
src/__tests__/integration/GoalDrivenAutoRun.test.tsx, src/__tests__/shared/goalDriven/goalRunLabel.test.ts
Full integration test driving useBatchProcessor.startBatchRun via renderHook, verifying goal-progress capture and completion/stall exits; label utility tests; comprehensive coverage of goal-runner lifecycle and batch processor dispatch.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • RunMaestro/Maestro#946: Modifies worktree-based Auto Run launch flow and spawnWorktreeAgentAndDispatch child-session spawning for startBatchRun.
  • RunMaestro/Maestro#971: Refactors batch orchestration layer (src/renderer/hooks/batch/useBatchProcessor.ts) while this PR extends it to support goal-driven dispatch.

Suggested labels

RC

Suggested reviewers

  • reachrazamair

🐰 A goal-driven dream takes flight,
With markers bright and progress tight,
Iterations spin, the stalls all yield,
As agents reach each milestone sealed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is overly long, uses non-descriptive task enumeration, and doesn't clearly convey the main feature being added. Simplify the title to highlight the main feature; e.g., 'Add Goal-Driven Auto Run mode' or 'Implement Goal-Driven Auto Run with modal, UI, and engine integration'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.
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 auto-run-goal-driven

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 26, 2026

Greptile Summary

This PR introduces Goal-Driven Auto Run mode — a document-less counterpart to the existing task/document loop where an agent pursues a free-text goal across repeated iterations, self-reports progress via <!-- maestro:... --> HTML comment markers, and stops on completion, deadlock, max-iterations, or stall detection.

  • Core engine (src/shared/goalDriven/, useGoalRunner.ts): pure marker parser, exit evaluator, and React hook orchestrator wired into useBatchProcessor via a goalConfig discriminator; shares lifecycle scaffolding (power inhibit, stats tracking, flush-state kill guard, web broadcast) with the existing document runner.
  • UI layer (BatchRunnerModal, GoalConfigPanel, AutoRunBottomPanel, web/mobile indicators): Goal/Spec tab switcher in the modal, goal progress readout replacing task counts in the bottom panel, and Usage Dashboard rendering goal runs with a "Goal" badge instead of file/task-count UI.

Confidence Score: 3/5

The feature is well-architected and broadly safe to merge, but there is a data-flow bug in the deadlock path that will cause the agent's stated reason to be silently discarded and replaced with the wrong text in the History panel.

The goal engine's deadlock handling has a disconnect between the prompt format and the evaluator: autorun-goal.md instructs agents to put the reason in the deadlock marker, but GoalIterationRecord has no deadlockReason field and useGoalRunner never stores markers.deadlockReason. The evaluator falls back to latest.rationale (the progress marker rationale), so the History detail will show the wrong text or 'no stated reason' every time a deadlock is declared with an explicit reason. This affects every user who triggers a deadlock in goal mode. The rest of the feature — stall detection, completion, max-iteration cap, UI, broadcast wiring, stats — is correct.

src/renderer/hooks/batch/internal/useGoalRunner.ts and src/shared/goalDriven/types.ts need the deadlockReason field added and threaded through to the evaluator; src/shared/goalDriven/goalExitEvaluator.ts needs the deadlock branch updated to read from it.

Important Files Changed

Filename Overview
src/renderer/hooks/batch/internal/useGoalRunner.ts New 634-line goal-driven loop orchestrator; correctly wires lifecycle (stats, power, flush-state, broadcast) but drops the deadlock reason from GoalIterationRecord, causing the exit evaluator to surface the wrong or empty reason in History.
src/shared/goalDriven/goalExitEvaluator.ts Pure exit evaluator with correct priority ordering; deadlock detail reads latest.rationale (progress rationale field) which the upstream runner never populates with the actual deadlock reason.
src/shared/goalDriven/goalMarkers.ts Regex-based marker parser correctly parses progress, complete, and deadlock markers including optional reasons; deadlockReason is returned but not used downstream.
src/shared/goalDriven/types.ts Clean type contracts; GoalIterationRecord is missing a deadlockReason field which causes the evaluator to fall back to the unrelated progress rationale when reporting a deadlock.
src/renderer/hooks/batch/useBatchProcessor.ts Goal mode routing added cleanly; presence of goalConfig selects goal runner over document runner without breaking existing callers.
src/renderer/components/BatchRunnerModal.tsx New Goal-Driven tab with proper debounced persistence of goal config to session; Go button correctly gated on non-empty goal; spec-mode unsaved-changes check not broken by goal fields.
src/renderer/components/GoalConfigPanel.tsx Clean goal config UI panel with Infinite/Limit toggle and numeric iteration cap; validation is delegated to the modal's isGoalEmpty check.
src/prompts/autorun-goal.md Well-structured goal prompt; {{LOOP_NUMBER}} renders as a 5-digit zero-padded number (e.g. "00003") which is odd for a human-readable iteration counter in agent context.
src/renderer/components/AutoRun/AutoRunBottomPanel.tsx Goal progress readout cleanly replaces task count when goal prop is set; reset button correctly hidden in goal mode.
src/renderer/components/UsageDashboard/LongestAutoRunsTable.tsx Correctly identifies goal runs by the Goal: document path prefix and renders them with a percentage and "Goal" badge instead of file/task-count UI.

Sequence Diagram

sequenceDiagram
    participant Modal as BatchRunnerModal
    participant Processor as useBatchProcessor
    participant GoalRunner as useGoalRunner
    participant Agent as onSpawnAgent
    participant Parser as parseGoalMarkers
    participant Evaluator as evaluateGoalExit
    participant State as BatchRunState

    Modal->>Processor: startBatchRun(config.goalConfig present)
    Processor->>GoalRunner: startGoalRun(sessionId, config, folderPath)
    GoalRunner->>State: dispatch START_BATCH (100 pseudo-tasks)
    GoalRunner->>State: "updateBatch goalMode=true, goalProgress=0"

    loop Each iteration
        GoalRunner->>Agent: onSpawnAgent(sessionId, prompt)
        Agent-->>GoalRunner: response with markers
        GoalRunner->>Parser: parseGoalMarkers(response)
        Parser-->>GoalRunner: "{progress, rationale, complete, deadlock, deadlockReason}"
        GoalRunner->>State: update goalProgress, goalIteration, goalRationale
        GoalRunner->>Evaluator: evaluateGoalExit(history, config)
        Evaluator-->>GoalRunner: "{action: continue | stop, reason}"
        alt "action == stop"
            GoalRunner->>State: dispatch COMPLETE_BATCH
            GoalRunner->>State: broadcastAutoRunState null
        end
    end
Loading

Reviews (1): Last reviewed commit: "MAESTRO: Stats, help docs, and hardening..." | Re-trigger Greptile

Comment on lines +388 to +396
};
const prompt = substituteTemplateVariables(goalPromptTemplate, templateContext);

const iterationStart = Date.now();
let result: Awaited<ReturnType<SpawnAgentFn>>;
try {
result = await onSpawnAgent(
sessionId,
prompt,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Deadlock reason silently dropped from iteration record

markers.deadlockReason is parsed from <!-- maestro:deadlock: reason --> by parseGoalMarkers, but it is never stored in the GoalIterationRecord. The exit evaluator then falls back to latest.rationale?.trim() (the progress marker rationale) as the deadlock detail. When an agent follows the prompt exactly — placing the reason in the deadlock marker rather than in the progress rationale — the History panel shows either the wrong text or "Agent reported a deadlock with no stated reason."

The GoalIterationRecord interface needs a deadlockReason field, and the history.push call should set it from markers.deadlockReason. The evaluator's deadlock branch should then prefer that field over latest.rationale. The integration test at line 258 happens to work only because the test response embeds the reason in the progress rationale marker, which is the opposite of what the autorun-goal.md prompt instructs agents to do.

- **Agent Path:** {{AGENT_PATH}}
- **Git Branch:** {{GIT_BRANCH}}
- **Auto Run Folder:** {{AUTORUN_FOLDER}}
- **Iteration:** {{LOOP_NUMBER}}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The {{LOOP_NUMBER}} template variable is formatted as a 5-digit zero-padded string (e.g. "00003") via String(n).padStart(5, '0') in templateVariables.ts. That padding exists for file-ordering purposes in document runs and is confusing when shown to the agent as its human-readable iteration counter in goal mode.

Suggested change
- **Iteration:** {{LOOP_NUMBER}}
- **Iteration:** {{LOOP_NUMBER_HUMAN}}

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/__tests__/renderer/components/BatchRunnerModal.test.tsx (1)

1186-1191: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid conditional assertions that let this test pass without validating close behavior.

At Line 1186, header?.querySelector('button') is optional and wrapped in if (closeButton), so the test can pass even if the close button is missing. Make existence mandatory before clicking/asserting.

✅ Suggested fix
-			const closeButton = header?.querySelector('button');
-			if (closeButton) {
-				fireEvent.click(closeButton);
-				expect(props.onClose).toHaveBeenCalled();
-			}
+			const closeButton = header?.querySelector('button');
+			expect(closeButton).not.toBeNull();
+			fireEvent.click(closeButton!);
+			expect(props.onClose).toHaveBeenCalled();
🤖 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 `@src/__tests__/renderer/components/BatchRunnerModal.test.tsx` around lines
1186 - 1191, The test currently uses an optional query
(header?.querySelector('button')) and wraps the click/assert in an if, allowing
the test to pass when the close button is missing; change it to require the
button exists before interacting by querying/asserting it directly (e.g., use a
non-optional lookup or expect(closeButton).toBeInTheDocument()) so you always
fail when the close button is not rendered, then fireEvent.click(closeButton)
and expect(props.onClose).toHaveBeenCalled(); keep references to
screen.getByRole('heading', { name: "Auto Run" }), header, closeButton and
props.onClose to locate the code to update.
🧹 Nitpick comments (3)
src/shared/goalDriven/types.ts (1)

49-60: 🏗️ Heavy lift

Preserve deadlockReason in iteration history to avoid semantic loss.

GoalMarkers carries deadlockReason, but GoalIterationRecord drops it (Line 49 onward). That forces downstream logic to overload rationale for deadlock details, which can misreport the reason when both progress + deadlock markers appear in one iteration.

Suggested contract change
 export interface GoalIterationRecord {
 	/** 1-based iteration number. */
 	iteration: number;
 	/** Normalized progress for this iteration (0–100, never null in history). */
 	progress: number;
 	/** Optional rationale captured from the iteration's progress marker. */
 	rationale: string | null;
 	/** Whether the iteration reported completion. */
 	complete: boolean;
 	/** Whether the iteration reported a deadlock. */
 	deadlock: boolean;
+	/** Optional deadlock reason captured from the deadlock marker. */
+	deadlockReason: string | null;
 }
🤖 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 `@src/shared/goalDriven/types.ts` around lines 49 - 60, The GoalIterationRecord
interface currently omits deadlockReason while GoalMarkers includes it, causing
loss of deadlock details; update the GoalIterationRecord type to include a
deadlockReason field (string | null, optional if appropriate) and then propagate
the deadlockReason from places that build GoalIterationRecord instances (look
for code that maps from GoalMarkers to GoalIterationRecord) so iteration history
preserves the deadlockReason instead of overloading rationale.
src/shared/goalDriven/goalExitEvaluator.ts (1)

93-101: 🏗️ Heavy lift

Deadlock detail should use a dedicated deadlock reason field, not rationale.

Line 94 currently reads deadlock reason from latest.rationale. That conflates two semantics (progress rationale vs deadlock reason) and can produce misleading stop details.

🤖 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 `@src/shared/goalDriven/goalExitEvaluator.ts` around lines 93 - 101, The code
is reading deadlock details from latest.rationale which mixes semantics; change
the deadlock handling in goalExitEvaluator (the block checking latest.deadlock)
to read from a dedicated deadlock reason field (e.g., latest.deadlockReason or
latest.deadlock.reason) instead of latest.rationale, and use that value for the
returned detail string with the same fallback text ('Agent reported a deadlock
with no stated reason.') so the stop.reason remains 'deadlock' but detail
reflects the explicit deadlock reason field.
src/main/ipc/handlers/web.ts (1)

274-279: ⚡ Quick win

Use the shared AutoRunState type instead of an inline payload shape.

This handler’s inline type is already drifting from the other AutoRun state declarations, which weakens contract consistency across IPC boundaries.

♻️ Suggested refactor
 import type { AITabData } from '../../web-server/services/broadcastService';
+import type { AutoRunState } from '../../web-server/types';
@@
-			state: {
-				isRunning: boolean;
-				totalTasks: number;
-				completedTasks: number;
-				currentTaskIndex: number;
-				isStopping?: boolean;
-				// Multi-document progress fields
-				totalDocuments?: number;
-				currentDocumentIndex?: number;
-				totalTasksAcrossAllDocs?: number;
-				completedTasksAcrossAllDocs?: number;
-				// Goal-Driven mode fields
-				goalMode?: boolean;
-				goalProgress?: number;
-				goalRationale?: string;
-				goalIteration?: number;
-			} | null
+			state: AutoRunState | null
🤖 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 `@src/main/ipc/handlers/web.ts` around lines 274 - 279, The handler currently
declares an inline AutoRun-like payload (the union type containing goalMode,
goalProgress, goalRationale, goalIteration | null); replace that inline shape
with the shared AutoRunState type by importing AutoRunState and using it for the
payload parameter/field instead of the ad-hoc type, ensuring the handler
signature and any runtime usages reference AutoRunState (and update any import
statement to pull AutoRunState from its defining module).
🤖 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 `@src/prompts/autorun-goal.md`:
- Around line 74-99: Update the three fenced code block examples that show
HTML-like markers to include a language tag (change ``` to ```html) for the
markers <!-- maestro:progress N | one-line rationale -->, <!-- maestro:progress
100 | goal achieved: <what was accomplished> --> <!-- maestro:goal-complete -->,
and <!-- maestro:deadlock: brief reason you cannot proceed --> to satisfy MD040,
and remove the internal trailing space in the inline code span `MAESTRO: `
(change to `MAESTRO:`) referenced in the Version control item so it no longer
triggers MD038; ensure the exact marker strings remain unchanged except for
code-fence language and the inline token spacing.

In `@src/renderer/components/AutoRun/AutoRun.tsx`:
- Around line 542-557: The goalInfo useMemo currently builds goal state whenever
goalMode is set even if the run stopped; modify the guard to require
batchRunState?.isRunning (e.g. change the ternary to batchRunState?.goalMode &&
batchRunState?.isRunning ? ...) and add batchRunState?.isRunning to the useMemo
dependency array so goalInfo only exists during active runs; apply the same
change to the other similar goal-derived slice elsewhere in this file (the other
goalInfo-like computation).

In `@src/renderer/components/AutoRun/AutoRunBottomPanel.tsx`:
- Around line 105-115: In AutoRunBottomPanel, normalize goal.progress into a
clamped value (e.g., const clampedProgress = Math.max(0, Math.min(100,
goal.progress || 0))) and use clampedProgress everywhere in this component
instead of goal.progress — update the color decision (goal.progress >= 100) and
the displayed text "Goal: {goal.progress}%" to use clampedProgress so the UI
consistently shows a 0–100% value and color logic matches the clamped state;
reference the AutoRunBottomPanel component and the span rendering the goal
percent.

In `@src/renderer/components/ThinkingStatusPill.tsx`:
- Around line 248-273: The expanded AutoRun dropdown still always renders "x/y
tasks" and should match the primary pill's goal-mode display; update the
rendering logic inside the expanded AutoRun row (the block that currently uses
completedTasks/totalTasks and the hardcoded "tasks" label) to conditionally show
goal UI when autoRunState.goalMode is true — display "Goal:" with
autoRunState.goalProgress (default 0%) and optional "· iteration
{autoRunState.goalIteration}" and use autoRunState.goalRationale as title,
otherwise keep the existing "Tasks:" + completedTasks/totalTasks rendering;
mirror the same classNames/styles used in the existing goal-mode block so
visuals stay consistent.

In `@src/renderer/hooks/batch/batchReducer.ts`:
- Around line 374-379: The reducer currently only spreads goal fields when
payload.goalRationale/goalExitReason !== undefined, preventing clearing them;
update the spread conditions to check for key presence (e.g.,
Object.prototype.hasOwnProperty.call(payload, 'goalRationale') and
'goalExitReason') so the reducer will apply explicit null/empty values to clear
stale metadata, and ensure the action creator/payload construction only omits
keys when truly not intended (i.e., include keys with null/'' when you want them
cleared).

In `@src/renderer/hooks/batch/internal/useBatchBroadcast.ts`:
- Around line 60-66: The broadcast payload for goal-driven state is missing
goalExitReason, so update the object created in useBatchBroadcast (the payload
that currently includes goalMode, goalProgress, goalRationale, goalIteration) to
also include goalExitReason; locate where the broadcast is assembled in
useBatchBroadcast.ts and add goalExitReason: state.goalExitReason to the same
payload so downstream web/mobile clients receive the exit reason.

In `@src/renderer/hooks/batch/internal/useGoalRunner.ts`:
- Around line 568-570: The catch block in useGoalRunner around the
onAddHistoryEntry call should not silently swallow errors; import and call the
Sentry utilities (captureException or captureMessage from src/utils/sentry.ts)
inside that catch to report the failure with context (include function name
"onAddHistoryEntry", the affected goal id/metadata and any relevant variables),
and only suppress the error after logging if it is known/recoverable; update
useGoalRunner to report unexpected exceptions via captureException and add a
brief contextual message via captureMessage when appropriate.

In `@src/web/mobile/AutoRunInline.tsx`:
- Around line 732-736: The empty-document goal-run branch currently hides the
goal banner when isErrorPaused is true, removing Resume/Skip/Abort controls;
update the conditional around AutoRunIndicator (referenced as AutoRunIndicator
and the surrounding checks isErrorPaused, isRunning, autoRunState?.goalMode) so
that when autoRunState?.goalMode is true you still render the banner or a
recovery banner even if isErrorPaused is true — e.g., change the guard to render
AutoRunIndicator (or an error-recovery variant) when goalMode is set and either
running or error-paused, and ensure the rendered component receives the recovery
handler props (onResume/onSkip/onAbort) when those handlers are present so the
controls remain visible during error pause.

---

Outside diff comments:
In `@src/__tests__/renderer/components/BatchRunnerModal.test.tsx`:
- Around line 1186-1191: The test currently uses an optional query
(header?.querySelector('button')) and wraps the click/assert in an if, allowing
the test to pass when the close button is missing; change it to require the
button exists before interacting by querying/asserting it directly (e.g., use a
non-optional lookup or expect(closeButton).toBeInTheDocument()) so you always
fail when the close button is not rendered, then fireEvent.click(closeButton)
and expect(props.onClose).toHaveBeenCalled(); keep references to
screen.getByRole('heading', { name: "Auto Run" }), header, closeButton and
props.onClose to locate the code to update.

---

Nitpick comments:
In `@src/main/ipc/handlers/web.ts`:
- Around line 274-279: The handler currently declares an inline AutoRun-like
payload (the union type containing goalMode, goalProgress, goalRationale,
goalIteration | null); replace that inline shape with the shared AutoRunState
type by importing AutoRunState and using it for the payload parameter/field
instead of the ad-hoc type, ensuring the handler signature and any runtime
usages reference AutoRunState (and update any import statement to pull
AutoRunState from its defining module).

In `@src/shared/goalDriven/goalExitEvaluator.ts`:
- Around line 93-101: The code is reading deadlock details from latest.rationale
which mixes semantics; change the deadlock handling in goalExitEvaluator (the
block checking latest.deadlock) to read from a dedicated deadlock reason field
(e.g., latest.deadlockReason or latest.deadlock.reason) instead of
latest.rationale, and use that value for the returned detail string with the
same fallback text ('Agent reported a deadlock with no stated reason.') so the
stop.reason remains 'deadlock' but detail reflects the explicit deadlock reason
field.

In `@src/shared/goalDriven/types.ts`:
- Around line 49-60: The GoalIterationRecord interface currently omits
deadlockReason while GoalMarkers includes it, causing loss of deadlock details;
update the GoalIterationRecord type to include a deadlockReason field (string |
null, optional if appropriate) and then propagate the deadlockReason from places
that build GoalIterationRecord instances (look for code that maps from
GoalMarkers to GoalIterationRecord) so iteration history preserves the
deadlockReason instead of overloading rationale.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1e6d3248-c082-443a-bc12-8b1d3134366e

📥 Commits

Reviewing files that changed from the base of the PR and between 5e6ab8a and 8ee173f.

📒 Files selected for processing (36)
  • src/__tests__/integration/GoalDrivenAutoRun.test.tsx
  • src/__tests__/renderer/components/AutoRun/AutoRunBottomPanel.test.tsx
  • src/__tests__/renderer/components/BatchRunnerModal.test.tsx
  • src/__tests__/renderer/hooks/batch/useGoalRunner.test.ts
  • src/__tests__/shared/goalDriven/goalExitEvaluator.test.ts
  • src/__tests__/shared/goalDriven/goalMarkers.test.ts
  • src/__tests__/shared/goalDriven/goalRun.simulation.test.ts
  • src/__tests__/shared/goalDriven/goalRunLabel.test.ts
  • src/__tests__/web/mobile/AutoRunIndicator.test.tsx
  • src/main/ipc/handlers/web.ts
  • src/main/preload/web.ts
  • src/main/web-server/types.ts
  • src/prompts/autorun-goal.md
  • src/renderer/components/AutoRun/AutoRun.tsx
  • src/renderer/components/AutoRun/AutoRunBottomPanel.tsx
  • src/renderer/components/AutoRun/AutoRunnerHelpModal.tsx
  • src/renderer/components/BatchRunnerModal.tsx
  • src/renderer/components/GoalConfigPanel.tsx
  • src/renderer/components/RightPanel.tsx
  • src/renderer/components/ThinkingStatusPill.tsx
  • src/renderer/components/UsageDashboard/LongestAutoRunsTable.tsx
  • src/renderer/global.d.ts
  • src/renderer/hooks/batch/batchReducer.ts
  • src/renderer/hooks/batch/internal/useBatchBroadcast.ts
  • src/renderer/hooks/batch/internal/useGoalRunner.ts
  • src/renderer/hooks/batch/useBatchProcessor.ts
  • src/renderer/types/index.ts
  • src/shared/goalDriven/goalExitEvaluator.ts
  • src/shared/goalDriven/goalMarkers.ts
  • src/shared/goalDriven/goalRunLabel.ts
  • src/shared/goalDriven/types.ts
  • src/shared/promptDefinitions.ts
  • src/shared/templateVariables.ts
  • src/web/hooks/useWebSocket.ts
  • src/web/mobile/AutoRunIndicator.tsx
  • src/web/mobile/AutoRunInline.tsx

Comment on lines +74 to +99
```
<!-- maestro:progress N | one-line rationale -->
```

- `N` is your honest **0–100** self-assessment of how far the work has come toward the goal and its exit criteria above. Be conservative and grounded — base it on what is actually built and verified, not on how much you intend to do.
- The `| one-line rationale` is a short human-readable note describing where things stand (e.g. `data layer migrated, UI still pending`). It is optional but strongly encouraged — it shows up in the progress UI.
- This marker is how the engine drives the progress bar and decides whether to run another iteration. **A response with no progress marker is treated as zero progress** and counts toward a stall.

5. **Declare completion only when genuinely done.** When the goal is fully achieved and the exit criteria are satisfied, end your response with both a 100 progress marker and the completion marker:

```
<!-- maestro:progress 100 | goal achieved: <what was accomplished> -->
<!-- maestro:goal-complete -->
```

A progress of `100` on its own is also treated as completion, but emit the explicit `goal-complete` marker when you are certain. Do not declare completion prematurely — if work or verification remains, report a lower number and keep going.

6. **Declare a deadlock only for a true blocker.** If you hit something that genuinely prevents any further progress toward the goal — a missing dependency or credential you cannot obtain, a contradiction in the goal itself, a destructive action you refuse to take, or a hard external blocker — stop and end your response with:

```
<!-- maestro:deadlock: brief reason you cannot proceed -->
```

The reason text is shown in the History panel. Reserve this for real dead-ends; do NOT use it for ordinary setbacks you can work around on the next iteration. When in doubt, report partial progress and continue instead of declaring a deadlock.

7. **Version control.** For any code or documentation changes, if we're in a GitHub repo: commit using a descriptive message prefixed with `MAESTRO: `, and push. Update CLAUDE.md / AGENTS.md / README.md when appropriate.
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 | 🟡 Minor | ⚡ Quick win

Resolve markdownlint warnings in marker examples and code span formatting.

Line 74, Line 84, and Line 93 use fenced code blocks without language tags (MD040).
Line 99 has a code span with internal spacing (MAESTRO: ) triggering MD038.

Lint-safe markdown diff
-   ```
+   ```html
    <!-- maestro:progress N | one-line rationale -->
    ```

-   ```
+   ```html
    <!-- maestro:progress 100 | goal achieved: <what was accomplished> -->
    <!-- maestro:goal-complete -->
    ```

-   ```
+   ```html
    <!-- maestro:deadlock: brief reason you cannot proceed -->
    ```

-7. **Version control.** For any code or documentation changes, if we're in a GitHub repo: commit using a descriptive message prefixed with `MAESTRO: `, and push. Update CLAUDE.md / AGENTS.md / README.md when appropriate.
+7. **Version control.** For any code or documentation changes, if we're in a GitHub repo: commit using a descriptive message prefixed with `MAESTRO:`, and push. Update CLAUDE.md / AGENTS.md / README.md when appropriate.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 74-74: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 84-84: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 93-93: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 99-99: Spaces inside code span elements

(MD038, no-space-in-code)

🤖 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 `@src/prompts/autorun-goal.md` around lines 74 - 99, Update the three fenced
code block examples that show HTML-like markers to include a language tag
(change ``` to ```html) for the markers <!-- maestro:progress N | one-line
rationale -->, <!-- maestro:progress 100 | goal achieved: <what was
accomplished> --> <!-- maestro:goal-complete -->, and <!-- maestro:deadlock:
brief reason you cannot proceed --> to satisfy MD040, and remove the internal
trailing space in the inline code span `MAESTRO: ` (change to `MAESTRO:`)
referenced in the Version control item so it no longer triggers MD038; ensure
the exact marker strings remain unchanged except for code-fence language and the
inline token spacing.

Comment on lines +542 to +557
const goalInfo = useMemo(
() =>
batchRunState?.goalMode
? {
progress: batchRunState.goalProgress ?? 0,
iteration: batchRunState.goalIteration ?? 0,
rationale: batchRunState.goalRationale,
}
: null,
[
batchRunState?.goalMode,
batchRunState?.goalProgress,
batchRunState?.goalIteration,
batchRunState?.goalRationale,
]
);
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 | 🟡 Minor | ⚡ Quick win

Gate goal panel state to active runs only.

goalInfo is derived whenever goal mode is set, even if the run is no longer active. That can keep the bottom panel in goal display mode after stop. Use isRunning as part of the guard so this slice is only present during an active goal run.

💡 Suggested patch
 	const goalInfo = useMemo(
 		() =>
-			batchRunState?.goalMode
+			batchRunState?.isRunning && batchRunState?.goalMode
 				? {
 						progress: batchRunState.goalProgress ?? 0,
 						iteration: batchRunState.goalIteration ?? 0,
 						rationale: batchRunState.goalRationale,
 					}
 				: null,
 		[
+			batchRunState?.isRunning,
 			batchRunState?.goalMode,
 			batchRunState?.goalProgress,
 			batchRunState?.goalIteration,
 			batchRunState?.goalRationale,
 		]
 	);

Also applies to: 849-850

🤖 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 `@src/renderer/components/AutoRun/AutoRun.tsx` around lines 542 - 557, The
goalInfo useMemo currently builds goal state whenever goalMode is set even if
the run stopped; modify the guard to require batchRunState?.isRunning (e.g.
change the ternary to batchRunState?.goalMode && batchRunState?.isRunning ? ...)
and add batchRunState?.isRunning to the useMemo dependency array so goalInfo
only exists during active runs; apply the same change to the other similar
goal-derived slice elsewhere in this file (the other goalInfo-like computation).

Comment on lines +105 to +115
{goal ? (
<span className="flex items-center gap-2 min-w-0" style={{ color: theme.colors.textDim }}>
{/* Goal percent — accent until complete, then success (mirrors task readout) */}
<span
className="shrink-0 font-medium"
style={{
color:
taskCounts.completed === taskCounts.total
? theme.colors.success
: theme.colors.accent,
color: goal.progress >= 100 ? theme.colors.success : theme.colors.accent,
}}
>
{taskCounts.completed}
</span>{' '}
of <span style={{ color: theme.colors.accent }}>{taskCounts.total}</span> task
{taskCounts.total !== 1 ? 's' : ''}
{!isCompact && ' completed'}
Goal: {goal.progress}%
</span>
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 | 🟡 Minor | ⚡ Quick win

Normalize goal percent before rendering.

goal.progress is rendered directly here, while other goal progress surfaces clamp it. Clamp once in this component to keep display/state semantics consistent and avoid out-of-range values in UI.

💡 Suggested patch
 export const AutoRunBottomPanel = memo(function AutoRunBottomPanel({
@@
 }: AutoRunBottomPanelProps) {
 	const bottomPanelRef = useRef<HTMLDivElement>(null);
 	const [isCompact, setIsCompact] = useState(false);
+	const normalizedGoalProgress =
+		goal == null ? null : Math.min(100, Math.max(0, Math.round(goal.progress)));
@@
 				{goal ? (
@@
 						<span
 							className="shrink-0 font-medium"
 							style={{
-								color: goal.progress >= 100 ? theme.colors.success : theme.colors.accent,
+								color:
+									normalizedGoalProgress !== null && normalizedGoalProgress >= 100
+										? theme.colors.success
+										: theme.colors.accent,
 							}}
 						>
-							Goal: {goal.progress}%
+							Goal: {normalizedGoalProgress}%
 						</span>
🤖 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 `@src/renderer/components/AutoRun/AutoRunBottomPanel.tsx` around lines 105 -
115, In AutoRunBottomPanel, normalize goal.progress into a clamped value (e.g.,
const clampedProgress = Math.max(0, Math.min(100, goal.progress || 0))) and use
clampedProgress everywhere in this component instead of goal.progress — update
the color decision (goal.progress >= 100) and the displayed text "Goal:
{goal.progress}%" to use clampedProgress so the UI consistently shows a 0–100%
value and color logic matches the clamped state; reference the
AutoRunBottomPanel component and the span rendering the goal percent.

Comment on lines +248 to +273
{/* Progress — goal percent for goal runs, task count otherwise */}
{autoRunState.goalMode ? (
<div
className="flex items-center gap-1 shrink-0 text-xs"
style={{ color: theme.colors.textDim }}
title={autoRunState.goalRationale || undefined}
>
<span>Goal:</span>
<span className="font-medium" style={{ color: theme.colors.textMain }}>
{autoRunState.goalProgress ?? 0}%
</span>
{autoRunState.goalIteration ? (
<span className="opacity-70">· iteration {autoRunState.goalIteration}</span>
) : null}
</div>
) : (
<div
className="flex items-center gap-1 shrink-0 text-xs"
style={{ color: theme.colors.textDim }}
>
<span>Tasks:</span>
<span className="font-medium" style={{ color: theme.colors.textMain }}>
{completedTasks}/{totalTasks}
</span>
</div>
)}
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 | 🟡 Minor | ⚡ Quick win

Keep the expanded AutoRun dropdown consistent in goal mode.

The primary pill now shows goal progress, but the expanded AutoRun row still hardcodes task wording at Line 383 (x/y tasks). In goal runs this becomes inconsistent/misleading.

💡 Suggested patch
 								<div
 									className="flex items-center justify-between gap-3 w-full px-3 py-2"
 									style={{ color: theme.colors.textMain }}
 								>
@@
-									<span className="text-xs" style={{ color: theme.colors.textDim }}>
-										{completedTasks}/{totalTasks} tasks
-									</span>
+									<span className="text-xs" style={{ color: theme.colors.textDim }}>
+										{autoRunState.goalMode
+											? `Goal ${Math.min(100, Math.max(0, autoRunState.goalProgress ?? 0))}%${
+													autoRunState.goalIteration
+														? ` · iteration ${autoRunState.goalIteration}`
+														: ''
+												}`
+											: `${completedTasks}/${totalTasks} tasks`}
+									</span>
 								</div>

Also applies to: 371-385

🤖 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 `@src/renderer/components/ThinkingStatusPill.tsx` around lines 248 - 273, The
expanded AutoRun dropdown still always renders "x/y tasks" and should match the
primary pill's goal-mode display; update the rendering logic inside the expanded
AutoRun row (the block that currently uses completedTasks/totalTasks and the
hardcoded "tasks" label) to conditionally show goal UI when
autoRunState.goalMode is true — display "Goal:" with autoRunState.goalProgress
(default 0%) and optional "· iteration {autoRunState.goalIteration}" and use
autoRunState.goalRationale as title, otherwise keep the existing "Tasks:" +
completedTasks/totalTasks rendering; mirror the same classNames/styles used in
the existing goal-mode block so visuals stay consistent.

Comment on lines +374 to +379
// Goal-Driven mode fields (only set by the goal runner)
...(payload.goalMode !== undefined && { goalMode: payload.goalMode }),
...(payload.goalProgress !== undefined && { goalProgress: payload.goalProgress }),
...(payload.goalRationale !== undefined && { goalRationale: payload.goalRationale }),
...(payload.goalIteration !== undefined && { goalIteration: payload.goalIteration }),
...(payload.goalExitReason !== undefined && { goalExitReason: payload.goalExitReason }),
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 | 🟡 Minor | ⚡ Quick win

Goal optional fields can’t be cleared once set.

Line 377/379 only applies updates when value is not undefined, so goalRationale/goalExitReason cannot be reset to empty/absent state via UPDATE_PROGRESS. This can leave stale goal metadata in store state.

Suggested direction
- ...(payload.goalRationale !== undefined && { goalRationale: payload.goalRationale }),
- ...(payload.goalExitReason !== undefined && { goalExitReason: payload.goalExitReason }),
+ ...(Object.prototype.hasOwnProperty.call(payload, 'goalRationale') && {
+ 	goalRationale: payload.goalRationale,
+ }),
+ ...(Object.prototype.hasOwnProperty.call(payload, 'goalExitReason') && {
+ 	goalExitReason: payload.goalExitReason,
+ }),

(And ensure payload construction only includes those keys when intended.)

🤖 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 `@src/renderer/hooks/batch/batchReducer.ts` around lines 374 - 379, The reducer
currently only spreads goal fields when payload.goalRationale/goalExitReason !==
undefined, preventing clearing them; update the spread conditions to check for
key presence (e.g., Object.prototype.hasOwnProperty.call(payload,
'goalRationale') and 'goalExitReason') so the reducer will apply explicit
null/empty values to clear stale metadata, and ensure the action creator/payload
construction only omits keys when truly not intended (i.e., include keys with
null/'' when you want them cleared).

Comment on lines +60 to 66
// Goal-Driven mode — web/mobile render goal percent + iteration in
// place of task counts when goalMode is true.
goalMode: state.goalMode,
goalProgress: state.goalProgress,
goalRationale: state.goalRationale,
goalIteration: state.goalIteration,
});
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 | ⚡ Quick win

goalExitReason is missing from the web/mobile broadcast contract.

The goal state broadcast includes progress fields but omits goalExitReason, so downstream clients can’t reliably render why a goal run ended.

Minimal fix
 				goalMode: state.goalMode,
 				goalProgress: state.goalProgress,
 				goalRationale: state.goalRationale,
 				goalIteration: state.goalIteration,
+				goalExitReason: state.goalExitReason,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Goal-Driven mode — web/mobile render goal percent + iteration in
// place of task counts when goalMode is true.
goalMode: state.goalMode,
goalProgress: state.goalProgress,
goalRationale: state.goalRationale,
goalIteration: state.goalIteration,
});
// Goal-Driven mode — web/mobile render goal percent + iteration in
// place of task counts when goalMode is true.
goalMode: state.goalMode,
goalProgress: state.goalProgress,
goalRationale: state.goalRationale,
goalIteration: state.goalIteration,
goalExitReason: state.goalExitReason,
});
🤖 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 `@src/renderer/hooks/batch/internal/useBatchBroadcast.ts` around lines 60 - 66,
The broadcast payload for goal-driven state is missing goalExitReason, so update
the object created in useBatchBroadcast (the payload that currently includes
goalMode, goalProgress, goalRationale, goalIteration) to also include
goalExitReason; locate where the broadcast is assembled in useBatchBroadcast.ts
and add goalExitReason: state.goalExitReason to the same payload so downstream
web/mobile clients receive the exit reason.

Comment on lines +568 to +570
} catch {
// Ignore history errors.
}
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 | ⚡ Quick win

Do not silently swallow final history-write errors.

Line 568 catches and ignores unexpected failures from onAddHistoryEntry, which hides production issues. Capture with Sentry context (and only swallow if explicitly expected/recoverable).

As per coding guidelines: “Do not silently swallow errors… Use Sentry utilities (captureException, captureMessage) from src/utils/sentry.ts for explicit error reporting with context.”

🤖 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 `@src/renderer/hooks/batch/internal/useGoalRunner.ts` around lines 568 - 570,
The catch block in useGoalRunner around the onAddHistoryEntry call should not
silently swallow errors; import and call the Sentry utilities (captureException
or captureMessage from src/utils/sentry.ts) inside that catch to report the
failure with context (include function name "onAddHistoryEntry", the affected
goal id/metadata and any relevant variables), and only suppress the error after
logging if it is known/recoverable; update useGoalRunner to report unexpected
exceptions via captureException and add a brief contextual message via
captureMessage when appropriate.

Comment on lines +732 to +736
{/* Goal runs need not have any documents — surface live goal progress
even in the empty-document state (reuses AutoRunIndicator). */}
{!isErrorPaused && isRunning && autoRunState?.goalMode && (
<AutoRunIndicator state={autoRunState} />
)}
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 | 🟡 Minor | ⚡ Quick win

Render error-recovery actions in empty-document goal runs.

When isErrorPaused is true in the empty-doc branch, the goal banner is hidden but no recovery banner is shown, so Resume/Skip/Abort controls disappear even if handlers are provided.

💡 Suggested fix
-				{!isErrorPaused && isRunning && autoRunState?.goalMode && (
-					<AutoRunIndicator state={autoRunState} />
-				)}
+				{isErrorPaused && (onResumeAfterError || onSkipAfterError || onAbortAfterError) && (
+					<AutoRunIndicator
+						state={autoRunState}
+						onResume={onResumeAfterError}
+						onSkipDocument={onSkipAfterError}
+						onAbort={onAbortAfterError}
+					/>
+				)}
+				{!isErrorPaused && isRunning && autoRunState?.goalMode && (
+					<AutoRunIndicator state={autoRunState} />
+				)}
🤖 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 `@src/web/mobile/AutoRunInline.tsx` around lines 732 - 736, The empty-document
goal-run branch currently hides the goal banner when isErrorPaused is true,
removing Resume/Skip/Abort controls; update the conditional around
AutoRunIndicator (referenced as AutoRunIndicator and the surrounding checks
isErrorPaused, isRunning, autoRunState?.goalMode) so that when
autoRunState?.goalMode is true you still render the banner or a recovery banner
even if isErrorPaused is true — e.g., change the guard to render
AutoRunIndicator (or an error-recovery variant) when goalMode is set and either
running or error-paused, and ensure the rendered component receives the recovery
handler props (onResume/onSkip/onAbort) when those handlers are present so the
controls remain visible during error pause.

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