Summary And Gameplay Impact
Add a clean-room campaign calendar display layer over canonical Campaign.day and existing time-of-day state. The goal is to make Atlas/Table/session surfaces feel like a living campaign world with year/month/day, weekday, season, moon/phase, and festival/event labels without changing the engine's time authority.
Current ClawDnD State
ClawDnD already tracks elapsed campaign day and uses time/world ticks for travel, rests, strategic clocks, and consequences. Closed issue #75 handled typed clocks and downtime projects. What is missing is a player-facing calendar projection that turns integer day state into readable in-world dates for the OpenWorlds app.
External Source Value And Recommendation
Use fantasycalendar/Fantasy-Calendar as reference-only. Its useful concepts are calendar specs, months, weekdays, leap rules, moon cycles, eras, and display projection. Direct adoption is not appropriate: it is a full Laravel/PHP app/runtime and the cloned repo did not expose a simple standalone license file in the places inspected.
Architecture Plan
- Keep
Campaign.day as canonical elapsed-day truth.
- Keep existing
time_of_day, travel, rest, and strategic clock mechanics as gameplay truth.
- Add optional
CalendarSpec and pure CalendarDate projection helpers.
- Store setting-specific calendar specs in content/world metadata where appropriate.
- Strategic clocks and downtime projects continue to store integer day cursors.
- Viewer/OpenWorlds renders labels only and never writes time directly.
- Degrade to
Day N when no calendar spec is configured.
Files And Modules Likely Touched
servers/engine/models.py
servers/engine/content.py
servers/engine/travel.py
servers/engine/worldsim.py
servers/engine/server.py
viewer/server.py
viewer/openworlds/screen-map.jsx
viewer/openworlds/screen-table.jsx
viewer/tests/test_atlas_surface.py
viewer/tests/test_session_surface.py
State-Authority Boundary
Calendar helpers are read-only projections. They must not mutate Campaign.day, time_of_day, clocks, rests, travel state, downtime projects, or consequences.
Acceptance Criteria
- Old snapshots load unchanged.
- Calendar labels are deterministic across month/year boundaries.
- Missing calendar spec falls back to existing day labels.
- Moon/season/phase fields are deterministic from epoch day when configured.
advance_time, travel_to, long_rest, downtime, and world_tick do not double-tick clocks because of calendar display.
- Atlas/Table/session read models expose calendar labels without introducing write paths.
- No Fantasy Calendar runtime/code is imported.
Focused Validation Commands
uv run --directory servers/engine --group dev pytest -q tests/test_travel.py tests/test_worldsim.py tests/test_content.py
python3 -m unittest viewer.tests.test_atlas_surface -q
python3 -m unittest viewer.tests.test_session_surface -q
git diff --check
Adversarial Risks
- Creating a second time authority beside
Campaign.day.
- Calendar helpers accidentally advancing state.
- Setting-specific lore leaking into engine core instead of content metadata.
- Making strategy clocks depend on display labels rather than integer day cursors.
Original-Agent Handoff
Start with:
servers/engine/models.py
servers/engine/worldsim.py
servers/engine/travel.py
viewer/server.py
viewer/openworlds/screen-map.jsx
Refs #60. Related to #75 and #117.
Summary And Gameplay Impact
Add a clean-room campaign calendar display layer over canonical
Campaign.dayand existing time-of-day state. The goal is to make Atlas/Table/session surfaces feel like a living campaign world with year/month/day, weekday, season, moon/phase, and festival/event labels without changing the engine's time authority.Current ClawDnD State
ClawDnD already tracks elapsed campaign day and uses time/world ticks for travel, rests, strategic clocks, and consequences. Closed issue #75 handled typed clocks and downtime projects. What is missing is a player-facing calendar projection that turns integer day state into readable in-world dates for the OpenWorlds app.
External Source Value And Recommendation
Use
fantasycalendar/Fantasy-Calendaras reference-only. Its useful concepts are calendar specs, months, weekdays, leap rules, moon cycles, eras, and display projection. Direct adoption is not appropriate: it is a full Laravel/PHP app/runtime and the cloned repo did not expose a simple standalone license file in the places inspected.Architecture Plan
Campaign.dayas canonical elapsed-day truth.time_of_day, travel, rest, and strategic clock mechanics as gameplay truth.CalendarSpecand pureCalendarDateprojection helpers.Day Nwhen no calendar spec is configured.Files And Modules Likely Touched
servers/engine/models.pyservers/engine/content.pyservers/engine/travel.pyservers/engine/worldsim.pyservers/engine/server.pyviewer/server.pyviewer/openworlds/screen-map.jsxviewer/openworlds/screen-table.jsxviewer/tests/test_atlas_surface.pyviewer/tests/test_session_surface.pyState-Authority Boundary
Calendar helpers are read-only projections. They must not mutate
Campaign.day,time_of_day, clocks, rests, travel state, downtime projects, or consequences.Acceptance Criteria
advance_time,travel_to,long_rest, downtime, andworld_tickdo not double-tick clocks because of calendar display.Focused Validation Commands
Adversarial Risks
Campaign.day.Original-Agent Handoff
Start with:
servers/engine/models.pyservers/engine/worldsim.pyservers/engine/travel.pyviewer/server.pyviewer/openworlds/screen-map.jsxRefs #60. Related to #75 and #117.