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
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
🚧 Files skipped from review as they are similar to previous changes (8)
📝 WalkthroughWalkthroughAdds Goal-Driven Auto Run: shared goal types and marker parser; a pure exit evaluator; a useGoalRunner orchestration hook; goal-config UI and modal refactor; batch-processor/state/broadcast wiring; prompt/template support; and extensive unit, hook, integration, and simulation tests. ChangesGoal-Driven Auto Run Feature
Sequence Diagram (high level): sequenceDiagram
participant useBatchProcessor
participant useGoalRunner
participant onSpawnAgent
participant evaluateGoalExit
useBatchProcessor->>useGoalRunner: startBatchRun(goalConfig)
useGoalRunner->>onSpawnAgent: spawn iteration prompt
onSpawnAgent-->>useGoalRunner: agent response (markers)
useGoalRunner->>evaluateGoalExit: evaluateGoalExit(history, config)
evaluateGoalExit-->>useGoalRunner: continue/stop decision
Estimated code review effort 🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
Greptile SummaryThis PR introduces Goal-Driven Auto Run mode — a new execution path where the agent pursues a free-text goal across repeated iterations rather than working through a checklist of spec documents. The implementation spans a new shared engine (
Confidence Score: 5/5The goal engine is well-isolated from the document runner and shares the same stop/kill/flush-state lifecycle contracts; the one finding (WorktreeRunSection visible in goal mode) is a cosmetic UX gap, not a crash or data-loss path. The core engine (marker parser, exit evaluator, orchestration loop) is pure and extensively tested. Lifecycle hooks (power management, stats, flush-state kill guard, history writes) mirror the battle-tested document runner. The deadlockReason field that was flagged in the previous review thread is correctly present in this version. The single notable gap — WorktreeRunSection shown in goal mode while the runner ignores the target — has no runtime consequence beyond user confusion. src/renderer/components/BatchRunnerModal.tsx — WorktreeRunSection visibility in goal mode. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([User clicks Go]) --> B{autoRunMode}
B -- spec --> C[startDocumentBatchRun]
B -- goal --> E[startGoalRun]
E --> G[Load autorun-goal template]
G --> H[START_BATCH dispatch]
H --> I[SET_RUNNING + power reason]
I --> J{stopRequested?}
J -- yes --> K[stopped-by-user]
J -- no --> L{infinite run at hard cap?}
L -- yes --> K
L -- no --> M[iteration++ / onSpawnAgent]
M --> N[parseGoalMarkers]
N --> O[history.push / updateBatchState]
O --> P[evaluateGoalExit]
P -- continue --> J
P -- stop --> Q[exit reason set]
K --> R[final history entry + stats]
Q --> R
R --> S[COMPLETE_BATCH / broadcast null / onComplete]
subgraph evaluateGoalExit
E1{complete or progress=100} -->|yes| E2[stop: completed]
E1 -->|no| E3{deadlock}
E3 -->|yes| E4[stop: deadlock]
E3 -->|no| E5{history len >= maxIterations}
E5 -->|yes| E6[stop: max-iterations]
E5 -->|no| E7{last STALL_THRESHOLD flat}
E7 -->|yes| E8[stop: stalled]
E7 -->|no| E9[continue]
end
Reviews (2): Last reviewed commit: "MAESTRO: Address Goal-Driven Auto Run PR..." | Re-trigger Greptile |
| }; | ||
| const prompt = substituteTemplateVariables(goalPromptTemplate, templateContext); | ||
|
|
||
| const iterationStart = Date.now(); | ||
| let result: Awaited<ReturnType<SpawnAgentFn>>; | ||
| try { | ||
| result = await onSpawnAgent( | ||
| sessionId, | ||
| prompt, |
There was a problem hiding this comment.
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}} |
There was a problem hiding this comment.
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.
| - **Iteration:** {{LOOP_NUMBER}} | |
| - **Iteration:** {{LOOP_NUMBER_HUMAN}} |
There was a problem hiding this comment.
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 winAvoid conditional assertions that let this test pass without validating close behavior.
At Line 1186,
header?.querySelector('button')is optional and wrapped inif (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 liftPreserve
deadlockReasonin iteration history to avoid semantic loss.
GoalMarkerscarriesdeadlockReason, butGoalIterationRecorddrops it (Line 49 onward). That forces downstream logic to overloadrationalefor 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 liftDeadlock 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 winUse the shared
AutoRunStatetype 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
📒 Files selected for processing (36)
src/__tests__/integration/GoalDrivenAutoRun.test.tsxsrc/__tests__/renderer/components/AutoRun/AutoRunBottomPanel.test.tsxsrc/__tests__/renderer/components/BatchRunnerModal.test.tsxsrc/__tests__/renderer/hooks/batch/useGoalRunner.test.tssrc/__tests__/shared/goalDriven/goalExitEvaluator.test.tssrc/__tests__/shared/goalDriven/goalMarkers.test.tssrc/__tests__/shared/goalDriven/goalRun.simulation.test.tssrc/__tests__/shared/goalDriven/goalRunLabel.test.tssrc/__tests__/web/mobile/AutoRunIndicator.test.tsxsrc/main/ipc/handlers/web.tssrc/main/preload/web.tssrc/main/web-server/types.tssrc/prompts/autorun-goal.mdsrc/renderer/components/AutoRun/AutoRun.tsxsrc/renderer/components/AutoRun/AutoRunBottomPanel.tsxsrc/renderer/components/AutoRun/AutoRunnerHelpModal.tsxsrc/renderer/components/BatchRunnerModal.tsxsrc/renderer/components/GoalConfigPanel.tsxsrc/renderer/components/RightPanel.tsxsrc/renderer/components/ThinkingStatusPill.tsxsrc/renderer/components/UsageDashboard/LongestAutoRunsTable.tsxsrc/renderer/global.d.tssrc/renderer/hooks/batch/batchReducer.tssrc/renderer/hooks/batch/internal/useBatchBroadcast.tssrc/renderer/hooks/batch/internal/useGoalRunner.tssrc/renderer/hooks/batch/useBatchProcessor.tssrc/renderer/types/index.tssrc/shared/goalDriven/goalExitEvaluator.tssrc/shared/goalDriven/goalMarkers.tssrc/shared/goalDriven/goalRunLabel.tssrc/shared/goalDriven/types.tssrc/shared/promptDefinitions.tssrc/shared/templateVariables.tssrc/web/hooks/useWebSocket.tssrc/web/mobile/AutoRunIndicator.tsxsrc/web/mobile/AutoRunInline.tsx
| 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, | ||
| ] | ||
| ); |
There was a problem hiding this comment.
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).
| {/* 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> | ||
| )} |
There was a problem hiding this comment.
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.
| // 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 }), |
There was a problem hiding this comment.
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).
| // 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, | ||
| }); |
There was a problem hiding this comment.
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.
| // 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.
| } catch { | ||
| // Ignore history errors. | ||
| } |
There was a problem hiding this comment.
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.
| {/* 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} /> | ||
| )} |
There was a problem hiding this comment.
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.
Squashed feature branch (was 5 commits) for a clean rebase onto RC: - Core engine: types, marker parser, exit evaluator (goalDriven/) - Wire Goal-Driven loop into the batch engine (useGoalRunner) - Goal-Driven tabs + GoalConfigPanel in the Auto Run modal - Render goal progress in Auto Run UI (desktop + web/mobile + History) - Stats, help docs, and hardening (Phase 05)
8ee173f to
50a7927
Compare
- Plumb the deadlock marker reason into GoalIterationRecord so the exit
evaluator surfaces the agent's stated deadlock reason instead of falling
back to the (unrelated) progress rationale. (greptile P1)
- Show the agent an unpadded iteration counter: add {{LOOP_NUMBER_HUMAN}}
and use it in the goal prompt so it reads "Iteration: 3", not "00003".
(greptile P2)
- Stop silently swallowing the final history-write error in the goal runner;
log a warning like the adjacent stats catch. (coderabbit, CLAUDE.md Sentry)
- Clamp the goal percent to 0-100 in AutoRunBottomPanel before display.
(coderabbit)
- Fix markdownlint MD040/MD038 in autorun-goal.md (html code fences, drop the
trailing space in the MAESTRO: code span). (coderabbit)
Updated goalExitEvaluator, goalRun.simulation, and templateVariables tests.
|
@greptile @greptile-apps https://github.com/coderabbitai please re-review |
Auto Run Summary
Documents processed:
Total tasks completed: 34
Changes
CHANGES
CHANGES
CHANGES
CHANGES
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 ✅
CHANGES
This PR was automatically created by Maestro Auto Run.
Summary by CodeRabbit
New Features
UI
Tests
Documentation
Types/State