# Simulation Clock ## Integer Time Architecture for OTIVM and the Simulator ### TheRON — OTIVM / CIVICVS / TESSERA Stack ### Status: Canonical. Do not alter without project owner instruction. ### Date: 2026-05-02 --- ## 0. Purpose This document defines the governing time model for OTIVM and the Simulator. It is a hard architectural constraint, not a preference. Every timed element in the system — routes, scenarios, otium intervals, epoch calculations — must conform to the integer clock rule defined here. A future assistant or developer who bypasses this constraint will corrupt the behavioral record. Read this document before touching any value that involves time, duration, or simulated date. --- ## 1. The Governing Constant ``` MS_PER_SIM_DAY = 3,000 ``` One simulated day equals 3,000 real milliseconds — 3 real seconds. This is a game compression constant. It has no historical basis and is not intended to represent any real ratio of Roman time. Its purpose is to make the game playable without making the simulation clock meaningless. It is defined once, in `src/constants.js`, and read from there everywhere it is used. It is never hardcoded in any other file. --- ## 2. The Integer Constraint **All `duration_ms` values must be exact integer multiples of `MS_PER_SIM_DAY`.** This is not a preference. It is a hard requirement. A `duration_ms` that is not a multiple of `MS_PER_SIM_DAY` produces a fractional `duration_days` value in `venture_legs`. Fractional days corrupt the behavioral record. The Simulator reads `duration_days` as an integer count. The epoch arithmetic depends on integer day counts. A single fractional value propagates error through every downstream calculation. ### The enforcement rule When adding a route, scenario, or any timed game element: 1. Decide the duration in **simulated days** first. This is the primary value. 2. Derive `duration_ms` by multiplication: `duration_ms = duration_days × MS_PER_SIM_DAY` 3. Never set `duration_ms` independently and then divide to get `duration_days`. The days are primary. The milliseconds are derived. Always in that order. ### The adjustment record When `MS_PER_SIM_DAY` was set to 3,000, the Grain route (Brundisium → Carthago) had a `duration_ms` of 13,000, which is not divisible by 3,000. It was adjusted to 12,000ms (4 simulated days). The difference is 1 real second — imperceptible in gameplay. This adjustment was made deliberately and is recorded here so it is not treated as an error in future review. --- ## 3. Current Route Durations | Route | From | To | mode | duration_ms | duration_days | |---|---|---|---|---|---| | olive | Ostia | Capua | road | 6,000 | 2 | | wine | Capua | Brundisium | road | 9,000 | 3 | | grain | Brundisium | Carthago | sea | 12,000 | 4 | | linen | Carthago | Alexandria | sea | 18,000 | 6 | All values are exact integer multiples of `MS_PER_SIM_DAY = 3,000`. --- ## 4. Otium Duration ``` OTIUM_DURATION_MS = 9,000 (3 simulated days) ``` Otium takes 3 simulated days. This value was chosen because: - Otium is purposeful social activity, not passive rest. It requires time proportional to its function. - 3 days gives otium weight relative to the shortest route (2 days). A merchant who completes the olive run and then takes otium has a 5-day cycle — a working week with meaning. - The otium duration is a social minimum, not a function of route distance. A merchant returning from Alexandria takes the same minimum otium as one returning from Capua. The social obligations otium represents do not scale with distance travelled. This produces the following complete cycles: | Route | route days | otium days | cycle total | |---|---|---|---| | olive | 2 | 3 | 5 | | wine | 3 | 3 | 6 | | grain | 4 | 3 | 7 | | linen | 6 | 3 | 9 | All integers. Pairs of linen runs with one otium: 6 + 6 + 3 = 15 days. Integer throughout. --- ## 5. The Simulation Epoch Window The simulation runs from **1 January 100 BCE to 31 December 100 CE**. | Boundary | Calendar date | Julian Day Number | |---|---|---| | Start | 1 January 100 BCE | 1684595 | | End | 31 December 100 CE | 1757641 | | Span | — | 73,047 simulated days | The epoch anchor is `SIM_EPOCH_JDN_START = 1684595`. All simulated dates are integer offsets from this anchor. The window closes when the simulated JDN exceeds `SIM_EPOCH_JDN_END = 1757641`. 73,047 is well within the safe range of JavaScript integer arithmetic. No floating point risk. No BigInt required. --- ## 6. What Is Never Stored Simulated dates are **never stored in the database**. `recorded_at` is always real wall-clock UTC in ISO 8601 format. This is the only timestamp column in the schema. Simulated dates are always derived at computation time from `recorded_at` and the session anchor. The database stores facts. Derivations are computation. This separation must never be violated. --- ## 7. Session Time Arithmetic Each player session has a `started_at` timestamp recorded in `actor_profile`. Simulated days elapsed in a session: ``` elapsed_sim_days = floor((now_ms - started_at_ms) / MS_PER_SIM_DAY) ``` This is integer arithmetic throughout. `floor()` ensures no fractional days enter the calculation. Current simulated JDN for a session: ``` current_jdn = SIM_EPOCH_JDN_START + elapsed_sim_days ``` Integer addition. No floating point at any step. --- ## 8. Future Route and Scenario Additions Before adding any route or timed scenario element, the author must: 1. Verify that `duration_days` is a positive integer. 2. Compute `duration_ms = duration_days × MS_PER_SIM_DAY`. 3. Record both values. Never record only one. 4. If `MS_PER_SIM_DAY` has changed since the last route was added, recompute all existing `duration_ms` values. The constant is the source of truth. Changing `MS_PER_SIM_DAY` is a breaking change. It requires updating every `duration_ms` in `constants.js` and every `duration_days` value in existing player databases. It must not be changed without project owner instruction and a documented migration plan. --- ## 9. Confidence and Maturity The compression ratio (`MS_PER_SIM_DAY = 3,000`) is a design decision, not a historical fact. It carries no `confidence_tag` because it is not a parameter derived from the Roman world. It is infrastructure. The epoch window (100 BCE to 100 CE) is historically grounded in the project's design intent — the period when Rome is most fully itself, as documented in `docs/DESCENSUS-genesis.md`. The JDN values are computed from the proleptic Julian calendar and are verifiable. Route durations in simulated days (`duration_days`) are currently tagged `confidence_tag: 'estimated'` in the behavioral record. They reflect compressed game time, not historical journey durations. When the corpus develops grounded data on route travel times, new `duration_days` values will enter the database as updated records. The compression ratio will adjust `duration_ms` accordingly, following the enforcement rule in Section 2. --- *Simulation Clock — 2026-05-02* *TheRON — single contributor. AI assistants implement, document, flag — do not direct.* *A constraint recorded here is a constraint enforced everywhere. No exceptions.*