At a table, a clock is a shared agreement. Losing a turn during a refresh, counting time behind a settings panel, or moving the pause button under someone’s finger breaks that agreement.
Model the turn before drawing the timer
Henge’s TypeScript domain layer handles events such as starting, pausing, finishing and handing off a turn. The previous state and a timestamped event produce the next state. Rendering, storage and native lifecycle adapters sit around that rule layer.
This makes timing behavior testable without waiting for a real five-minute turn. Tests construct a known state, advance the timestamp, apply an event, and inspect the result. The same rules serve the browser app and the Capacitor mobile shells.
Elapsed time and animation have different jobs
A delayed browser callback must not silently make a player’s turn longer. Henge calculates elapsed time from clock timestamps, with an explicit preference for counting while hidden. On mobile, a shared visibility adapter also listens to native application events: an Android activity can stop without its WebView reporting the same visibility state.
The display has a separate cadence. The launch-polish work preserves board and timer nodes through ticks and handoffs, updates progress through transform layers, and updates subsecond digits at up to 30Hz. That separation lets the screen look fluid without making animation callbacks the authority on elapsed time.
Frame callbacks measured in a browser are useful performance evidence. They do not establish presented frame rate on an iPhone, or long-session battery behavior.
Make interruptions explicit
Opening settings holds the clocks. Closing an unchanged form preserves progress. Changing a theme or a player profile can retain the current session; changing the game’s timing rules is a different operation and must make its reset consequences clear.
Recovery stores one interrupted game locally. It is deliberately separate from a library of named saves or cloud synchronization. The rebrand from Fairness Clock also migrates existing browser keys so a new name does not discard the user’s settings.
Later persistence work stopped idle writes and checkpointed active games once per second, alongside immediate user and lifecycle saves. Recovery tests cover administrative holds as well as the active player and timer progress.
A concrete rule becomes a concrete test
Flat Bonus rewards an early finish with a configured bonus on that player’s next turn. Overtime reduces the next allowance. It is not a growing time bank.
EXAMPLE / NEXT-TURN ALLOWANCE
5:00 base + 0:30 early-finish bonus = 5:30 next turn
The product copy, domain tests and store demo all need to describe the same rule.
The test suite also exercises the basic lifecycle across two through eight players, hidden-time policy, refresh recovery, and unchanged settings. These checks establish specific behavior; physical-device interruption and longer tabletop sessions still need their own evidence.