Scheduling & self-healing
The daemon’s scheduler decides what to work on next at every tick. Its logic is deliberately simple and inspectable.
Scheduler modes
Section titled “Scheduler modes”Each iteration starts by picking a mode for the selected project:
| Mode | When | What happens |
|---|---|---|
| implement | Tasks in ready |
Pick a task, run the agent, validate, promote. |
| plan | Tasks in proposed, none ready |
Generate a spec (.openloop/specs/) so the task can move to ready. |
| idle | Nothing queued | Look for improvements to propose; see below. |
Preview the choice without running anything:
openloop run-once -p api --dry-run{ "mode": "implement", "role": "implementer", "taskId": "harden-webhook-retries", "reason": "…" }Worker roles
Section titled “Worker roles”The prompt given to the agent depends on the task and mode:
sdd-planner, spec-driven planning of proposed tasks.implementer, implements ready tasks.repo-improver, idle-time discovery of missing tests, docs, refactors.ci-healer, self-healing fix tasks.
Project selection
Section titled “Project selection”With multiple linked projects the daemon picks one per tick:
round-robin(default), fair turns across all projects.priority, prefer the active project (openloop project activate <alias>) whenever it has work.focus, work the active project exclusively until it’s drained.
openloop config set runtime.projectSelectionStrategy priorityOne active worker per project at a time, repos are never processed concurrently against themselves.
Self-healing
Section titled “Self-healing”When validations fail after a run (or pre-existing breakage is detected), the daemon can triage a bounded fix task:
- Allowed kinds come from
policy.yaml→selfHealing.allowedTaskKinds(default:lint-fix,type-fix,localized-test-fix). - Anything else (broken builds, broad test failures) is not run unattended; it blocks and waits for a human.
- Self-healing respects the same attempt ceilings and timeouts as normal work.
Idle mode & continuous improvement
Section titled “Idle mode & continuous improvement”When a project has no queued work, the daemon uses the idle window to look for gaps (missing tests, stale docs, small refactors) and proposes tasks (discovery, scope-proposal). Proposals land in proposed; nothing runs until they’re promoted to ready (by you, or by plan mode on the next pass).
Cron schedules
Section titled “Cron schedules”Per-project schedules in project.json create tasks on a cron cadence:
{ "schedule": [ { "id": "weekly-deps-audit", "cron": "0 9 * * 1", "title": "Audit dependency updates" }, { "id": "nightly-tests", "cron": "0 2 * * *", "title": "Run flaky-test sweep", "prompt": "Re-run and stabilize recently flaky tests" } ]}- Five-field cron, evaluated in UTC.
- Fires create
proposedtasks (medium-risk by default). - After downtime, missed schedules catch up for up to 7 days.
Worktree isolation
Section titled “Worktree isolation”With runtime.useWorktree: true, each run executes in a dedicated git worktree on an openloop/<taskId> branch; your working tree is never touched. The scheduler refuses to fall back to the main tree; if worktree setup fails, the run is blocked. Customize the branch prefix with runtime.branchPrefix.