# `Snodo.Progress`
[🔗](https://github.com/joshrotenberg/snodo/blob/v0.1.0/lib/snodo/progress.ex#L1)

Acknowledged progress reporting for an active ordinary MCP request.

Application handlers call `report/3` using their request context. A client must
have supplied a string/integer `progressToken`, and the transport must have
installed a sink. Otherwise reporting valid values is a harmless no-op.

Only the original request worker may report. Each sink permits one outstanding
update, acknowledged only after its transport writes it. Values must increase
strictly. The defaults cap each request at 1,000 updates, messages at 4 KiB,
and each acknowledgement wait at five seconds. Timeout does not clear the
outstanding-update gate, so retrying cannot grow an unbounded mailbox.

The transport owns `state/1`, handles `:mcp_progress` synchronous call messages
with `accept/3`, writes their notification, then calls `reply/3`. It closes the
sink after execution, cancellation, disconnect, or a failed write. No process
is started by this module, and the synchronous protocol core is unchanged.

# `report`

```elixir
@type report() :: %{binding: t(), fields: map()}
```

# `result`

```elixir
@type result() :: :ok | {:error, atom()}
```

# `t`

```elixir
@opaque t()
```

# `accept`

```elixir
@spec accept(Snodo.Progress.State.t(), GenServer.from(), report()) ::
  {:ok, map(), Snodo.Progress.State.t()} | {:error, atom()}
```

Admits a synchronous report in the transport owner before writing it.

`from` is the original `GenServer.from()` received with the progress call.
The returned state belongs to the transport. Complete the acknowledgement
with `reply/3` only after the write succeeds or fails.

# `bind`

```elixir
@spec bind(term(), Snodo.Context.t()) :: t() | nil
```

Binds an installed sink to a validated request context and its current worker.

# `close`

```elixir
@spec close(Snodo.Progress.Sink.t()) :: :ok
```

Marks a sink terminal. Safe to call repeatedly from transport cleanup paths.

# `reply`

```elixir
@spec reply(GenServer.from(), report(), result()) :: :ok
```

Acknowledges a consumed report after writing, releasing its one-outstanding-update gate.

# `report`

```elixir
@spec report(Snodo.Context.t(), number(), keyword()) :: result()
```

Reports increasing progress, optionally including numeric total and a human-readable message.

# `sink`

```elixir
@spec sink(pid(), keyword()) :: Snodo.Progress.Sink.t()
```

Creates an inactive-until-bound sink handle for an existing transport owner.

# `state`

```elixir
@spec state(Snodo.Progress.Sink.t()) :: Snodo.Progress.State.t()
```

Initializes counters retained by the transport owning the sink.

---

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