Skip to content

Runtime contracts

Browser authority

SimulationState is the validated projection consumed by the HUD, living stage, world, audio, conversation context, and persistence layer. The current contract is broader than a dialogue snapshot:

interface SimulationState {
  readonly schemaVersion: 1;
  readonly runId: string;
  readonly worldRevision: number;
  readonly campaignDayCount: 7;
  readonly absoluteMinute: number;
  readonly dayIndex: number;
  readonly minuteOfDay: number;
  readonly timeMode: "paused" | "real-time" | "accelerated";
  readonly timeRate: number;
  readonly playerActorId: "jesse-rourke";
  readonly actors: readonly ActorState[];
  readonly relationships: readonly RelationshipState[];
  readonly tasks: readonly TaskState[];
  readonly commitments: readonly CommitmentState[];
  readonly memories: readonly ActorMemoryState[];
  readonly artifacts: readonly PhysicalArtifactState[];
  readonly inventory: readonly InventoryItemState[];
  readonly roomPurposes: readonly RoomPurposeState[];
  readonly weather: WeatherState;
  readonly exteriorEvents: readonly ExteriorEventState[];
  readonly dnd: DndState;
  readonly dailyMusic: readonly DailyMusicGenome[];
  readonly curator: CuratorState;
  readonly weeklyBuild: WeeklyBuildState;
  readonly donnaStatus: DonnaStatus;
  readonly conversations: readonly ConversationRecord[];
  readonly livingRuntime: LivingRuntimeState;
}

No UI, runtime rule, or provider mutates this object directly. A state change must be represented as a finite event, reduced against the current projection, checked against all invariants, and appended at the expected event head before the new projection is published.

Event authority

interface SimulationEvent {
  readonly schemaVersion: 1;
  readonly runId: string;
  readonly sequence: number;
  readonly eventId: string; // `${runId}:${sequence}`
  readonly atMinuteOfDay: number;
  readonly causationId: string;
  readonly correlationId: string;
  readonly source:
    | "player"
    | "rule"
    | "agent"
    | "curator"
    | "validated-provider";
  readonly payload: SimulationEventPayload;
}

Every payload carries expectedWorldRevision. The reducer accepts an event only when:

  • runId matches the current run;
  • sequence === worldRevision + 1;
  • expectedWorldRevision === worldRevision;
  • atMinuteOfDay matches the authoritative clock;
  • the envelope and exact payload shape pass finite-data validation;
  • source-specific authority and all resulting state invariants hold.

The commit path dry-runs the complete batch through the pure reducer. It then compare-and-appends against the pre-batch head and publishes the already validated projection. A stale head, invalid event, or reducer failure leaves the persisted log and visible state unchanged.

Event families cover:

Domain Event examples
Clock advance, day transition, mode change
People arrival, departure, activity, location, schedule, and needs
Work task creation, assignment, progress, blocking, completion, and build state
Social relationship shifts, commitments, conversations, and memories
Place artifacts, inventory, room purpose, weather, and exterior events
Ritual D&D campaign/session state and daily music genomes
Direction Curator seeds, plans, and plan status
Optional staff Donna transition
Resume state living-runtime checkpoint

validated-provider may record a validated conversation, but it cannot directly advance the clock, move an actor, complete work, change weather, or choose Donna's state. Follow-on task, relationship, commitment, memory, and artifact events are browser-authored rule projections with browser-selected IDs, locations, bounds, and provenance.

Persistence and replay

The current run ID is stored at suite666.simulation-current-run.v1. Its event log uses suite666.simulation-events.v1:<runId> and contains:

interface PersistedSimulationLog {
  readonly version: 1;
  readonly kind: "suite666.simulation-event-log";
  readonly runId: string;
  readonly baseSequence: number;
  readonly snapshot: { sequence: number; state: SimulationState } | null;
  readonly events: readonly SimulationEvent[];
}

On load, the browser validates the exact envelope, snapshot invariants, run and sequence identity, every event, and a full replay of the tail. Sensitive field names such as keys, tokens, secrets, and authorization data are forbidden at every stored depth. Corrupt or unsupported current-run data is discarded and a new run is created; unavailable localStorage produces a visible memory-only run instead of disabling the office.

The store re-reads the raw log immediately before writing to detect a competing store instance. After more than 256 tail events, validated history is compacted into a snapshot and the contiguous new tail is retained. Compaction changes the storage shape, not the authoritative revision or replay result. New Seven-Day Run clears the selected event log before creating a new run.

Clock and runtime

absoluteMinute is the campaign clock. dayIndex is zero-based internally and minuteOfDay === absoluteMinute % 1440; the UI displays Day 1 through Day 7. The default rate is one simulated minute per real second. Player focus defaults to a 20x clock rate, an interruption returns to the normal rate, and sleep advances through authored minute boundaries to the next day. At the final minute of Day 7 the runtime pauses and publishes the campaign-end condition.

