Recipes

Every command below runs as written. Edit the quoted task text to match your job. A launch uses the harness's own default model. Add --model sonnet (or opus, gpt-5.5, ...) to pick a different one. The manual's Control plane chapter documents the flags and states these recipes use.

There are two ways to run a workflow. Run a recipe from this page directly: copy it, edit the task text, and run the launch command. This suits a scheduled job or a known one-shot. Or hand the coordinator behavior a goal. The coordinator behavior (behaviors/coordinator.md, preset: --behavior coordinator) is the first-class entry point to every recipe here, so you give it a goal instead of copying and editing recipe snippets by hand. The quickstart's Two ways to run a workflow section covers both paths.

These recipes use the short ax "prompt" form. Set a default harness once so a bare prompt launches it, then nothing else needs configuring:

# ~/.config/ax/config.toml
default_harness = "claude"

A known verb or harness name shadows a bare prompt, so ax read, ax runs, and ax codex "prompt" keep their usual meaning. Write ax <harness> "prompt" anytime you want a specific harness instead of the default.

Run a Task

ax "run go test ./... and fix any failures"

This runs in your current directory. ax tracks it in a background window that closes when the task finishes. The command prints the session id. Watch it with ax read <id> --follow.

Use this for a task that needs no supervision. The task runs to completion on its own. It stays visible in the picker while it works. Afterward its transcript is indexed like any other session.

Run an Accept Check on a Task (CI)

ax "make 'go vet ./...' pass" --wait --unattended --accept "go vet ./..." --timeout 30m

This blocks and exits 0 only if the accept command passes when the run concludes.

Use this in CI. --wait turns the run into an ordinary blocking command with a meaningful exit code. --unattended makes ax ask return a default instead of waiting on a human, so nothing deadlocks in CI. --accept means success requires the check to pass, not just the model claiming it is done. --timeout caps the wall-clock time. A session sitting idle never counts as done.

Organize Sessions with Tags

In the picker: i to filter, Enter to keep the filter, v + j/k to select rows, l to edit their labels, b to pivot the list by any tag key. From the shell:

ax tag <id> --add-label project=blog
ax move --tag project=blog

The second command moves every tagged window into its own tmux session. Tags are key=value labels stored in ax's sidecar metadata, never in the harness's transcript, so they are safe to add, change, and remove. A TAGS column appears in the picker once anything is tagged.

Watch a Run from a Shell

ax list --run myrun
ax read --run myrun --follow
ax runs

Snapshot, live event stream (one JSON line per turn/waiting/exit/crash), and concluded runs with outcomes.

These three commands are the same view a coordinator uses, so anything it can see, you can see from any terminal. list shows who is alive and what each session costs. read --follow shows every turn boundary as it happens. runs shows what past runs concluded.

Get Notified When a Run Needs You

Set notify in ~/.config/ax/config.toml to get a message when a run blocks on you, or finishes and wants review, without having to watch the picker:

notify = "tmux"     # status-line message on every attached client
# notify = "bell"   # rings the session's tmux window

For anything richer, point it at a command. Write each placeholder bare, with no quotes of your own around it, because ax quotes every value into a single shell word. {state} is either needs-you or done-review, so one command can handle both cases:

notify = "notify-send ax {state}:{summary}"       # desktop notification
# notify = "curl -s --data-urlencode text={state}:{summary} https://hooks.slack.com/..."

The notify command fires from ax ask, and from claude's own permission prompts once you run ax hook install claude. It does not fire on a crash. Catching a crash would need a daemon, and ax does not run one.

Coordinate a Goal with Multiple Agents

ax "Add dark mode to this app. Done when: the toggle works and 'npm test' passes." --behavior coordinator --model opus --max-cost 25 --max-workers 3

--behavior coordinator resolves the coordinator behavior by preset name. The behavior file is behaviors/coordinator.md in the repository. The quick start covers getting the checkout.

One session drives the run. It splits the work, launches other sessions, judges their results, and concludes when the criteria pass. The fences put a hard cap on cost and fan-out. Watch the run in the picker (f filters to the run, T shows the run tree). Answer its questions with r when a row shows "needs you".

State the done-criteria in the task, and make them concrete enough to check. The coordinator behavior concludes on evidence. "Done when: 'npm test' passes" gives it a command to run. "Make it nice" gives it nothing to check. The coordinated task tutorial walks through one of these runs end to end.

