# observe_after_step

Schedules one keyed READ at the checkpoint after a zero-based PROGRAM step.

Canonical HTML: <https://determica.com/docs/functions/observe_after_step>

- Kind: scalar function
- Category: [Simulation](/docs/category/simulation.md)
- Tags: Program Compiler, Simulate

Schedules one keyed READ at the checkpoint after a zero-based PROGRAM step.

## Overload 1: observe_after_step(VARCHAR, UINTEGER, READ)

```sql
observe_after_step(VARCHAR, UINTEGER, READ)
```

Schedules one keyed READ at the checkpoint after a zero-based PROGRAM step.

- Kind: scalar
- Execution context: Offline
- Behavior: Transform
- Execution modes: Offline
- Input shape: Scalar Arguments
- Planning contract: Runtime
- Cardinality: One Per Input
- Determinism: Deterministic
- Return shape: Scalar
- Schema stability: Fixed
- Stability: Stable
- Side effects: None documented

### Inputs

- `observation_key` (VARCHAR; required; positional) — Non-NULL valid UTF-8 key; the consumed request requires 1-256 bytes and no NUL byte.
- `step_index` (UINTEGER; required; positional) — Zero-based UINTEGER step index. Out-of-range points are unavailable; points after skipped steps are skipped.
- `read` (READ; required; positional) — Non-NULL READ evaluated against the retained post-step state.

### Returns

Post-step OBSERVATION

Returns one OBSERVATION that expands to a single after_step point.

- `observation` (OBSERVATION)

Post-step OBSERVATION. Returns one OBSERVATION that expands to a single after_step point.

### Examples

_Local SQL · Local_

```sql
WITH actors AS (
  SELECT
    '0x1000000000000000000000000000000000000001'::ADDRESS AS sender,
    '0x2000000000000000000000000000000000000002'::ADDRESS AS writer
),
runnable_candidate AS (
  SELECT
    'pass'::VARCHAR AS candidate_key,
    program([execute_call(
      sender,
      writer,
      '0x000000000000000000000000000000000000000000000000000000000000002a'::BYTES,
      1::UINT256,
      100000::UBIGINT
    )]) AS program,
    [observe_after_step('writer.slot.checkpoint'::VARCHAR, 0::UINTEGER, storage_at(writer, '0x0000000000000000000000000000000000000000000000000000000000000000'::BYTES32))] AS observations
  FROM actors
)
SELECT
  candidate_key,
  len(observations)::INTEGER AS scheduled_observations,
  observations[1] IS NOT NULL AS scheduled
FROM runnable_candidate;
-- => [{"candidate_key":"pass","scheduled_observations":1,"scheduled":"true"}]
```

## Guidance

### Request validation

Key length, NUL bytes, and duplicate points are validated when the observation list is consumed. A request may contain at most 256 expanded points across all OBSERVATION values.

## Related functions

- [assume_native_balance](/docs/functions/assume_native_balance.md) — See Also
- [call_context](/docs/functions/call_context.md) — See Also
- [execute_call](/docs/functions/execute_call.md) — See Also
- [program](/docs/functions/program.md) — See Also
- [observe](/docs/functions/observe.md) — See Also
- [run](/docs/functions/run.md) — See Also
