Routines

A routine is a task that runs on a schedule. Same prompt, same agent, same output shape, every Friday at 5pm. While heartbeats decide what to do at a moment, routines do the same thing reliably.

Where routines live

Each routine is a YAML file in .jobs/:

my-cabinet/
├── .agents/
│   └── gtm-lead/persona.md
└── .jobs/
    ├── monday-launch-room.yaml
    ├── friday-investor-update.yaml
    └── weekly-competitor-sweep.yaml

The filename becomes the routine's slug. Add a file, the routine appears. Delete it, the routine disappears. Git tracks it.

A complete routine

schedule: "0 17 * * 5"        # Fridays at 5pm
ownerAgent: revenue-analyst
enabled: true
title: "Friday investor update packet"

prompt: |
  Compile this week's metrics into /investors/$WEEK/.
  Pull from /metrics/, /sales/pipeline.csv, /support/tickets.csv.
  End with a 3-sentence narrative for the cover email.

model: claude-opus-4-7
effort: high
budget:
  maxTokens: 80000
  maxCostUsd: 2.50

outputs:
  - path: "investors/${YYYY-WW}/packet.md"
  - path: "investors/${YYYY-WW}/cover.md"

Every field

FieldTypeRequiredWhat it does
schedulecron string | "manual"yesWhen the routine fires. Use manual for run-on-button.
ownerAgentagent slugyesWhich persona runs it. Inherits their model, tools, and memory.
enabledbooleanno (default true)Off without deleting.
titlestringyesShown in the schedule UI and approval logs.
promptstringyesThe instruction. Variables like $DATE, $WEEK, $YYYY-WW get interpolated.
modelstringnoOverride the agent's default model for this routine.
effort"low" | "medium" | "high"noReasoning effort. High = slower, more careful, more expensive.
budgetobjectnoCap tokens or dollars per run.
outputsarraynoHint Cabinet which files this routine writes. Helps with link-checking.
requiresApprovalbooleanno (default false)If true, queue the result before it lands.

Routines vs. heartbeats — which do I want?

Pick a routine when you can describe the output before it runs:

"Every Friday, summarize the week's metrics into a packet."

Pick a heartbeat when you can only describe the trigger:

"Every weekday morning, look at the launch room and tell me what changed — if anything."

A rough rule: if the output goes in a predictable folder structure, it's a routine. If the output might be "nothing today, sorry," it's a heartbeat.

Manual routines

Set schedule: "manual" for a routine you trigger from the UI:

schedule: "manual"
ownerAgent: research-lead
title: "Brief 10 competitors"
prompt: |
  Take the company name from the trigger context.
  Produce one /research/competitors/<slug>.md per competitor.
  Use the brief template at /research/templates/competitor.md.

These show up as "Run" buttons in the agent's profile. Click to trigger, optionally pass arguments.

Variable interpolation

VariableExpands toExample
$DATEYYYY-MM-DD2026-05-03
$WEEKYYYY-WW (ISO week)2026-W18
$MONTHYYYY-MM2026-05
$AGENTowner agent's sluggtm-lead
$CABINETcurrent cabinet's idmy-startup
$RUN_IDunique id for this runr_42a8

Useful for routing outputs into dated folders that won't collide.

Watching a routine run

Open the agent's profile (or Routines in the sidebar). Each scheduled run shows up as a row with:

  • The trigger (cron / manual / heartbeat dispatch).
  • The model and effort actually used.
  • A live token counter.
  • The output paths as they get written.

You can interrupt a long-running routine the same way you'd interrupt a chat — Cabinet writes a clean partial page so nothing is lost.

Read on