toryo docs
Documentation navigation

Writing your own sequences

The sequences we ship are defaults, not doctrine. code-task, review-task and research-task encode how we build. They exist so a new install is useful on day one, and plenty of people run them unchanged and never write one.

This page is for when your process differs. It is a capability, not a step you owe anybody.

code-task is written in the format this page describes, which makes it the longest worked example there is. toryo sequence source code-task prints it, comments and all, once it is installed.

What you are actually writing#

A sequence is a YAML file, either global in ~/.toryo/sequences/ or scoped to a repository under .toryo/sequences/. Nothing is compiled into toryo and there is no plugin to register. The engine reads your file and runs it exactly as it runs ours.

What goes in it is your own process: the steps, what each hands the next, which of them run an agent, and where the run should stop and ask a person. The things a team argues about in review, or that a senior engineer does from memory.

Because it is a file in your repository, it travels the way the rest of your code does. It is reviewed in a pull request, versioned with the code it describes, forked by another team, and handed to a new hire as a working definition of how you build rather than a wiki page about it.

Where a sequence can live#

Two homes, and a project beats the global one:

  • ~/.toryo/sequences/: yours, on this machine, for every project.
  • <repo>/.toryo/sequences/: the project's, and travels with it in git.

In a team project, the repository home is how a sequence is shared with the team. See Share sequences with your team.

Name a project sequence after a global one and the project copy runs. Board marks the shadowed name with a fork badge so it is never a surprise which one won. Shipped YAML is installed into the global home as an ordinary operator-owned file: you may edit, clone, source, or delete that installed copy. The installer only upgrades an untouched copy and preserves one you changed.

One name is reserved. ideas-verify is toryo's own maintenance machinery, fired on a schedule, and it refuses to be replaced by a file of that name.

Four ways to make one#

None of them is a text editor unless you want it to be.

Describe it to an agent#

Board's Sequence Design screen has an Author with agent door. You say what you want in prose, an agent drafts the YAML, and you keep talking until it is right. Every turn snapshots the draft it started from, so any turn can be reverted.

The draft lives outside your live sequences directory until you accept it, so nothing half-finished is ever runnable. And board re-validates the file itself on every turn rather than trusting the agent's summary: Accept stays disabled until that check is clean, whatever the agent claimed it did.

Fill in a form#

The same screen's detail rail has a Parameters view over any sequence's input: fields. Each one binds to Ask at run time, Default, or Fixed.

Fixed is the one worth knowing. Pin baseBranch: main for your team and the run form stops asking: the field disappears from the launcher instead of being answered identically forever.

Amendments patch the document rather than rewriting it, so your comments and layout survive.

Write the YAML#

The editor is right there on the same screen, and toryo sequence validate is the same gate from a terminal:

toryo sequence validate ./my-sequence.yaml

It compiles the file and reports unknown steps, steps nothing can reach, disallowed fields, bad routing, and template tokens that will not resolve. Run it before anything else.

If you are writing YAML with an agent's help in your own session, the /toryo-sequence-authoring skill is the full format reference and is already installed.

Import somebody else's#

toryo sequence import ForceBuilders/toryo-sequences --path sequences/nightly.yaml

import fetches the file, compiles it, prints the whole thing, and writes it only once you say yes. A GitHub source is pinned to a commit sha first, and the written file carries a provenance header naming where it came from and the digest of what arrived.

Read the prompts before you accept. The format is pure data, so loading a stranger's file cannot execute anything inside toryo itself. That says nothing about what the sequence does: a worker step's prompt reaches an agent session with a shell. That is why the preview prints every prompt verbatim and never truncates, and why import is a terminal command rather than a button.

Managing the files#

Five verbs, so nothing has to be dropped into a directory by hand. Each takes an optional --project naming which home to act on.

toryo sequence create nightly-triage --file draft.yaml
toryo sequence source nightly-triage > draft.yaml
toryo sequence clone  nightly-triage weekly-triage
toryo sequence edit   nightly-triage --file draft.yaml
toryo sequence delete weekly-triage --project my-repo
  • source gives you the file's bytes back, comments and all, and piping it into edit round-trips exactly.
  • create, clone and edit run the validate gate before touching disk, and the write is atomic. A rejected edit leaves the previous file exactly as it was, so a bad change can never leave you with half a sequence.
  • clone rewrites the name textually, so the copy keeps your comments.
  • edit never renames. Renaming is clone then delete.

One thing is refused on purpose. toryo's own maintenance sequences refuse edit and delete, so a scheduled job cannot have its machinery pulled out from under it. The three shipped workflows are YAML, not maintenance built-ins, and become ordinary files you own when installed.

