= Target architecture: boundaries and models
Part of xref:index.adoc[the target architecture]. The capability and effect
boundaries (including the bounded-resources faces and the resilience pattern),
the concurrency model, and the error model.
== Capability and effect boundaries
Determinism by construction:: The ordering core is a pure function of
`(current state, physical reading, optional remote stamp, kairotic)`. The only
effect is reading time, isolated behind the `TimeSource` trait. Backoff (the
`Backoff` trait) spins a bounded count and reads no clock, so it adds no
nondeterminism (S12); contention spacing stays off the time seam.
`no_std` first:: The crate is `#![no_std]` with `alloc`. The default `rand`
feature pulls in `std` for jitter entropy; the `no_std` path substitutes a
small in-crate LCG. An opt-in `std` feature enables the `chronos` OS-backed
sources; it is never in `default`, so the pure `no_std` build is unaffected and
remains a supported, verified target (`wasm32-unknown-unknown
--no-default-features`).
Least privilege:: A `Clock` holds no ambient authority. It cannot read the OS
clock, allocate after construction, or spawn work; it only advances an atomic
word and consults the seams it was given.
Bounded resources:: Stamp generation does no allocation. State is a single
128-bit word carrying an explicit init tag plus the last `(physical, logical)`.
Backoff never aborts; it only spaces out retries with a bounded spin count (not a
measured delay), since generation is infallible and lock-free (see the error model).
Above the stamp, the boundary has three faces, one per axis a long-running deployment
can exhaust, each shipped in the same shape (the resilience pattern, next entry):
the *backlog* bound (`Ideal::try_insert(event, capacity)` / `AtCapacity`, PRD 0005 R8,
S30, beside the unbounded `insert` and its `pending_len()` monitor), the *skew* bound
(`Clock::try_observe(remote, max_forward_skew)` / `SkewExceeded`, S41, lifted over the
whole producer receipt in S73), and the *retention* bound's enabling mechanism
(`Stability::watermark()`, the safe-to-forget cut, with `report` refusing non-roster
reporters through `UnknownStation`, PRD 0011, S78; completed by the exact have-set,
`DotSet::floor()`, PRD 0012, S83, which makes the gap-free report R8 demands
constructible under disordered receipt).
Mechanism before policy (the resilience pattern):: Each face of the resource boundary
ships as the same four-part material, and a future face must too: an opt-in
*mechanism* beside the untouched infallible default (`insert` / `try_insert`,
`observe` / `try_observe`); a *typed refusal* carrying everything the caller needs to
act (`AtCapacity` hands back the event, `SkewExceeded` the measured skew,
`UnknownStation` the reporter); the *policy* left with the caller (give-up, trust,
reclamation and membership), because every such policy trades liveness against safety
in application terms the crate cannot see; and an *observability read* beside the
bound (`pending_len`, `ClockStats`, `Stability::reports`), so the caller's policy has
something to look at. Building a mechanism *ahead of a caller* is justified only
where a crate-wide boundary already owns the concern *and* the hand-rolled
alternative fails unrecoverably rather than merely redundantly (owner ruling R-6
records the test; S30, S41, and S78 are the three instances, and S83's exact
have-set, ruled in R-7, is its fourth exercise, completing the retention face's
input contract). Everything that encodes
application semantics still waits for a caller.
== Concurrency model
`Clock` is lock-free. State lives in one `AtomicU128` (through `portable-atomic`, so
the primitive works where the platform lacks native 128-bit CAS and under
`no_std`). Generation reads the word, computes the next stamp purely, and commits
with `compare_exchange`; on contention it consults the `Backoff` strategy and
retries until it commits. `Clock<TS>` is `Send + Sync` and is shared as
`Arc<Clock<TS>>`; `DynClock` is the explicit erased-source form.
Since S86 the claims split by assurance rung (ruling R-9): the pure fold and the
packed representations are machine-checked by Kani harnesses (the core-invariant
and wire-order laws totally, the fold's monotonicity and domination laws over
fully symbolic readings and stamps below the physical ceiling), while the CAS
loop under real interleavings is exercised by the threaded tests and Miri's
weak-memory interpreter. The first proof pass found and fixed a fold bug the
sampled suites had missed (PRD 0001's resolved-gaps record).
== Error model
Layered and typed, with fallibility reserved for conditions a caller can act on:
* *Construction* returns `Result<_, InvalidStationId>`. A zero `station_id` is
rejected at the boundary. The caller must fix the input.
* *Generation is infallible* (`now`/`after` return `Kairos`). Contention is not a
failure a caller can act on: a stamp is a precondition for the work, so every
caller would simply retry. The lock-free loop already retries in place, so a
`Result` would only relocate that spin into the caller. Generation therefore
never returns an error and never panics. The pure HLC receive rule `observe`
(S20) shares the same infallible fold: it advances the clock past a remote stamp
without minting one, so every later stamp exceeds it. The bounded receive
`try_observe(remote, max_forward_skew)` (S41) is the fallible trust-boundary sibling:
it rejects a too-far-future remote with `SkewExceeded` and leaves the clock unchanged.
* *Observability* is carried in `ClockStats`: skew counters and `peak_attempts`,
the high-water mark of CAS attempts, redacted and bounded, never logged from
inside the `no_std` core.
Generation is *lock-free, not wait-free*. Lock-freedom guarantees system progress
(some thread commits each round); it does not bound an individual `now()` call,
which is wait-free territory. Per-call latency is bounded in practice by
randomized backoff and a finite contender count, not in theory. This is
sufficient for footprint-grade `no_std` use; it would not satisfy a wait-free or
certification (DO-178C / ISO 26262) requirement. A future hard-real-time,
degrade-on-miss caller would get an additive `try_now` / `try_after -> Result`
alongside the infallible methods (the `lock` / `try_lock` precedent), so `now`
never changes signature.
NOTE: Implemented as described. `now` and `after` are infallible. The four
clock constructors return `InvalidStationId` for station zero.
`try_observe` is the fallible state transition.