# `Raxol.Gateway.Handler.Lifecycle`
[🔗](https://github.com/DROOdotFOO/raxol/blob/raxol_gateway-v0.2.0/lib/raxol/gateway/handler/lifecycle.ex#L1)

A `Raxol.Gateway.Handler` that runs a full TEA app per chat under
`environment: :gateway`.

`init/2` starts a `Raxol.Core.Runtime.Lifecycle` for the configured
`:app_module`. The `:gateway` environment starts no terminal driver and no
plugin manager, and registers no process names, so any number of chats can
run the same app module concurrently. Each inbound gateway event is
translated to a Raxol event, dispatched into the app's update cycle, and
the reply is the app's next rendered frame as plain text (the engine
delivers frames through an `io_writer` closure pointing at the session
process).

Requires the optional `:raxol` dependency; `init/2` returns
`{:error, :raxol_not_loaded}` without it.

## Options

  * `:app_module` (required) - the TEA app module
  * `:width` / `:height` - frame dimensions (default 80x24)
  * `:render_timeout_ms` - budget for the post-event fold barrier and the
    synchronous render (default 5000); on timeout the event is absorbed
    with no reply
  * `:event_fn` - `(gateway_event -> raxol_event | nil)`; default maps
    `%{text: t}` to a char key event (single grapheme) or a paste event,
    mirroring the Telegram input adapter. `nil` skips the event.
  * `:format_fn` - `(render_data -> binary | nil)`; default renders the
    delivered buffer to plain text. `nil` (or an empty result) skips the
    reply.
  * `:lifecycle_opts` - extra options appended to
    `Raxol.Core.Runtime.Lifecycle.start_link/2`

## Turn semantics

The handler runs inside the per-chat `Raxol.Gateway.Session` process, so
the render `io_writer` targets that process. A turn is collected
deterministically: stale frames (startup renders, a late frame from a
previous turn) are flushed, the event is cast to the dispatcher, a `:sys`
barrier confirms the model fold, and a synchronous engine render produces
the reply frame (the newest wins if several arrive). Frames the app
renders BETWEEN turns are consumed and discarded by the session's
catch-all `handle_info` -- a chat surface replies to messages;
spontaneous pushes are `Raxol.Gateway.Delivery`'s job.

## Teardown

The Lifecycle GenServer is linked to the session, but a session's idle
timeout is a `:normal` exit, which does not propagate over links. The
handler's `terminate/2` (invoked by the session on clean stops) stops the
Lifecycle explicitly so per-chat apps never outlive their chat.

---

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