Skip to content

Clients, API, engine

designed · Phase 1 (#39, #40, #41) · PLAN §13.1–§13.4

 browser ─── http://localhost:3000 ─── Vite dev server (clients/app) ── /api proxy ──▶ ASP.NET Core API (src/NextIteration.Api, :5000)
                 │                                                                            │
                 ├── /      Universe Viewer   (the chronicle — players)                        ├── SimulationHost: one thread ticks; reader-writer lock vs. API reads
                 └── /dev   Developer Studio  (the instrument panel — developers)              ├── Engine: graph, pipeline, resolver, clock, bus, trace, snapshots
                                                                                              ├── PlayerActionQueue ◀── POST /worlds/{id}/player-actions
                 ◀── SignalR  /worlds/{id}/events, /worlds/{id}/interest ─────────────────────┤
                                                                                              └── saves/{universeId}/tick_N.json, trace/*.jsonl

No orchestrator and no containers in Phase 1 (PLAN §13.1). The harness and the tests run the engine in-process and never go through the API.

Who reads and writes what

Surface Reads Writes Notes
Viewer / graph (/graph, /entities), events stream, interest stream, Explain, History, snapshots player actions (Tinkerer only), speed, rewind Speaks world time and prose only; never a tick number or an attribute key (CLAUDE.md → PLAYER-FACING LEGIBILITY).
Studio /dev everything above plus /systems (timing), WhyChanged, raw events, saves system toggles, player actions, save/load, step May show raw data, paired with a readable summary.
API engine state under the read lock the player-action queue; toggles at tick boundaries All mutation is a player action → intent; no endpoint writes the graph directly.
Engine its own graph and trace graph (resolver), trace (recorder), saves Ticks on one thread; API reads take the read lock so they never observe a half-applied batch.

The two routes are one app

clients/app is a single React + TypeScript app with two routes sharing src/api/ (typed REST client, SignalR hooks) and src/types/. The Studio embeds a small instance of the Viewer in its preview slot, so any improvement to the Viewer shows up in the Studio.

Observability

OpenTelemetry instrumentation only — ActivitySource spans tick → phase → system, Meter metrics, ILogger scopes — with no exporter configured by default. Per-system timing is returned by GET /universes/{id}/systems for the Studio's panel; an OTLP viewer (the standalone Aspire dashboard, Jaeger, Seq) can attach by setting OTEL_EXPORTER_OTLP_ENDPOINT (PLAN §13.2).

See also

API · Player actions · Persistence · Player: Ways to play