What a step can be#

Six kinds, covered on Sequence: worker (an agent session), completion (one model call returning JSON you declared the shape of), transform (reshape the last step's output), setup (the side-effecting one: mint a git worktree, clone a repo, copy files into place, run a command), human (stop and ask you), and acceptance (run mechanical commands through dispatch with no model and grade their exit codes). Acceptance takes commandsFrom: for a computed command list and reuse: true to reuse a passing grade recorded in the same run at the same head, clean tracked tree and command list.

An exec setup step can run an ordered list of labelled commands:, at most 10. Each command's output is readable by label. With onFailure: stop, the first failure not allowed by allowFailure ends the step successfully with the output so far recorded, leaving later commands unrun for the next step to inspect.

A human gate is worth designing rather than accepting. It can show the operator files the run produced, link out to something an earlier step made (the pull request, say), put the agent's own questions in front of them as pickable options, and render decision buttons you named rather than a generic form. Use actionsFrom: to offer only the actions that apply on this park, chosen by the gate's promptFrom: output. An action's picksField carries the operator's answered question picks as structured data for a later step, beside textField for prose.

A few smaller knobs are worth knowing because they answer questions the obvious form of the file cannot:

  • A step's harness and model can be chosen per run, from a value the caller passed or an earlier step produced (llm: {from: run.providers.plan, cases: …}). The path may reach into a nested input field, and every segment is checked when the file loads rather than silently taking the fallback forever.
  • A step's write scope can come from an earlier step (writeScopeFrom: steps.plan.output.predictedPaths) instead of being a list you type. That is how two runs touching the same files queue behind each other rather than racing: the planner predicts the change-set and the implementer declares it. Beside it, refreshBaseFrom: names the base dispatch tries to fast-forward onto immediately before spawning the worker. The refresh is best-effort.
  • Reasoning effort is per provider, with effort: {claude: medium, codex: high} inside an llm block.
  • Observations can survive between steps, through cache:, a run-scoped keyed store. Sandboxed code or a setup step writes entries and later sandboxed code reads them, stamped with the producing step and attempt.
  • An object can accept extra keys with additionalProperties: true on an object field with declared properties, or beside schema: in a jsonFile success check. Declared fields keep their types; undeclared keys are accepted.
  • A sandbox transform is handed the run's artifact directory as state.runDir, so a transform that builds a prompt can name a path the agent it feeds will read. There is still no filesystem inside the sandbox.
  • A step's prompt can have its varying parts computed (promptFrom:), by your own TypeScript in that same sandbox. The prose stays in the file, where you can read and grep it; only the parts that actually change (a block a retry carries, a capped list, a sentence that pluralises) come from code, as fields the template names. It runs inside the step it builds, so what it printed and how long it took land on that step's own row. toryo sequence transform <file> --emit-types writes the step types and one shared State file per sequence, so transforms can import their checked shapes.

code-task exposes the first knob as one picker per worker role. A bare claude, codex, copilot, or cursor keeps that role's default model; harness:model pins both. The allowed pairs come from the harness model vocabulary, so an impossible pair is rejected before the run starts.

An edit never disturbs a running run#

A run pins the sequence it started with. Edit the file mid-run and the run in flight carries on against what it began with, while the next run picks up your change. There is no daemon to restart and no window where a half-saved file is what executes.

A parked gate keeps the acknowledgement form of its pinned version, so board shows the form the run will accept even after you edit the gate. An answer with fields wholly unlike the pinned form is refused with a message naming that version; other schema mismatches name the validation error.

Queued and dependency-blocked runs have not started, so their input and routing metadata can be updated safely. After a run starts, re-pin can move a failed, paused or cancelled run onto a repaired sequence. It compares the new graph against the recorded steps, asks for --force for changed steps already run, and refuses incompatible history or successful output. It does not resume the run; retry or acknowledgement is still the separate action that moves it again.

Terminal runs can be archived and unarchived. Archive changes list visibility, not execution state, and is deliberately separate from retry.

Where things go wrong#

  • validate fails on a template token. The step you referenced does not run before this one, or you spelled its output field differently.
  • A warning names a disabled harness. The file is fine; this machine has that harness switched off. See Harnesses.
  • Board shows a fork badge. A file is shadowing a shipped sequence of the same name. That is the mechanism working, but it is worth knowing which one ran.
  • An installed shipped sequence is not upgraded. You edited its bytes, so the installer preserves your copy. Clone it before replacing it if you want to compare with the newer shipped version.

Full command surface: CLI & contract reference.