"""``Clock`` Protocol — replay/live-agnostic monotonic + wall-clock time. Frozen at AZ-398 v1.0.0 per the replay contract: ``_docs/02_document/contracts/replay/replay_protocol.md``. The Protocol is Layer 1 cross-cutting per ``module-layout.md`` — every component that previously called :func:`time.monotonic_ns`, :func:`time.time_ns`, or :func:`time.sleep` MUST consume an injected :class:`Clock` instead (Invariant 2). The strategy is selected exactly once at composition time (Invariant — Single Clock per process): - **Live / research / operator** binaries inject :class:`WallClock`. - **Replay** binary injects :class:`TlogDerivedClock` (ASAP) or :class:`WallClock` (REALTIME pace). Mode-specific behaviour lives in the strategy; consumers see only the ``Clock`` interface (R-DEMO-4 mitigation). """ from __future__ import annotations from typing import Protocol, runtime_checkable @runtime_checkable class Clock(Protocol): """Monotonic + wall-clock + sleep-until abstraction (AZ-398 v1.0.0). All three methods are non-blocking except :meth:`sleep_until_ns`, which honours the configured replay pace: - ``WallClock.sleep_until_ns(t)`` blocks until ``time.monotonic_ns()`` catches up to ``t`` (live + REALTIME replay). - ``TlogDerivedClock.sleep_until_ns(t)`` is a no-op (ASAP replay). Strategies MUST guarantee :meth:`monotonic_ns` is non-decreasing across calls within the same process (Invariant 3 spirit). """ def monotonic_ns(self) -> int: """Return the strategy's monotonic time in nanoseconds. For :class:`WallClock` this delegates to :func:`time.monotonic_ns`. For :class:`TlogDerivedClock` this returns the most recently advanced tlog timestamp (advance-on- call semantics — see AC-6). """ ... def time_ns(self) -> int: """Return the strategy's UTC wall-clock time in nanoseconds. Used for log timestamps that need calendar alignment (FDR records, STATUSTEXT). For :class:`WallClock` this is :func:`time.time_ns`; for :class:`TlogDerivedClock` this is the tlog message's wall-clock timestamp (the ``time_unix_usec`` / ``time_boot_ms`` field, normalised to ns). """ ... def sleep_until_ns(self, target_ns: int) -> None: """Block until :meth:`monotonic_ns` would return ``target_ns``. Honours ``pace=REALTIME`` by sleeping the wall-clock delta; honours ``pace=ASAP`` by no-op'ing. ``target_ns`` already in the past is a no-op (no exception, no negative sleep). The Protocol does not prescribe spurious-wakeup behaviour; strategies SHOULD use :func:`time.sleep` (which retries internally on POSIX). """ ...