Playbooks

Published

Updated

A playbook is a written process in your account's Handbook that starts tasks automatically and runs them as your agents. You describe when it should run and who does each step, and Wallfacer starts the task and routes the work when a matching event arrives.

An agent is an AI teammate with a name, a role, its own computer, and its own credentials. A task started by a playbook runs as one of your agents, and its work appears under that agent on GitHub and in your account.

Where the Handbook Lives

On web, open Handbook from the navigation sidebar or drawer. Its page tree shows your pages, the playbooks filed under them, and a link to archived pages.

On iOS and Android, switch to Configure and open Handbook. The page tree appears on the Handbook screen.

On web, open the + menu beside search in the handbook rail: New page writes a page, New playbook writes a playbook. A new document is filed under whatever you are currently viewing, so open the section you want it in first.

Steps

A playbook is an ordered list of steps. Each step has a kind that says what it does:

  • Act. An agent runs a coding session to do the work.
  • Wait. The playbook pauses until its condition is met before moving on.

Set a step's kind from the step card. Changing the kind keeps the step's body text and rebuilds the rest for the new kind.

Nothing in a playbook waits for somebody to come into Wallfacer and press approve. Where a process must not continue until a person has decided, write a Wait step on the signal that decision leaves in your own system: an approving review on the pull request, a label on the issue, a passing check. You decide where you already work, and the playbook reads the result.

Adding a Step

While editing, choose Add step and select Act or Wait. On iPhone the + menu sits in the bottom toolbar; on Android a + button opens a sheet. On iPad and web, use the Add step row at the end of the playbook. The phone controls remain reachable while you scroll.

Replying While an Act Step Runs

You can send a message on the task while an Act step is running. Wallfacer queues it until the step's current turn finishes, so the message does not interrupt the step or start a separate conversation. The agent then receives the message in the step's existing conversation and answers with the completed work in context.

If the agent has asked a question, your answer resumes the paused step instead. Once a playbook run has stopped, a message follows the takeover path below.

Objective

A playbook's Objective states in one sentence what must be true for a run to finish. The optional Abandon if sentence states when the work is no longer needed. People and agents working a step can see these sentences.

Wallfacer watches the conditions published with the objective. A matching event finishes the task as done from its current step, including when a person completes the work outside Wallfacer. A matching abandon condition closes it as cancelled. Both outcomes stop the current step and any running session.

The editor compiles a changed sentence and previews the watch conditions when you publish. Over the API, POST /v1/accounts/{account_id}/pipelines/{pipeline_id}/objective compiles and previews a sentence without saving it. Put the sentence in objective or abandon_if, add the returned conditions to resolutions, and publish a version. Set outcome to cancelled when compiling the abandon sentence. See Tasks and Sessions for finishing a task by hand.

The seeded Implement Assigned GitHub Issues, Review Prepared Pull Requests, Validate and Triage Bugs, and Research and Improve Ideas playbooks include objectives.

Who Performs a Step

Every step carries a Performed by line naming who does it. Both kinds run a session, so an agent performs every step, and the picker offers your account's agents.

  • An agent. What powers an agent, its computer, harness, and model, is set on the agent's own record, not on the step.
  • The task's performer. A step can follow whoever is already performing the task instead of naming a fixed agent. When the playbook's trigger routes an event to a specific agent, that agent performs the step; on a task with no routed performer, your account's default agent does. Pick this when one playbook should run as whichever agent the incoming event was meant for.

Picking a specific agent pins the step to that agent. Picking the task's performer clears the pin, so the step tracks the task instead. See Tasks for how a task shows which agent is performing it.

Waiting on Something

A Wait step watches for a condition and re-checks it on a cadence: a pull request that gets reviewed, an issue that moves, an answer to a question the agent asked. Give every wait an On failure route, so the playbook decides what happens when the condition is never met instead of the task stopping where it stands.

Write the check in plain English. The step's body says what to look at, what counts as passed, and what counts as failed. Each check reports one of three verdicts: pass continues the playbook, fail takes the step's On failure route, and pending leaves the wait in place for the next check.

Choose how the check is made. The card's Check by line offers two kinds. reading the events it was sent judges the check against what has already arrived, which is cheap enough to run often, and carries a Settle at 80% confidence line you can raise or lower. running an agent with tools starts a session that can go and read your systems, which costs more and runs less often. Pick tools when the check cannot be answered from the events alone.

