Architecture Overview¶
See also: simulation-pipeline.md for the detailed per-tick flow, including the Phase 7 long-running action continuation branch and the Phase 6 playtest/compression plumbing. This page stays high-level on purpose.
System Components¶
graph TB
subgraph OL["Operator Layer"]
OP["Agent Harness\n(Claude Code / Codex)"]
end
subgraph MCP["MCP Tools"]
direction LR
RT["resume_tick()"]
RB["rollback()"]
LM["list_mechanics()"]
end
subgraph SE["Simulation Engine"]
SC["Scheduler"] -->|"prior observations\n+ context"| RA["Resident Agent\n(Haiku)"]
RA -->|"action text"| AI["Interpreter\n(Haiku)"]
AI -->|"classified action"| MM["Matcher"]
MM -->|match| ME["Executor"]
MM -->|no match| PAUSE(["⏸ returns to Operator"])
ME -->|"mutations"| KG[("Knowledge\nGraph")]
KG -->|"updated state"| OG["Observer\n(Sonnet)"]
OG --> DONE(["✓ tick result + observation"])
end
UF[("Universe Folder\nuniverse.db · mechanics/ · tick_summaries/")]
OP --> RT & RB & LM
RT --> SC
RT -->|auto-scan mechanics/| UF
KG <-->|persist| UF
ME -->|write| UF
OP -->|"direct file I/O"| UF Core Simulation Loop¶
sequenceDiagram
participant O as Operator (Agent Harness)
participant E as Simulation Engine
participant A as Resident Agent (Haiku)
participant M as Mechanic Registry
participant G as Knowledge Graph
O->>E: resume_tick()
Note over E: Scheduler determines agent execution order
loop For each scheduled agent
E->>G: Query agent context + prior observations
G-->>E: Agent state
E->>A: Feed observations + context
A-->>E: Action text (free-form)
E->>E: Interpret & classify action (Haiku)
E->>M: Find matching mechanic
alt Mechanic exists
M-->>E: Return mechanic
E->>G: Check preconditions
alt Preconditions met
E->>G: Apply mutations
G-->>E: Updated state
E->>E: Generate grounded observation (Sonnet)
E->>A: Deliver observation to agent memory
else Preconditions not met
E-->>O: Tick result ("preconditions failed")
end
else No match — needs new mechanic
E-->>O: Tick paused ("no mechanic for: fly")
Note over O: Operator authors mechanics/fly.py<br/>using file writes + subagents
O->>E: resume_tick() (auto-scans mechanics/, validates, continues paused tick)
E->>M: Discover & register mechanics/fly.py
E->>G: Execute new mechanic
G-->>E: Updated state
E->>E: Generate grounded observation (Sonnet)
E->>A: Deliver observation to agent memory
end
end
E-->>O: Tick result + observations
O->>O: Write tick summary to tick_summaries/ Mechanic Structure¶
classDiagram
class Mechanic {
+str name
+str description
+int version
+check(graph, context) bool
+apply(graph, context) list~Mutation~
}
class KnowledgeGraph {
+query(node, property) Any
+set(node, property, value) Mutation
+add_node(id, **props) Mutation
+add_edge(src, dst, **props) Mutation
+snapshot() GraphState
+restore(snapshot) void
}
class MechanicContext {
+KnowledgeGraph kg
+SpatialIndex spatial
+TemporalIndex temporal
}
class SpatialIndex {
+nearest(point, k, ...) list
+within(bbox, ...) list
+intersects(node_id, ...) list
}
class TemporalIndex {
+query_history(node_id, tick_range) list
+query_changes(property, ...) list
+find_state_at_tick(node_id, tick) dict
+last_change(node_id, property) Event?
}
class Mutation {
+str type
+str target
+str property
+Any old_value
+Any new_value
}
Mechanic ..> KnowledgeGraph : queries/mutates
Mechanic ..> Mutation : produces
KnowledgeGraph ..> Mutation : applies
Mechanic ..> MechanicContext : reads
MechanicContext --> KnowledgeGraph
MechanicContext --> SpatialIndex : lazy
MechanicContext --> TemporalIndex : lazy
SpatialIndex ..> KnowledgeGraph : rebuilds from
TemporalIndex ..> KnowledgeGraph : replays events of Spatial & Temporal Primitives¶
Mechanics reach the graph through MechanicContext. Two lazy accessors extend the DSL without adding cost to mechanics that don't use them:
ctx.spatial-- R-tree-backed queries (nearest,within,intersects) over nodes withposition=[x, y]orbbox=[x1, y1, x2, y2]properties. Malformed coords are logged and skipped, not raised.ctx.temporal-- Event-log queries (query_history,query_changes,find_state_at_tick,last_change) over the mem+disk-merged event stream. Reconstructs node state at any reachable tick via snapshot baseline + event replay.
Both are pay-for-what-you-use: ctx._spatial / ctx._temporal stay None until first access.
Operator Tooling¶
token-world create <slug>-- scaffold a new universe folder.token-world list-- enumerate known universes.token-world viz-graph <slug>-- render the knowledge graph as a Mermaidflowchart LRwith category styling, label/ID sanitisation, and filters (--node,--depth,--type,--has-property,--exclude-property,--max-nodes). See the viz-graph guide.
Quality¶
Two canonical discipline docs gate merges and overnight runs:
- dashboard-qa-checklist.md -- required PR pass for any
src/token_world/dashboard/change (nine interactive checks + Playwright routine + user-mode cooldown). - sim-quality-rubric.md -- seven-dimension scorecard for "is the run healthy?" CI gate on release-tier overnight runs.
Phase 4.1: Operator Harness¶
The operator harness catches simulation yields and drives mechanic authoring via the Claude Agent SDK. Two entry points share the same universe MCP surface and the same mechanic-author subagent definition:
- Programmatic —
token-world run-tick(Click CLI) instantiatesOperatorHarness.handle_yield(signal)directly; used by automated tests and the Phase-6 playtest runner. - Interactive — a human opens Claude Code inside a universe folder. CLAUDE.md's
Operator Flow: When a Tick Yieldssection teaches the outer Claude Code session to invoke themechanic-authorsubagent (filesystem-defined at.claude/agents/mechanic-author.md).
Both paths source their mechanic-author prompt from the same mechanic_author_prompt(universe, yield_json) Python function (src/token_world/operator/subagent.py), so prompt drift between the two is structurally impossible (T-04.1-22 mitigation).
flowchart TB
subgraph Engines["Yield sources"]
Phase5["Phase 5 engine<br/>(not built yet)"]
Stub["EngineStub<br/>(test-only)"]
end
subgraph Entry["Entry points"]
CLI["token-world run-tick<br/>(Click CLI)"]
CC["Claude Code<br/>(CLAUDE.md-driven)"]
end
Harness["OperatorHarness<br/>(Agent SDK, Opus)"]
subgraph SDKSession["Outer SDK session"]
direction LR
Subagent["mechanic-author<br/>subagent (Opus)"]
ValTool["validate_mechanic<br/>@tool wrapper"]
MCP3["3-tool MCP surface<br/>resume_tick · rollback · list_mechanics"]
end
Diag["DiagnosticsSink<br/>+ operator/ namespace<br/>(Phase 4.1 extension)"]
Mechanics["mechanics/*.py<br/>+ tests/"]
Phase5 -. "YieldSignal" .-> Harness
Stub -. "YieldSignal (test)" .-> Harness
CLI --> Harness
CC --> Harness
Harness --> Subagent
Subagent --> ValTool
Subagent --> Mechanics
Harness --> MCP3
Harness --> Diag Key contracts:
- YieldSignal (
src/token_world/operator/yield_signal.py) is the locked interface between engine and operator. Frozen+slots dataclass with 7 fields (tick_id,universe_path,schema_version,reason,action_text,classified_action,actor_state,candidate_mechanic_ids), deterministic JSON round-trip (sort_keys=True, indent=2), schema-versioned rejection on unknownschema_versionvalues (T-04.1-01). - mechanic-author subagent prompt (
src/token_world/operator/subagent.py::mechanic_author_prompt) is the single source of truth for both the programmaticAgentDefinitionand the per-universe.claude/agents/mechanic-author.md. Scaffold writes the filesystem agent duringscaffold_universe()viatoken_world.universe.templates.mechanic_author_agent.render_mechanic_author_md(). - Diagnostics operator namespace (
src/token_world/operator/diagnostics.py) extends Phase 4'sDiagnosticsSinkwith anoperator/subfolder per tick. Artefacts:yield_signal.json,authoring_attempts.jsonl,validation/attempt_NN.json,mechanic_diff.patch,resume_outcome.json. Atomic writes usetempfile.mkstemp + os.fsync + os.replacemirroring Phase 4's helper. JSONL appends are tolerant of malformed lines on read. TheOperatorDiagnosticsReaderis the single sanctioned parser (D-16). - Subagent tool whitelist excludes
Agent(Pitfall 5 / T-04.1-23: subagents must not spawn sub-subagents). Includesmcp__validation__validate_mechanic(in-process SDK MCP server wrapping thevalidate-mechanicCLI withshell=False— T-04.1-11) andmcp__token-world__list_mechanics(from the universe's.mcp.json). - Safety caps on the outer session:
max_turns=20andmax_budget_usd=5.0(SDK-enforced hard cap per BLOCKER-4 resolution in Plan 03). The integration test assertscost_usd < 5.0.