Repository navigation
feat(goal-loop): harness-owned persistence loop that works until the goal is complete - #1003
Merged
Merged
Conversation
Harness-owned persistence loop: agent starts a loop via MCP with a self-written continuation prompt; Agent Code re-prompts on every idle without complete. Provider stop hooks were researched and rejected as the loop driver (Claude Code force-stops after 9 consecutive blocks; OpenCode has no block-capable Stop hook), so the loop rides our own deliverPromptToAgent + turn_completed infrastructure instead.
Eight tasks with complete code: shared contract, prompt builder, durable store, loop service state machine, MCP tools + system test, main wiring and IPC, renderer control surface, verification and PR. Refs #1001.
Main-process state machine over SessionManager semantic events: working→idle via reduceWorkingState triggers deliverPromptToAgent with the agent-written continuation prompt. Cap pauses before exceeding budget; failed deliveries self-retry with backoff (a failed delivery means no turn_completed will ever rescue the loop) and land in paused(error) after three rounds; control actions racing an in-flight continuation hand off through pendingContinue instead of being dropped.
…#1001 GoalLoopService constructed before the one-shot MCP setDependencies, IPC mirrors the TLDR read/control split (renderer steers, never creates), and the changed broadcast is a payload-free invalidation ping so panes re-read instead of racing two copies of the same loop state.
… - Refs #1001 Always-on per-pane status strip while a loop exists, latched control overlay (pause/resume/raise-cap/stop), palette commands and a Settings row. Final keybinding is Cmd+Shift+Y: Cmd+Shift+G is owned by global-dispatch and check:keybindings rejects unapproved overlaps.
…re owner - Refs #1001 132 governed commands (goal-loop-preview, goal-loop-stop), the goal-loop feature reference, and a guard in GoalLoopPane so partially-stubbed window.api surfaces render nothing instead of crashing pane tests.
…nuations Review of #1003 found the state machine could double-deliver, prompt an agent mid-turn, and stall with nothing left to observe. The backoff retry honoured retrySafe only for the immediate retry, so an acceptance timeout after Enter re-sent a continuation that was probably already running. An uncertain delivery now pauses the loop instead. Working state was reduced only while a loop was active and seeded blank at goal_loop_start. Claude publishes turn_completed per assistant message and those events reach main on a 200 ms poll, so the completion of the very message that called goal_loop_start arrived after the blank seed and triggered continuation #1 inside that turn. The same gap left a loop paused mid-turn permanently "working", so Resume never delivered. State is now tracked for every session and the seed only fills in a non-working one. Every trigger now goes through one request path that re-checks the tracked state, which closes pause->resume during an in-flight delivery (delivered twice) and a backoff retry dropped by the re-entrancy guard on a slow disk. persist() evicted the newest entries rather than the oldest and never bounded paused loops, which every removed session and restart mints. start() also overwrote the corrupt file the store claimed to preserve; unreadable storage is now moved aside first. Adds a dismiss control action so an ended loop can be cleared, one shared action union for main, preload and the service, and a cross-session isolation test over a shared service, which the per-session-service system test could not observe. Refs #1001 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An ended loop is persisted and its strip sits over the pane's top line, but it rendered no controls at all, so it could never be cleared. It now offers Dismiss. Raise cap sent maxContinuations + 25 unclamped; past 175 that exceeded the IPC schema maximum, main rejected the request, and the rejection surfaced only as an unhandled promise. The value is clamped to the shared ceiling and the button is hidden once there is nothing left to raise. The latched overlay is opaque over the whole pane and had no exit other than the chord, so it gains a Close button. Escape is deliberately not bound: in an agent pane it interrupts the running turn. Refs #1001 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…departures Off-by-default and the missing parked-agent wake both differ from the approved spec and were not in the plan's list of deliberate refinements. Neither was changed in review; writing them down makes them decisions to confirm rather than accidents to rediscover. Refs #1001 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ibling domain - Refs #1001
This was referenced Sep 19, 2026
Closed
Closed
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Agents stop at the first natural pause, leaving multi-step goals half-finished until a human re-prompts (#1001). Provider Stop hooks cannot be the fix: Claude Code force-stops after 9 consecutive blocks (which kills every ralph/ultrawork-style mode — oh-my-claudecode #3138), and OpenCode has no block-capable Stop hook at all.
Implemented behavior
goal_loop(off by default, Settings row like Goal):goal_loop_start(goal, loopPrompt, maxContinuations?)— the agent writes its own continuation prompt when the user asks for a loop — andgoal_loop_complete(outcome: done | blocked, summary)— instructed to fire only when the goal is utterly satisfied, withblockedas the honest needs-the-user exit.GoalLoopService(main process): subscribes to SessionManager semantic events, detects working→idle viareduceWorkingState(never continues while tools are pending or the agent is blocked on the user), and delivers the continuation prompt throughdeliverPromptToAgentwith an iteration/budget header. Works identically for Claude, Codex and OpenCode — no provider hooks.errorafter three rounds; control actions racing an in-flight continuation hand off through a pending flag instead of being dropped; state is durable so a crash surfaces aspaused(interrupted)with one-click resume; session removal pauses asinterrupted.Goal loop · active · iteration 3/25 · <goal>) with Pause/Stop, a latched overlay (Cmd+Shift+Y) adding Resume/Raise cap, palette commandsGoal LoopandStop Goal Loop, and acontrolGoalLoopIPC. Creating loops stays MCP-only; the renderer only steers.Design decisions and tradeoffs
deliverPromptToAgent(sessionId, …)), a latch + always-on strip instead of extending tldr's hold-peek PreviewKind (different data model; the loop surface's job is control), and domain teaching via MCP init instructions (workflows-domain pattern) rather than a managed product skill.global-dispatchandcheck:keybindingsrejects unapproved overlaps.Tests and verification
npm run typecheckclean;npm run check:keybindingsclean; fullnpm test: 4417 passed, 1 failed —imageAttachment.test.tscorpus invariant, verified failing identically onmain(pre-existing, unrelated).Known limitations
interrupted; resume is manual (conservative v1).complete(done)is the agent's claim, not a verified fact; the instructions put the verification burden on the model and the summary surfaces what it believes finished.Fixes #1001