A matching event can settle the wait outright. Where an event decides the step by itself, the card lists it under Settles as soon as, reading github · pull_request_review passes it or fails it. A wait like that reaches its verdict the moment the event lands, with no check of any kind and none of its budget spent. The line is read-only: a wait carrying such a rule round-trips it untouched, and the editor does not author one yet.

Set the cadence and the budget. The numbers follow the kind of check: a judged check reads "Re-check every 1 minute, up to 60 times", one with tools "Re-check every 5 minutes, up to 6 times". Both are yours: an interval from a minute to a year, and anywhere from 1 to 1000 checks. A wait you never pace keeps whichever default its kind carries, and follows that default if it changes; set either number and it is kept exactly as you wrote it. A wait that spends its checks without passing fails through its On failure route, so a signal that never arrives ends the step rather than parking the task forever.

Waking on the Signal

Polling alone means the playbook moves at the cadence you set. Add a signal and it moves the moment the fact lands: under the cadence, wake early when an event arrives opens an editor where you name the source and type (github · pull_request_review) and the conditions that make the event this task's. The step then reads Wakes early when github · pull_request_review arrives, 1 condition.

Conditions are rows, not an expression. Each row is a field of the event, is or is not, and a value: the pull request's head ref is this task's branch, the review's state is not commented. Add rows with add condition. From the second row on, the editor appends and all of these are true, so the conjunction is copy on the card rather than something implied, and every row has to hold for the event to wake the wait. There is no or in the grammar, and nothing you write is compiled into anything else.

A wait with a signal parks without an eager check and lets the event wake it. A wake checks immediately and spends none of the scheduled checks, so the budget is what bounds the wait, not how often the world moves.

Match the event to this task. A signal that names only the source and type wakes every task waiting on that event. Match a field of the task as well, its branch or its issue number, and the step card tells you when you have not: "Add a condition matching this task's branch, or this wakes every task waiting on this event."

A wait written in the old match grammar wakes nothing until you rewrite it. Waits authored before conditions existed carried a match expression, and that grammar is gone. The step card says so, and the wait moves on its cadence alone. Republishing does not convert the expression: open the step and write the condition again as rows. Publish without them and the step keeps its source and type but loses the wake watcher, rather than waking on every event of that kind.

The conditions editor is a browser control. A phone shows the signal read-only; write the cadence there and the conditions at a desk.

The Seeded Playbooks as Examples

Every account starts with playbooks that pause this way, and they are the shortest way to see the pattern:

  • Implement Assigned GitHub Issues ends at "Wait for an approving code review". The check reads the pull request for the task's branch and passes when a person has submitted an approving review, fails when the newest review requests changes (which loops the work back to the step that wrote the code), and stays pending while only the reviewing agent has spoken. It wakes on github · pull_request_review for its own branch.
  • Validate and Triage Bugs ends at "Wait for someone to label the issue invalid". The agent posts its reasoning on the issue, and a person confirms it by adding the invalid label where they are already reading. It wakes on github · issues for that issue.
  • Research and Improve Ideas ends at "Wait for an Engineer to label the issue ready". Same shape, with the ready label as the sign-off.

All three check daily and give up after fourteen checks, about two weeks, then take their On failure route.

For an assigned issue being implemented across playbook steps, Wallfacer finds an open pull request whose body uses a GitHub closing keyword for the issue before the next step starts and continues on that pull request's head branch. If multiple open pull requests close the issue, or GitHub cannot be checked, the task stops instead of opening another pull request.

What the Task Shows While It Waits

A task parked on a wait carries a Waiting on a signal callout: the step's title, the check as you wrote it, the signal it continues on, when it checks again, and how many of its scheduled checks it has used. It ends with "Nothing to do here." There are no buttons on it, because there is nothing to press: the task is watching your systems, and it moves when they do.

The callout names the conditions. A signal with conditions reads "Continues when github · pull_request_review arrives and:" followed by each one, so a wait narrowed to this task's branch does not read like one that continues on any review in your account. A condition on several values reads "is one of", and one on a field that must be absent reads "is not set". The callout names each field by its event path (event.data.action) where the editor shows the humanized label, so the same condition is spelled differently on the two surfaces. A wait still carrying an old match expression gets its own sentence instead, saying it wakes nothing until the playbook is republished.

