Formulas describe what work looks like. Orders describe when it should happen. An order pairs a trigger condition with an action — either a formula or a shell script — and the controller checks those triggers automatically. When a trigger opens, the order fires. No human dispatch needed. When you runDocumentation Index
Fetch the complete documentation index at: https://gascityinc-5c0069dd-work-default-pack-registry.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
gc start, you launch a controller — a background process that
wakes up every 30 seconds (a tick), checks the state of the city, and takes
action. One of the things it does on each tick is evaluate the triggers that
unblock an order from running. That periodic check is what makes orders work.
We’ll pick up where Tutorial 06 left off. You should
have my-city running with agents and formulas configured.
If you’ve been dispatching formulas by hand with gc sling, orders are the next
step: they turn that manual dispatch into something the city does on its own, on
a schedule or in response to events.
A simple order
Orders live in anorders/ directory at the top level of your city, alongside
formulas/ and agents/. Each order is a flat *.toml file in that
directory.
review formula from Tutorial 04
every five minutes:
pool field tells the controller where to send the work. A pool is a
named group of one or more agents that share a work queue — the agents chapter
introduced them briefly. When an order fires, the controller creates a wisp from
the formula and routes it to the named pool. Any agent in that pool can pick it
up.
The controller evaluates trigger conditions on every tick. When five minutes have
passed since the last run, it instantiates the review formula as a wisp and
routes it to the worker pool. The order name comes from the file basename
(review-check.toml → review-check), not from anything in the TOML.
Orders are discovered when the city starts and whenever the controller reloads
config. You don’t need to restart anything if the city is already watching the
orders directory.
Inspecting orders
Once you’ve defined some orders, you’ll want to see what the controller sees — which orders exist, what their triggers look like, and whether any are due. Three commands give you that view.gc order list shows every enabled order in your city — whether or not it has
ever fired:
TARGET column is the pool the order will route to (the field is still
pool in the TOML).
To see the full definition:
Running an order manually
Any order can be triggered by hand, bypassing its trigger:Order "<name>" executed (exec).
This is useful for testing a new order or for kicking off work that’s almost due
anyway.
Trigger types
The trigger is what makes an order tick. It controls when the order fires. There are five trigger types.Cooldown
The most common trigger. The name comes from the idea of a cooldown timer — after the order fires, it has to cool down for a set interval before it can fire again:interval has elapsed since the last run. The interval is a Go
duration string — 30s, 5m, 1h, 24h.
Cron
Fires on an absolute schedule, like Unix cron job:* (any),
exact integers, and comma-separated values (1,15 for the 1st and 15th).
The difference from cooldown: a cooldown fires relative to the last run
(“every 5 minutes”), while cron fires at absolute times (“at 3 AM daily”).
Cooldown drifts — if the last run was at 3:02, the next is at 3:07. Cron hits
the same wall-clock times every day.
Cron triggers fire at most once per minute — if the order already ran during the
current minute, it waits for the next match.
Condition
Fires when a shell command exits 0:sh -c "<check>" with a 10-second timeout on each tick. If
the command exits 0, the order fires. Any other exit code, and it doesn’t. This
is the trigger for dynamic, external triggers — check a file, ping an endpoint,
query a database.
One caveat: the check runs synchronously during trigger evaluation. A slow check
delays evaluation of subsequent orders on that tick. Keep checks fast.
Event
Fires in response to system events:bead.closed event appears on the event bus. Event triggers
use cursor-based tracking — each firing advances a sequence marker so the same
event isn’t processed twice.
Manual
Never auto-fires. Only triggered bygc order run:
gc order check (there’s nothing to check —
they’re never due automatically). They do appear in gc order list.
Formula orders vs. exec orders
So far every example has used a formula as the action. But orders can also run shell scripts directly:- Every order has either
formulaorexec, never both. - Exec orders can’t have a
pool— there’s no agent pipeline to route to. - The script receives
ORDER_DIRin its environment, set to the directory containing the order file. Pack-sourced orders also getPACK_DIR.
Timeouts
Each order can set a timeout:city.toml:
Disabling and skipping orders
An order can be disabled in its own definition:gc order list or get evaluated.
You can also skip orders by name in city.toml without editing the order file:
Overrides
Sometimes a pack’s order is almost right but you need to tweak the interval or change the pool. Rather than copying and modifying the order file, use overrides incity.toml:
enabled, trigger, interval, schedule, check, on,
pool, and timeout. The override matches by order name. An override that
targets a nonexistent order produces an error rather than silently no-opping
— gc order CLI commands fail; gc start logs the error and continues
running with the unmatched override skipped.
Rig scoping
Many orders expand at scan time into one instance per rig (anything in a rig’sorders/ directory or a pack imported into a rig). When the same
order appears city-wide AND per-rig, an override must say which:
"*" is reserved as the wildcard token and may not be used as a real rig
name.
Order history
Every time an order fires, Gas City creates a tracking bead labeled with the order name. You can query the history:Duplicate prevention
Before dispatching, the controller checks whether the order already has open (non-closed) work. If it does, the order is skipped even if the trigger says it’s due. This prevents pileup — if an agent is still working through the last review check, the controller won’t dispatch another one.Rig-scoped orders
Orders don’t just live at the city level. When a pack is applied to a rig, that pack’s orders come along and run scoped to that rig. Say you have a pack calleddev-ops that includes a test-suite order:
my-api/worker vs my-frontend/worker). To
act on a specific one, pass --rig:
test-suite has its own
cooldown timer, its own tracking beads, its own history. The my-api version
tracks separately — if the city-level order fired two minutes ago, that doesn’t
affect whether the my-api order is due. Internally, Gas City distinguishes
them by scoped name: test-suite vs test-suite:rig:my-api vs
test-suite:rig:my-frontend.
Pool targets are auto-qualified: pool = "worker" in the order definition
becomes gc.routed_to=my-api/worker on the dispatched wisp, routing work to
the rig’s own agents rather than the city-level pool.
Order layering
With orders coming from packs, rigs, and your city’s ownorders/ directory,
the same order name can exist in multiple places. When that happens, the
highest-priority layer wins. The layers, from lowest to highest priority:
- City packs — orders that ship with a pack you’ve included (e.g., the
dev-opspack’stest-suite) - City local — orders in your city’s own
orders/directory - Rig packs — orders from packs applied to a specific rig
- Rig local — orders in a rig’s own
orders/directory
dev-ops pack defines test-suite with a 5-minute cooldown and
you create your own orders/test-suite.toml with a 1-minute cooldown,
yours wins — the pack version is ignored entirely.
Putting it together
Here’s a city with two orders: a frequent lint check (exec, no agent needed) and weekly release notes (formula, dispatched to an agent). Assume you’ve already created aworker agent as in
Tutorial 05. The remaining pieces are just the order
files and the formula they dispatch.
worker pool. Neither requires anyone to type
gc sling.
Orders are formulas and scripts on autopilot, gated by time, schedule,
conditions, or events, evaluated by the controller on every tick.