Agentic loops: Ralph worker + manager, two-terminal pane, Dash-driven scheduler - #179
Open
nthomsencph wants to merge 10 commits into
Open
nthomsencph wants to merge 10 commits into
nthomsencph wants to merge 10 commits into
Conversation
Design doc for first-class agentic loops: Ralph-primary loop core (Dash-owned scheduler, fresh context per iteration, external verification), two-terminal worker+manager model with an MCP control bridge, durable state spine, loop policies (ralph/goal/cadence/count), and a file-by-file change list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Introduce the `loop` task kind and its LoopConfig (policy/level/goal/budget/ constraints) across shared types, the Drizzle schema, a guarded ALTER migration, and DatabaseService (map/persist as INSERT-only deep config + a duplicateTask helper for "duplicate as loop"). Type-checks clean; no behavior change for existing standard tasks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Seeds and reads the durable loop files under <worktree>/.dash/loop/ (PROMPT/LOOP/STATE/loop-constraints/loop-run-log). Derived files refresh from config; evolving STATE/run-log are preserved across re-seeds. Templates encode loop-engineering practice: role separation, external verification over self-grading, one-task-per-iteration, constraints injected each cycle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Add LoopScheduler — the dependency-injected state machine that owns the Ralph iteration while-loop (per-policy stop check, maxIterations, cadence waits, token budget auto-pause, pause/resume/stop). Only busy→idle edges advance the loop, so completion is judged by an external check, never self-grading. ptyManager gains an opt-in fresh-context spawn (no --resume) plus a distinct taskId for composite loop:/mgr: PTY ids; ActivityMonitor gains an in-process subscribe() the scheduler watches. 10 unit tests cover the policy/iteration logic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Render a horizontal worker|manager split for loop tasks (LoopTerminalPane), branched in MainContent on taskKind. Both terminals spawn fresh-context with the real task id threaded through TerminalPane → SessionRegistry → session manager → ptyStartDirect IPC, so their Claude sessions don't collide on the shared worktree cwd; each seeds from the on-disk .dash/loop spine. projectsStore disposes the loop:/mgr: PTYs on task close. Standard tasks are untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Re-check run state after the awaited stop check in onIterationComplete: a pause()/stop() that lands while the check is in flight no longer gets overridden by spawning the next iteration. A cadence loop with no positive interval now pauses with a clear reason instead of busy-spawning workers with zero delay. Two regression tests added (12 total). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
Execute the locked agent-settings decisions: - LoopConfig gains per-agent worker/manager config (model/effort/permission), specRefs (PRD/spec files), verifier, and subAgentCap. - loopSpawn.ts centralises per-role spawn policy in main: manager model defaults to the worker's tier (managerWeakerThanWorker flags the anti-pattern), worker permission follows the L1/L2/L3 level, and the manager runs unprompted with a write-deny policy (MANAGER_DENY) passed via `claude --settings` — the right enforcement since `default` would also block the manager's reads/greps. - buildClaudeArgs/startDirectPty learn --model and merged --settings; the renderer threads only loopRole and main derives the rest (it can't be spoofed). - LoopService adds the manager-notes steer file (worker reads it each pass) and a canonical managerPrompt, removing the duplicated renderer seed prompts. - 6 loopSpawn unit tests; plan doc gains the authority/model/worktree section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FmYjhqiW9Wxb9wXRpNzFbc
nthomsencph
force-pushed
the
claude/agentic-loops-dash-ka6xwn
branch
from
September 9, 2026 07:56
ba6b105 to
971a7e3
Compare
Claude Code shipped a family of parallelism surfaces since this plan was written — subagents, agent view, agent teams, dynamic workflows. Agent teams is the one that superficially matches our manager/worker split, so §1 now records why it is rejected as the loop engine: coordination lives inside the lead's process (no insertion point for the scheduler tick, budget gate or pause/kill), there is no fresh-context reset, teammate permissions are inherited and cannot be set per-teammate at spawn, and teammates render into the lead's agent panel or tmux rather than a PTY Dash can attach to. Also notes what is worth adopting from the newer CLI instead (`--agents`, `--permission-mode auto`, teams inside the manager session post-MVP) and the flags the branch's spawn path depends on, verified present at v2.1.240. There is no Claude Code feature called "workstreams" — that is Cowork vocabulary — and the section says so, to stop the question being re-asked. Claude goes brr.. via Dash Claude-Session: https://claude.ai/code/session_017gtmbWYyAsYEBiYQDnf4vy
Completes the runtime wiring the plan listed as remaining (§6–§8), leaving only duplicate-as-loop. LoopController is the single owner of both agents. It implements LoopDriver over ptyManager (startDirectPty / killPtyAwait plus a `loop:` output tap for the count policy), child_process for the stop check, LoopService for the state spine and run log, and webContents for status. The renderer's loop panes are display-only: they reattach via attachOnly and never spawn, so a mount or reload cannot race the scheduler into a second Claude. The manager is spawned once and kept alive; the worker is reset each pass. The loop MCP bridge is a per-task, stateless streamable-HTTP server on the existing HookServer (/mcp/loop?taskId=), attached to the manager only via an inline --mcp-config at spawn so it is trusted without an approval prompt. Control tools are level-gated — at L1 pause and kill stay human. TaskModal gains a Task | Loop toggle; Loop mode collects goal, policy, level, stop check, max iterations, cadence, token budget and constraints, forces a new-branch worktree, and creates the task with taskKind='loop'. The spine is seeded lazily on first Start, so creation needs no seed step. Carries the SpawnModel reconciliation from the rebase onto main: the task picker keeps the narrow TaskModel alias set while the PTY boundary accepts any id `claude --model` takes, which is what loop agents need. Claude goes brr.. via Dash Claude-Session: https://claude.ai/code/session_017gtmbWYyAsYEBiYQDnf4vy
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.
What this is
First-class support for agentic loops in Dash. A task can be a
loop, which runs two terminals side-by-side in the main pane — a Ralph worker that iterates on a goal (fresh context every pass) and a persistent manager that orchestrates — driven by a Dash-owned scheduler. Conceptual base: loop-engineering (role separation + durable state spine) × the Ralph loop (fresh context each iteration, completion judged by an external check, never self-grading).Design + rationale live in
docs/agentic-loops-plan.md.Locked decisions baked into the code
while(not the CLI's/loop//ralph-loop) so budget gates, the stop check, and pause/kill are clean control points..dash/loop/files, not the conversation.claude --settings(Write/Edit/MultiEdit/NotebookEdit+ git-write Bash) —defaultpermission would wrongly block its own reads/greps.What's landed (this PR)
taskKind+loopConfigacross shared types, Drizzle schema, guarded migration,DatabaseService(+duplicateTask).LoopServiceseeds/reads.dash/loop/(PROMPT/LOOP/STATE/loop-constraints/loop-run-log/manager-notes).ptyManageropt-infreshContext(no--resume),--model, merged--settings, distincttaskIdforloop:/mgr:ids; threaded through the renderer spawn path.LoopScheduler, a dependency-injected state machine (per-policy stop check, maxIterations, cadence waits, token-budget auto-pause, pause/resume/stop) +ActivityMonitor.subscribe().loopSpawn.tscentralizes worker/manager model/permission/prompt/deny-settings in main; the renderer only passesloopRole.LoopTerminalPane;MainContentbranches ontaskKind; standard tasks untouched.Testing
pnpm type-checkclean (both processes).LoopScheduler(12, incl. a pause/stop-races-the-check regression and the cadence-runaway guard) andloopSpawn(6)./code-review) found and fixed two concurrency/runaway bugs in the scheduler.Not in this PR (needs a machine that can run the Electron app to verify)
LoopController) +loop:start/pause/resume/stopIPC.loop_status/steer/pause/kill/escalate/...) + injecting the manager deny-settings/hook at spawn.Verification caveat: the
--modeland--settingsdeny-list args follow Claude Code's documented flags but haven't been exercised end-to-end here — confirming the manager's write-deny actually blocks edits is the first check when wiring the MCP bridge. Until the creation modal lands, a loop task can be created by settingtask_kind='loop'on a row to exercise the split pane.🤖 Generated with Claude Code
Generated by Claude Code