# `Raxol.AgentClientProtocol.Session.Supervisor`
[🔗](https://github.com/DROOdotFOO/raxol-agent/blob/raxol_agent_client_protocol-v0.1.0/packages/raxol_agent_client_protocol/lib/raxol/agent_client_protocol/session/supervisor.ex#L1)

The per-connection `DynamicSupervisor` for `Raxol.AgentClientProtocol.Session`
processes.

This is child #2 of the `ConnectionSupervisor` subtree
(`:one_for_all, auto_shutdown: :any_significant`); its siblings are the
per-connection `Task.Supervisor` (#1) and the `Connection` (#3, significant).
It is `restart: :permanent`, and its children — Sessions — are **`:temporary`**:

  * A session crash is NOT auto-restarted. A blank respawned session would lie to
    the client (accept prompts against state the client believes exists). Instead
    the crash surfaces as an error on the in-flight request (adopter-death →
    `-32603` at the Connection) and the client re-establishes via
    `session/load` — journal replay is the ONLY resurrection path.
  * Restart intensity is therefore irrelevant (temporary children never count),
    so a hot-crashing handler can't take the subtree down with it.

Cleanup is entirely monitor-based: a dead Session's `SessionRegistry` key is
pruned by the Registry's own owner-monitor — there is **no hand-rolled sessions
map** anywhere. The Session registers itself via its `:via` name inside
`Session.start_link/1`, so `start_session/2` need only pass the child spec; the
register-before-respond ordering is a `start_link`/`init` property, not
something this module arranges.

## Package `Registry` and `Task.Supervisor` wiring

The `SessionRegistry` is a single package-level, name-registered `Registry` with
`:unique` keys `{conn_pid, session_id}`. Registry names must be atoms, so
a per-connection registry would need dynamic atoms — the atom-DoS this package
bans; hence ONE shared Registry, keyed by the (stable-for-the-subtree-lifetime)
Connection pid. It is normally started by the library `Application`; the helpers
here (`registry_child_spec/0`, `child_specs/1`) exist so a host tree — or a test
— can assemble the pieces without reaching across module boundaries.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `child_specs`

```elixir
@spec child_specs(
  task_sup_name: term(),
  session_sup_name: term(),
  max_inbound_tasks: pos_integer(),
  max_sessions: pos_integer()
) :: [Supervisor.child_spec()]
```

The two per-connection infrastructure children that must start BEFORE the
Connection: the `Task.Supervisor` and this
`Session.Supervisor`. A host `ConnectionSupervisor` prepends these to its child
list (Connection last, `significant: true`); the returned `:id`s let the
Connection resolve the sibling pids in `handle_continue/2`.

# `registry_child_spec`

```elixir
@spec registry_child_spec() :: Supervisor.child_spec()
```

Child spec for the package-level `SessionRegistry` (unique keys). A host tree's
`Application` starts exactly this once; the Connection reads the same Registry
for `session/cancel` direct dispatch, so the key shape is contract.

# `start_link`

```elixir
@spec start_link(keyword()) :: Supervisor.on_start()
```

Start the per-connection Session `DynamicSupervisor`. Pass `:name` to register
it (a host tree keys one per connection; tests may start it anonymously);
`:max_sessions` caps concurrent live Sessions (default 1000).

# `start_session`

```elixir
@spec start_session(Supervisor.supervisor(), keyword()) ::
  DynamicSupervisor.on_start_child()
```

Start a Session under this supervisor as a `:temporary` child. The Session
registers itself on `{conn, session_id}` via its `:via` name, so a duplicate live
id returns `{:error, {:already_started, pid}}` — the caller (the `session/new` /
`session/load` handler) maps that to invalid-params.

`session_opts` is the `Session.start_link/1` keyword list (`:session_id`,
`:conn`, `:mode_state`, `:task_sup`, `:turn_runner`, `:config`, `:conn_mod`).

---

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