# `Statifier.Effect.CancelInvoke`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.10.0/lib/statifier/effect/cancel_invoke.ex#L1)

Payload for `{:cancel_invoke, %__MODULE__{}}` - the cancellation Appendix
D's `exitStates` and `exitInterpreter` both perform with `for inv in
s.invoke: cancelInvoke(inv)` when a state carrying live invocations
exits. `invoke_id` names the invocation to cancel; `state_index` is the
constraint-3 identity of the state that owned it. `macrostep`/`microstep`/
`round` are the counters as they stood at the moment of the cancel.

## Not `Statifier.Effect.Cancel`

`Effect.Cancel` is spec 6.3's `<cancel sendid>` - an authored element that
cancels a *delayed send*, resolved against `Statifier.Session.Timers` by
`send_id`. This effect has no `<cancel>` element behind it at all: it is
the interpreter's own reaction to a state exiting while one of its
`<invoke>`s is still live, and it has no notion of a delayed send to look
up. The two share no session-side machinery, which is why they are two
structs rather than one with an optional field.

## One effect per invocation, not one per state

Appendix D's loop is `for inv in s.invoke: cancelInvoke(inv)` - one call
per invocation - and every other effect this vocabulary produces is
one-per-action (`Effect.Invoke` is one-per-`<invoke>`, `Effect.Log` is
one-per-`<log>`). A state with two live invocations therefore emits two
`CancelInvoke` effects, never one carrying a list.

## `caller_context`

`caller_context` is the opaque host term the current macrostep's
triggering external event carried (ADR-0063, amended 2026-09-01),
copied off `Statifier.MachineState`'s transient slot at
construction - `nil` when no context was attached. Both invoke
lifecycle effects carry it, not just the start, for the same
both-or-neither reason ADR-0063 decision 2 gave `%Cancel{}` the slot
beside `%SendDelayed{}`: the same handler and the same durable store
process both, and the cancellation act wants the same attribution as
the start. `Statifier.Effect.Autoforward` deliberately gains nothing -
it carries the triggering `%Statifier.Event{}` whole, and that event's
own slot already travels with it (ADR-0063 decision 3). The library
never reads the value.

# `t`

```elixir
@type t() :: %Statifier.Effect.CancelInvoke{
  caller_context: term(),
  invoke_id: String.t(),
  macrostep: non_neg_integer(),
  microstep: non_neg_integer(),
  round: non_neg_integer(),
  state_index: non_neg_integer()
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
