Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Recipes

Workflows to copy into .tome/workflows/ and adapt. Each is a real file under docs/examples/ in the repo, and CI runs tome validate on each one. They’re written for a Rust project. Rather than editing one by hand, ask your coding agent to adapt it, for example “adapt the fix-failing-tests tome recipe to this repo”; see Create workflows with an agent.

RecipeStarts when
Plan a feature, then build ita feature file is added, then when its plan is ready
Fix failing testsa source file changes
Review a branch in parallelyou run it
Implement, then reviewa message arrives, then when the first run succeeds
Split work across branchesyou run it
A weekday-morning checka schedule
Keep one agent on callmessages arrive
Tidy docs as you writea doc file changes
A software factorya request, an alert, a schedule, or another workflow

Plan a feature, then build it

Two workflows chained through the message bus. You add features/<slug>/feature.md. plan-feature has an agent investigate the codebase, writes tasks.md, and publishes feature.planned:

---
name: plan-feature
description: When a feature file is added, investigate the codebase and break the feature into tasks.
mode: orchestrated
triggers:
  - manual
  - file: "features/*/feature.md"
    on: [created]
    debounce: 5s
---
A feature was just written down. Work out how it fits this codebase and
break it into tasks.

New feature files (empty for a manual run):

{{trigger.paths}}

## Find features
Each path above looks like `features/<slug>/feature.md`. If there are no
paths, use every directory under `features/` that has a `feature.md` but no
`tasks.md`. Skip any feature that already has a `tasks.md`. If nothing is
left, finish the run successfully and say so.

## Investigate
Create a group named `investigate`. For each feature, spawn an agent worker
in it named `investigate-<slug>`. Tell it to read the feature file, then
find the code the feature touches: the modules and entry points, the tests
that cover them, and any similar feature that was built before. It must not
change any files. It reports as its summary the files that will change, the
patterns to follow, and any risks or open questions. Wait for the group.

## Break down
For each feature, write `features/<slug>/tasks.md` from the feature file and
its investigator's summary. Give each task a `### <N>. <title>` heading, a
`**Blocked by:**` line (task numbers, or `none`), and a short paragraph on
what changes, where, and how to tell it's done. Keep each task small enough
for one agent to implement and test as a single commit. List open questions
at the end.

## Hand off
For each feature that got a `tasks.md`, publish an event so the next
workflow can pick it up:

    tome publish feature.planned <slug>

## Finish
Finish the run with each feature and its number of tasks. If an
investigator failed, name its feature and fail the run.

implement-feature subscribes to that event and implements each task with its own agent on its own branch. Tasks that don’t block each other run in parallel, and a blocked task branches from the task it waits for:

---
name: implement-feature
description: Implement a planned feature, one agent and one branch per task.
mode: orchestrated
params:
  base: {type: string, default: main, description: "Branch the worktrees start from"}
triggers:
  - on: feature.planned
concurrency: 1
---
A feature has a task plan. Implement it.

Feature slug: {{trigger.payload}}

## Order
Read `features/<slug>/feature.md` and `tasks.md`. If the slug is empty, fail
the run and say to fire it with
`tome triggers fire implement-feature --payload <slug>`. Work out the order
from each task's "Blocked by" line.

## Implement
Create a group named `tasks`. A task is ready once every task it's blocked
by has finished successfully. Start each ready task as an agent worker in
the group, named `task-<N>`, on its own worktree:

    tome worker spawn --name task-<N> --group tasks --worktree --base <base> --prompt "<the task>"

`<base>` is {{params.base}} for a task that isn't blocked, and the branch of
its blocker otherwise. If it has several blockers, branch from one and tell
the worker to merge the others' branches first.

Tell each worker to read `feature.md` and `tasks.md`, implement only its
task, run the tests and fix failures, commit as `<slug>-<N>: <title>`, and
report done with a summary of any choices it made. Keep starting tasks as
their blockers finish. Don't start a task whose blocker failed.

## Finish
Don't merge anything: the branches are for review. Finish the run with each
task's branch and its worker's summary. If any task failed or was blocked,
fail the run and name them.

Nothing is merged, so you review the branches and merge them yourself. To re-run just the build for a feature, publish the event by hand:

tome publish feature.planned export-csv

Fix failing tests

Every time you save a Rust file, run the tests. If they fail, an agent fixes them on its own branch and leaves the branch for you to review.

