# `MingaAgent.Session.SubscriberLifecycle`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_agent/session/subscriber_lifecycle.ex#L1)

Owns subscriber roles, monitor identity, and detached-session reclamation.

Session performs every process and timer effect. This value validates the operation, returns the next lifecycle or a named preparation, and accepts the resulting OTP reference through a completion transition.

## Transition table

| Command | Valid source | Result |
|---|---|---|
| subscribe | valid role, PID absent | monitor preparation |
| complete subscribe | unchanged preparation source | attachment installed, prior timer returned for cancellation |
| claim driver | attached PID, driver vacant or same PID | role transition |
| detach | attached PID | attachment removed with monitor cleanup identity |
| DOWN | matching PID and monitor reference | attachment removed |
| reconcile reclaim | detached and turn reclaimable | timer preparation or unchanged |
| complete reclaim schedule | unchanged preparation source | exact OTP timer reference installed |
| reclaim timeout | matching timer, detached, turn reclaimable | reclaim accepted |
| stop | any | empty lifecycle plus ordered timer and monitor cleanup identity |

# `reclaim_preparation`

```elixir
@type reclaim_preparation() :: t()
```

A validated reclaim schedule awaiting timer creation.

# `role`

```elixir
@type role() :: MingaAgent.Session.SubscriberAttachment.role()
```

Remote attachment role.

# `subscription_change`

```elixir
@type subscription_change() ::
  :driver_initialized | :driver_changed | :viewer_attached
```

Role outcome produced by installing a new subscriber.

# `subscription_preparation`

```elixir
@type subscription_preparation() :: {source :: t(), pid(), role()}
```

A validated subscription awaiting monitor creation.

# `t`

```elixir
@type t() :: %MingaAgent.Session.SubscriberLifecycle{
  attachments: %{required(pid()) =&gt; MingaAgent.Session.SubscriberAttachment.t()},
  reclaim_timer: reference() | nil
}
```

Subscriber lifecycle state.

# `claim_driver`

```elixir
@spec claim_driver(t(), pid()) ::
  {:changed, t()} | :unchanged | {:error, :driver_taken | :not_subscribed}
```

Claims a vacant driver role for an attached subscriber.

# `complete_reclaim_schedule`

```elixir
@spec complete_reclaim_schedule(t(), reclaim_preparation(), reference()) ::
  {:ok, t()}
  | {:error,
     :stale_preparation | :timer_already_installed | :subscriber_attached}
```

Installs an OTP reclaim timer when the validated source is still current.

# `complete_subscribe`

```elixir
@spec complete_subscribe(t(), subscription_preparation(), reference()) ::
  {:ok, t(), subscription_change(), timer_to_cancel :: reference() | nil}
  | {:error, :stale_preparation | :already_attached | :driver_taken}
```

Installs a subscription monitor when the validated source is still current.

# `default_role`

```elixir
@spec default_role(t()) :: role()
```

Returns the default role for a new subscriber.

# `detach`

```elixir
@spec detach(t(), pid()) ::
  {:removed, t(), MingaAgent.Session.SubscriberAttachment.t()} | :unchanged
```

Detaches a subscriber and returns its monitor identity for cleanup.

# `driver`

```elixir
@spec driver(t()) :: pid() | nil
```

Returns the active driver derived from canonical attachment roles.

# `driver?`

```elixir
@spec driver?(t(), pid()) :: boolean()
```

Returns true when the PID owns the driver role.

# `new`

```elixir
@spec new() :: t()
```

Builds an empty detached lifecycle.

# `prepare_subscribe`

```elixir
@spec prepare_subscribe(t(), pid(), term()) ::
  {:monitor, subscription_preparation()}
  | {:already_attached, t()}
  | {:error, :invalid_role}
```

Validates a subscription before Session creates its process monitor.

# `reclaim_timeout`

```elixir
@spec reclaim_timeout(t(), reference(), boolean()) ::
  {:reclaim, t()} | {:keep, t()} | {:error, :stale_timer}
```

Accepts only the installed timer identity and rechecks reclaim eligibility.

# `reclaim_timer`

```elixir
@spec reclaim_timer(t()) :: reference() | nil
```

Returns the installed reclaim timer reference.

# `reconcile_reclaim`

```elixir
@spec reconcile_reclaim(t(), boolean(), boolean()) ::
  {:start_timer, reclaim_preparation()}
  | {:cancel_timer, reference(), t()}
  | :unchanged
```

Reconciles the installed reclaim timer with attachment and turn eligibility.

# `role`

```elixir
@spec role(t(), pid()) :: role() | nil
```

Returns the role for an attached subscriber.

# `stop`

```elixir
@spec stop(t()) ::
  {t(), timer_ref :: reference() | nil, monitor_refs :: [reference()]}
```

Clears lifecycle ownership and returns timer then monitor cleanup identities.

# `subscriber_down`

```elixir
@spec subscriber_down(t(), pid(), reference()) ::
  {:removed, t(), MingaAgent.Session.SubscriberAttachment.t()}
  | {:error, :stale_monitor}
```

Consumes a subscriber DOWN only when both PID and monitor identity match.

# `subscribers`

```elixir
@spec subscribers(t()) :: [pid()]
```

Returns all attached subscriber PIDs.

---

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