# The frame

> What to call each frame, in what order, and why the order is fixed.

Carte does not own a window or a loop. You call it from yours. A frame has five steps, and their order is part of the contract.

## Each frame

```rust
// 1. Draw what the path says is visible, from committed state only.
for id in router.visible_views() {
    match id {
        "home" => { /* draw from store.read::<Shared>(Scope::Process) */ }
        _ => {}
    }
}

// 2. Input becomes actions. Escape goes through the router.
if escape_pressed {
    let _outcome = router.escape(model, outbox);
}

// 3. Tick: drain the outbox, apply actions, run producers, commit.
let notice = store.tick(outbox, |actions, barrier| {
    for a in actions {
        model.apply(a);
    }
    producers.produce(model, barrier);
});

// 4. Re-derive the path from the model and the committed store.
let moved = router.resolve(model, store);

// 5. If the path moved, tick once more so new scopes are filled
//    before anything draws them.
if moved {
    store.tick(outbox, |actions, barrier| { /* same as step 3 */ });
}
```

Before the first frame, run steps 3 to 5 once, so the path and the store are in place for the first draw. The [Quick start](/docs/quick-start) wraps steps 3 to 5 in a `settle` function and calls it from `new`.

`Router::update(cx, out, dt)` ticks every node on the path, outermost first, if your nodes do per-frame work. Call it before step 3, so anything it pushes lands in this frame's tick.

## What each step may touch

| Step | Reads | Writes |
| --- | --- | --- |
| Draw | Committed store, the router's path | The outbox |
| Input | The model (`&`) | The outbox |
| Tick | Drained actions, working store | The model, store fields through producers |
| Resolve | The model, committed store | The path, node lifecycle, scope mounts |

The draw step and the input step can only *ask*. They push actions. Nothing reaches into the model or the store while the frame is being drawn.

## Why the order is pinned

Inside `Store::tick` the order is fixed:

```text
outbox drain → producer sets → commit → notify
```

The signature enforces it. The outbox is drained before your closure runs, and the closure receives the actions. A field can only be set through a `Writer`, a `Writer` only comes from the `Barrier`, and the `Barrier` only exists inside that closure. So there is no way to write to the store outside the tick, and no way for a producer to run before the actions are applied.

Three things follow.

**No torn reads.** The `Barrier` borrows the store mutably for its whole life, so no view can call `store.read` while producers are running. A view never sees field A from this frame and field B from the last one. This is not checked at runtime. The borrow checker makes it impossible to write.

**A click costs zero frames.** An action pushed during draw is applied in the same frame's tick, before producers run, so its effect is in this frame's commit and the next draw shows it.

**Ten sets are one notice.** Commit happens once, on the way out of `tick`, and returns one `Notice`. A subscriber watching ten moved fields appears in it once. An idle frame's notice is empty, because sets that change nothing are dropped.

## Why resolve comes after the tick

The walk reads committed state. Each node's `resolve_child` reads the model, and `resolve_child_in` and every guard read the store's last commit. Running resolve after the tick means the path reflects what this frame asked for.

When the path changes, `resolve` runs in this order:

1. The deepest departing node's `depart` is asked how it leaves.
2. `leave` runs on each departing node, deepest first.
3. Departing scopes are dropped.
4. Arriving scopes are mounted at their declared starting values.
5. `enter` runs on each arriving node, outermost first.

A node's `leave` can still read the facts it stood on. A node's `enter` finds its scope already standing.

## Why tick twice when the path moves

A scope that mounts in step 4 starts at its declared starting value, not at what your producers would write. If you drew it straight away, the editor would flash empty for one frame. The second tick runs producers over the newly mounted scope before the next draw. It costs one extra commit on frames where the path changed, and nothing on the rest.

## Using the notice

```rust
let watcher = store.subscriber();
store.subscribe(watcher, EDITOR, "dirty")?;

// after a tick:
if notice.woke(watcher) {
    // re-render whatever reads `dirty`
}
for c in notice.changed() {
    println!("{}.{}", c.scope, c.field);
}
```

The store never calls you back. A callback would run inside the commit, which is exactly when nothing should be looking. The tick hands back who to wake, and your binding does the waking.

## Next

- [Nodes and the walk](/docs/routing/nodes): what `resolve` asks each node.
- [Producers and the tick](/docs/state/producers): writing fields inside the barrier.
