Files
otivm/CLAUDE.md

13 KiB
Raw Blame History

CLAUDE.md — OTIVM Project

This file is read automatically by Claude Code at the start of every session. Read it completely before doing anything else. It describes not just the project but exactly how the development process works — including how Claude chat, Claude Code, and the human work together. A new assistant who skips this file will immediately make mistakes that waste the session.


What OTIVM Is

A browser-based Roman merchant idle game. The narrative unfolds through trade goods and journal entries. It is a standalone light-hearted project under TheRON, but it is built on the same H3/TESSERA substrate as the CIVICVS Mesolithic Simulator. Every architectural decision is permanent and load-bearing for future releases up to OTIVM-X.

Live URL: https://otium.civicus.us Repo: https://gitea.barternetwork.us/TheRON/OTIVM (branch: main) Roadmap: docs/roadmap.md — read it to understand where this is going


The Three-Shell Model — Read This First

Development always uses three terminal shells simultaneously. Each shell has one fixed role and must never be used for another role's work.

Shell 1 — Claude Code shell (otivm user)

  • Started by typing work at the otivm prompt
  • work is an alias for: cd ~/OTIVM && claude
  • This shell runs Claude Code continuously for the entire session
  • Claude Code reads CLAUDE.md on startup, checks git status, checks pm2 list
  • All file writes, git commits, and git pushes happen here and nowhere else
  • Do not use this shell for npm commands, pm2 restarts, or manual file edits

Shell 2 — otivm shell (otivm user)

  • A second terminal logged in as otivm, in ~/OTIVM
  • Used for: npm install, npm run build, pm2 restart, curl tests, git pull
  • The Proxmox console drops the otivm user directly into ~/OTIVM with venv active
  • Treat every Proxmox console visit as a fresh terminal — no scroll-back history

Shell 3 — root shell (root user)

  • Used only for: vzdump backups, pct commands, chown fixes, systemctl for PM2
  • Never run pm2 as root — PM2 belongs to the otivm user
  • Never modify /opt/civicvs or /opt/civmap from this shell

How Claude Chat and Claude Code Divide Responsibilities

These are two different Claude instances with different roles. Confusing them is the most common source of wasted sessions.

Claude chat (claude.ai in the browser)

  • Reads the repo via Gitea MCP (read-only access to gitea.barternetwork.us)
  • Discusses architecture, makes decisions, designs files
  • Produces one file at a time as a downloadable attachment
  • Each file has a Claude Code instruction header at the top, followed by the file content that Claude Code writes to disk
  • Claude chat does NOT write to disk, does NOT commit, does NOT push

Claude Code (running in Shell 1 on the container)

  • Receives one file at a time — the human downloads it from Claude chat and pastes it
  • Reads the instruction header at the top of the file
  • Writes the content below the instructions to the path specified in the header
  • Commits and pushes to Gitea
  • Does NOT design, does NOT make architectural decisions
  • Does NOT modify content — writes exactly what is given
  • Confirms what it did in one sentence — does not reprint file contents

The human

  • Downloads the file from Claude chat
  • Pastes it into Claude Code (Shell 1)
  • Runs build and test commands in Shell 2 (otivm shell)
  • Runs backup commands in Shell 3 (root shell)
  • Confirms results before the next step begins

The Exact Workflow — Step by Step

Every change to the codebase follows this sequence without exception:

  1. Claude chat discusses the change and produces one downloadable file
  2. The file header contains the Claude Code instruction (path, commit message)
  3. The file body contains the exact content to write to disk
  4. Human downloads the file from Claude chat
  5. Human pastes the file into Claude Code (Shell 1)
  6. Claude Code reads the instruction header, writes the content to the specified path
  7. Claude Code commits and pushes to main
  8. Human runs npm run build in the otivm shell (Shell 2)
  9. Human runs pm2 restart otivm in Shell 2
  10. Human confirms the result in the browser at https://otium.civicus.us
  11. Human reports the result back to Claude chat
  12. Claude chat proceeds to the next step

One file. One step. One confirmation. Never batch.


What Claude Code Must Never Do

  • Never modify file contents — write exactly what is given
  • Never reformat code — indentation, spacing, structure are intentional
  • Never add imports, exports, or dependencies not in the instruction
  • Never run npm install without explicit instruction
  • Never run pm2 as root
  • Never commit without explicit instruction to commit
  • Never push to any branch other than main
  • Never reprint long file contents back into chat — confirm with one sentence

What Claude Chat Must Never Do

  • Never print file contents or code blocks in the chat window
  • All files and instructions are produced as downloadable attachments only
  • Printing code in chat wastes context window and shortens the session
  • Never make assumptions about what is on disk — always read from Gitea MCP first

Deployment Facts — Do Not Guess These

These facts were learned during initial deployment. They are not in any tutorial.

  • PM2 ecosystem file must be named .cjs not .js — Vite sets "type": "module" in package.json which breaks CommonJS module.exports
  • PM2 ecosystem file uses absolute path /usr/bin/node for the script field
  • Always run pm2 start ecosystem.config.cjs from ~/OTIVM, not from ~
  • PM2 startup is configured via systemd unit pm2-otivm.service — do not run pm2 startup again
  • pm2 save must be run after any change to the process list
  • Nginx for otium.civicus.us lives on wg-pk (198.58.111.109), not on this container — do not look for a vhost here
  • The app is proxied from wg-pk to this container at 10.110.0.18:3000
  • The Proxmox console loses scroll history on every container switch — treat every shell session as fresh

