# `type.batch_order_updatable`

A batch walks a query ordered by a field an update can change.

Area 12, [Jobs and events](../topics/jobs-and-events.md); declinable; the next step is usually the agent's.

A run walks live records a page at a time, each page after the record
the one before ended on, so a `from:` whose query orders by a field an
update can change can miss a record whose field moved behind that place
mid-run, or meet it twice. An order no update changes keeps every record
in exactly once: `id`, a field marked `immutable: true`, or a time
generated as the record is created, then `id` ("How a run is
kept"):


```jadpo
/// The items, a page at a time, in code order.
query items_by_code(page: ItemPage) -> Page<Item> {
    from: Item
    order: [Item.code asc]
    page: {
        size: page.page_size
        after: page.after
    }
}

/// Starts marking every item seen.
action sweep_shelves(page: ItemPage) -> BatchRun {
    return batches.each(from: items_by_code(page), each: item -> mark_seen(item))
}
```

```text
error: `batches.each` walks `items_by_code` in an order an update can change
 --> main.jadpo:13:31
   |
13 |     return batches.each(from: items_by_code(page), each: item -> mark_seen(item))
   |                               ^^^^^^^^^^^^^^^^^^^ ordered by `Item.code`, which an update can change
  reason: A run walks live records a page at a time, each page after the record the one before ended on, so a record whose `Item.code` changes mid-run can move behind that place and be missed, or ahead of it and be met twice. An order no update changes meets every record once: `id`, or a time generated as the record is created, then `id`.
  next: Order `items_by_code` by `Item.id`, or by a field marked `immutable: true` or generated with `Generated.on_create` and then `Item.id`, or write `decline type.batch_order_updatable: "<why>"` above it if this is deliberate (agent)
  rule: type.batch_order_updatable
  help: https://jadpo.dev/docs/errors/type.batch_order_updatable

check failed: 1 error, 2 files
```

It is a judgement call, so it can be declined on the action, with the
reason the order is safe. Here the walk goes from the most stock to the
least while sales only ever lower an item's stock: a sale moves an item
further along the walk, so it can be met twice but never missed, and
marking an item seen twice does no harm:


```jadpo
/// The items, a page at a time, the most stock first.
query items_by_stock(page: ItemPage) -> Page<Item> {
    from: Item
    order: [Item.stock desc]
    page: {
        size: page.page_size
        after: page.after
    }
}

/// Starts marking every item seen, the best stocked first.
decline type.batch_order_updatable: "Sales only lower stock, which moves an item further along the walk, never past it; marking one seen twice does no harm"
action sweep_by_stock(page: ItemPage) -> BatchRun {
    return batches.each(from: items_by_stock(page), each: item -> mark_seen(item))
}
```
