arcature 2026.2.1

Arcature application framework: a high-level Application facade over the certified Arcature subsystems, with the low-level Axum/Tower escape hatch preserved.
Documentation
//! The self-contained realtime drain seam the application calls from its
//! own shutdown hook (PROGRAM.md §AP2.1-8).
//!
//! This is a thin, state-free orchestration over two app-owned values —
//! [`ShutdownConfig`] (flip the drain signal so in-flight WS/SSE
//! connections stop forwarding and close) and [`Registry::drain`] (await
//! the live connection count reaching zero or the bound). It is a free
//! function, not a method on `Application`, because the realtime registry
//! is app-owned, not engine-owned, this wave. It owns no state of its own
//! and adds no protocol; it coordinates the two values the application
//! already shares between its endpoints and its shutdown hook (AGENTS.md
//! §20 — no hidden global). The master wires this seam into the global
//! drain after the Production lane lands; the realtime module needs no
//! edit for that.
//!
//! See [`drain`] for the integration-seam note.

use crate::realtime::error::RealtimeError;
use crate::realtime::registry::Registry;
use crate::realtime::shutdown::ShutdownConfig;

/// The self-contained realtime drain seam the application calls from its
/// own shutdown hook (PROGRAM.md §AP2.1-8).
///
/// Flips `shutdown` to draining (so in-flight WS/SSE connections stop
/// forwarding new frames and close gracefully) and waits for the
/// `registry`'s live connection count to reach zero or `bound` to elapse.
/// Returns `Ok(())` if every connection drained, or
/// [`RealtimeError::Shutdown`] with the remaining count if the bound
/// elapsed (honest about what did not drain — AGENTS.md §9).
///
/// # Integration seam (for the master / Production lane)
///
/// This module does not edit `crates/arcature/src/application/shutdown.rs`
/// this wave. The application calls `drain(...)` from its shutdown hook
/// (e.g. inside the `serve_with_lifecycle` shutdown sequence). After the
/// Production lane lands the global lifecycle state machine, the master
/// wires this seam in: the global drain sets `ShutdownConfig` to draining
/// and awaits `Registry::drain`. The realtime module needs no edit for
/// that — the seam is this function plus the `Registry` and
/// `ShutdownConfig` the app shares between its endpoints and its hook.
///
/// # Errors
///
/// See [`Registry::drain`].
pub async fn drain(
    registry: &Registry,
    shutdown: &ShutdownConfig,
    bound: std::time::Duration,
) -> Result<(), RealtimeError> {
    shutdown.begin_drain();
    registry.drain(bound).await
}