← All guided builds

Guided build · 13

Automate operations with rules and schedules

React to conditions, retain deliberate operational state, and choose explicit timing and missed-run behavior.

You will finish with: An operational playbook with predicate and transition rules, atomic writes, durable state, and three timing contracts.

25 minIntermediateSource checked for Grid 0.61.0Reviewed 2026-08-24
Related canonical example05-rulebook-operations.grid
Get Grid
Starter modelrulebook-starter.gridInputs and reactive status formulas before durable actions and timers are added.

Watch it in Grid

See the workflow before you build it.

Follow the finished interaction, then use the written steps below to build and inspect it yourself.

Companion film

Rules that act

Cross an operating threshold and see the model escalate review work automatically.

19 secGrid 0.61.0
Open film page
On this page

What you will build

An operational playbook that derives current status, reacts to live conditions, records durable events, and handles recurring and one-shot schedules deliberately. You will start from the canonical Rulebook Operations example and test each trigger in a copy you can safely change.

By the end, you will know when a formula is enough, when a rule should act, and how Grid preserves operational state across evaluation waves, model reloads, and runtime restarts.

1. Establish the declarative baseline

Open Grid and paste the canonical Rulebook Operations source from the linked example into a new model. Its five authored source values begin at:

A1 = 0
A2 = 0
A3 = FALSE
A4 = 0pct
A5 = "clear"

The first three outputs are ordinary formulas:

B1 = A3 THEN "frozen" ELSE A2 > 0 THEN "incident" ELSE "normal"
B2 = A4 > 5pct THEN "gap-open" ELSE "steady"
B3 = A1 > 100 THEN "heavy-flow" ELSE "steady-flow"
Binding Expected value
B1 "normal"
B2 "steady"
B3 "steady-flow"

The model also captures its first successful evaluation:

H1 = NOW() ONCE

H1 should contain that evaluation's timestamp. It is not a recurring clock: an unchanged ONCE assignment keeps the captured value through same-model replacement and runtime-host restart.

The other targets are owned only by rules. C1:F2 begin as BLANK until their rules fire, unless this model identity already has persisted scheduler state. G1:G2 are also blank on a fresh identity admitted before 2026-12-31T23:59:00Z; at or after that moment, their BACKFILL policy admits the one-shot close immediately.

Checkpoint: B1:B3 match the table and H1 contains a timestamp. On a fresh model identity, C1:F2 have no value before their first trigger or tick. Before the declared year-end moment, G1:G2 are blank as well; afterward, expect the one-shot values described in step 5.

2. Fire a predicate rule

Keep A3 = FALSE and change A1 from 0 to 125:

WHEN A1 > 100 AND A3 = FALSE THEN
  C1 = "risk-review"
  C2 = NOW()
END

The settled predicate is true, so Grid commits both actions as one rule wave.

Binding Expected value
B3 "heavy-flow"
C1 "risk-review"
C2 The rule's firing timestamp

Change A1 again while the predicate remains true. The rule is eligible again, and C2 receives the later firing time.

This is predicate behavior, not transition behavior. A predicate rule is evaluated when one of its dependencies is touched and may fire whenever its newly settled result is true. Use BECOMES TRUE when only the false-to-true transition should act.

3. Observe durable operational state

Change A3 to TRUE. B1 becomes "frozen", but the existing values in C1 and C2 remain.

A rule write is sticky operational state. Making the trigger false does not undo an earlier write. This is useful when the model must remember that a review was requested, rather than merely describe whether review conditions are true now.

Now change A5 to "degraded":

WHEN A2 > 0 OR A5 = "degraded" THEN
  C3 = "ops-paged"
  C4 = NOW()
END

Checkpoint: B1 = "frozen", while C1 still reads "risk-review". C3 = "ops-paged" and C4 contains this rule's firing timestamp.

Use a formula for a value that should continuously describe current inputs. Use a rule for an event or fact that should persist. If operational state must clear later, author a separate rule with the intended reset behavior.

4. Broadcast one rule wave across a range

Change A4 from 0pct to 6pct:

