# `Flezha.SDK.Ctx`
[🔗](https://github.com/flezha/flezha_elixir_sdk/blob/v0.1.0/lib/flezha/sdk/ctx.ex#L1)

The invocation context handed to `Tasklet.run/3`. `use Flezha.SDK.Tasklet`
imports the functions below, so a tasklet writes `emit(ctx, value)` and
`complete(ctx, result)`; `apply/3` is not imported, since it would shadow
`Kernel.apply/3`, and a `use` tasklet never calls it: its `defchange`
recorders do.

`emit/2` and `emit/3` (with `key:` and `metadata:`) accumulate outputs for
this invocation; `apply/3` folds a change into the state through the
tasklet's `apply_change/2` and records it for the Cell's record stream, the
only recorded mutation path; `now/1` is the declared clock's value,
evaluated once by the core for this input, and the scheduled timestamp
inside a firing; `cell_key/1` is the owning Cell's key (or nil for
a Single Cell). Emits, changes, timer operations and the completion are held
in the invoking worker's dictionary under a per-invocation ref and are taken
only when `run/3` returns `{:ok, _}`: a failing invocation discards them all.
Reserved `flz.`-prefixed metadata keys are refused.

A `key:` rides an emit only where the node's output is declared `keyed/1`, and a
keyless emit there from a Cell with no key of its own is refused at the call.

`timer_after/2` and `timer_at_event_time/2` record a registration and answer
`{:ok, at}`, the absolute timestamp `cancel_timer/3` takes beside the kind;
a cancel answers `:ok`. `flz_core` judges the item's timer ops when it settles: past the
tasklet's `max_timers_per_cell/0`, or an event-time timer under another
clock, the item fails, not retried. Inside a `compensate/3` hook the three
timer calls answer `runtime.timers_unavailable` and arm nothing.

`spawn_steps/4`, `effect/4` and `part/2` reach `flz_core` from the live
activation, and only from the worker running the invocation: the same call
from another process is refused.

# `emit_option`

```elixir
@type emit_option() :: {:key, String.t()} | {:metadata, map()}
```

An `emit/3` option.

# `t`

```elixir
@type t() :: %Flezha.SDK.Ctx{
  cell_key: term(),
  channel: term(),
  core: term(),
  identity: term(),
  item: term(),
  keyed_sink?: term(),
  module: term(),
  now_ms: term(),
  offset: term(),
  owner: term(),
  part_encode: term(),
  ref: term(),
  replay?: term(),
  seq: term(),
  timers?: term(),
  token: term()
}
```

# `apply`

```elixir
@spec apply(t(), term(), term()) :: term()
```

Fold `change` into `state` with the tasklet's `apply_change/2` and record
it, so the Cell ships it after the invocation succeeds. The returned state
is what `run/3` returns in its `{:ok, _}`.

# `cancel_timer`

```elixir
@spec cancel_timer(t(), Flezha.SDK.TimerFiring.kind(), non_neg_integer()) ::
  :ok | {:error, Flezha.SDK.Error.t()}
```

Cancel the `kind` timer (`:processing_time` or `:event_time`) its registration
answered `at` for; one not pending changes nothing.

# `cell_key`

The owning Cell's key, or nil for a Single Cell.

# `checkpoint`

```elixir
@spec checkpoint(t()) :: :ok
```

Close the staged checkpoint interval at the end of this invocation. A no-op
on a class that did not declare checkpoint granularity.

# `complete`

```elixir
@spec complete(t(), term()) :: :ok
```

End the instance this tasklet runs in, with `result` as the result its
completion reports. `result` is encoded as `spawn_steps/4` encodes its
payload; `nil` carries no result.

In a class that runs in steps the rest of the iteration still runs and
commits; once that commit is durable the instance ends instead of waiting
for its next input. In a flow class the instance runs what its Cells already
hold to its outputs, then ends; a later call before that replaces the result.

# `effect`

```elixir
@spec effect(
  t(),
  String.t(),
  (-&gt; term()),
  {(term() -&gt; binary()), (binary() -&gt; term())} | nil
) ::
  {:ok, term()}
  | {:error, Flezha.SDK.Error.t() | {:non_retryable, Flezha.SDK.Error.t()}}
```