---
name: fix-tests
description: Whenever Rust source changes, run the tests and fix any failures on a branch.
mode: orchestrated
triggers:
  - manual
  - file: "src/**/*.rs"
    debounce: 10s
    while_running: queue
concurrency: 1
---
These files just changed:

{{trigger.paths}}

## Test
Spawn a command worker named `tests` that runs `cargo test`, and wait for it.
If it succeeded, finish the run successfully.

## Fix
Spawn an agent worker named `fixer` with its own worktree. Tell it to make the
failing tests pass without changing what the tests check, and to commit its
work. Give it the tail of the test output from the `tests` worker's summary.
Wait for it. If it failed, fail the run and say why.

## Report
Don't merge the branch. Finish the run with the `fixer` branch name and a
one-line description of the fix as the summary, so it can be reviewed.

The fixer works in a worktree under .tome/, which is never watched, so its edits don’t start another run. concurrency: 1 and while_running: queue mean saves made during a run are collected into one follow-up run.

Review a branch in parallel

One reviewer agent per changed file, all at once, combined into one report.

---
name: fan-out
description: Review every file changed on a branch in parallel, then combine the reviews.
mode: orchestrated
params:
  base: {type: string, default: main, description: "Compare the current branch to this one"}
concurrency: 1
---
Review the files changed since {{params.base}}, one worker per file.

## Split
List the files that changed between {{params.base}} and HEAD. If there are
none, finish the run successfully and say so.

## Review
Create a group named `reviews`. For each changed file, spawn an agent worker
in that group, named `review-<n>`, told to review only that file and to
report its findings as its summary. Then wait for the group.

## Combine
Read every member's summary from the group's status. Write one report to
`review.md`, most serious findings first. If any reviewer failed, list it in
the report and carry on. Finish the run successfully with the path to the
report.
tome run fan-out --param base=main

Implement, then review

Two workflows chained through the message bus. Publishing a request starts implement, and every implement run that succeeds starts review.

---
name: implement
description: Implement one requested feature at a time.
triggers:
  - on: feature.requested
concurrency: 1
---
Implement this feature:

{{trigger.payload}}

## Implement
Make the change on a new branch and commit it.

## Finish
Finish the run successfully, with the branch name as the summary.
---
name: review
description: Review the change that a successful implement run made.
triggers:
  - on: tome.run.implement.succeeded
---
Review the change made by this run. The event says which run it was and
gives its summary:

{{trigger.payload}}

## Review
Read the branch named in the summary and write your findings to `review.md`.
tome publish feature.requested "Add a --verbose flag to the export command"

Anything that can run a shell command can publish: a git hook, CI, a script, or another agent.

Split work across branches

Give it a list of tasks. Each gets its own agent on its own branch, and the orchestrator merges them and runs the tests.

---
name: fan-in-worktrees
description: Do independent tasks on separate branches, then merge the branches.
mode: orchestrated
params:
  tasks: {type: string, description: "One task per line"}
  base: {type: string, default: main}
---
Do each of these tasks on its own branch, then merge the results.

{{params.tasks}}

## Implement
Create a group `tasks`. For each task, spawn an agent worker in it with its
own worktree, branching from {{params.base}}:

    tome worker spawn --name task-<n> --group tasks --worktree --base {{params.base}} --prompt "<the task>"

Tell each worker to commit its work to its branch before it reports done.
Wait for the group.

## Merge
Merge the branch of every worker that succeeded into the current branch, one
at a time, and resolve conflicts as they come. Run the project's tests after
the last merge. Fail the run if a worker failed, or if the tests fail, and
say which.
tome run fan-in-worktrees --param tasks="$(cat tasks.txt)"

A weekday-morning check

A cron trigger, with a manual entry to show you can also run it by hand.

---
name: nightly
description: A weekday-morning check of the open branches.
triggers:
  - manual
  - cron: "0 9 * * 1-5"
    params: {base: main}
params:
  base: {type: string, default: main}
---
Started at {{trigger.time}} for the schedule {{trigger.scheduled}}.

## Check
List the branches that are ahead of {{params.base}} and say which have gone
stale.

Keep one agent on call

One long-lived run takes work items as they arrive, instead of starting a run for each.

---
name: watcher
description: Keep one run going and hand it new work as messages arrive.
triggers:
  - manual
  - on: work.new
    to: running-or-new
concurrency: 1
---
Handle work items as they come in. This run is meant to stay alive.

Start with:

{{trigger.payload}}