A wait ends when the work it is watching ends. A pull request closed without merging, an issue somebody closes, and a question nobody answered are all endings. The wait settles as soon as one arrives and takes the step's On failure route, rather than re-checking for hours or days for a result that can no longer arrive. The wait's outcome names what ended and why.

A close is an ending only when the wait says so. The same closed issue means different things to different waits: to a playbook watching its sub-issues land it is progress, and to the task implementing one of them it is the end of the work. So a wait watching GitHub states which it means, and a wait that does not keeps reading a close as movement. A merged pull request is always movement.

When a Step Fails

Each step card ends with a failure sentence naming where the playbook goes if the step does not succeed: halt the task, continue to the next step, or go back to an earlier step. Halting is the default.

Routing back to an earlier step makes a loop, so work returns to the step that can fix it. A wait on a code review that comes back with changes requested can send the task back to the step that wrote the code. A step other steps loop back to also carries an attempt ceiling, "Allow up to 5 attempts before giving up," which you can set anywhere from 1 to 50. Once the ceiling is reached, the task stops with an error rather than looping again.

An agent step can recover automatically when its harness disappears or a temporary harness startup failure interrupts it. Wallfacer waits about 30 seconds before relaunching the step, with at most two infrastructure retries shared across these failures and VM failures. The task shows a retrying state while it waits. An infrastructure relaunch reuses the same playbook attempt and does not consume the step's max_attempts ceiling. An open question is not relaunched. If its VM dies, the action fails immediately and follows the configured failure route. A missing or rejected model credential, an exhausted agent budget, and other failures that require a change also fail immediately.

A step that keeps failing to start is bounded by time as well as by attempts. Every attempt draws on one budget for that step, about 18 hours by default, and a retry does not reset it. When the budget runs out or the infrastructure retry limit is reached, the step fails and the playbook follows its failure route. With the default halt route, that ends the task.

When to Run This

Every playbook has a When to run this section, and a trigger in it is two sentences. Run this when describes, in plain language, what should start the playbook. Don't run it when is optional and carves out the cases it should skip.

Run this when somebody asks me to review a contract before it goes out.

Don't run it when they're asking about a contract that has already been signed.

That is the whole grammar. Nothing is compiled, there is no filter to build, and there are no field paths to type: the sentences are stored as you wrote them and read at the moment an event arrives.

Wallfacer checks each possible trigger against the full incoming event before starting a task. A trigger fires only when that check confirms the event matches its Run this when and Don't run it when sentences. If the check is unavailable, it starts no task for that trigger. One merged pull request confirmed for three playbooks starts three tasks.

Triggers judge the events your account receives, not Wallfacer's own. A task starting, a step running, a playbook spawning work: the platform raises events as it runs your tasks, and those never reach a trigger. A playbook run cannot set another playbook off, and a trigger sentence describing one of these runs will not fire. Write triggers about what happens in your own systems: a pull request merging, a message arriving, an issue being assigned. A Wait step still wakes on platform events, and a schedule is unaffected.

Playbooks written before triggers took plain language were converted for you. The source, type, and filters each one carried now read as the same two sentences, editable like any others.

Or run it on a clock. A trigger can be a schedule instead: pick every day, every weekday, every week, or every month, then a time and a timezone. A schedule runs on that cadence, and nothing about the events your account receives affects it. A schedule set outside the editor that these controls cannot express is shown as written rather than rewritten into the nearest thing they can say.

What each trigger shows under "When it runs". Two lines: whether it opens a new task each time or keeps one task per entity you choose, and the Performs as line deciding which agent the task runs as.

The field pickers on those two lines offer only the fields your account's events actually carry, so you route and deduplicate against the real shape of your GitHub issues and comments rather than a guessed schema.

A trigger reads what an event is, not what it mentions. An event's source, type, and action say what happened; the titles, bodies, comments, and names inside it are details of that one happening. An event that only mentions, describes, or links to the happening your trigger describes does not start the playbook, and neither does one that names the playbook or its subject. A pull request about what a playbook does is not that playbook's trigger.

A playbook with no trigger runs only when someone clicks Start task from the Tasks page.

Disabling, Archiving, and Renaming

