Files
otivm/docs/architecture/simulation-clock.md
2026-05-02 17:40:15 -04:00

7.5 KiB
Raw Blame History

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.