The runtime advances only whole simulated minutes through events. It also owns non-derivable scheduler bookkeeping: next decisions and breaks, in-progress routes, required door actions, work carry, timed activities, handled deadlines, open structural doors, focused task, queued minutes, and the campaign-end marker. That bounded data is checkpointed as livingRuntime so reload can resume semantically rather than recompute a different future.

Fractional frame residue is intentionally not written on every animation frame. It becomes authoritative with the next whole-minute or meaningful mutation boundary. BFCache suspension stops the frame loop and resets the resume delta, so hidden elapsed wall time is not converted into campaign time.

Cast, work, and Donna

The authored state contains seven actor records:

  • Jesse Rourke is the player founder and is embodied by #player rather than a duplicate autonomous character entity.
  • Five other founders are autonomous and receive deterministic daily arrival, departure, work, collaboration, break, meeting, and D&D decisions.
  • Donna Jackson is optional staff. Her record exists from run creation, but her visual and scheduling state follow the browser-owned employment state.

Tasks carry owners, collaborators, semantic target anchors, prerequisites, effort, spent time, due times, progress, risk, blockers, and produced artifacts. The weekly build can complete only after every required feature is complete and all named artifact references exist. Only then does the build terminal expose the fixed browser-authored playable.

Donna transitions are:

absent    -> absent | candidate
candidate -> candidate | hired | declined
hired     -> hired
declined  -> declined

Introduce, Hire, and Decline require a dedicated twice-confirmed browser action. Remote conversation text cannot select a transition. A candidate is rendered at the threshold without becoming an available office participant; a hired Donna is projected at reception, becomes scheduled optional staff, and can join ordinary office conversations. campaignParticipant remains false, and the runtime and conversation validators exclude her from campaign activity and campaign knowledge paths.

Semantic space and navigation

tour/src/office/semantics/suite-map.ts defines nine fictional semantic rooms, activity anchors, portal identities, route nodes, capacities, and navigation obstacles over the structural reconstruction. Startup validation checks that rooms and routes remain within the generated footprint and that structural door sources exist. The overlay is never written into id.svg or tour/floorplan.json and is not a historical room-placement claim.

Autonomous movement is browser-authored:

  1. A schedule, task, break, meeting, delivery, or campaign rule chooses a known semantic destination.
  2. The navigation runtime plans over known nodes and portals and reserves the destination capacity.
  3. A closed structural portal creates a bounded door-action request before locomotion continues.
  4. The living stage animates the validated route and releases or replans the reservation on arrival, obstruction, or cancellation.

The player's free movement remains governed by rendered-shell, closed-door, and registered living-stage collision. Semantic routing does not replace physical collision, and noclip remains an explicit tour command.

Foreground conversations

A conversation starts only near an available character. The browser selects at most six eligible participants from presence, current room, distance, portal hearing, visibility, audibility, focus, and campaign boundaries. The request echoes expectedWorldRevision, conversation ID, day, and minute.

Internal context can distinguish private, shared, and public commitments, memories, observed events, and previous conversation summaries. Before a remote call, the public projection removes every fact that is not public to all current participants. The response is parsed against that public request and again against the full browser context before projection.

An accepted result may contain bounded speech/action/reaction beats and proposals for tasks, assignments, commitments, relationship interpretations, memories, and artifacts. The browser owns all final IDs, relationship deltas, deadlines, risk, semantic locations, event provenance, and campaign checks. actorIntents are returned separately as advisory data; the released UI neither persists nor executes them as actor commands.

Conversation text is rendered as inert text. It cannot assign coordinates, routes, doors, component strings, geometry, time, or executable behavior.

Silent Curator

The browser creates one run-local seed and at most one plan for each campaign day. A Curator context can model the current day arc, actors, tasks, weather, outside events, knowledge audiences, and daily music genome, but the remote projection contains public knowledge only. A valid suggestion supplies bounded pressure, autonomous-founder targets, known task IDs, and a time within the current day. It cannot target Jesse or Donna.

Campaign advancement is held while a remote daily plan is outstanding, up to the shared 35-second authority deadline. Success, rejection, timeout, cancellation, stale revision, or invalid output all resolve to either the validated proposal or one deterministic local plan. A provider outage therefore cannot leave the seven-day clock permanently blocked.

Provider and image state

Text and image setup share a panel but remain separate capabilities. Raw keys stay in private orchestration state; subscribers see only provider/model choices and configured booleans. Keys are memory-only unless Remember setup writes the versioned setup record after its warning. Provider setup never enters the simulation event log.

MiniMax accepts only the exact versioned DEFAULT_DONNA_IMAGE_PROMPT and requests one 512 by 512 image-01 Base64 result with prompt optimization off. The browser validates bounds, complete decoding, static JPEG/PNG structure, dimensions, animation exclusion, and native decoding. Generated image data is memory-only and has no path into simulation events, snapshots, or artifacts.