/**
 * WS4.2 (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.2, F7) — Idle vs broken machine
 * empty-state for the pooled sessions view.
 *
 * The blind spot this closes (2026-06-12 live incident): a machine with zero
 * running sessions rendered as NOTHING on the dashboard — indistinguishable
 * from a machine that is broken or unreachable. The operator could not tell
 * "idle but healthy" from "offline" from "was online, now not answering".
 *
 * This module is the HONEST classifier: it derives the explicit per-machine
 * state from REAL inputs only — the registry's `online` flag + last-seen
 * timestamp + whether the pool fan-out's actual HTTP fetch failed (and why).
 * It never fabricates a state and it never invents a "looks fine" default; a
 * machine that genuinely can't be classified surfaces as `unreachable`, the
 * conservative direction (broken, not silently-fine).
 *
 * Three states (spec §WS4.2):
 *   - `online`      → "online — no active sessions" (heartbeat-fresh, just idle)
 *   - `offline`     → "offline since <t>"           (registry knows it is offline)
 *   - `unreachable` → "unreachable (last seen <t>)" (registry thought it was
 *                      online, but the live fetch failed — the pool.failed case)
 *
 * Pure: no I/O, no clock reads beyond the caller-supplied `now`. Both the route
 * (server-side, authoritative) and the dashboard render against its output.
 */
/** The discrete empty-state kind for a machine with zero active sessions. */
export type MachineEmptyStateKind = 'online' | 'offline' | 'unreachable';
/** Per-machine inputs the classifier needs — all from REAL registry/fan-out state. */
export interface MachineStateInput {
    /** Registry liveness: (now − routerReceivedAt) < failoverThreshold. */
    online: boolean;
    /**
     * The fan-out's verdict for this machine, when it was actually queried:
     * `null`     → not in the failed set (fetch succeeded, or never attempted
     *              because the registry already knew it was offline);
     * a reason   → the live HTTP attempt failed with this classified reason
     *              (e.g. 'timeout', 'unreachable', 'error', 'offline',
     *              'no-known-url', 'unauthorized', 'route-missing').
     */
    failedReason: string | null;
    /** Last time the router observed this machine, ISO (router clock). */
    routerReceivedAt?: string;
    /** The machine's self-reported last-heartbeat time, ISO (fallback for display). */
    selfReportedLastSeen?: string;
}
/** Classified per-machine empty-state. */
export interface MachineEmptyState {
    /** Discrete state kind (drives the dashboard styling + copy). */
    kind: MachineEmptyStateKind;
    /** Human-readable phrase, exactly as the spec names it. */
    text: string;
    /** The "last seen" timestamp (ISO) used to render <t>, when known. */
    lastSeen: string | null;
}
/**
 * Format a last-seen ISO timestamp as a relative phrase ("3m ago", "2h ago").
 * Returns 'unknown' when there is no timestamp — never a fabricated time.
 */
export declare function formatLastSeen(lastSeen: string | null, now: number): string;
/**
 * Classify ONE machine's empty-state from its real registry + fan-out inputs.
 *
 * Decision boundaries (each side covered by a unit test):
 *   - online && no fetch failure                → `online`   (idle but healthy)
 *   - registry offline, OR a fail reason that
 *     means known-offline                       → `offline`  (known absence)
 *   - everything else (registry thought online
 *     but the live fetch failed)                → `unreachable` (surprise silence)
 *
 * Note: this is only meaningful for a machine with ZERO active sessions — the
 * caller must not assign an empty-state to a machine that has session tiles
 * (a busy machine is already self-evident from its sessions).
 */
export declare function classifyMachineEmptyState(input: MachineStateInput, now: number): MachineEmptyState;
//# sourceMappingURL=poolEmptyState.d.ts.map