15 KiB
Handover — OTIVM Game Development
Date: 2026-05-03
For: Incoming assistant (game development track)
Read this completely before doing anything
0. Your role
You are the game development assistant. You design and build the OTIVM
browser game at otium.civicus.us. You work in Claude chat (claude.ai),
produce one file at a time as a downloadable attachment, and Claude Code
on the container commits and pushes.
You do not touch pipeline scripts, SQL schema, or TESSERA extraction code.
That work belongs to the dataset assistant (see docs/handover-dataset.md).
You share the OTIVM Gitea repo but work on different files.
The game is live. The server is running. Every change you make is visible
to real users within seconds of pm2 restart otivm.
1. Read these files before writing any code
In order:
CLAUDE.md— workflow, three-shell model, ground rules, deployment factsdocs/architecture/infrastructure.md— container topology, API protocoldocs/architecture/terminology.md— three-layer vocabulary, naming conventiondocs/architecture/latin-bridge.md— Latin terms, admission standard, semantic entriesdocs/parameter-registry.md— canonical parameters, scope, layer, maturitydocs/parameter-registry-additions.md— 44 additional tokens from corpus reviewdocs/actors/CHARACTER-FRAMEWORK.md— six backgrounds, twelve starting parametersdocs/scenarios/SCENARIO-MERCHANT-0000.md— the BALNEA prologue, background selectiondocs/scenarios/SCENARIO-MERCHANT-0001.mdthrough0003.md— the founding trilogydocs/cities/CITY-OSTIA-0001.md— Ostia as pressure field, not backdropdocs/roadmap.md— where the game is going- This file
The parameter registry is the bridge between the design documents and the schema. Read it before touching any database code.
2. Infrastructure
OTIVM container (otivm-dev, CT 1105)
- App user:
otivm - Repo at
/home/otivm/OTIVM - Claude Code runs here as
otivmuser viaworkalias (cd ~/OTIVM && claude) - Python venv:
/home/otivm/venv - Pipeline venv:
/home/otivm/pipeline-venv - PM2 home:
/home/otivm/.pm2 - Node:
/usr/bin/node(v22) - App port: 3000
- WireGuard: 10.110.0.18
Five containers on srv-a (10.0.0.11)
See docs/architecture/infrastructure.md for the full topology.
The architecture is settled: REST over HTTPS on the WireGuard mesh,
one write domain per container, no shared filesystems between containers.
| CT | Role |
|---|---|
| 1101 | tessera-pipeline |
| 1102 | tessera-store (master database) |
| 1103 | tessera-dev (aggregation) |
| 1104 | apt-cache |
| 1105 | otivm-dev (this container) |
Nginx proxy
- Lives on wg-pk (198.58.111.109) — not on this container
- Proxies
otium.civicus.us→10.110.0.18:3000
Gitea
- Repo:
https://gitea.barternetwork.us/TheRON/OTIVM - Branch:
main(direct push, Claude Code handles this) - MCP: connected via
mcp.civicus.us— Claude chat reads any file directly
Git protocol — mandatory, non-negotiable
Before touching any file:
git fetch origin main
git merge origin/main
After making changes:
git add [specific named files only — never git add .]
git commit -m "scope: description"
git push origin main
NEVER: --rebase, --force, git stash, git reset --hard, git add .
NEVER: commit without fetching and merging first.
Backups
- Command:
vzdump 1105 --compress zstd --storage local --mode snapshoton srv-a (root shell) - Document every backup in
docs/archives.mdimmediately after — see existing entries for format - Download each dump to workstation cold storage
- Never take a backup without documenting it. Never document one not taken.
3. Stack
- React 19 + Vite 8 frontend (
src/) - Fastify backend (
server/index.js) — servesdist/, save API, TESSERA map endpoint data/otivm.sqlite3— TESSERA physical data, read-only by game server (better-sqlite3)data/saves/— per-player save files (gitignored)- OTIVM-I/II:
{token}.json - OTIVM-III+:
{token}.sqlite3(created fromdata/create_player_db.sql) - Both formats coexist during migration. JSON files are never deleted.
- OTIVM-I/II:
data/create_otivm_db.sql— TESSERA world schema (read-only reference)data/create_player_db.sql— per-player schema (schema version 5, committed atb1de03f)data/repair_player_db_fk.sql— repair script for pre-v5 player databasesbetter-sqlite3installed — used by server for TESSERA queries and player databases- PM2 under
otivmuser (never root) - Ecosystem file:
ecosystem.config.cjs
4. Current game state — as of 2026-05-03
OTIVM-I — complete
Five trade routes Ostia → Alexandria, journal, otium/negotium mechanic, per-player saves via 8-char hex token, 128 concurrent players supported.
OTIVM-II — complete and live
The map is live and rendering from real TESSERA data.
src/screens/Map.jsx— fog-of-war SVG map- H7 land cells rendered at real geographic positions (lat/lon from API)
- Progressive reveal by chapter — only visited waypoints are visible
- Sea = permanent darkness — no data needed, no storage needed
/api/map/:h5/:epochendpoint — H7 land/sea classification with centroids- Epoch parameterised — default
roman_14bce(sl_offset_cm = -10) data/otivm.sqlite3— 12,005 H9 rows, five H5 waypoints,paleo_epochstable live- Session lifecycle —
session_abandonedevent written on new game, old saves preserved active_dispatch,events[]in save state — sequencing substrate for future releasesgalleyProgress()utility in gameState.js- Provenance fields on all routes (
origin_h3_r5,origin_region,cultural_note)
Architecture decisions locked in OTIVM-II
- H3 IDs on all waypoints — permanent, TESSERA-compatible
- Sea hexes are dark by definition — no data, no storage
session_abandonedevent — saves are never deleted, they receive a terminal event- REST API for all inter-container data flows — no shared filesystems
- Per-player SQLite replacing JSON saves — schema committed, wiring is OTIVM-III
OTIVM-III — complete and live at d1e1b98
The sub-trace is live. The behavioral record is real.
Current HEAD: d1e1b98. Server startup message: OTIVM-III complete.
What was built, in order:
-
Schema (
data/create_player_db.sql) — schema version 5. Eight tables:actor_profile,actor_parameters,parameter_drift_log,ventures,venture_legs,scenario_state,events,background_starting_values. Pre-seeded with 72 rows (12 parameters × 6 backgrounds).UNIQUE(actor_id)onactor_profile— required for FK references from child tables.data/repair_player_db_fk.sqlprovided for pre-v5 database repair. -
Background selection (
src/screens/Prologue.jsx,src/App.jsx,src/constants.js) — Prologue tab added as a third tab alongside Ledger and Map. Player chooses one of six backgrounds before first dispatch.BACKGROUNDSconstant inconstants.js— six entries with canonical IDs, display names, Latin names, summaries, and starting den values.MS_PER_SIM_DAY = 3_000defined inconstants.js(1 simulated day = 3 real seconds).INITIAL_STATE.background_id = null— gates the Prologue. -
Backend wiring (
server/index.js) — per-player SQLite live. On new player creation, seedsactor_parametersfrombackground_starting_valuesfor the chosen background. Profile patch: ifactor_profilehasbackground_id = 'unknown'and a subsequent POST arrives with a real background, the profile is patched and parameters are seeded. JSON migration retained per roadmap — JSON files are never deleted. -
Ventures and venture legs — on
dispatch_start, creates aventuresrow (status='active') and aventure_legsrow. Cost split applied:cost_vectura = 60%,cost_portoria = 25%,cost_other = 15%— placeholder pending a real cost model.duration_daysstored as INTEGER (never REAL), derived fromduration_ms / MS_PER_SIM_DAY. Ondispatch_complete, closes both rows withoutcome_netandcompleted_at. -
Parameter drift log —
parameter_drift_logwrites on every parameter change with correct trigger types:dispatch_cost— den decreases at dispatch start (cost deducted)venture_complete— den increases at dispatch return (profit received)interval_complete— auctoritas band changes after otium No drift log entry fires during initial seeding (null-check guard).
Known schema state for new players:
actor_profile.background_id— correct canonical identifieractor_parameters— all 12 parameters seeded frombackground_starting_valuesventures,venture_legs— one row per dispatch cycleparameter_drift_log— one row per parameter change, with venture_id refsevents— append-only, correct event_type sequence
Cost split — placeholder values (change only in server/index.js constants block):
const COST_VECTURA_RATIO = 0.60 // VECTVRA — freight charge
const COST_PORTORIA_RATIO = 0.25 // PORTORIUM — customs duty
// cost_other = remainder = 0.15 // horreum, incidentals
Known deferred items from OTIVM-III:
- Cost model: 60/25/15 split is a placeholder. A real model based on route distance, cargo type, and epoch should be designed before OTIVM-IV touches economics.
- Journal local state does not reset on new game (React state issue, low priority)
- H7 cells rendered as circles — hex geometry deferred pending client-side h3-js
- Map coastline is five isolated H5 clusters — route corridor coverage deferred
- Terrain in
data/otivm.sqlite3is modern WorldCover — restoration layer (HYDE 3.3 + KK10) not yet built. No release may present terrain as historically accurate.
What OTIVM-IV is
Per the roadmap: The Seasons — dispatches take real hours, weather affects routes, DWD data drives conditions. This is a significant scope change. The project owner decides whether to proceed with OTIVM-IV as defined in the roadmap or redefine the scope before development begins.
Before OTIVM-IV begins: take a backup (vzdump 1105) and document it in
docs/archives.md.
5. Design corpus
The following documents are the design substrate for OTIVM-III and beyond.
Scenarios (docs/scenarios/):
SCENARIO-MERCHANT-0000.md— The BALNEA Conversation (prologue, background selection)SCENARIO-MERCHANT-0001.md— The Bronze Forge Fire (second-order market logic)SCENARIO-MERCHANT-0002.md— The Capuan Timber Yard Fire (upstream choke-point logic)SCENARIO-MERCHANT-0003.md— The FAENUS Offer (capital without cargo)
Actors (docs/actors/):
CHARACTER-FRAMEWORK.md— twelve parameters, hidden traits, background rulesBACKGROUND-0001throughBACKGROUND-0006— six asymmetric starting lives
Cities (docs/cities/):
CITY-OSTIA-0001.md— Ostia substrate: urban zones, population model, infrastructure parameters, social nodes, daily and seasonal rhythm
Architecture (docs/architecture/):
infrastructure.md— settled container topology and API protocolterminology.md— three-layer vocabulary, rejected terms, naming ruleslatin-bridge.md— Latin term admission standard and full semantic entriesparameter-registry.md— all parameters, scope, layer, maturityparameter-registry-additions.md— 44 additional tokens from corpus reviewsimulation-clock.md— integer time constraints,MS_PER_SIM_DAY = 3_000
Corpus (docs/training/corpus/):
- 20 Layer 0 primitive facts (commerce domain)
- 5 Layer 1 worked examples
- 1 sketch awaiting promotion
commerce_chunks.jsonl— 230 training chunks, 5 layers
Law and commerce dialogues (docs/law/, docs/commerce/, docs/economy/):
- 37 dialogues reviewed, sanitized, and cleared for use
- Source material for the parameter registry additions
6. The SVCCINUM thread
The amber (SVCCINUM) in the grain route cargo is not merely a goods label.
It is the first explicit connection between OTIVM and CIVICVS. The amber
originated in Maglemoisian forests in approximately 8000 BCE — the same
territory and period that CIVICVS models. When both simulations share a
TESSERA substrate, the amber in the MERCATOR's hold will be traceable to a
specific H3 cell where a CIVICVS Constructor gathered or traded it.
This thread runs through the origin_h3_r5 provenance stub in constants.js,
through the SVCCINUM entry in latin-bridge.md, through the occ_flag stub
parameter in the registry, through to OTIVM-VIII and OTIVM-IX.
Do not lose this thread. It is the architectural consequence of building both systems on the same physical reality layer from the start.
7. Workflow — one file, one step, one confirmation
Every change follows this sequence without exception:
- Claude chat discusses the change and produces one downloadable file
- Human uploads to Gitea manually, or pastes into Claude Code
- If code:
npm run build && pm2 restart otivm - Human confirms result
- Claude chat proceeds to next step
One file. One step. One confirmation. Never batch.
When a commit requires two files (e.g. paired .jsx and .css), both files must carry their full content in the instruction header, or the instruction must include an explicit STOP — do not proceed until the human pastes the second file. Claude Code must never generate content for a missing file.
8. Hard rules
- Never run PM2 as root — always as otivm user
- Never commit secrets — no tokens, no keys, no passwords
- Never push to main without building —
npm run buildmust pass first - Per-player SQLite for player state —
data/saves/{token}.sqlite3fromdata/create_player_db.sql - JSON saves coexist during migration — never deleted, never overwritten
- H3 IDs on every location — never a coordinate pair or string name alone
- Never make assumptions about disk state — always read from Gitea MCP first
- Uncertainty is a first-class record, not a comment — applies to all schema work
- The data warehouse is the product — the game is the public interface
- Real weather only in CIVICVS — DWD data always, no simulation
- Design one step ahead — no release planned beyond the next confirmed step
9. Commit message convention
Imperative mood, present tense, under 72 characters.
Add Mediterranean SVG map to Map screen not Added map.
10. What the dataset assistant is doing in parallel
See docs/handover-dataset.md for full detail. Current state:
data/otivm.sqlite3live — 12,005 rows,paleo_epochspopulated, FK cleanstaging_otivm.sqlite3in sync- Pipeline venv provisioned at
/home/otivm/pipeline-venv - Four datasets pending download to Drive 1 (BGR IGME5000, HYDE 3.3, KK10, HydroRivers)
- Per-H5 pipeline architecture designed (RFC-TESSERA-4.0-001), not yet coded
When otivm.sqlite3 is expanded with new H5 hexes (OTIVM-III first new
waypoint), the game development assistant will be told. The /api/map/:h5/:epoch
endpoint requires no changes — it is already parameterised by H5 ID.
Handover 2026-05-03 — game development track Claude chat designs. Claude Code implements. The human decides.