## Loop
Do the item. Then pull the next message from your `events-$TOME_RUN_ID` queue with
`tome queue pull events-$TOME_RUN_ID --wait 5m`, ack it, and do that item. When the queue
is empty after the wait, finish the run successfully.
tome publish work.new "Rename the config loader to settings"

Tidy docs as you write

A file trigger that fixes only the files that changed, so it never triggers itself.

---
name: doc-check
description: Check the docs whenever a Markdown file under docs/ changes.
triggers:
  - file: "docs/**/*.md"
    on: [created, modified]
    debounce: 5s
    ignore: ["docs/drafts/**"]
    while_running: queue
---
These files changed:

{{trigger.paths}}

## Check
Read each changed file and fix broken links and typos. Do not touch any file
that isn't on the list, so this run doesn't start another one.

A software factory

Four workflows that pass work to each other on the message bus, so a change goes from request to production with a person involved only for risky changes. It’s modelled on the “agentic software factory” that The Pragmatic Engineer described inside OpenAI.

WorkflowStarts onPublishes
factory-buildfactory.requestedfactory.ship or factory.review-needed
factory-deployfactory.shipalert.outage if the rollout regresses
factory-perfevery hour, or alert.latencyfactory.requested for each fix
factory-sevbotalert.outage or incident.questionincident.proposed, incident.answer, factory.requested

factory-build builds the change, loops until CI and four specialist reviewers pass, and sorts it into low or high risk:

---
name: factory-build
description: Build a requested change, loop until CI and the reviewers pass, then ship it or ask an engineer.
mode: orchestrated
params:
  base: {type: string, default: main, description: "Branch the change starts from"}
  max_attempts: {type: int, default: 3, description: "Fix rounds before giving up"}
triggers:
  - manual
  - on: factory.requested
concurrency: 1
---
Build this change:

{{trigger.payload}}

## Implement
Spawn an agent worker named `builder` on its own worktree, branching from
{{params.base}}. Give it the request above. Tell it to use whatever context
it needs (the code and its docs, and any Slack, Notion or metrics tools its
harness has), to commit its work, and to report the branch and what it
changed as its summary. Wait for it. If it failed, fail the run. Its branch
is now the change's branch.

## CI
Spawn a command worker named `ci-<n>` that runs `cargo build && cargo test`
in the change branch's worktree, and wait for it. If it failed, spawn an
agent worker `fix-<n>` on a new worktree branching from the change's branch,
with the tail of the output from the CI worker's summary. Tell it to commit a
fix. Its branch becomes the change's branch; run CI again. Stop after
{{params.max_attempts}} rounds and fail the run with the last output.

## Review
Create a group named `reviews`. Spawn four agent workers in it, each told to
review the change's branch against {{params.base}} from one angle only:
`review-data` (schemas, migrations, queries), `review-infra` (config,
deploys, resource use), `review-cloud` (cloud services and permissions) and
`review-security` (auth, secrets, input handling). Each must not change any
files. It reports its findings as its summary, each marked `blocking` or
`minor`, and ends with a risk of `low` or `high`. Wait for the group.

If any reviewer has a blocking finding, send those findings to a new fix
worker the same way, go back to CI, and review again. This counts
towards the same {{params.max_attempts}} rounds.

## Classify
The change is low-risk only if every reviewer said `low` and nothing is
blocking. Write the reviews and the verdict to `.tome/factory/<branch>.md`.

## Hand off
If the change is low-risk, ship it:

    tome publish factory.ship <branch>

Otherwise ask for an engineer and don't ship:

    tome publish factory.review-needed "<branch>: <why it is high-risk>"

## Finish
Finish the run successfully with the branch, the verdict and where the
review file is.

A low-risk change goes straight to factory-deploy. A high-risk one waits for an engineer, who reviews the branch and ships it by hand:

tome publish factory.ship <branch>

factory-deploy merges and deploys it, then rolls it out behind a flag while it watches a dashboard it made for the change:

---
name: factory-deploy
description: Roll out a shipped branch behind a feature flag and watch it until it is fully live.
mode: orchestrated
triggers:
  - on: factory.ship
concurrency: 1
---
Deploy this branch: {{trigger.payload}}

Deploys use `./scripts/deploy` and feature flags use `./scripts/flag`. Change
these to your own tools.

## Merge
Merge the branch into main and push. If the merge conflicts, fail the run:
the change needs to go back through `factory-build`.

## Deploy
Run `./scripts/deploy main` as a command worker and wait for it. Fail the
run if it fails.

