12 KiB
Handover — OTIVM Game Development
Date: 2026-04-27
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/roadmap.md— read with the warnings in Section 5 of this document in mind. The roadmap needs rewriting. Do not treat it as current.docs/RFC-TESSERA-4.0-001.md— the database schema all future releases depend ondocs/TESSERA-dataset-registry.md— what data exists, what is pending, and critically: the restoration layer concept- This file
2. Infrastructure
OTIVM container (otium-dev, 10.0.0.23)
- App user:
otivm - Repo at
/home/otivm/OTIVM - Claude Code runs here as
otivmuser viaworkalias (cd ~/OTIVM && claude) - Python venv (game):
/home/otivm/venv - Python venv (pipeline — do not use):
/home/otivm/pipeline-venv - PM2 home:
/home/otivm/.pm2 - Node:
/usr/bin/node(v22) - App port: 3000
- WireGuard: 10.110.0.18
Three Proxmox boxes
- proliant-dev (srv-a, 10.0.0.11) — development work happens here
- staging box — validation before production
- production box — live game served from here
Nginx proxy
- Lives on wg-pk (198.58.111.109) — not on this container
- Proxies
otium.civicus.us→10.110.0.18:3000 - Do not look for a vhost on the container
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
Backups
vzdump 1105 --compress zstd --storage local --mode snapshoton srv-a (root shell)- Document every backup in
docs/archives.mdimmediately after - 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/and save API on port 3000 - Player state: JSON flat files in
data/saves/— no database - TESSERA data:
data/otivm.sqlite3— read-only by game server, owned and populated by dataset assistant - PM2 under
otivmuser (never root) - Ecosystem file:
ecosystem.config.cjs(must be.cjs— Vite sets"type": "module")
4. Current game state — as of 2026-04-27
OTIVM-I — complete
- Fastify backend serving
dist/and save API on port 3000 - Five trade routes, Ostia → Alexandria, all working
- Journal entries firing on dispatch milestones
- Otium/negotium mechanic working
- Per-player save files in
data/saves/via 8-char hex token - Token displayed in UI — player can record it to resume on another device
- 128 concurrent players supported
Navigation scaffold — complete
src/screens/directory established- Game.jsx renamed to
src/screens/Ledger.jsx App.jsxmanages screen state — ledger and map- Both screens stay mounted — no state lost on switch
OTIVM-II — complete and live
src/screens/Map.jsx— Mediterranean SVG map- Two-polygon land outline (Europe + Asia Minor, North Africa) — placeholder coastline, accurate mainland only
- Bounding box: 5°E–38°E / 28°N–48°N, equirectangular projection, 800×460 canvas
- Five waypoints plotted at hardcoded H3 res-5 cell centres
- Route lines between waypoints — gold when unlocked, muted dashed when locked
- Current chapter waypoint highlighted in gold, reached waypoints in green
src/constants.js— provenance fields added to all routes:origin_h3_r5,origin_region,cultural_noteThese are stub values today — become live TESSERA queries in OTIVM-IIIsrc/gameState.js— structural additions:active_dispatch: null— records in-progress dispatchevents: []— append-only event log Each entry:{ type, route_id, timestamp_utc }Types:dispatch_start,dispatch_complete,otium,chapter_advance,journal_unlockThis is the sequencing substrate for OTIVM-IX attestationgalleyProgress(active_dispatch, now_ms)— pure function, returns 0–1 progress float. Returns null if no dispatch active.- All apply* functions now append to
eventsand set/clearactive_dispatch
Architecture decisions locked
- H3 IDs on all waypoints — permanent, TESSERA-compatible
constants.js/gameState.js/api.jsseparation — permanent- Virtual screens via
display:none— state preserved in browser - Save on meaningful events only — not on every tick
- H3 cell centre coordinates hardcoded in
Map.jsx— h3-js is server-side only - Two-polygon land outline — to be replaced in OTIVM-III
- Two polygons not one — avoids cross-sea rendering lines
Known issue — recorded
- Claude Code collapsed
INITIAL_STATEonto one line during a prior session, causing a Vite build failure. Fixed in commit34176dc. - Going forward: Claude Code writes content exactly as received, regardless of how it arrives.
5. The roadmap — needs rewriting ⚠
Read docs/roadmap.md but do not treat it as authoritative.
The roadmap was written before the TESSERA 4.0 architecture was
decided. The following assumptions in the current roadmap are now
wrong:
1. It assumes a completed global TESSERA database.
The global database (tessera.db) no longer exists on a reachable
server. data/otivm.sqlite3 is a purpose-built per-waypoint database
(TESSERA 4.0 model). New hexes are added one H5 at a time by the
dataset assistant as the game expands. The roadmap must reflect this.
2. It does not mention the restoration layer.
terrain in otivm.sqlite3 is modern WorldCover 2021 data. It is
wrong for any historical period. The Mediterranean was 60–70% forested
in Roman times and Mesolithic times. Today the same cells are
classified as built-up, cropland, or drained wetland.
The restoration layer (HYDE 3.3 + KK10 datasets, not yet on drives)
will correct terrain to historically appropriate values. Until that
layer is active, the game must not present terrain data as
historically accurate.
This affects the roadmap significantly — terrain-dependent features (city physical character, environmental hazard, resource availability) cannot be implemented correctly until the restoration layer is in place.
3. Release numbering is out of date. OTIVM-III in the current roadmap describes "The Factor" (NPC model). The actual next release should establish the SQLite server connection and replace the map coastline — both triggered by the database arriving on the container. Discuss scope with the project owner before any code.
The roadmap rewrite is the first task for the game development assistant. Do not write code until the roadmap is current. The project owner will direct the rewrite.
6. The database — what the game can and cannot do
data/otivm.sqlite3 is present on the container. It is read-only
from the game's perspective. The dataset assistant owns it.
What is in the database
- 12,005 H9 rows across five waypoints, all
status=2(current) paleo_epochstable — 9 epochs from Eemian to present with sea level offsets
Five waypoints
| City | H5 TEXT | H9 cells |
|---|---|---|
| Ostia | 851e805bfffffff |
2401 |
| Capua | 851e8333fffffff |
2401 |
| Brundisium | 851e8ba3fffffff |
2401 |
| Carthago | 85386e23fffffff |
2401 |
| Alexandria | 853f5ba7fffffff |
2401 |
The canonical game query
Only use cells from complete, current H5 hexes:
SELECT tc.*
FROM tessera_cells tc
JOIN h5_coverage h5c ON tc.h5 = h5c.h5
WHERE h5c.status = 2
AND tc.status = 2
AND tc.h5 = ?
Field status — what the game can trust
| Field | Trust level | Reason |
|---|---|---|
elev_cm |
✅ Use — with epoch offset | GEBCO 2025, modern MSL. Apply sl_offset_cm from paleo_epochs for historical periods. |
terrain |
⚠ Modern only | WorldCover 2021. Wrong for Roman/Mesolithic. Do not present as historical until restoration layer is active. |
hydro |
✅ Use with caution | HydroSHEDS v1.1. Rivers have migrated — major drainage correct, fine detail approximate. |
geo_dep |
✅ Use | USGS MRDS. Sparse in places but correct where present. |
geo_flag |
✅ Use | BGR IGME5000. European coverage only. |
occ_flag |
❌ Do not use | Placeholder 0x00 everywhere. Stage 06 not written. |
H3 conversion in JavaScript
H3 IDs are stored as INTEGER (64-bit) in the database.
In JavaScript: h3.indexToCell(BigInt(row.h9)) to convert to string.
Server-side only — h3-js is not available in the browser bundle.
7. Dataset assistant — what they are doing in parallel
The dataset assistant owns the pipeline. Current status:
data/otivm.sqlite3— production database, 12,005 rows ✅data/staging_otivm.sqlite3— their working copy, not in gitpipeline/seed_extract.py— old extractor, do not re-run ✅pipeline/seed_promote.py— old promotion script, do not re-run ✅docs/TESSERA-dataset-registry.md— full dataset inventory ✅docs/handover-dataset.md— their track orientation ✅
Next dataset work:
- Four datasets to be added to USB Drive 1 (project owner action)
- Per-H5 pipeline to be designed and built
- Restoration layer — HYDE 3.3 + KK10 integration
You will be told when the database is updated. You do not run
pipeline scripts. You do not touch pipeline/ or data/create_otivm_db.sql.
8. Workflow — one file, one step, one confirmation
Every change follows this sequence without exception:
- Claude chat discusses the change and produces one downloadable file
- The file header contains the Claude Code instruction (path, commit message)
- The file body contains the exact content to write to disk
- Human downloads from Claude chat and pastes into Claude Code (Shell 1)
- Claude Code writes to the specified path, commits, pushes
- Human runs
npm run buildin Shell 2 (otivm shell) - Human runs
pm2 restart otivmin Shell 2 - Human confirms in browser at https://otium.civicus.us
- Human reports result back to Claude chat
- Claude chat proceeds to the next step
One file. One step. One confirmation. Never batch.
9. Hard rules
- Never run PM2 as root — always as otivm user
- Never commit secrets — no tokens, no keys, no passwords in any file
- Never push to main without building —
npm run buildmust pass first - No database for player state — JSON flat files only
- H3 IDs on every location — never a coordinate pair or string name alone
- One change confirmed before the next — no batching steps
- Never print file contents or code blocks in chat — always downloadable attachments
- Never make assumptions about what is on disk — always read from Gitea MCP first
- Do not query
otivm.sqlite3with raw coordinates — always H3 IDs - Do not present
terrainas historically accurate until the restoration layer is confirmed active by the dataset assistant
10. Commit messages
Imperative mood, present tense, under 72 characters.
Example: Add Mediterranean SVG map to Map screen not Added map.
Handover 2026-04-27 — game development track Database present, paleo_epochs added, 12,005 current rows. Roadmap needs rewriting — first task before any code. terrain field is modern WorldCover — not historically accurate yet. Claude chat designs. Claude Code implements. The human decides.