Skip to main content

ic-jg5o: GH#7: ADR: Temporal Pacing Model

Snapshot: 2026-03-30T08:40:31Z

FieldValue
Statusopen
Assignee(unassigned)
Priority2
Labelsatlas
Created bygithub-bridge
Created2026-03-28T18:50:18Z
Updated2026-03-28T18:50:18Z

Description

GitHub issue: b4arena/spellkave#7 URL: https://github.com/b4arena/spellkave/issues/7

Context

PRD Open Question #1 (raised by @durandom in review):

How does the world handle the speed difference between AI agents (machine time) and human players (session time)? Options include time dilation near human players, temporal buffering (humans interact with a recent-past snapshot), or full acceptance (the world moves on; absence has consequences). This must be resolved before world mechanics are specified.

With the SpacetimeDB architecture (see #3), this becomes more concrete: the world runs as scheduled reducers ticking at defined intervals. AI agents submit actions via reducers (fast path) or via the ThinkRequest/ThinkResult cycle (slow path). Human players submit actions when they're online.

The key tension: AI agents can act every tick (seconds). Humans act every session (hours/days). The world must feel alive for both.

What Atlas Should Deliver

An ADR evaluating and deciding between:

  1. Full acceptance — the world moves on at a fixed rate. Humans who are absent return to a changed world. Absence has consequences. The world never pauses.

    • Pro: simplest to implement, most "persistent"
    • Con: humans may feel overwhelmed, miss key events, lose agency
  2. Temporal buffering — the world runs at full speed, but human actions interact with a recent-past snapshot. When a human acts, the world "catches up" to integrate their decision.

    • Pro: humans feel they have time to decide
    • Con: complex to implement, potential for jarring state jumps
  3. Variable tick rate / time dilation — world ticks slow down in areas where humans are present. AI agents in those areas adapt their pace.

    • Pro: humans feel the world responds to their pace
    • Con: creates "time zones" that are hard to reason about
  4. Hybrid — full acceptance at world scale, but human interaction windows that give players time to act before consequences finalize (e.g., "your tavern is under attack — you have 24 hours to respond before it's destroyed").

    • Pro: respects both timescales
    • Con: needs careful UX design for notification/response windows

Design considerations

  • The PRD's Death Stranding reference: "players never need to be online simultaneously to cooperate"
  • The PRD's consequence model: "actions have durable, irreversible consequences"
  • Phase 0 exit criteria require 72 hours continuous world operation
  • Observer experience must be coherent regardless of pacing model

Reference Material

From the PRD

  • Section 6, Open Question #1 — full statement of the problem
  • Section 5, Key Design Decisions — "Asynchronous social design" following Death Stranding model
  • Section 5, Core Properties — "The world is not paused between sessions"

Architecture context

  • SpacetimeDB scheduled reducers define the tick system — see BitCraft's agents/ directory for how 18 independent tick loops run at different frequencies
  • BitCraft Design Blog #3: Longevity — permanence and pacing in MMO design

Dependencies

  • Depends on #3 (SpacetimeDB as runtime) for architecture context
  • Blocks #5 (World State Data Model) — time model affects how state is stored
  • Blocks #7 (Phase 0 Minimal Module) — tick loop design depends on this

Acceptance Criteria

  • ADR evaluates all four options with pros/cons
  • Decision is justified with reference to PRD goals and success metrics
  • Documents how the chosen model maps to SpacetimeDB scheduled reducers
  • Addresses the human notification/re-entry problem
  • Committed to spellkave repo

Conversation

github-bridgeMar 28, 08:20 PMsystem
[GH @durandom] ## Addendum: Temporal Pacing Insights (from design exploration) ### How BitCraft Handles Time BitCraft's `day_night_agent.rs` shows a concrete pacing implementation: - Day/night cycle runs as a self-rescheduling reducer - Tick frequency is **variable** — the next tick is scheduled for exactly when day→night or night→day transition happens - Duration values (`daytime`, `nighttime`) are loaded from CSV config (`parameters_desc`), not hardcoded ```rust pub fn day_tick(ctx: &ReducerContext) { let time = time_of_day(ctx); let night_fall_time = ((day_duration(ctx) - time) * 1000) as u64; // Schedule next tick at exactly nightfall ctx.db.day_night_loop_timer().try_insert(DayNightLoopTimer { scheduled_at: now_plus_millis(night_fall_time, ctx.timestamp), .. }); } ``` ### BitCraft's 18 Independent Tick Rates Different world aspects tick at different frequencies (all configurable via CSV): | Agent | Frequency | Notes | |---|---|---| | Enemy regen | fast (ms) | HP recovery needs to feel smooth | | Player regen | fast (ms) | Same | | NPC AI | 300s | Merchant NPCs think every 5 minutes | | Resource regen | configurable | Respawn rates per resource type | | Building decay | configurable | Slow degradation | | Day/night | variable | Exactly at transition points | Key insight: **there is no single "world tick rate."** Each concern runs at its own frequency. ### Implications for Spellkave's Pacing Decision The SpacetimeDB scheduled reducer model naturally supports **different time scales for different concerns**: - Combat resolution: sub-second (attack timers with precise delays) - NPC fast-path behavior: 10-60s ticks - LLM think cycle: async, 3-30s whenever the LLM responds - World events (weather, faction tension): minutes to hours - Building/infrastructure decay: hours to days The temporal pacing ADR should consider that the answer might not be a single model but a **per-concern pacing strategy** — which is what BitCraft already does. ### The Natural Answer from the ThinkRequest Pattern The two-phase LLM pattern (ThinkRequest → external LLM → ThinkResult) implicitly resolves the AI-vs-human pacing problem: agents act "when they're ready," just like humans. An agent thinking for 5 seconds is equivalent to a player thinking for 5 seconds. The world doesn't wait for either. This suggests **Option 4 (Hybrid)** may be the natural fit: full acceptance at world scale, but human interaction windows for consequential decisions.
github-bridgeMar 28, 08:30 PMsystem
[GH @durandom] ## Addendum: The Relativistic Time Model ### Core Concept Inspired by Einstein's General Relativity: **human players are massive objects that warp the flow of time around them.** | General Relativity | Spellkave | |---|---| | **Mass** | Human player presence | | **Gravitational time dilation** | Near humans, world time slows → turns, deliberation, tactical combat | | **Flat spacetime** (far from mass) | No human nearby → time flows freely, AI acts at lightspeed | | **Speed of light** | Maximum world tick rate (AI speed) | | **Proper time** | Each location has its own time resolution based on who's present | | **Light cone** | The events an observer can causally see or influence | | **Frame-dragging** | A player "drags" nearby NPCs into their slower timeframe | | **Event horizon** | The boundary where events become "live" vs "historical" for a player | | **Redshift** | Events from the "fast" zone arrive at humans as compressed narrative summaries | ### How It Works **Location time states:** ```rust pub enum TimeFrame { /// Deep space — no human nearby. Maximum speed. /// AI resolves actions instantly. Time is narrative. FlatSpacetime, /// Human entered the area. Time dilates. /// NPCs shift to human-paced interaction. GravitationalWell { player_count: u32 }, /// Multiple humans. Maximum dilation. /// Full D&D turn structure. BlackHole { player_count: u32 }, /// Large-scale event (siege, faction war). /// Action queues instead of turns. HighEnergyEvent { participant_count: u32 }, } ``` **Time dilation follows an inverse-square law** (like gravity): ```rust fn calculate_time_dilation(location, human_locations, world_graph) -> f32 { let mut gravity = 0.0; for (human_loc, count) in human_locations { let distance = world_graph.distance(location, human_loc); if distance == 0 { return 1.0; } // human is HERE gravity += count as f32 / (distance * distance) as f32; } gravity.min(1.0).max(0.001) // 0.001 = near-instant, 1.0 = human speed } ``` **Frame-dragging zones:** - NPCs in the same room as a player: full time dilation (human-speed reactions) - NPCs in adjacent rooms: partial dilation (slower than AI-speed, faster than human) - NPCs 3+ rooms away: flat spacetime (instant resolution, narrative mode) ### This Resolves Three PRD Open Questions **1. Temporal Pacing (OQ#1):** No global decision needed. Time resolution is local and relative — determined by the "mass" (human presence) nearby. The world never pauses globally; it dilates locally. **2. Session Re-Entry (OQ#4):** When a player returns after absence, they experience "redshift" — a compressed summary of all events that happened at lightspeed (AI-speed) while they were away. The transition from FlatSpacetime → GravitationalWell triggers a catch-up narrative. **3. AI-Human Coexistence:** AI agents experience the world like photons — at lightspeed, without subjective time. Humans experience it like massive particles — slower, but with the ability to bend spacetime around them. Both exist in the same universe, in different time frames. ### Adaptive Combat Modes (tied to TimeFrame) The combat mode automatically follows from the time frame: | TimeFrame | Combat Mode | D&D Feel | Performance | |---|---|---|---| | `FlatSpacetime` (0 humans) | Instant Resolution | N/A (narrative only) | Microseconds | | `GravitationalWell` (1 human) | Relaxed Turns (60s/turn) | High — solo D&D encounter | ~minutes per combat | | `BlackHole` (2-3 humans) | Classic D&D Turns (60s timeout) | Maximum — full party combat | ~minutes per combat | | `HighEnergyEvent` (5+ combatants) | Action Queues / Cooldowns | Lower — spectacle over tactics | Real-time | **Phase 0:** Implement only `FlatSpacetime` + `GravitationalWell` (AI-vs-AI instant + single-human relaxed turns). **Phase 2-3:** Add `BlackHole` and `HighEnergyEvent` when multi-human and large-scale events arrive. ### Observer Experience per TimeFrame - **FlatSpacetime events:** Observer sees a stream of resolved events — "Goblin raiders attacked the eastern tunnel (repelled by militia)." Fast, narrative, like reading a news ticker. - **GravitationalWell/BlackHole events:** Observer watches live — "Greymantle is considering their next move..." Real-time drama. - **HighEnergyEvent:** Observer watches a spectacle — simultaneous actions, faction-scale conflict, like watching a battle from a hilltop.