Run a Project with a Coordinator

The previous recipe concludes once the goal is met. To run an ongoing project instead, launch a coordinator that keeps running and give it a file to manage the project from:

ax "Manage the blog project: triage requests into backlog.md, delegate work, verify, report." --behavior coordinator --interactive

--interactive keeps the coordinator running so you can steer it. Leave --max-cost and --max-workers off for an open-ended project: a tripped fence cascade-kills every session in the run. Add them only when you want that hard stop. There is no project feature to turn on. The coordinator keeps a plain backlog.md that it reads and rewrites. Each item carries a state and an owner, and the file also holds the decisions waiting on you and the coordinator's own follow-ups.

# backlog.md: blog

## In flight
- [~] add RSS feed  (owner: worker/rss)  -- generating feed.xml, verifying links

## Ready
- [ ] dark-mode toggle  (owner: unassigned)  -- done when the toggle persists across reloads

## Decisions for you
- [?] drop IE11 support? blocks the CSS cleanup item

## Done
- [x] fix broken image paths  (verified: all <img> resolve, site build is green)

The coordinator delegates each Ready item to a new session (ax "task"). The child session inherits the run and labels, so the whole run stays one tree in the picker. Before moving a row to Done, the coordinator verifies the result against something real: the built site, the passing test, the file on disk. It never marks work done on trust alone.

Send the coordinator a message and it sorts each item into the backlog. A new request becomes a Ready item. A question becomes a Decisions row. An answer unblocks whatever was waiting on it.

ax send <id> "ship the dark-mode toggle next, and yes, drop IE11"

When several sessions edit files at the same time, give each one its own git worktree so their edits cannot clobber each other. This is a plain git convention, not an ax feature. Pass each session its worktree path in the task text. Tell the coordinator to integrate a session's work only after that session's verification passes.

git worktree add ../worker-rss feature/rss
git worktree add ../worker-darkmode feature/darkmode

Watch the run in the picker (f filters to the run, T shows the run tree). Answer with r when a row shows "needs you". The backlog file is the one place the project's state lives, so you or any session can see where things stand by reading a single file.

Supervise an Existing Session Toward a Goal

Use this for a session whose context is too valuable to restart, driven by a model that works in bursts and stops early. Copy the session's id from the picker preview into the first line, then paste the rest as is:

W=the-session-id
ax tag "$W" --run myrun --name worker
ax "Supervise worker $W, an already-running session. Do not restart it. Goal: finish its current task. Done when: it demonstrates the working result. It stalls after bursts. Keep it moving." --behavior coordinator --model sonnet --run myrun --name supervisor --interactive

The ax tag line adopts the existing session into a named run. The launch then starts a supervisor in that same run, kept running and steerable by --interactive. The supervisor never does the work itself. It watches the session's turns, sends the next concrete instruction whenever the session stalls, verifies "done" against evidence, and asks you only when the decision is yours. Leave --max-cost off here. If a fence trips, ax cascade-kills the whole run, including the supervised session.

Change the Goalposts Mid-Run

The accept check (--accept) usually points at a script. Editing that script is how you update the criteria, because the next success claim runs the new version. After editing it, tell the root session:

ax send <root-id> "the acceptance criteria changed: re-read ./check.sh and continue until it passes"

If the run already concluded, relaunch with the same run id. The new run picks up the same history:

ax "Continue the previous run. Read your state file first. Updated criteria: ..." --behavior coordinator --run myrun

The same reply channel works at review time. When a finished run shows the green ✓ review state, answer its question with new criteria instead of accepting, and it goes back to work.

Route a Task Up Through Model Tiers

Run a task on the cheapest model first. If the accept check fails, move to a stronger tier. Stop as soon as one tier passes.

for MODEL in haiku sonnet opus; do
    ax "$TASK" --model "$MODEL" --max-tokens 100000 \
        --wait --accept ./check.sh && exit 0
done
exit 1

Write check.sh to verify the concrete deliverable, not to trust the model's claim. It runs automatically when the session tags itself done. Exit 0 means the tier passed. Any other exit means the run gave up, the fence tripped, or the check rejected the result. The loop moves to the next tier on any non-zero exit.

Fence each tier on --max-tokens to prevent runaway spend. On subscription auth (the default), --max-cost is inert. On API auth (--api), use --max-cost in USD instead. After any --wait run, the record at ~/.local/state/ax/runs/<run-id>.json carries cost, token counts, and the outcome field (success, gave_up, budget_hit, crashed):

