Skip to content

feat(fork-loops): run an agent in a loop until the work is verified done - #121

Merged
zpyoung merged 112 commits into
mainfrom
zpyoung/loops
Oct 19, 2026
Merged

zpyoung merged 112 commits into
mainfrom
zpyoung/loops

Conversation

@zpyoung

@zpyoung zpyoung commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner
Files Added Deleted Net
Test 33 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​9736 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​9734
Prod 105 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​12504 $\color{#cf222e}{\Huge{\mathbf{−}}}$​30 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​12474

ELI5

You can now tell Orca "keep running this agent until the work is actually done." Orca sends the agent a prompt, waits for it to finish, checks whether it's done (by running a command like pnpm test, asking an evaluator model, or both), and if not, sends it again. You can also run a prompt on a timer, or feed the agent a queue of prompts one at a time.

What Changed

Before, getting an agent to keep going meant sitting next to it and re-prompting by hand. Now there's a Loop entry in the tab bar's + menu and an orca loop CLI.

  • Three kinds of loop. Until done (re-prompt whenever the agent goes idle), on an interval (every N minutes, from the end of the last run or on a fixed clock), and queue (one prompt per iteration, in order, optionally repeating).
  • Done is verified, not trusted. The agent printing LOOP_DONE is only a claim. A loop completes when its check command exits 0 and/or its evaluator passes. Evidence from a done signal the agent edited is rejected.
  • Guardrails. Caps on iterations, running minutes, and cost; pause on no progress (3 iterations with no git change and identical check output); pause when an iteration touches a protected path; optional checkpoint commit per iteration; optional pause-and-ask between iterations; auto-resume when a usage limit resets.
  • Where you see it. A setup dialog (two panes, laid out like the automation editor), a loop rail and status strip beside the terminal with pause/resume/steer/edit/stop and per-iteration detail, a glyph on the worktree card, a close guard on the loop's tab, a summary in orca worktree ps, and a label on mobile worktree rows.

Mechanism: a main-process LoopService owns each loop and a LoopDriver runs its iterations against the runtime's terminal API. State lives in SQLite, so a loop keeps running after the CLI returns or its tab closes. Each iteration composes a prompt with a header and a notes file, sends it, watches the agent's status until it settles, then gathers evidence (check output, git diff, usage, evaluator verdict) and decides whether to continue, pause, or complete. Input you type into the agent's pane during an iteration is attributed to you, and a loop won't send over it.

All fork code lives in fork-loops/ directories in shared, main, renderer, cli, and mobile. Upstream files carry only declared one-line seams (registration and passthrough), recorded in config/fork-ownership.json.

Why

The alternatives were a shell while loop around the CLI, or an automation that fires on a schedule. Neither knows when the agent has actually finished an iteration, and neither can tell "the agent says it's done" apart from "the tests pass." Owning the loop in the app's main process lets it reuse the hook server's agent status, the existing terminal runtime, and SSH execution, so a remote worktree loops the same way a local one does.

Linked Issue

N/A (fork feature)

Visual Proof

[ASK: attach before/after of the + menu, setup dialog, and loop rail]

Testing

  • Fork-loops unit suites (main and renderer) plus the fork localization catalog test on the remote sandbox: 24 files, 407 tests passing.

  • pnpm run check:code-quality:changed, pnpm typecheck:node, and pnpm typecheck:web are clean.

  • Platforms: [ASK: which platforms and SSH were exercised manually]

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Written with Claude (Claude Code).

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • SSH. The check command, diff, and notes file run on the loop's host. Lost contact holds the loop as unverifiable between iterations instead of counting the agent as exited.
  • Mixed versions. Every loop RPC is gated on the new loops.v1 runtime capability, so an older host returns incompatible_runtime. The new worktree summary field is optional.
  • Folder workspaces. The setup dialog hides checkpoint commits when the workspace isn't a git worktree.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…irst, publish unverifiable holds, require git exclude, log skipped ticks

Claude-Session: https://claude.ai/code/session_01Ra4nVsfKdztCCHFzhu1dNn
… relaunch

The handle-to-pty lookup answers only for headless ptys, so binding, revealing and
relaunching a loop in a renderer tab fell back to the pane resolver.
Copilot AI balanced review requested due to automatic review settings October 19, 2026 10:29

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

React Doctor's no-impure-state-updater flagged setSteerText inside the
run() callback. run() now reports success and sendSteer clears the
draft only when the steer went through.
Re-home the loops seams onto v1.4.230's module splits: the worktree ps
listing moved to runtime/workspaces, the mobile row type moved to
workspace-list-types (structural list equality now covers the loop
summary), and the RPC catalog and skill manifest are regenerated.

Loops on ssh: hosts now refuse with SshHostNotServedError, matching the
sitter, since upstream retired the desktop SSH git provider and relay
exec. Loop agent detection routes by the worktree's resolved host so it
stays off the Active Server focus ratchet.
…-host ratchet

The loop runner is a fork copy of upstream's precheck-runner.ts, which
stays on the same baseline; declared as a fork-loops seam.
The fork does not carry upstream's plain release tags, so fetching
v1.4.224 from origin failed the managed-orcad SSH e2e job at setup.
Upstream's census tests require every runtime RPC method to have a host
ownership and every SQLite table a WSL-move disposition.
Importing beginLoopsShutdown from registration pulled the whole runtime
into main-process-quit, so upstream's quit-veto test timed out loading
it with its dependencies mocked.
@zpyoung
zpyoung merged commit d34de7a into main Oct 19, 2026
77 of 80 checks passed
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.

2 participants