Skip to content
WIP

This guide is a work in progress. Barry is also a work in progress.

Add actions and schedules to a bag

Use this when a bag should carry a repeatable procedure, or run work on a timer, for every identity that packs it. An action is a named procedure with typed inputs and a recorded run, in actions/<name>/action.yaml. A schedule in bag.yaml runs an action, a job, or a script on a trigger. The example is standup, a bag that writes standup notes every weekday morning and sweeps old ones every hour.

  • Create a bag.
  • Read Using actions to see how a person runs an action and reads its result.
  • A prompt action that runs on a schedule goes to Barry’s API, so the API must be running when it fires.

An action lives in its own directory under actions/, named after it. To scaffold one, run barry new action standup-notes --bag standup. This is the finished file:

name: standup-notes
description: Summarize a repository's last day of commits as three standup bullets. Use when the user asks for standup notes.
context: none
input_schema:
type: object
properties:
repo:
type: string
description: Path to the repository, such as ~/repos/app.
required: [repo]
output_schema:
type: string
description: Three standup bullets as Markdown.
timeout: 10m
prompt: |
Read the commits made in ${INPUT.repo} in the last 24 hours with `git log`.
Write three one-line bullets: what changed, what is in progress, and what is
blocked. Save them to ~/standup-notes/ as a Markdown file named for today's
date, and return them.

The fields that shape an action:

  • name is kebab-case, and description says when to use the action, in at most 1,024 characters. The description joins the actions index in every session’s prompt, so the agent finds the action by it.
  • prompt, run, or job says what does the work: an agent following a prompt, a command Barry runs itself, or one of the bag’s jobs. Declare exactly one.
  • context is session when the prompt refers to the calling conversation, and none when the action stands alone. A prompt action defaults to session, which runs it inside the conversation that calls it. A schedule has no calling conversation, so Barry refuses to fire a prompt action with context: session; set context: none, as this file does, and a fresh agent runs it detached.
  • input_schema is a JSON Schema whose root has type: object. Barry rejects malformed inputs before it starts a run, and fills each ${INPUT.<name>} in the prompt with the input’s value.
  • timeout bounds the run, in seconds or with an s, m, h, or d suffix.

The action.yaml reference covers requires, preflight, validate, steps, and the rest.

name: standup
description: Writes standup notes every weekday morning and sweeps old ones every hour.
schedules:
weekday-notes:
description: Standup notes for the main repository
cron: "0 9 * * 1-5"
timezone: America/New_York
action: standup-notes
inputs:
repo: ~/repos/app
bounds: [no-network-write]
overlap: skip
catch_up: none
sweep:
description: Delete standup notes older than a week
every: 1h
action:
description: Delete standup notes older than a week
run:
entry: src/sweep.ts
timeout: 2m

Each key under schedules: is a kebab-case name, and Barry gives the schedule the ID <bag>/<name>, such as standup/weekday-notes. A schedule declares exactly one trigger and exactly one payload.

The trigger is one of:

  • cron: a five-field cron expression, read in timezone, an IANA zone name that defaults to UTC.
  • every: 1m, 5m, 15m, 30m, 1h, or 1d, aligned to the clock. 1d fires at midnight in the schedule’s timezone.
  • at: one ISO 8601 time with an explicit offset, which fires once, or login, which fires once for each login session.

The payload is one of:

  • action: an action by name, or by bag:name for another bag’s, or an inline action body, as the sweep schedule shows. An inline action takes every action field except name, which comes from the schedule’s key, and defaults to context: none.
  • job: one of the bag’s jobs, by name or as bag:name.
  • entry: a script inside the bag, with optional args, env, secrets, and timeout.

The rest of a schedule says how it runs:

Field What it does
inputs Inputs for the action, checked against its input_schema when it runs
identity The identity an agent action runs as; omit it to use the default resolution
traits, bounds Grants and restrictions for the run’s session. bounds: [no-network-write] blocks recognized network writes, such as a git push, and still lets the notes run read git log and save its file. The readonly bound denies Bash and file writes, so the notes run fails under it
overlap A fire that lands while the last run is still going: skip, the default, or queue
catch_up A fire missed while the machine slept or was off: once, the default, runs it on return; none drops it
enabled false keeps the schedule declared but off

Barry checks every trigger when it parses the manifest: an invalid cron expression, an unknown timezone, or a schedule with two triggers fails the bag, not its first fire.

The sweep schedule’s inline action runs src/sweep.ts itself, with no agent. Barry runs a TypeScript entry with the tsx in the bag’s own node_modules, so the bag depends on tsx:

import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
const dir = join(homedir(), "standup-notes");
const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
let removed = 0;
if (existsSync(dir)) {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const path = join(dir, entry.name);
if (entry.isFile() && statSync(path).mtimeMs < weekAgo) {
rmSync(path);
removed += 1;
}
}
}
console.log(`removed ${removed} note(s)`);
{
"name": "standup-bag",
"private": true,
"type": "module",
"dependencies": {
"tsx": "4.23.12"
}
}

Check the bag, install it, and list the schedules:

Terminal
barry bag check ~/repos/standup
barry install ~/repos/standup
bonanza schedule list

The list shows standup/weekday-notes and standup/sweep, each with its next fire time. To fire a schedule now instead of waiting, run bonanza schedule run standup/sweep, then read the run with bonanza schedule show standup/sweep. To run the action by itself, run bonanza action run standup-notes --input repo=~/repos/app.

  • The bag fails to load after you add a schedule. The schedule has no trigger or two, a cron expression without five fields, a timezone that isn’t an IANA name, or two payloads. The error names the schedule.
  • barry bag check says the schedule refers to an action the bag doesn’t ship. A bare action name must match a directory actions/<name>/ that holds an action.yaml. Name another bag’s action as bag:name.
  • A scheduled prompt action refuses to run. It declares context: session, or the API isn’t running.
  • For anything else, see Troubleshooting.