for f in ~/.local/state/ax/runs/*.json; do
    jq -r '[.group, .outcome, .cost] | @tsv' "$f"
done | column -t

Starting from zero: copy the directory recipes/cost-routing/. It ships the escalation wrapper (escalate.sh, with the tier list inline) and a sample accept check (check.sh). Replace the check with your task's real acceptance criteria and run ./escalate.sh "your task" (it defaults to ./check.sh). No prior sessions or data required.

Share State Between Agents with a Blackboard

Two agents coordinate through a shared JSON file on disk. A producer writes its conclusions to the file. A critic reads those conclusions, evaluates them, and appends its verdicts. Neither agent has access to the other's context window, only to what was written.

BB=/tmp/blackboard.json
echo '{"items":[],"verdicts":[]}' > "$BB"

PROD=$(ax \
    "Blackboard: $BB. Read it. Append one item to 'items'. Write it back." \
    --wait --json | jq -r .id)
ax wait "$PROD" --timeout 5m
ax result "$PROD"

CRIT=$(ax \
    "Blackboard: $BB. Read it. For each item, append a verdict to 'verdicts'. Write it back." \
    --wait --json | jq -r .id)
ax wait "$CRIT" --timeout 5m
ax result "$CRIT"

cat "$BB" | jq .

The primitives: --json on the launch prints the session id before --wait blocks, so you can capture both. ax wait is a clean block point between pipeline stages. ax result reads the final report after the session exits. The blackboard is append-only by convention: each agent writes only to its own array key and never modifies what another agent wrote. That makes it a tamper-evident audit log you can inspect at any point with cat "$BB" | jq .

The same pattern extends to N agents and M rounds. Add a judge worker that reads both items and verdicts and writes a judgment array. Run the critic loop twice for a second-pass refinement. Fan out N critics in parallel (each with a distinct key) and join on all keys being populated. The blackboard is the queue.

Starting from zero: create the blackboard file, write a task prompt that tells each agent its file path and its section to write to, and run the two-stage sequence above. No prior sessions or shared infrastructure required.

Fan Out Workers Over a File Collection

Split a corpus into chunks, launch one worker per chunk in parallel, and join on a single verify pass. Each worker reads its chunk and writes structured notes to a shared vault directory.

VAULT=./vault
RUN_ID="vault-$(date +%s)"
mkdir -p "$VAULT"

PIDS=()

for CHUNK in fixture/chunks/chunk-*.txt; do
    SLUG="${CHUNK##*/chunk-}"
    SLUG="${SLUG%.txt}"
    ax "Read $CHUNK. Extract key topics, commitments, and dates. Write notes to $VAULT/notes-${SLUG}.md." \
        --run "$RUN_ID" --label "role=worker" --label "correspondent=$SLUG" \
        --max-workers 5 --max-tokens 500000 --timeout 10m \
        --wait --json > "/tmp/launch-${SLUG}.json" &
    PIDS+=($!)
done

wait "${PIDS[@]}"

ax "Check each file in $VAULT/notes-*.md for required sections.
    Report PASS or FAIL per file. Exit 0 only if all pass." \
    --run "$RUN_ID" --label "role=verifier" \
    --wait --accept ./check-vault.sh

Each worker is launched with & before any is awaited, so all chunks run in parallel. wait "${PIDS[@]}" is the join point. The --run flag ties all sessions into one tree in the picker (T to expand it). The verify worker reads every notes file and the --accept check enforces a hard pass before the run can conclude.

For large corpora, add --model haiku to the extract workers (mechanical note-taking) and keep the default model for the verify worker. This composes directly with the cost-routing pattern above.

Starting from zero: write a chunking script that splits your source files into per-item text files, define the fields each notes file must contain, write a check-vault.sh that verifies those fields are present, and run the fan-out. No prior sessions or vault contents required.

Audit Session History for Behavior Patterns

Search your session history for a topic, pull transcripts, and run a headless analysis worker that produces a human-reviewable document with recurring patterns, failure modes, and proposed behavior file edits.

QUERY="coordinator behavior"
CORPUS=$(mktemp /tmp/audit-corpus.XXXXXX)
PROMPT=$(mktemp /tmp/audit-prompt.XXXXXX)