## Dashboard
Make a dashboard for the metrics this change could move: its endpoints'
latency and error rate, and anything the review file in `.tome/factory/`
calls out. Record where it is.

## Roll out
Turn the change's flag on for 1%, then 10%, 50% and 100%. At each stage,
wait 10 minutes and check the dashboard against the hour before. If latency
or errors get worse, turn the flag off, then publish what you saw:

    tome publish alert.outage "<branch>: <what regressed, and the dashboard>"

and fail the run.

## Finish
Finish the run successfully with the branch, the dashboard and the final
flag state.

factory-perf looks for latency regressions and sends each fix back to factory-build, which closes the loop:

---
name: factory-perf
description: Look for latency regressions, drop duplicates, and request fixes for the real ones.
mode: orchestrated
triggers:
  - manual
  - cron: "0 * * * *"
  - on: alert.latency
concurrency: 1
---
Look for latency regressions in production. An alert that started this run,
if any:

{{trigger.payload}}

## Detect
Compare the last hour's latency for each service with the same hour last
week. List every endpoint whose p95 is more than 20% slower. Add the alert
above if it isn't on the list already.

## Dedupe
Drop anything already in `.tome/factory/perf-open.md`, and merge
regressions that share a cause, such as endpoints slowed by one deploy. If
nothing is left, finish the run successfully and say so.

## Investigate
Create a group named `perf`. For each regression, spawn an agent worker in
it named `perf-<n>`, told to find the cause from the metrics, traces and
recent commits, without changing any files, and to report the cause and a
proposed fix as its summary. Wait for the group.

## Request fixes
For each regression with a proposed fix, add it to
`.tome/factory/perf-open.md` and hand it to the factory:

    tome publish factory.requested "<the regression, its cause and the proposed fix>"

## Finish
Finish the run with each regression and whether a fix was requested.

factory-sevbot keeps one run going for an incident. More alerts and people’s questions reach it on its events-<run id> queue:

---
name: factory-sevbot
description: Investigate an outage, propose mitigations, and answer questions until it is resolved.
mode: orchestrated
triggers:
  - manual
  - on: alert.outage
    to: running-or-new
  - on: incident.question
    to: running-or-new
concurrency: 1
---
There is an incident. The first report:

{{trigger.payload}}

Keep a log in `.tome/factory/incident-{{run.id}}.md`, and add to it as you go.

## Investigate
Create a group named `investigate`. Spawn agent workers in it: one reads the
metrics and alerts, one reads the logs, and one reads the deploys and
commits from the last day. None of them changes anything. Each reports what
it found, with times, as its summary. Wait for the group.

## Mitigate
From the summaries, write down the most likely cause and a ranked list of
mitigations, such as turning off a flag or rolling back a deploy. Don't
apply any of them. Publish the top one so an engineer can act on it:

    tome publish incident.proposed "<cause>: <mitigation>"

## Answer
Pull from your `events-$TOME_RUN_ID` queue with `tome queue pull events-$TOME_RUN_ID --wait 15m`. An
event on `alert.outage` is more evidence: add it to the log and look again
at the cause. An event on `incident.question` is a question: answer it in the
log and publish the answer on `incident.answer`. Ack each one. Keep going
until an event says the incident is resolved, or the queue has been empty
for an hour.

## Finish
Write a timeline and a summary at the top of the log. If a fix is needed,
publish it:

    tome publish factory.requested "<the fix, and a link to the log>"

Finish the run with the path to the log.
tome publish factory.requested "Add a --since flag to the export command"
tome publish incident.question "Is the EU region affected?"
tome publish incident.question "Resolved: rolled back the uploader deploy"

Some things to know before you use it:

  • Context comes from the harness. Slack, Notion, metrics and deploy tools are whatever MCP servers and CLIs your agents have. The ./scripts/deploy and ./scripts/flag commands are placeholders.
  • Alerts need a bridge. tome doesn’t receive webhooks. Point your alerting at a small script, or a cron job, that runs tome publish alert.outage "...".
  • Loops stop themselves. A rollout that regresses goes deploy → sevbot → build → deploy, and each hop adds one to the event’s depth. Events deeper than 8 aren’t delivered, so the factory gets about two tries at fixing its own outage before a person has to step in. Each factory-perf run on the hourly schedule starts again at depth 0.
  • Runs aren’t timed out. The deploy and sevbot runs say in their bodies when to stop, because tome doesn’t enforce timeout.

Next

Write a workflow covers every frontmatter option, and Triggers and the message bus covers every trigger field.