loopy-loop

Workflows

A workflow is a single named unit of work the loop can run: a prompt plus a small scheduling config. Workflows are grouped into workflow sets, and the coordinator picks one eligible workflow to run each iteration. This page covers how workflows are laid out on disk, how their scheduling fields decide what runs next, and the three templates that ship with loopy-loop.

Workflow sets and folder layout

Workflow sets are mandatory — even a single-loop repo uses one. A set is a directory of workflows under .loopy_loop/workflow_sets/, and each workflow is a folder holding exactly two files:

.loopy_loop/workflow_sets/<workflow_set>/workflows/<workflow_id>/
├── config.yaml
└── prompt.txt

The workflow id is the folder name. prompt.txt is the prompt the workflow runs — the coordinator renders it with the concrete session paths before handing it to a worker. config.yaml holds the scheduling fields described below. For example, the recommended template lays out four workflows like this:

.loopy_loop/workflow_sets/inner_outer_eval/workflows/
├── outer/
├── inner/
├── eval_reviewer/
└── eval_runner/

Your loopy_loop_config.yaml selects the default set with workflow_set, and loopy coordinator --workflow-set NAME can override it for a single session. Workflow definitions are part of your repo and should be committed.

The older flat .loopy_loop/workflows/... layout is not loaded at runtime; the coordinator will point you at the workflow_sets/ location if it finds workflows in the legacy spot.

Scheduling fields

Each config.yaml describes when its workflow is eligible to run. A full config looks like this:

enabled: true
priority: 0
run_every: 1
must_follow: null
not_before_iteration: 0
run_on_start: false
run_after_successes: null
emits_goal_check: false
description: ""

Each turn, the coordinator collects every eligible workflow and picks a winner. Eligibility and ordering come from these fields:

FieldTypeDefaultMeaning
enabledbooleantrueWhether this workflow can be scheduled at all.
priorityinteger0Tie-breaker among eligible workflows; the higher value runs first.
run_everyinteger1Minimum number of completed iterations between runs of this workflow. Counts completed iterations, not wall-clock time.
must_followstring or nullnullThe workflow only becomes eligible immediately after a successful run of the named workflow.
not_before_iterationinteger0Earliest completed-iteration count at which the workflow is eligible.
run_on_startbooleanfalseMakes the workflow eligible before any workflow has succeeded — useful for a first-turn setup step.
run_after_successesmapping or nullnullUnlocks the workflow after every N successful runs of another workflow (see below).
emits_goal_checkbooleanfalseDeclares that this workflow writes goal_check.json; the worker then includes that output path in the prompt.
descriptionstring""Human-readable note about the workflow's purpose.

A few rules are worth calling out:

  • must_follow and run_after_successes.workflow_id must reference workflow ids that exist in the same set. A dangling reference fails the coordinator's startup preflight.
  • run_every is based on completed iteration count, so run_every: 3 means "at most once every three completed iterations," not "every three minutes."
  • priority only decides ties among workflows that are already eligible — it never makes an ineligible workflow run.

run_after_successes

run_after_successes schedules a workflow on the cadence of another workflow's successes. This is how the eval workflows fire periodically rather than every turn:

run_after_successes:
  workflow_id: inner
  every: 10

Here the workflow becomes eligible again after every 10 successful inner runs. Combined with run_on_start: true, it can also run once at the very beginning before any inner success exists yet.

The reserved goal_check workflow

goal_check is a reserved workflow id. The default template scaffolds exactly this one workflow, and it plays a specific role: it evaluates whether the repo already satisfies the goal and writes the per-iteration goal_check.json eval artifact. Don't rename or delete it in a set that relies on it.

Two behaviors are special:

  • It waits for real work. goal_check is not eligible until at least one non-goal_check workflow has succeeded, so it never runs against an untouched repo.
  • Other workflows can emit goal checks too. Setting emits_goal_check: true on any workflow tells the worker to include a goal_check.json output path in that workflow's prompt, letting it write the same eval artifact. In the inner_outer_eval template, eval_runner uses this instead of a standalone goal_check workflow.

Writing goal_check.json is evidence, not a stop signal. Stopping the loop always requires updating the session's control.json. See Success & Control for the stop contract and Evaluation for how eval checks are produced and consumed.

The shipped templates

loopy init --template NAME scaffolds one of three workflow sets. Pick based on how much structure your goal needs.

default

The minimal starting point. It creates only the reserved goal_check workflow in a set named main. Choose this when you want to design your own workflows from a clean slate and only need the built-in goal check to begin with.

inner_outer_eval

The recommended template for most real work. It creates four workflows that form a plan → implement → evaluate rhythm:

  • outer — reviews durable project state, updates the high-level plan, and prepares the next available leaf task.
  • inner — implements the selected leaf task in the target repo and marks it waiting for review (must_follow: outer).
  • eval_reviewer — creates or refreshes session-scoped eval-banana checks (run_on_start: true, then after every 10 inner successes).
  • eval_runner — runs those checks and writes goal_check.json (must_follow: eval_reviewer, emits_goal_check: true).

Choose this when you have a substantial goal with observable completion criteria and want planning, implementation, and evaluation cleanly separated.

pm_planner_dispatcher

A PM-orchestration set for breaking a large effort into child sessions. Its two workflows coordinate work rather than implementing it directly:

  • planner — maintains PM state, selects one work item, and reviews terminal child-session evidence (run_on_start: true).
  • dispatcher — writes one child-session request for the selected item, or imports finished child evidence back into PM state (must_follow: planner).

Choose this when the goal is really a portfolio of separable sub-goals, each of which deserves its own focused session. See Child Sessions for how the parent spawns and resumes children.

Where to go next