# `PromptRunner.Control.Snapshot`
[🔗](https://github.com/nshkrdotcom/prompt_runner_sdk/blob/v0.12.1/lib/prompt_runner/control/snapshot.ex#L1)

What a run looks like from outside it.

Everything a dashboard needs for a header, and nothing that requires touching
the session. A snapshot is read from a file the runner rewrites at event
boundaries, so reading it cannot disturb, block, or slow the run.

# `status`

```elixir
@type status() :: :running | :completed | :failed | :unknown
```

# `t`

```elixir
@type t() :: %PromptRunner.Control.Snapshot{
  attempt: pos_integer() | nil,
  elapsed_ms: non_neg_integer(),
  event_count: non_neg_integer(),
  input_tokens: non_neg_integer(),
  mode: :run | :retry | :repair | nil,
  model: String.t() | nil,
  output_tokens: non_neg_integer(),
  packet: String.t() | nil,
  packet_dir: String.t() | nil,
  prompt_elapsed_ms: non_neg_integer(),
  prompt_id: String.t() | nil,
  prompt_name: String.t() | nil,
  prompt_started_at: DateTime.t() | nil,
  provider: atom() | nil,
  run_id: String.t() | nil,
  run_started_at: DateTime.t() | nil,
  status: status(),
  steer_count: non_neg_integer(),
  tool_count: non_neg_integer(),
  updated_at: DateTime.t() | nil,
  view: view()
}
```

# `view`

```elixir
@type view() :: %{
  log_mode: atom(),
  tool_output: atom(),
  thinking: atom(),
  diff: atom()
}
```

# `from_map`

```elixir
@spec from_map(map()) :: t()
```

Rebuilds a snapshot from the map read back out of `snapshot.json`.

Every field is optional. A snapshot written by an older runner, or one caught
mid-write by a reader, yields defaults rather than an exception — a dashboard
that crashes on a partial header is worse than one showing an empty one.

# `normalize_view`

```elixir
@spec normalize_view(map() | keyword()) :: {:ok, map()} | {:error, term()}
```

Normalizes view settings from strings or atoms into the settled vocabulary.

Returns `{:ok, view_updates}` with only the keys the caller actually named,
or `{:error, {:invalid_view, key, value}}`. An unknown key is an error rather
than a silent no-op: a dashboard that sets a setting the runner ignores has
no way to discover that it did nothing.

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Serialises a snapshot to the JSON-ready map written to `snapshot.json`.

# `view_keys`

```elixir
@spec view_keys() :: %{optional(String.t()) =&gt; [String.t()]}
```

The settings `set_view/2` accepts, and the values each one takes.

---

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