Skip to main content

Architecture

clockwright is one engine with two halves that want opposite things, and a documented seam between them. This page is the shape of that arrangement. The detailed internals — the event loop, the phase and state machines, how a document is compiled into flat arrays — are held back until release.

note

This is an overview. The full architectural breakdown will be published when clockwright is released.

Two regimes, and a seam between them​

Domain-shaped code and a fast event loop pull in opposite directions. A model document wants names, maps and validation messages that quote them back to you; an event loop that fires tens of millions of times per simulated year wants none of that — every string lookup and pointer chase on that path is a cache miss multiplied by the length of the run.

clockwright does not compromise between the two. It runs both, and puts the boundary somewhere explicit:

  • The engine core is data-oriented. Flat arrays, plain-old data, no allocation per event, no abstraction that costs a cache miss. This is the code that runs 10⁸ times.
  • The application layer is domain-driven. Experiments, scenarios, runs, KPI contracts, persistence — named things, validated as documents, with messages a human or an agent can act on. This is the code that runs 10⁰–10³ times.
  • The seam between them is deliberate and load-bearing. Domain language where somebody reads it; data-oriented discipline where a cache miss costs a percent of the run.

Nothing in the model document reaches the inner loop as a name. A document is validated as a whole — so a bad cross-reference is reported once, clearly, rather than panicking somewhere deep in a run — and then compiled into index-addressed structures. That compile step is the seam.

⚠️ And if one happens anyway, it says where it was. The moment an event comes off the event list — before the engine has even accepted its timestamp, let alone handed it to the model — the engine records it: its kind, its target, the simulated time and how many events have run. cwright installs a panic hook that prints that record. ⭐ Coming off the list rather than into the model is the load-bearing part: a corrupted event list is refused by the clock check rather than by anything in the model, so a record written any later would name the previous, innocent event. So a corrupted index arrives as "the panic happened inside the engine's run loop: event 1204553, t = 8913220 ticks, kind 3, target 412" rather than as a line number in a loop that ran a hundred million times. The record is kept per thread, so a replication names its own position and never a sibling's, and a panic outside the run loop — in argument parsing, in the loader — gets no note at all, because a note that appears on every panic teaches a reader to skip it.

The nine server states​

A server is never simply "busy". Time on a resource is attributed to one of nine mutually exclusive states, and they sum to the elapsed time by construction, which is what makes a utilisation figure auditable rather than merely plausible: a tool that is idle because it is starved, blocked because the queue downstream is full, down for a breakdown, and stopped for maintenance are four different facts about a fab, and a single "busy percentage" cannot tell you which you are looking at.

That accounting is why the result document reports a breakdown per resource rather than one number, and why cwright ls can show where a run's capacity actually went.

Checkpoint and resume​

A long run survives a power cut. The engine snapshots its own state — the pending event list, the model's state, and metadata identifying exactly what produced it — and a resumed run is proved bit-identical to the run that was never interrupted, rather than merely close to it.

Two properties make that claim checkable rather than hopeful: an event is small, fixed-size Copy data, so a snapshot is close to a raw memory copy rather than a serialisation problem; and the format refuses a checkpoint it cannot vouch for, naming which problem it found. See Checkpointing.

Next​

  • The Model — the vocabulary you write, and what each keyword means.
  • Determinism — what "the same answer" is guaranteed to mean.
  • Performance — measured throughput, and the machine it was measured on.