Disable a playbook to stop its event and schedule triggers. The playbook stays visible and editable, and you can still start a task from it manually. Tasks already running from it continue. Enabling a disabled playbook turns its triggers back on; if a step's performer needs a computer but has none, the enable request is refused until you give that performer a computer or publish a version with a different performer. Renaming or moving an enabled playbook does not require that check.

Archiving a playbook disables it and removes its triggers. You can publish a version while it is archived, but that does not turn its triggers back on. Restore it first, review it while disabled, then enable it separately when you want automatic runs again. An archived playbook cannot be enabled directly.

A playbook name must be unique among active playbooks in the account. Renaming one to a name already in use reports a conflict and leaves the playbook unchanged, including any other changes sent with that rename. Creating or restoring a playbook may add a numeric suffix when its desired name is taken.

Routing Work to an Agent

The Performs as line on a trigger decides which agent a started task runs as. By default the task runs as your default agent. Point it at a field on the event instead to route each task to the agent the event names. Wallfacer reads how to route from the kind of field you pick.

Match an identity field. Point Performs as at a structured identity field, such as the issue's assignee login, and the task runs as the agent whose GitHub username matches that field. Match on a different attribute, such as name, display name, email, or job title, when your agents are not keyed by GitHub username. At most one agent matches, so exactly one task starts.

@-mention in a free-text field. Point Performs as at a free-text field such as the issue body or a comment body, and every agent @-mentioned in that text gets its own task. Mention two agents in one issue or comment and each one picks up a separate task and does the work as itself, on its own computer and under its own identity. A handle counts as an address only when it stands on its own: @ada routes to Ada, while an email like [email protected] or a URL like github.com/ada does not.

Only your agents are addressable. A mention of a teammate who is not a runnable agent, or a handle that matches nobody, is skipped.

One Task at a Time

A trigger either opens a new task each time it fires, or keeps one task per entity you choose, for example one task per issue. When it keeps one task per entity, a repeat of the same event folds into the task that is already running instead of starting a duplicate: editing the same issue again updates the open task rather than spawning another.

A task that has finished or failed no longer holds its place. After a triggered task fails, repeating the action that started it, such as reassigning or relabeling the issue, starts a fresh task. The failed task stays in place so you can inspect it.

When routing fans out to several @-mentioned agents, each agent keeps its own task. Re-mentioning one agent folds into that agent's open task, while a first mention of another agent on the same issue starts a task for them.

The Run Overview

A task started by a playbook has an Overview alongside its chat, showing how the run is going: the playbook's name, the agent performing the task, a link through to the playbook in the Handbook, and a timeline of the steps that have run. Each step row carries its number, title, performer, and status, and expands to show its result. A question the run is waiting on appears in the same list, and a finished run ends with its outcome.

Export downloads the whole run as a Markdown audit record: the playbook and its version, every step with its performer, model, and verdict, and the event timeline. On the web it saves a file; on iOS and Android it opens the share sheet.

Taking Over a Blocked Run

A playbook task that has stopped keeps its composer, so you can steer it from the task screen. That covers a task that ended in an error, and a task parked on a wait whose signal is not coming. The placeholder says as much: "Take over: tell the agent what to change, and the task resumes."

What you send there directs the next step and re-enters the playbook. It is not a reply to whichever session ran last, which on a review loop would be the reviewer rather than the step that can fix the work.

When the task stopped on an error, the error sits above the composer rather than replacing it, so the reason it stopped and the way forward are on screen together.

Editing a Playbook on a Phone

The playbook editor works on iOS and Android as well as in a browser. A playbook opens read-only on a phone or tablet: tap Edit in the nav bar to change the title, description, steps, step routing, and the When to run this sentence, then tap Done. Changes save as you type, and leaving edit mode or the screen saves whatever is still in flight.

Three controls stay in the browser, and a phone shows them read-only:

  • The choice between opening a new task each time and keeping one task per entity, and the field it deduplicates on.
  • The Performs as line on a trigger, which a phone shows as a sentence once the trigger has a performer field set, and not at all while it has none.
  • The wake-early conditions on a Wait step. A wait with no signal yet offers nothing to tap on a phone: add the signal in a browser, and the phone then shows it read-only.

Both trigger sentences, the schedule controls, and a wait's cadence and Check by settings are authorable on a phone.

What's Next