352 lines
13 KiB
Markdown
352 lines
13 KiB
Markdown
# 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)
|
||
|
||
```bash
|
||
pm2 restart otivm
|
||
pm2 list
|
||
pm2 logs otivm --lines 30 --nostream
|
||
pm2 save
|
||
```
|
||
|
||
---
|
||
|
||
## Git Workflow
|
||
|
||
```bash
|
||
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
|
||
- **Gitea** — https://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°E–38°E / 28°N–48°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)
|
||
|
||
```bash
|
||
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)
|
||
|
||
```bash
|
||
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.*
|