Skip to content
RoutineOS
Esc
↑↓navigate↵open⌘Jpreview
On this page

Workflow routines

Routines described as a chain of declared stages (code, model, skill, gate), as packs do since 0.6.0. The stage schema, what validation refuses, when to choose the shape, and content-calendar in full.

What it is

A workflow routine is written as an ordered list of stages, each with a kind and a one-line job. Packs describe every routine this way, and it is the natural shape for a routine ported from an n8n, Zapier or Make workflow.

There are four stage kinds:

Kind Who does it Example
model The model, with any connectors the stage names Search, read, label, draft
code A script, run by one exact command routineos.py write, lib/check_citations.py
skill The model, following a named skill Draft in the house style
gate A person, through an approval Approve each post

The stage list is a description, not an engine. The prompt still carries the steps and the run follows the prompt. There is no separate workflow runner in RoutineOS: stages exist so a person can read the routine’s shape, and so pack validate and pack install can check each step’s connectors and writes before anything runs.

Why it exists

A list of stages is easy to read, review and port. It makes the mix visible (which steps are code, which are the model, where the gate sits) without reading the whole prompt. It also gives validation something to hold on to: a stage that writes externally must sit behind a gate, a code stage must name a script the pack ships, and a skill stage must ship its skill.

When to choose it

Write the stage list first when:

  • You are porting an existing workflow. Each node becomes a stage; see n8n workflow to routine.
  • The routine mixes all four kinds and a reviewer needs to see where each sits.
  • You will share the routine as a pack. Every pack has stages, from 1 to 20.

A workflow is a way of describing a routine, not an alternative to the other kinds. content-calendar below is a workflow, a skill routine and a hybrid at once. A routine you never share as a pack can skip the stage list and keep the same split in its prompt.

Use cases

Use case Stages Shipped example
Search, record, learn, write, approve model, model, code, code, gate jobs-watch (piloted)
Read, label, draft, approve, send model, model, skill, code, code, gate, model inbox-triage (unpiloted)
Research, draft, check, approve, publish model, skill, code, code, code, gate, model content-calendar (unpiloted)
Collect, compare, audit, report model, code, model, code, code weekly-marketing-report (unpiloted)

How it works

The stage schema

Stages are declared in a pack’s stages array, 1 to 20 entries, in run order.

Field Required Notes
id yes Lowercase letters, digits and hyphens; unique in the pack
kind yes code, model, skill or gate
title yes Short name, up to 80 characters
does yes One plain sentence, up to 500 characters
connectors no Connector ids from the pack’s connectors; each connector must list this stage back
reads, writes no Store collections; never a people collection
command code only One plain line starting python3, naming routineos.py or a lib/*.py file the pack ships
skill skill only A skill under skills/<name>/SKILL.md in the pack’s files

Any other key is refused. A command on a non-code stage, or a skill on a non-skill stage, is refused. See Pack schema for the whole file.

What validation refuses

pack validate (and pack install, which validates first) checks the stages against the rest of the pack. These are the real messages, from a copy of content-calendar with its gate switched off, a pipe added to a command, and decisions added to a write:

FAIL routine: action 'publish-post' (kind post): gate required, set [gate] enabled = true
FAIL stages[2]: command must be a single plain line with no shell operators
FAIL stages[4].writes: 'decisions' is a people collection; only people write it
FAIL stages[5]: a gate stage needs routine.gate.enabled = true
FAIL connectors[2]: a write connector needs routine.gate.enabled = true
FAIL connectors[3]: a write connector needs routine.gate.enabled = true

In words:

  • A gate stage needs a gate. routine.gate.enabled must be true.
  • A write connector needs a gate. Any connector with access = "write" is refused without one.
  • People collections are off limits. A stage that reads or writes decisions or settings is refused; only people write those.
  • Connectors and stages agree both ways. A stage that names a connector the connector does not list back, a connector that names an unknown stage, or a required connector no stage uses, is refused.
  • Commands are narrow. No shell operators, no -m or -c, and only routineos.py or a shipped lib/*.py file.
  • Skills ship. A skill stage whose skills/<name>/SKILL.md is missing from files, or whose frontmatter name differs, is refused.

From stages to prompt

Each stage should map to one or more numbered steps in prompt.md, in the same order. The stages say what happens; the prompt says how, with exact commands for code stages and the skill’s name for skill stages. Two stages appear in every shipped pack, in this order: learn, then write. The prompt appends learn’s proposals to the front of the run’s inbox between them, because proposals added after the write are lost.

How it compares

Code Skill Workflow Hybrid
What it describes Where the work runs Where the judgement is written The order of steps The split between model and code
Checked before a run Commands, in a pack Frontmatter, by lint Every stage, by pack validate Settings filters, by write at run time
Required for a pack No No Yes No

Worked example

content-calendar drafts posts on a set of topics, checks that every claim has a source, and publishes only what a person approves. It runs Monday and Wednesday at 21:00 UTC (schedule = "0 21 * * 1,3", type approval-gated), and it is unpiloted. It uses every stage kind:

Stage Kind Does Connectors
research model Reads the feeds and searches each topic, keeping every source URL web-search, web
draft skill Drafts each post in each format in the house style, with a marker per claim (house-style)
cite code python3 lib/check_citations.py --draft draft.md --sources sources.json
learn code routineos.py learn
write code routineos.py write; writes runs, entities, inbox and pending
approve gate A person approves, edits or declines each draft; the gate times out after 72 hours gate-mail
publish model On a later run, publishes only approved drafts, exactly as approved cms

Reading it as a reviewer:

  1. Where the model judges. research and draft. The draft is shaped by skills/house-style/SKILL.md, so the voice changes by editing that file.
  2. Where code holds the line. cite checks every citation marker has a source and every source is cited; the model cannot wave a claim through. learn and write keep the store contract.
  3. Where a person decides. approve. The two write connectors, gate-mail and cms, are both optional, and both are allowed only because the gate is on.
  4. What sends. Only publish, and only on a later run, only for drafts whose decision is approved. Its cms connector lists “Copy the approved draft by hand from the Desk” as the alternative, so the routine works with no CMS connected.

Install it and check it:

python3 skills/routineos/scripts/routineos.py pack validate site/templates/content-calendar.json
python3 skills/routineos/scripts/routineos.py pack install site/templates/content-calendar.json --dir content-calendar

The install writes lib/check_citations.py and skills/house-style/SKILL.md with the routine files, and prints the connector report. It connects nothing and runs nothing.

For a piloted example, read jobs-watch as stages in Hybrid routines.

Common pitfalls

  • Expecting stages to run themselves. The prompt runs; stages are the declared shape. A stage with no matching prompt step does nothing.
  • Stages and prompt drifting apart. Reorder one, reorder the other. Reviewers read the stages and trust them.
  • A write connector without a gate. Refused, even when the connector is optional.
  • A stage that writes decisions or settings. People own those; refused.
  • A shell pipeline in a code stage. Refused. Put the logic in a lib/ script and call it once.
  • Learning after write. Proposals appended after the write are lost. Keep learn before write.
  • Porting every node. An n8n “Set” or “Merge” node is usually part of a model or code stage, not a stage of its own.

Was this page helpful?