Skip to content

41 · Rules without Colang: the rule sheet

A rule sheet states the rules over your tools as YAML a person can read aloud: a refund over 200 needs a supervisor's approval; closing an account waits for the customer to confirm; nobody deletes a customer. It compiles to the same rulebook a Colang policy becomes, so every check that reads a rulebook reads a sheet exactly as it reads Colang. This chapter is the sheet, construct by construct, and what happens when a sheet says something the compiler cannot state exactly.

A sheet

# The shop's rules, as a sheet a person can read aloud.
rules:
  - action: refund_card
    requires: [supervisor_approved]
    when: { amount_over: 200 }
  - action: charge_card
    requires_confirmation: true
  - action: send_receipt
    after: [charge_card]
    at_most: 1

One rule per action. Each rule names its action, a tool of your catalogue, and says one or more of:

Key What it says The Colang it equals
requires: [a, b] the action runs only where every named context holds a deny-by-default flow for the action, guarded on $a and $b
when: {amount_over: 200} when the requirements apply, as conditions on the call's own arguments: <argument>_over, _under, _at_least, _at_most or _is the guard holds only where the condition does; the condition becomes a context the tool check computes from the checked call's arguments, so the host cannot set it
never: true the action never runs a flow that refuses it unconditionally
requires_confirmation: true a person confirms each call not in the rulebook: a confirmation the catalogue declares, which the tool check enforces
confirmation: "…" the same, with the words the person is asked as above, with the wording
after: [tool] the action runs only after each named tool, in the same session the catalogue's order rule, enforced by the tool check
not_after: [tool] the action never runs after a named tool as above
at_most: 3 the action runs at most that many times in a session as above

A sheet's confirmations and order rules are not in the rulebook, because the rulebook decides whether an action may happen at all, and those decide when and how. They are returned as an overlay on your catalogue, and the tool check enforces them like any declaration (chapter 32).

Compiling and saving

$ stalwart rules compile rules.yaml            # compiled and linted; nothing is kept
$ stalwart rules compile rules.yaml --save     # registered on your newest catalogue

The service compiles the sheet into the Colang policy it equals, and the Colang front end every policy goes through emits the rulebook from that. A sheet and the Colang it equals are therefore one gate, and every verdict over one is a verdict over the other. --out-dir writes what the compiler made: rule_sheet.co, the Colang; rulebook.ir.json, the rulebook; and catalogue_overlay.json, the confirmations and order the sheet sets.

--save keeps the sheet and registers a new catalogue version: your newest catalogue's tools, with the sheet's confirmations and order laid over them, and the sheet's rulebook as its rules. Every tool check enforces it from then on. A rulebook and a rule sheet are one record on a catalogue: an agent that declares a Colang rulebook: registers it again at its next start, in place of a sheet saved since (chapter 45).

The console's Rules page is a form over the same sheet: it compiles and lints on the page, and saves as --save does (chapter 5).

The lints

The sheet is checked against your newest catalogue, and each problem is a lint, not a failure:

  • rule_action_unknown: the sheet has a rule for an action the catalogue does not hold;
  • write_ungoverned: a write the catalogue holds that the sheet never names, so a new write is never left ungoverned by omission.

What every mode reads

The rulebook a sheet compiles to is what the tool check enforces at run time, what stalwart verify and the bug hunter scan, and what a guardrail job verifies when you send the compiled rulebook as rulebook_ir. Each answers over it exactly as over a rulebook from Colang.

Refused, never approximated: rule_sheet_unsupported

A sheet the compiler cannot state exactly is refused, naming its line and the reason, and nothing is written. The reasons are concrete:

  • a key that is not one of the nine above;
  • a second rule for the same action: how two rules would combine is never guessed;
  • never with anything beside it: an action that never runs has no other condition;
  • when on a rule that requires nothing: the conditions say when the requirements apply;
  • a condition with no comparison the compiler can state, or a number compared with something that is not one;
  • YAML outside the sheet's subset: tabs, a block list, a value on the line below its key, a second document.
$ stalwart rules compile unsupported.yaml
unsupported.yaml:4: rule_sheet_unsupported: line 4: `refund_card` has a rule already: one rule per action, so how two would combine is never guessed. The sheet was not compiled; nothing was changed.
$ echo $?
1

A sheet is refused rather than approximated because an approximation would be a rule you did not write, enforced as though you had. stalwart rules compile exits 1 on a refusal, 0 on a sheet that compiled, 2 where the file could not be read and 3 where the service could not be reached.

From a drafted rule to a sheet

A rule Guardrail Writing drafted and verified is Colang. Where its rule is one of the shapes above, it can be restated as a sheet a reviewer reads more easily, and the two compile to one gate: compile both and compare the rulebooks, or verify the same obligation over each.

The limits

  • A sheet states what its constructs express and no more. A rule that needs a construct the sheet does not have is written in Colang.
  • The contexts a sheet requires are set by your systems at run time; the sheet proves the action waits for them, not that they are set truthfully.
  • Lints are advice. A sheet with lints still compiles; reading them is how a new write is not left without a rule.