ax search "$QUERY" --json \
    | jq -r '.ids[:8][]' \
    | while read -r id; do
        echo "--- session: $id ---" >> "$CORPUS"
        ax read "$id" --format text | tail -80 >> "$CORPUS"
    done

cat > "$PROMPT" <<PROMPT_EOF
You are a behavior-audit worker. Corpus:
$(cat "$CORPUS")

Write: recurring successful patterns (with session citations), recurring
failure modes (with citations), curated lessons, and proposed behavior file
edits as [ADD to section X] / [REVISE Y to] blocks. Mark single-instance
observations as 'watch' not 'pattern'.
PROMPT_EOF

ID=$(ax claude - --wait --unattended --max-tokens 400000 --timeout 15m --json \
    < "$PROMPT" | jq -r .id)
ax result "$ID" > audit-output.md

The primitives: ax search --json returns a ranked list of session IDs matching the query. ax read --format text extracts the normalized transcript. ax claude - reads the task from stdin, so the whole corpus rides inside the prompt file. With --wait the launch blocks until the worker finishes and ax result reads its report. The output goes to a file you review and apply manually.

This is not persistent learning. The worker re-derives the summary from transcripts at call time. The behavior file is version-controlled. A human decides what edits to apply. Run it again after a batch of sessions to see what changed.

The default depth fence (--max-depth 1) allows a root session plus one level of workers, so this launch works from a plain shell and from inside a root session. It is refused only when the script runs inside a session that is already a worker (depth 1), because the analysis worker would land at depth 2. In that case, run it from a shell outside any ax session, or launch the root with --max-depth 2.

Starting from zero: the script writes a blank template if ax search returns no sessions. Run it again once you have sessions that match your query.

Triage Email: Act on Everything, Notify on Exceptions

A cron job reads new mail, dispatches archive and unsubscribe actions automatically, and fires a notification only when something requires human attention. The default is silence. The notification is the exception.

# ~/.config/ax/config.toml
notify = "notify-send ax {state}:{summary}"
# cron: every 4 hours
0 */4 * * *  /path/to/triage-wrapper.sh ~/Maildir >> ~/.local/state/ax/log/email-triage.log 2>&1

Inside the wrapper:

TASK="Process these emails. For each: emit ACTION|||ARCHIVE|||filename,
    ACTION|||UNSUBSCRIBE|||filename, or ACTION|||DRAFT_REPLY|||filename|||text.
    If all were noise, emit SILENT on its own line and stop.
    If any need human attention, emit IMPORTANT: summary."

SESSION=$(ax "$TASK" --behavior behaviors/email-triage.md --model haiku \
    --wait --unattended --timeout 3m --max-cost 0.25 --max-tokens 200000 --json | jq -r .id)
OUTPUT=$(ax result "$SESSION")

# dispatch action lines; ||| separator preserves maildir filenames containing ":"
while IFS= read -r line; do
    if [[ "$line" == "ACTION|||ARCHIVE|||"* ]]; then
        archive "${line#ACTION|||ARCHIVE|||}"
    elif [[ "$line" == "ACTION|||UNSUBSCRIBE|||"* ]]; then
        unsubscribe "${line#ACTION|||UNSUBSCRIBE|||}"
    elif [[ "$line" == "ACTION|||DRAFT_REPLY|||"*"|||"* ]]; then
        rest="${line#ACTION|||DRAFT_REPLY|||}"
        draft_reply "${rest%%|||*}" "${rest#*|||}"
    fi
done <<< "$OUTPUT"

# sentinel gate: silence is the normal case
echo "$OUTPUT" | grep -q '^SILENT$' && exit 0

# something needs attention: notify
"$NOTIFY_CMD" "$(echo "$OUTPUT" | grep '^IMPORTANT:' | head -1)"

The sentinel gate inverts the usual pattern. Most recipes notify on completion. This one notifies only when SILENT is absent. The worker decides what each email is and what action to take. The wrapper owns the notification decision with a single grep.

--unattended means ax ask returns a default instead of waiting on a human, so nothing deadlocks in cron. --timeout 3m caps the wall-clock time so a hung session never blocks the cron slot. On the default subscription auth --max-cost is inert, so --max-tokens 200000 is the fence that binds.

Starting from zero: create a maildir with a few test messages (mix of newsletters and one real item), write a behavior file that tells the worker how to classify mail and what format to emit, run the wrapper once manually, and verify the sentinel fires correctly before wiring cron.