Stack

  • React 19 + Vite 8 frontend
  • Fastify backend (server/index.js) — serves dist/ and save API on port 3000
  • Node.js v22 at /usr/bin/node
  • PM2 under otivm user (never root)
  • No database — JSON flat files in data/saves/ for player state

Container Facts

Property Value
Hostname otivm-dev
LAN IP 10.0.0.23
WireGuard 10.110.0.18
Cores 4
RAM 2048 MB
App user otivm
App port 3000
Python venv /home/otivm/venv
PM2 home /home/otivm/.pm2
Repo path /home/otivm/OTIVM
Node path /usr/bin/node

Ground Rules — Do Not Violate

  • Never run PM2 as root — always as otivm user
  • Never run Python outside the venv — /home/otivm/venv always
  • Never commit secrets — no tokens, no keys, no passwords in any file
  • Never push to main without building — npm run build must pass first
  • No database — 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

PM2 Commands (always as otivm user)

pm2 restart otivm
pm2 list
pm2 logs otivm --lines 30 --nostream
pm2 save

Git Workflow

git pull origin main
git add <files>
git commit -m "imperative mood, under 72 chars"
git push origin main

Commit messages: imperative mood, present tense, under 72 characters. Example: Add Mediterranean SVG map to Map screen not Added map.


Session Start Checklist (Claude Code)

  1. Read this file completely ✓
  2. Run git log --oneline -5 — know what was last committed
  3. Run git status — confirm working tree is clean
  4. Run pm2 list — confirm otivm is online
  5. Wait for instruction from the human — do not act until instructed

TheRON Infrastructure Context

  • wg-pk (198.58.111.109) — Linode hub, Nginx proxy, WireGuard hub
  • srv-a (10.0.0.11) — dev Proxmox host, runs LXC 1105 (this container)
  • mcp.civicus.us — gitea-mcp server, gives Claude chat read-only access
  • Giteahttps://gitea.barternetwork.us (owner: TheRON)
  • TESSERA — H3-based physical world model, same grid OTIVM uses for waypoints

Aliases

Location Alias Effect
root@otivm-dev ~/.bashrc webmin-on systemctl start webmin
root@otivm-dev ~/.bashrc webmin-off systemctl stop webmin
otivm@otivm-dev ~/.bashrc work cd ~/OTIVM && claude

Design Notes

The game carries quiet atmospheric references to Mesolithic cultures (Maglemosian, Ertebølle, Sauveterrian, Azilian) through trade goods, place names, and merchant journal entries. These are never labelled academically — they are atmosphere only. The amber road, the northern forests, the flint from the Pyrenean foothills.

The Roman merchant's journey: Ostia → Capua → Brundisium → Carthago → Alexandria. Five chapters. Five trade routes. Releases OTIVM-I through OTIVM-X build on this foundation — see docs/roadmap.md.


Current Development State — updated 2026-04-25

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.jsx manages screen state — ledger and map
  • Both screens stay mounted — no state lost on switch

OTIVM-II — complete

  • src/screens/Map.jsx created and committed
  • Mediterranean SVG map, equirectangular projection, bounding box 5°E38°E / 28°N48°N
  • 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
  • Land outline is a rough placeholder polygon — replaced in a later release
  • App.jsx updated to import and render Map component
  • Map screen CSS added to App.css
  • Build and PM2 restart still required on container — code is committed but dist/ has not been rebuilt yet

Architecture decisions locked

  • H3 IDs on all waypoints — permanent, TESSERA-compatible
  • constants.js / gameState.js / api.js separation — 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
  • Mediterranean map replaced in a later release (Azgaar bridge planned OTIVM-VII)

OTIVM-III — not yet defined

  • To be discussed in Claude chat before any code is written

Backup and Archive Procedure

Backups are vzdump snapshots of LXC 1105 taken on srv-a (the Proxmox host). Every backup must be documented in docs/archives.md immediately after it is taken. Never take a backup without documenting it. Never document a backup that was not taken.

When to take a backup

Take a backup at these moments, in this order:

  1. Before starting any new release (e.g. before OTIVM-II work begins)
  2. After completing and confirming a release milestone
  3. Any time the human requests one explicitly

How to take a backup (root shell on srv-a)

vzdump 1105 --compress zstd --storage local --mode snapshot

Wait for it to complete. Note the filename from the output — it follows the pattern: vzdump-lxc-1105-YYYY_MM_DD-HH_MM_SS.tar.zst

Copy the archive to USB immediately after. The human does this manually.

How to document a backup

After the backup is taken and confirmed, append a new entry to docs/archives.md. The entry must follow the exact format of existing entries in that file. Each entry must include:

  • Filename, date, size, container, host, storage locations
  • Exact state of the container at time of archive — what is installed, what is committed, what is NOT yet present
  • What the archive is good for — what a restoring agent would need to do after restore
  • The exact pct restore command

How Claude Code handles this

Claude chat produces the docs/archives.md append as a downloadable file. The file header instructs Claude Code to append to docs/archives.md. Claude Code appends exactly the content given, commits, and pushes.

Restore (root shell on srv-a)

pct restore 1105 /var/lib/vz/dump/FILENAME.tar.zst --force

After restore: pull latest from Gitea, run npm install, npm run build, pm2 restart otivm, pm2 save. The game will be live again.

Full archive inventory and restore notes: docs/archives.md


CLAUDE.md — OTIVM — TheRON Claude Code implements. Claude chat designs. The human decides.