# Rule bodies

This is the rule that catches everyone once, human and AI alike.

**Every line in a rule body must be a step call or an `if`.** No variables. No loops. No
function declarations.

## What works

```js
if ($.player.fields.satiety <= 0) {
  if ($.dial('starvationDamagePerHour') > 0) {
    $.damage({ amount: $.dial('starvationDamagePerHour') })
  }
}
```

Branching is fine. Nesting is fine. Arithmetic inside an argument is fine.

## What is rejected

```js
var rate = $.dial('starvationDamagePerHour')
$.damage({ amount: rate })
```

`var` is not a step. And it does not fail alone — **it takes the whole body with it.** The
rule does not partially run; it does not run.

That is the trap: you get a rule that silently never fires, and the cause is a line that
looks like ordinary, correct JavaScript.

## Say it again without the variable

Whatever you wanted the variable for, put it in the argument:

```js
$.damage({ amount: $.dial('starvationDamagePerHour') })
```

If you used it twice, write it twice. Bodies are short by design; the duplication you are
avoiding costs less than the rule not running.

## The conditional operator is fine inside an argument

```js
// WORKS — ?: lives inside the argument
$.adjustField({ field: "satiety", by: -($.timed
  ? ($.event.minutes / 60) * $.dial('foodDrainPerHour')
  : $.dial('foodDrainPerTurn')) })
```

It is a `?:` used as a *statement*, outside any argument, that fails.

## When you genuinely need code

```js
$.script(`
  for (var i = 0; i < 3; i++) { $.damage(1) }
`)
```

`$.script()` takes a real block of JavaScript. The cost: you lose the round-trip to the
visual editor, and the body becomes opaque to anyone editing with steps. Reach for it last.

Inside a script, the verbs are positional rather than object-shaped, and the list is
shorter:

<!-- api:generated:verbs -->
- `$.damage(...)`
- `$.heal(...)`
- `$.adjustField(...)`
- `$.setField(...)`
- `$.narrate(...)`
- `$.refuse(...)`
- `$.giveItem(...)`
- `$.setWorldState(...)`
- `$.passTime(...)`
<!-- /api:generated:verbs -->

A script may record at most 200 steps. That is a bound on runaway loops, not a budget to
spend — real rules record one or two.

## Code view and step view are one rule

The editor shows a rule either as visual steps or as code. They are two renderings of the
**same artifact** — editing either edits the same rule.

The round-trip is lossless or refused: roll tables (whose entries are not in the text) and
step types the renderer cannot yet write stay read-only rather than being guessed at. You
will never silently lose a step by switching views.

> **Image: `docs/rule-code-and-steps.png`**
> The same rule shown twice side by side — the visual step list and the code view — so the
> correspondence between one step and one line is obvious.

## Next

[Hooks](/docs/hooks) — when your rule body actually runs.
