piloop.toml.
Every role has the same lifecycle. The daemon writes a
workers row, starts the agent, waits until the agent ends or
times out, saves the screen transcript, reads the tokens that the agent used, attaches the session to
Entire, and closes the row. piloop status lists the running agents with their ids.
The worker does runs: the run queue starts it, it works at the starting point of its run, and its commits are
scored and judged when it ends. A role that you define can do runs too (Define a role that does
runs). Every other role is a sweeping role. Its triggers start it, it works at the
head or in the project checkout, and nothing is scored when it ends.
The orchestrator is not a role. The daemon never starts it.
Configure a role
Each role has a table inpiloop.toml. These keys are common to every role:
A role that does runs also has these keys:
A sweeping role also has these keys:
The defaults:
[max_concurrent] table limits one harness across every role, for example claude = 2 when your
account limits the request rate. When it is the turn of a harness at its limit, the slot goes to the next harness of
the rotation.
Triggers
A trigger is a condition that the daemon checks on each tick, for each project. When a trigger of a sweeping role fires, the daemon requests one more agent of that role. The agent’s brief shows the reason. Each entry oftriggers is
a table with the trigger’s kind and its parameters:
The daemon checks the triggers in the order of the list. It requests at most one batch for each role on each tick,
and it requests nothing while a request of the role waits to start. An
anomaly or scout fire changes what the
agent does, so it gets an agent of its own: it fires alone when it is the first trigger to fire, and otherwise waits
for a later tick.
A sql query can use two parameters: :project, the project’s id, and :since, when the role’s last agent started.
Compare :since with a created_at column to fire only on what is new. piloop init checks the query on an empty
copy of the database: it must be one statement that only reads.
Change the triggers of a built-in role with its triggers key. For example, this watchdog does not investigate
anomalies, and only reviews progress every hour:
piloop launch).
Define a role
A table with a new name defines a new sweeping role. It needsprompt, the file that starts its brief, and
triggers. The other keys have the defaults of the configuration reference.
This reviewer reads each new keep and files a seed when it finds a problem:
brief.md, the database procedures and its context: its signature
(reviewer#<worker id>), the reason it started, the project overview and the research pack. Run piloop init after
you change the table. piloop launch <project> reviewer asks for one more reviewer by hand.
A role name has at most 20 characters: lowercase letters, digits, _ and -, and a letter first. scout, run,
orchestrator, human and daemon are taken.
A Python package can also add roles and triggers. See
Extension points.
Define a role that does runs
A table with a new name andruns = true defines a role that does runs. It takes the worker’s defaults, its prompt
included, so every key is optional. This role takes the cheap ideas, with a short timeout and its own harness:
[roles.worker] takes = "priority < 3".
Some rules apply to each role that does runs, and some to every run of the project:
- Each role: its own
max_concurrent, itstakes, its harness rotation, its prompt and timeout, itsexplore_every(counted over its own runs), and the track shares (of its own slots). - Every run:
[stop] max_runsandplateau, the attempts of an idea (ideas.max_runs), and the verdict. A keep of a run of any role moves the head. A keep against a bar that the head left behind is merged onto the head in the same way. Research keeps a backlog of one idea per run slot of every role, plusbuffer.
runs.role records the role of each run, and its agents have that workers.kind. When piloop init removes a role,
its queued runs fail. They do not count as attempts, so their ideas go back to the queue.
Worker
A worker gets one idea. Its brief has the worker instructions, yourbrief.md, the idea, the starting point and the
recent results. It may edit the code in its worktree and commit. It writes a finding comment on its idea at the end.
It must not change the rows of runs, candidates or measurements, and it files no ideas or seeds.
Watchdog
The watchdog informs. It never edits code or changes a row. It writes comments: findings, notes and escalations for the orchestrator, and seeds for research. It runs in one of two modes:- An incident sweep, when the daemon detects an anomaly. Anomalies are a failed or timed-out run, a worker or research agent that died at startup, and a worker with no comment or commit for an hour while its jobs run.
- A progress review, every
[roles.watchdog] intervalseconds. It asks whether the campaign is getting closer to its goal. When the campaign stops, one last progress review writes the closing report.
Research
The research agent keeps the workers supplied with ideas. A sweep:- Reads the research pack that the daemon builds: the metrics, what each source of ideas produced, the analysis
files listed in
[roles.research] context, what was tried for each mechanism, the latest 60 ideas with their outcomes (and the total count), the kept lineage, and the findings.piloop historyprints the same pack. - Develops each waiting seed into an idea, or rejects it with the reason.
- Adds ideas of its own, up to
max_ideas, where the head loses the most. - Writes one
findingon the project.
- A seed is filed.
- The backlog is short: fewer ready ideas than one per worker slot plus
buffer. When a sweep adds no idea, research waits longer before the next one.
piloop launch <project> research.
Scout mode
Experimental: scout mode.
- It writes a problem statement first: the open problems as measured losses, what was tried, and the constraints.
- It searches X, GitHub and arXiv by problem. It also checks a watch list: the X accounts and GitHub repos whose earlier seeds paid off. It uses no curated list of people.
- It ranks the results by substance: a number against a baseline, code, a method, an ablation.
- It files at most
seedsof them as seeds. The others go to the technique library, thetechniquestable. - It writes one
findingon the project, at priority 3, so that it reaches your inbox.
interval has no default:
piloop launch <project> research --scout. A search tool for X needs an MCP server; see
MCP servers.
Ask for one more agent
piloop launch <project> research (or watchdog, or a role that you defined) asks the daemon for one more agent of
that role. The daemon starts
it on its next tick, within max_concurrent. Use --harness and --model to choose the agent’s harness and model,
and --reason to add a line to its brief.
An agent can do the same with SQL. The workers table is the launch queue:
Look at an agent
piloop watchshows the agents with their last output.piloop attach <worker-id>focuses the agent’s tab in the piloop Herdr session and attaches your terminal.piloop prompt <worker-id> "<text>"types text into the agent’s pane.
piloop attach needs runtime = "herdr", and piloop prompt needs a runtime that can deliver a message, such as
Herdr. With the default subprocess runtime, every agent is a background process without a pane.
Each agent’s brief, log and transcript are in ~/.piloop/runs/<run>/ for an agent that does a run, and in
~/.piloop/workers/<worker-id>/ for the sweeping roles.