Draft Listings and Hold for Human Approval Before Publishing

A read-only coordinator fans out one worker per niche in parallel. The coordinator itself writes nothing. Each worker produces a design brief, generates artwork via an image API adapter, writes listing copy, and creates a draft product via your platform's API. An accept check verifies every draft mechanically. Then ax ask holds the pipeline for explicit human approval before anything publishes.

ax "Read niches.json. For each niche, launch a worker that:
    1. Writes a design brief to drops/NICHE/brief.md
    2. Calls scripts/image-api.sh to generate drops/NICHE/design.png
    3. Calls scripts/listing-copy.sh to write drops/NICHE/listing.json
       (title <= 140 chars, exactly 13 tags)
    4. Calls scripts/create-draft.sh to write drops/NICHE/draft.json
       (state=draft, published=false)
    Run all workers in parallel. Done when all niches have passing drafts." \
    --behavior behaviors/pod.md \
    --read-only \
    --max-workers 5 --max-tokens 2000000 \
    --accept "bash scripts/check-drop.sh drops" \
    --interactive

The accept check runs before the coordinator can declare success. It verifies that every niche has a design file, a listing with a title under the character limit and the right tag count, and a draft marked as unpublished. No niche passes on the coordinator's word alone.

After the checks pass, the coordinator calls ax ask before the publish step:

ans=$(ax ask "3 drafts ready. Approve to publish?")
[ "$ans" = "yes" ] || exit 0    # stop at drafts if not approved

In attended mode, ax ask shows the session as "needs you" in the picker and fires the notify hook. Answer with r in the picker or ax reply <id> yes from a shell. In unattended mode (--unattended), ax ask returns immediately with no reply and the coordinator stops at drafts. Nothing publishes unattended. This is the structural guarantee for any cron or CI run.

Starting from zero: create a niches.json with two or three entries (each with a slug and keywords), write stub adapter scripts for each platform API call, run the coordinator once with --unattended, and verify that drafts are produced but nothing publishes.

Collect Data in a Script, Format It with an Agent

A bash script collects ground-truth data (service health, dependency audit, metrics). Its output is injected into an ax task. A headless formatter session produces a human-readable report or emits SILENT if there is nothing to report. The agent never fetches data itself.

# Stage 1: collect data deterministically
SCRIPT_OUTPUT=$(./check-services.sh)