Run `fun`, a call to anything outside the instance (a model, a tool, a
service), and keep its result: when this invocation runs again (a crash
re-run, a replay, a wake, a move), the call answers the kept result and
`fun` does not run. The result is kept, synced on the controller, before
this returns; a crash between `fun` finishing and that point runs `fun`
again, so give the outside service `stable_id/1` as an idempotency key where
it takes one.

`name` names the call: a non-empty string of at most 255 bytes, counted per
invocation. One call runs at a time; a concurrent one is refused. The result
is encoded as `spawn_steps/4` encodes its payload and read back as JSON, else
as the binary; `codec`, an `{encode, decode}` pair of one-argument
functions, replaces both.

An invocation that took no input-log offset runs `fun` each time and keeps
nothing. A raise in `fun` propagates and nothing is kept. Answers
`{:ok, result}`, or `{:error, reason}` for `run/3` to return as it is:
`{:non_retryable, error}` for `effect.bad_name`, `effect.nested`,
`effect.outside_invocation` and `effect.too_large`, and the retryable
`effect.not_recorded` error.

# `emit`

```elixir
@spec emit(t(), term()) :: :ok | {:error, Flezha.SDK.Error.t()}
```

Emit a value on the tasklet's outgoing channel.

# `emit`

```elixir
@spec emit(t(), term(), [emit_option()]) :: :ok | {:error, Flezha.SDK.Error.t()}
```

Emit a value with options: `key:`, the output's natural key, only on an
output declared `keyed/1`; `metadata:`, a map whose reserved `flz.`-prefixed
keys are refused. Any other option raises `ArgumentError`.

# `now`

The declared clock's value for this input; the scheduled timestamp inside a firing.

# `part`

```elixir
@spec part(t(), term()) :: :ok | {:error, Flezha.SDK.Error.t()}
```

Send `value` as a part: a preview of this invocation's output on its
channel, which leaves now rather than when `run/3` returns, encoded as an
emit of `value` would be. It never waits: a part the wire cannot take at once
is dropped, and an invocation of a replayed input sends none.

`:ok`, or `{:error, %Error{}}`: `part.no_output` when the node's channel does
not end in an output (a `call` end is not one), `part.outside_invocation`
through a ctx whose invocation has returned or from another process.

# `replay`

```elixir
@spec replay(t()) :: boolean()
```

Whether this invocation replays an input the Cell already ran.

# `spawn_steps`

```elixir
@spec spawn_steps(t(), String.t(), term(), [Flezha.SDK.Spawn.option()]) ::
  Flezha.SDK.Spawn.t()
```

Request spawn-or-deliver of a steps instance of `factory_id` in this
runner's namespace, with `payload` as its first input: bytes as they are,
anything else as JSON, and a term with no JSON form raises. Answers a
`Flezha.SDK.Spawn` handle without waiting, read with
`Flezha.SDK.Spawn.await/2` or `Flezha.SDK.Spawn.try_result/1` inside this
invocation, or not at all.

With `id:` the instance starts under that id when it is free; while it runs,
`if_running: :deliver` (the default) hands it `payload` as one more input and
`if_running: :error` resolves `already_running`. `if_running:` without `id:`
resolves the core's `spawn.if_running_without_id` at the call and sends
nothing.

Without `id:` the controller generates one, and every call starts a new
instance: a call reissued after a lost or timed-out answer starts a second
one, and a replayed invocation resolves `skipped_on_replay`. A crash re-run
of an input the Cell had not durably consumed asks again under the same
identity, and the controller answers the instance the first ask started.

# `stable_id`

```elixir
@spec stable_id(t()) :: String.t()
```

A replay-stable 128-bit id, 32 lowercase hex digits: FNV-1a-128 over the
Cell's identity, the change seq this invocation started at and a
per-invocation ordinal. The same invocation on a replay derives the same
ids in the same order, which is what makes an effect idempotent.

# `timer_after`

```elixir
@spec timer_after(t(), integer()) ::
  {:ok, non_neg_integer()} | {:error, Flezha.SDK.Error.t()}
```

Register a processing-time timer `delay_ms` after `now/1`; `{:ok, at}`, the cancellation handle.

# `timer_at_event_time`

```elixir
@spec timer_at_event_time(t(), non_neg_integer()) ::
  {:ok, non_neg_integer()} | {:error, Flezha.SDK.Error.t()}
```

Register an event-time timer at `at`; `{:ok, at}`.

---

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