WHEN A4 > 5pct THEN
  D1:D3 = ROW_INDEX
END

Expected results:

Binding Expected value
B2 "gap-open"
D1 1
D2 2
D3 3

Grid broadcasts the expression over the finite range and commits the writes as one atomic rule wave. Every action expression in a wave reads the same pre-commit snapshot. Dependent formulas recompute only after the rule's writes commit.

5. Compare the three timing contracts

The model contains an interval, a cron schedule, and a one-shot moment:

EVERY duration"PT15M" SKIP MISSED THEN
  E1 = E1 + 1
  E2 = NOW()
END

EVERY cron"0 * * * *" BACKFILL THEN
  F1 = F1 + 1
  F2 = "hourly-reconciliation"
END

AT dt"2026-12-31T23:59:00Z" BACKFILL THEN
  G1 = TRUE
  G2 = "year-end-close"
END

On their next admitted occurrences:

  • The interval increments E1 and stamps E2.
  • The hourly cron increments F1 and labels the reconciliation in F2.
  • The AT block fires once at its declared UTC timestamp.

The missed-run policies make downtime behavior explicit:

  • SKIP MISSED ignores interval occurrences that passed while the runtime was down and resumes at the next future tick.
  • BACKFILL performs one catch-up wave after missed work, even if several occurrences passed.
  • If the AT timestamp is already past when an unfired model is admitted, BACKFILL runs that block once.

Exact counter values and timestamps depend on the runtime clock and its persisted scheduler state. Treat these as observation checkpoints, not fixed fixture values. In a disposable training copy, you can temporarily change PT15M to PT10S, observe a future tick, and then restore the canonical interval.

Checkpoint: after one newly admitted interval tick, E1 has increased by one and E2 contains that tick's time. A restarted model may begin from previously persisted values rather than BLANK.

6. Make ownership explicit

The canonical example deliberately demonstrates rule-only targets. For a production copy, give counters an explicit rule-owned base:

STATE E1 = 0
STATE F1 = 0

STATE declares the rule system as owner and supplies the initial or reset base. Later rule writes become sticky revisions over that base.

Do not write to B1, B2, or B3 from a rule. Those bindings already have ordinary formulas, and a rule write would durably shadow the formula until RESET and earn GRID_MIXED_OWNERSHIP. If a rule is intended to override a computed binding, mark the base formula default; otherwise keep one owner per target.

The canonical A1:A5 bindings are authored source assignments. If an API or UI client must write them externally, replace those declarations with input bindings rather than treating ordinary formulas as a write surface.

Try it yourself

Page operations only when the combined condition changes from false to true. Add an explicit counter and replace the existing paging rule with:

STATE PageCount = 0

WHEN (A2 > 0 OR A5 = "degraded") BECOMES TRUE THEN
  PageCount += 1
  C3 = "ops-paged"
  C4 = NOW()
END

Begin with A2 = 0 and A5 = "clear". Then verify this sequence:

  1. Changing A2 to 1 increments PageCount to 1.
  2. Touching A2 again while the expression remains true does not increment it.
  3. Restore A2 = 0, then change A5 to "degraded"; PageCount becomes 2.

A transition rule establishes its initial observation without firing. If this copy reused persisted rule state, use a fresh model identity before treating the first count as a fixed checkpoint.

For a later production extension that performs external effects, choose an explicit admission policy such as SERIAL QUEUE 100 OVERFLOW LATEST. External delivery is at least once across replay, so effect receivers should deduplicate by execution identity when the business action must happen exactly once.

You are done when

  • The baseline formula values match the first checkpoint.
  • You can explain why a true predicate may fire more than once.
  • Earlier reactive writes remain after their trigger becomes false.
  • The range rule produces 1, 2, and 3 in one committed wave.
  • You can distinguish SKIP MISSED, BACKFILL, and ONCE.
  • Rule-owned counters have a STATE base.
  • The transition challenge fires once per false-to-true change.
  • The model has no GRID_MIXED_OWNERSHIP warning.
Build statusReached the expected checkpoint?