A routine hands the crew the same brief on a schedule. Nightly QA sweep. Monday dependency review. First-of-the-month changelog. You write the brief once; the platform files it as an issue every time the clock says so, and the crew builds it exactly as it builds any issue you file by hand. This page covers what a routine is, how to create one, and where to watch it.
What a routine is
A routine is one file in your app's repo: .forgejo/workflows/routine-<name>.yml. It carries three things — a cron (when), a title, and the brief (what). It is a real Forgejo Actions workflow, so it shows in your repo's Actions tab like any other, with a Run workflow button.
When it fires — on schedule or by the button — its one job asks the platform to open the brief as an issue in the same repo, labeled agent-work and routine/<name>. From there, everything on Building with Issues applies: the crew claims it, builds it, previews it, and ships it through a reviewed PR that closes the issue.
Two records, one truth:
- The Actions run is the trigger's record: when it fired, who fired it, and which issue it opened (the log says
opened #N). - The issue is the work's record: the brief, the crew's heartbeat, the PR, the verdict.
Times are UTC. A routine fires at most hourly by design.
Create one
Three doors, same result. Pick the one in front of you.
From the console — open the app in the console, find Routines, press New routine. Choose a schedule from the list (or type a cron), write the brief, press Schedule it. The console lists every routine with its schedule, its state, and when it last fired.
From your agent — with your agent connected:
create_routine(owner:"acme", name:"shop", routine:"nightly-qa",
title:"Nightly QA sweep", cron:"0 3 * * *",
brief:"Open the live app in a real browser and click through every page. Fix anything broken — 404s, console errors, dead buttons — in ONE pull request. Done looks like: every page loads and the check gate is green.")
list_routines, get_routine, update_routine, delete_routine and run_routine complete the set. get_routine shows the brief, the last ten times it fired, and the issues it opened.
From Forgejo — the file is the routine. Add or edit .forgejo/workflows/routine-<name>.yml on the default branch. The platform writes it in a fixed shape; copy an existing one and change the cron: line and the ROUTINE_BRIEF block. Delete the file to remove the routine.
Write a brief that runs well
The brief becomes the issue body, verbatim, every time. Everything on writing an issue that builds well applies, plus one thing: the brief must make sense on a day you have not looked at.
- Bound the work. "Review the open issues and propose the next three" runs well. "Improve the app" runs forever.
- Say what done looks like when nothing is wrong. A QA sweep that finds nothing should close its issue with a one-line report, not invent work. Say so.
- Ask for one pull request. A routine that opens five PRs a week is noise.
- Know how the crew ends a clean check. The crew opens its PR before it reads far, so it needs a way out when there is nothing to change: it reports in two lines, closes the issue, and closes the empty PR. Say "a clean check is the normal outcome" and it will. Say "if nothing is broken, improve one small thing" and you get a small PR every time — choose on purpose.
- Put it on an app the crew is working. A brief on an org whose v1 is still unmerged waits forever; the routine will skip, honestly, every time.
Read a routine
Every place shows the same three facts, from the same sources:
- What the last firing did —
Opened #N,Skipped, orFailed. Forgejo only knows green or red; the brief's footer names the run that opened it, so the platform reads the outcome back from the issues. A green run with no brief is a skip. - What it waits on — the open brief a firing would skip for: queued for the crew, or claimed (
agent-working), and since when. Close that issue to reset the routine. - Where the history is — the routine's page in the Actions tab.
In the console, the Routines section on the app page shows all three per row. From your agent, list_routines returns last_run.outcome and waiting_on; get_routine adds the last ten firings and the issues each one opened. On the fleet board, plat_routine_runs_total{owner,repo,routine,outcome,trigger} counts firings.
If a routine skips every time, look at what it waits on. A brief the crew never claims means the org is not being worked — its v1 is unmerged, its flow is paused, or its budget is spent — see Steering the Fleet. The routine is honest about it; it will not pile up briefs behind a stuck org.
Fire it now
Press Run workflow on the routine's Actions page, press Fire now in the console, or call run_routine. All three send the same dispatch, so the run lands in the same history as the scheduled ones.
Never doubled, never stuck
If the previous brief is still open and the crew has it — label agent-work (queued) or agent-working (in progress) — the new run skips and its log names the issue it is waiting on. One brief in flight per routine, by construction.
If the previous brief is open but the crew has let go of it — the PR merged without closing it, or the crew handed it back to you — the new run closes it as superseded and opens a fresh one. A routine keeps its rhythm; it does not stall forever behind one forgotten thread.
Pause and resume
Pause from the console, or update_routine(paused:true). The schedule trigger leaves the file; the button stays, so a paused routine still fires by hand. Resume restores the schedule. In the file, a paused routine is one with the schedule: block commented out.
Cost
Every run that opens a brief is one crew job, paced like any other by the org's flow control and the global governor — see Steering the Fleet. A routine that skips costs nothing. Check the spend pane before you schedule something hourly.
Under the hood
The workflow's job calls the platform's in-cluster POST /ci/routine with the run's own repo-scoped token — the same provenance proof the sync-env workflow uses (Secrets and Config). The platform reads the file at the run's commit, decides overlap, ensures the labels, and opens the issue as plat, like every other platform-opened brief. No scheduler lives inside the platform: Forgejo's cron is the clock, git is the memory, and the crew's existing queue is the executor. Delete the platform and reinstall it, and every routine is still there.
For everyone
Experience
- Build Your First App
- Building with Issues
- Dependency Trees
- Routines
- Orgs and Planning
- Preview Environments
- Connect Your Agent
Operate
Under the hood
Grow
This wiki ships inside open-platform/mitosis (wiki/) — edit it there, not here. Grown by the platform it describes.