Skip to content

[time] Add clean-room campaign calendar display layer #176

Description

@100yenadmin

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttier-1Tier 1: Claude Code plugin

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions