Files
otivm/CLAUDE.md

352 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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°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)
```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.*