---
title: Run on a schedule
description: Add an eve schedule so Foreman sweeps for queued work on a cron instead of waiting for a webhook to arrive.
type: guide
summary: One file under agent/schedules, and the trust behavior the policies already give schedule turns.
related:
  - /docs/intake
  - /docs/trust-model
---

# Run on a schedule



<CopyPrompt
  className="block"
  text={`Help me customize the eve Software Factory template. I want a schedule that sweeps the target repository for queued work on a cron, so the factory does not only react to webhooks.

Ground truth first: read AGENTS.md in the repository root, read node_modules/eve/docs/schedules.mdx in full, and read agent/instructions.ts so the sweep prompt matches how the orchestrator already talks about the pipeline. Read agent/lib/github/approval.ts to confirm how isScheduleAppAuth is treated before you assume anything about permissions.

Facts you need:
- Schedules live in agent/schedules/<name>.ts and are root-only.
- defineSchedule takes a cron plus exactly one of markdown or run.
- The markdown form runs in task mode: it cannot park for a person or an OAuth sign-in.
- On Vercel, cron expressions are evaluated in UTC.
- The approval policies already treat schedule turns as trusted via isScheduleAppAuth, so no policy changes are needed.

1. Ask me how often the sweep should run and what it should look for before writing anything. Default to a weekday-morning cadence if I have no preference.
2. Create the schedule using the markdown form unless I asked for channel delivery.
3. Write the prompt so the sweep is read-first and conservative: it should search FACTORY_REPO for issues carrying FACTORY_LABEL that have no factory branch or draft pull request yet, and act on at most one of them per run. It must never re-run work that is already in flight.
4. Because task mode cannot park, the prompt must tell the run never to attempt an action that needs approval and never to ask a question.
5. Do not change agent/lib/github/approval.ts or agent/lib/trust.ts. If you believe a policy change is required, stop and explain why instead of editing them.
6. Remind me that eve dev does not fire schedules on their cadence, and that the dev-only dispatch route is POST /eve/v1/dev/schedules/<scheduleId>, where the id is the path-derived name.

Finish by running pnpm validate and confirming 0 errors and 0 warnings, then run npx eve info and show me the schedule in the discovered surface. Do not deploy.

Full recipe, with the reasoning behind each step: https://ask-foreman.dev/recipes/run-on-a-schedule`}
/>

Every way work currently reaches Foreman is inbound: a label, a mention, a Linear session, a red check suite. Nothing makes the factory look for work on its own.

An eve schedule fixes that. It is one file under `agent/schedules/` carrying a cron expression, and the approval policies already understand the resulting principal.

## When to use it

Reach for this when work piles up faster than anyone remembers to hand it over, and you want the factory checking for queued items on its own rather than waiting for someone to label one.

Skip it if your volume is low enough that a label or a mention already covers you. Cron fires whether or not there is work to find, and every firing costs a session.

## What it touches

| File                        | Change                                         |
| --------------------------- | ---------------------------------------------- |
| `agent/schedules/<name>.ts` | The new schedule, with its cron and its prompt |

Schedules are root-only. A declared subagent cannot have a `schedules/` directory, so the sweep runs as the orchestrator and delegates to stations the usual way.

<Callout title="Cron is UTC, and dev never fires">
  On Vercel each schedule becomes a Cron Job evaluated in UTC, so `0 9 * * 1-5` is 09:00 UTC on weekdays. `eve dev` never fires schedules on their cadence, so test with the dispatch route rather than waiting.
</Callout>

## Before you run it

Cron fires whether or not anyone is watching. This is the surface most likely to spend tokens while you sleep, so start with a wide interval and narrow it once you have seen a few runs.

The sweep also cannot stop and ask. A `markdown` schedule runs in task mode, so anything needing approval fails rather than waiting, which means keeping the sweep inside what the trust model already allows for a schedule turn.

## The trust part is already done

`agent/lib/github/approval.ts` recognizes schedule turns through `isScheduleAppAuth` and treats them as trusted callers. So a schedule can run reversible writes without approval cards, while shipping still waits for a person.

That means this recipe adds no policy code. The template shipped ready for it.

## Verify

```bash
pnpm validate
npx eve info
```

The schedule should appear in the discovered surface under its path-derived name. Then trigger it once by hand, since the cron never fires in dev:

```bash
curl -X POST http://localhost:2000/eve/v1/dev/schedules/sweep
```

The response carries the session ids it started, so you can confirm it picked up at most one item and stopped. That route is dev-only and production builds never mount it.

## What to expect

A sweep that is not conservative will redo work. Nothing in the framework stops a schedule from starting a second run on an issue that already has a factory branch, which is why the prompt makes the run check first and cap itself at one item.

## FAQ

<Accordions type="single">
  <Accordion title="Why does nothing happen when I run pnpm dev?">
    By design. `eve dev` never fires schedules on their cron cadence, so the only way to exercise one locally is the dev-only dispatch route:

    ```bash
    curl -X POST http://localhost:2000/eve/v1/dev/schedules/sweep
    ```

    It uses the same dispatch path production cron does and returns the session ids it created. An unknown id returns 404 along with the available schedule ids, which is a quick way to check the name eve derived from your file path. A built app served with `eve start` does run schedules on their cadence.
  </Accordion>

  <Accordion title="Do I need to change the approval policies?">
    No, and you should not. `agent/lib/github/approval.ts` already recognizes schedule turns through `isScheduleAppAuth` and treats them as trusted callers, so reversible writes run without approval cards while shipping still parks.

    The template shipped ready for this, which is why the recipe's prompt explicitly tells the agent not to touch `approval.ts` or `trust.ts`.
  </Accordion>

  <Accordion title="What timezone does the cron use?">
    UTC. On Vercel each schedule becomes a Cron Job and Vercel evaluates the expression in UTC, so `0 9 * * 1-5` fires at 09:00 UTC on weekdays, not 09:00 local. Adjust the hour yourself if you want a local-morning sweep.
  </Accordion>

  <Accordion title="Can a scheduled run stop and ask me something?">
    It depends which form you used. The `markdown` form runs in task mode, which runs to completion or fails and cannot park for a person or an OAuth sign-in. The `run` handler form can park.

    This recipe defaults to the `markdown` form, so keep the sweep inside what the trust model already allows rather than relying on an approval card nobody will see.
  </Accordion>

  <Accordion title="Can a station have its own schedule?">
    No. Schedules are root-only, so a declared subagent cannot have a `schedules/` directory. The sweep runs as the orchestrator and delegates to stations the same way any other run does.
  </Accordion>
</Accordions>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)