# `Parlora.SessionHealth`
[🔗](https://github.com/hfiguera/parlora/blob/v0.1.0/lib/parlora/session_health.ex#L1)

Content-free observations in `Parlora.status/1` under `:health`.

Input/output counters count successful pipeline writes to the provider/channel
adapter, including silence. They do not prove network receipt, speech, audibility,
or an answer. Counters are approximate concurrent samples, not atomic bundles.
Event counts cover observations delivered to the coordinator, not all wire events.
Timestamps are signed VM-local monotonic microseconds; absent activity has nil time.

`Parlora.expect_progress/4` arms a single deadline (1–300,000 milliseconds).
`until:` selects `:assistant_caption` (default), `:user_caption`, `:delegation`,
`:conversation` (any of these three), or `:explicit`. Automatic resolution requires a new, nonblank matching caption
or a delegation observed after arming and processed before the deadline. It
establishes activity only, not correctness or correlation with a particular turn.
Use `:explicit` and `Parlora.resolve_progress/3` when the host needs semantic or
request-specific completion. Acknowledgments and audio alone never satisfy it.

`on_timeout: :notify` (default) emits a session `:progress_changed` event with
`status: :timed_out` and leaves the session ready. `:close` also emits `:failed`
with `cause: :progress_timeout` and starts bounded cancellation/cleanup. External
work can remain uncertain. No reconnect or work replay occurs automatically.

No deadline runs by default. The host arms it before the operation it expects,
cancels it on interruption or a changed expectation, and chooses a suitable budget.
Silence, muted input and long backend work are not independently classified as faults.
Closing cancels a pending expectation. Ordinary activity never extends its deadline.

One current/latest expectation is retained. Repeated IDs with identical settings
are idempotent, including after resolution; conflicting reuse is rejected. Records
are bounded by `max_records`; exhaustion rejects further expectations. IDs appear
in status/events, so use opaque IDs rather than user content.

# `activity`

```elixir
@type activity() :: %{count: non_neg_integer(), last_at_us: integer()}
```

# `audio`

```elixir
@type audio() :: %{
  frames: non_neg_integer(),
  bytes: non_neg_integer(),
  last_at_us: integer() | nil
}
```

# `expectation`

```elixir
@type expectation() :: %{
  :id =&gt; String.t(),
  :status =&gt; :pending | :observed | :completed | :cancelled | :timed_out,
  :until =&gt;
    :assistant_caption | :user_caption | :delegation | :conversation | :explicit,
  :on_timeout =&gt; :notify | :close,
  :timeout_ms =&gt; pos_integer(),
  :started_at_us =&gt; integer(),
  :deadline_at_us =&gt; integer(),
  optional(:resolved_at_us) =&gt; integer()
}
```

# `t`

```elixir
@type t() :: %{
  input_forwarded: audio(),
  output_forwarded: audio(),
  observations: %{
    optional(
      :provider_event
      | :user_caption
      | :assistant_caption
      | :delegation
      | :playback_event
    ) =&gt; activity()
  },
  expectation: expectation() | nil
}
```

---

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