# Stage 2: inject into task, let the agent format only
ID=$(ax "A health check ran. Its output:

== OUTPUT START ==
${SCRIPT_OUTPUT}
== OUTPUT END ==

If the output contains NO_ISSUES: respond with exactly the word SILENT and stop.
If it contains OUTAGE_DETECTED: write a concise incident summary and stop.
Do not run any commands. Do not fetch additional data." \
    --behavior recipes/prescript-formatter/behaviors/lm-formatter.md \
    --wait --unattended --timeout 5m --max-tokens 200000 \
    --json 2>/dev/null | head -1 | jq -r .id)

# Stage 3: sentinel gate
OUTPUT=$(ax result "$ID" --json | jq -r .result)
echo "$OUTPUT" | grep -q '^SILENT$' && exit 0

# Stage 4: deliver
"$NOTIFY_CMD" "$OUTPUT"

The behavior file constrains the formatter: read the task, format only, no tool calls. This matters because a curl call inside an agent session is billed and subject to permission prompts. A curl call in bash is free and deterministic. The script handles data collection. The agent handles formatting.

The --json | head -1 | jq -r .id pattern captures the session ID from the JSON line printed before --wait blocks. After --wait returns, ax result --json reads the formatter's output cleanly via the .result field.

Adapt to any monitoring job by replacing check-services.sh with your real check tool and adjusting the sentinel tokens (NO_ISSUES / OUTAGE_DETECTED) to match what your script emits.

Starting from zero: write a check script that outputs a fixed sentinel word for the clean case, run the wrapper once in each fixture mode, and confirm the sentinel gate suppresses delivery in the clean case before wiring cron.

Schedule Any Recurring Workflow

One wrapper, one behavior file, any task string on any schedule. A cron entry calls the wrapper, which runs the task headless, reads the output, and delivers a report only when the output does not contain the suppression sentinel.

# ax-scheduled-chain.sh: parametrized cron wrapper
TASK="$1"
NOTIFY_CMD="${NOTIFY_CMD:-echo}"
SENTINEL="${SENTINEL:-SILENT}"
RUN_ID="chain-$(date +%Y%m%d-%H%M%S)"

SESSION=$(ax "$TASK" \
    --behavior recipes/scheduled-chain.md \
    --wait --unattended --timeout 10m --max-cost 2.00 --max-tokens 400000 \
    --run "$RUN_ID" --json | jq -r .id)

OUTPUT=$(ax result "$SESSION")

printf '%s\n' "$OUTPUT" | grep -q "^${SENTINEL}$" \
    && exit 0

"$NOTIFY_CMD" "$OUTPUT"
# crontab: daily briefing at 07:00
0 7 * * *  NOTIFY_CMD=/usr/local/bin/ax-slack \
    /path/to/ax-scheduled-chain.sh \
    "Search the web for the top 5 AI agent developments from yesterday.
     Summarize each in 2-3 sentences." \
    >> ~/.local/state/ax/log/briefing.log 2>&1

Only the task string changes between workflows. The same wrapper handles a daily briefing, a nightly dependency audit, a weekly digest, or a competitive repo watch. Wire the sentinel: the worker emits SILENT when there is nothing to report (no new vulnerabilities, empty inbox, no commits), and the wrapper suppresses delivery.

For the dependency audit variant, run the deterministic scanner in bash first (the pre-script pattern above), then inject its output into the task string so the agent only interprets, not fetches:

VULN=$(govulncheck ./... 2>&1)

ax-scheduled-chain.sh \
    "A Go vulnerability scan ran. Output:

$VULN

If it says 'No vulnerabilities found', output SILENT. Otherwise summarize each
finding: ID, affected module, version found, fixed version, upgrade action." \
    >> ~/.local/state/ax/log/audit.log 2>&1

ax has no built-in scheduler. Wire it with cron, systemd.timer, or launchd. The notify hook in ~/.config/ax/config.toml is an alternative to the NOTIFY_CMD variable for delivery targets you already have configured.

Starting from zero: copy the wrapper, set the task string, run it manually once with NOTIFY_CMD=echo, confirm the sentinel fires on a clean run, then add the cron entry.

Run a Produce-Judge-Iterate Loop Against a Rubric

A producer writes or revises a deliverable. A fresh reviewer scores it against a rubric file that encodes your taste. If the score clears the threshold, the loop stops and emits the deliverable. If not, the reviewer's specific violations are folded into the next producer iteration. At the iteration cap, if still failing, the loop emits the best attempt marked NOT PASSING. No human is involved until the loop concludes.

# Improve an existing draft (seed mode): score the draft on iteration 1,
# then revise it against the violations on each subsequent iteration.
bash taste-loop.sh \
    --task-file goal.md \
    --seed draft.md \
    --rubric rubrics/prose-flat-register.md \
    --threshold 85 \
    --max-iter 5
# Produce from scratch: the producer writes the first draft,
# then iterates against the reviewer's violations.
bash taste-loop.sh \
    --task "Write the README intro in flat developer-doc register." \
    --rubric rubrics/prose-flat-register.md \
    --threshold 85 \
    --max-iter 4

Exit 0 means the deliverable passed. Exit 2 means the cap was hit without passing and the output carries the best attempt plus its remaining violations. evidence/trajectory.tsv records the score at each iteration so you can see whether the loop is converging.

The loop ships two behavior files. behaviors/producer.md constrains the producer to emit only the deliverable text. No preamble, no explanation. behaviors/reviewer.md constrains the reviewer to return one JSON object with a score, a violations list with exact quoted spans, and a pass flag. Both workers run --read-only. The reviewer is launched fresh each iteration with no memory of prior rounds. The rubric is where the taste lives: copy rubrics/TEMPLATE.md to write your own. The flagship rubric (rubrics/prose-flat-register.md) penalizes rhetorical openers, marketing verbs, em-dashes, semicolons, and rule-of-three padding.

The coordinator behavior uses this recipe autonomously. Given a goal that requires prose at a specific register, a coordinator session runs taste-loop.sh and passes the reviewer behaviors/reviewer.md as its behavior file. The human sees only the passing result or the cap report, not the intermediate drafts.

Starting from zero: copy recipes/taste-gate/ from the repository, write a rubric by editing rubrics/TEMPLATE.md, and run the loop against a draft or a task prompt. Add --model sonnet (or any model name) to set the model for both workers. Bind each worker's spend with --max-tokens N.