Files
coorl-lost-cities/AGENTS.md
T
coolguyandClaude Opus 4.7 b561fa8457 Document run comparison protocol: sequential, single seed, shared tag
Default is one baseline + one treatment, sequential, same seed, with a
shared --wandb-tag hypothesis label for W&B Compare Runs filtering.
Multi-seed only on explicit request; never run two trainings on the same
GPU.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:56:17 +09:00

352 lines
10 KiB
Markdown
Raw 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.
# AGENTS.md
This repository is managed with `uv`. Use `uv run ...` for commands so the
project environment and Cython extensions are built/loaded consistently.
## Project Layout
- `src/coolrl_lost_cities/games/classic/game.pyx`: Cython Lost Cities engine.
- `src/coolrl_lost_cities/games/classic/deep_cfr/`: Deep CFR training,
traversal, evaluation, analysis, and CLI code.
- `configs/deep_cfr/`: Deep CFR YAML configs (kebab-case filenames).
- `runs/`: generated training runs. Gitignored, may be a symlink to larger
storage. Layout:
- `runs/archive/`: past runs. **Do not modify or delete.**
- `runs/tmp/`: smoke, tests, throwaway. Free to `rm -rf` anytime.
- `runs/<YYYY-MM-DD_HHMMSS>_<kebab-name>/`: real experiments (flat).
Promote a `runs/<...>` directory to `runs/archive/` with a manual `mv`
once analysis is complete.
- `docs/`: profiling notes, migration notes, and experiment documentation.
## Core Commands
Run lint:
```bash
uv run ruff check .
```
Run all tests:
```bash
uv run pytest -q
```
Run focused Deep CFR tests:
```bash
uv run pytest -q tests/games/classic/test_deep_cfr_trainer.py
```
Run the CLI through the console script:
```bash
uv run lost-cities-deep-cfr --help
```
Equivalent module form:
```bash
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.cli --help
```
## Deep CFR Training
The CLI auto-derives the run directory from `run.experiment_name` plus a
timestamp. By default runs land under `runs/tmp/`; pass `--keep` for a real
experiment that should live under `runs/`.
Smoke / throwaway run (lands in `runs/tmp/`):
```bash
uv run lost-cities-deep-cfr train --config configs/deep_cfr/smoke.yaml
# → runs/tmp/<YYYY-MM-DD_HHMMSS>_smoke/
```
Real experiment (lands in `runs/`):
```bash
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep-cfr-selfplay-full-depth-slot-playability.yaml \
--keep
# → runs/<YYYY-MM-DD_HHMMSS>_lost-cities-deep-cfr-selfplay-full-depth-slot-playability/
```
Variant of the same config (override slug):
```bash
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep-cfr-color-shared-attention-512x3.yaml \
--keep \
--set run.experiment_name=color-attn-v2
# → runs/<YYYY-MM-DD_HHMMSS>_color-attn-v2/
```
Short fixed-iteration run:
```bash
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep-cfr-selfplay-full-depth-slot-playability.yaml \
--set run.max_iterations=100 \
--set run.max_minutes=null \
--set checkpoint.save_every=0
```
Resume (path required, no shortcut):
```bash
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep-cfr-selfplay-full-depth-slot-playability.yaml \
--resume runs/<YYYY-MM-DD_HHMMSS>_<slug>/latest.pt
```
When `--resume` is given, the trainer reuses the resumed checkpoint's parent
directory; no new timestamped folder is created.
Useful train controls:
- `--keep`: real experiment, write under `runs/` (default is `runs/tmp/`).
- `--resume PATH`: resume from a specific checkpoint. `PATH` is required.
- `--set PATH=VALUE`: override config fields. Repeatable, parses values as
YAML (e.g. `--set traversal.num_workers=4`, `--set run.max_minutes=null`).
Common `--set` overrides:
- `--set run.device=cuda`: set the trainer device.
- `--set run.experiment_name=foo-v2`: change the slug used in the run dir
name (kebab-case).
- `--set checkpoint.exact_resume=true`: require checkpoint config compatibility.
- `--set checkpoint.save_latest=false --set checkpoint.save_every=0`:
disable checkpoint writes.
- `--set checkpoint.save_every=0`: keep only `latest.pt` (no archives).
- `--set checkpoint.save_every=N`: archive every N iterations.
## Naming Conventions
- **Directory names, run dirs, config filenames, `experiment_name` values**:
kebab-case (`deep-cfr-color-shared-512x3.yaml`,
`runs/2026-05-08_103045_color-attn-v2/`).
- **YAML keys, Python identifiers, config field names**: snake_case
(unchanged: `hidden_size`, `traversals_per_player`, `experiment_name`).
- The CLI converts `run.experiment_name` to a kebab slug when building the
run directory, so values may contain spaces or mixed case.
## Long Runs
Run long jobs in a real `tmux` session so the user can attach and stop them.
Do not rely on Codex command sessions for long user-observable training runs.
Start a long unbounded run:
```bash
tmux new-session -s coolrl-deepcfr-unbounded \
-c /home/coolguy/dev/coolrl-lost-cities \
'uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep-cfr-selfplay-full-depth-slot-playability-unbounded.yaml \
--keep'
```
Attach later:
```bash
tmux attach -t coolrl-deepcfr-unbounded
```
Detach without stopping:
```text
Ctrl+B, D
```
Stop training:
```text
Ctrl+C
```
Follow logs from another terminal:
```bash
tail -f runs/<YYYY-MM-DD_HHMMSS>_<slug>/train.log
```
The unbounded config intentionally has:
```yaml
run:
max_iterations: null
max_minutes: null
checkpoint:
save_every: 100
```
`latest.pt` is updated continuously; archive checkpoints are written every 100
iterations. If disk is tight, set `--set checkpoint.save_every=0` (keep only
`latest.pt`) or increase `save_every`.
## Evaluation And Analysis
Evaluate a checkpoint:
```bash
uv run lost-cities-deep-cfr eval \
--checkpoint runs/<run-dir>/latest.pt \
--opponent random \
--games 100 \
--device cpu
```
Save evaluation game records:
```bash
uv run lost-cities-deep-cfr eval \
--checkpoint runs/<run-dir>/latest.pt \
--opponent random \
--games 100 \
--device cpu \
--save-games runs/<run-dir>/eval_random_games.json
```
Generate analysis plots from `metrics.jsonl`:
```bash
uv run lost-cities-deep-cfr analyze \
--run runs/<run-dir>
```
Write plots to a separate directory:
```bash
uv run lost-cities-deep-cfr analyze \
--run runs/<run-dir> \
--output-dir runs/<run-dir>/analysis
```
The analyzer reads `metrics.jsonl` and writes PNG files grouped by diagnostic
section. Opponents are compared within each plot using fixed colors. The
`lost-cities-deep-cfr analyze` subcommand uses the analyzer default smoothing
window, currently 1 iteration (no smoothing), and supports `--max-iteration`.
For smoothing controls, run the analyzer module directly:
```bash
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.analyze \
--run runs/<run-dir> \
--smoothing-window 5
```
Use `--no-smoothing` to force no moving average.
Current output files:
- `analysis_01_loss.png`
- `analysis_02_match.png`
- `analysis_03_action.png`
- `analysis_04_gameflow.png`
- `analysis_05_open_quality.png`
- `analysis_06_expedition_outcomes.png`
- `analysis_07_calibration.png`
- `analysis_08_traversal.png`
- `analysis_09_selectivity.png`
- `analysis_final_eval_summary.png`
## Runtime Artifacts
Each training run writes:
- `metrics.jsonl`: structured metrics, one completed iteration per line.
- `train.log`: human-readable timestamped logs.
- `runtime_progress.json`: latest progress snapshot.
- `latest.pt`: latest checkpoint.
- `iteration_*.pt`: archive checkpoints when enabled.
- `config.json`: resolved config for the run.
If a run is stopped mid-iteration, the in-progress iteration may not appear in
`metrics.jsonl`. Analyze the latest completed metric row.
## Weights & Biases (Optional)
Metrics can be mirrored to W&B. `wandb` is an optional extra; default
installs and runs do not require it.
Install:
```bash
uv sync --extra wandb
```
Run with W&B:
```bash
# Offline: no login, writes to <run_dir>/wandb/offline-run-*/
uv run lost-cities-deep-cfr train --config <...> --wandb --wandb-mode offline
# Online: requires `uv run wandb login` once, then real-time upload
uv run lost-cities-deep-cfr train --config <...> --wandb
```
W&B data is stored **per run** at `<run_dir>/wandb/`, not at a global
`runs/wandb/`. Each training run gets its own subfolder, so moving or
deleting a run directory carries its W&B data along with it.
Sync offline runs to wandb.ai later:
```bash
wandb sync runs/<run-dir>/wandb/offline-run-*
```
Flags:
- `--wandb`: enable W&B mirroring.
- `--wandb-project <name>`: defaults to `coolrl-lost-cities`.
- `--wandb-name <name>`: W&B run name; defaults to `run.experiment_name`.
- `--wandb-mode {online,offline,disabled}`: default `online`.
- `--wandb-tag <tag>`: tag the run; repeatable.
W&B is purely additive — `metrics.jsonl` remains the source of truth, and
`analyze` reads `metrics.jsonl`, not W&B. Disabling W&B never breaks
training, resume, or analysis.
### Notes and tags
Use `--wandb-notes` for the run's *purpose* (free-form prose) and
`--wandb-tag` for *categories you might filter on later* (short kebab-case
keywords, repeatable). Otherwise use them however you like. Just avoid:
- Tags that duplicate `config` (`lr-1e-4`, `traversal-280`) — W&B already
indexes config fields.
- Tags that are unique per run (`test-1`, `2026-05-07`) — that's the run
name and timestamp's job.
- Tag-as-sentence (`tested-bigger-traversal-with-lcfr`) — that belongs in
`--wandb-notes`.
**Notes length**: 35 lines, commit-message-body length. Should answer
*why* (hypothesis), *what* (key config delta), and *baseline* (run/iter
to compare against). Long analyses go in `docs/` and are linked from
notes; don't paste them in.
### Comparing two runs
Default is **sequential, single seed**. Run baseline first, then the
treatment with exactly one config change, both with the same `run.seed`.
Tag both with a shared hypothesis tag (e.g. `--wandb-tag lr-bump`) so
they show up together in W&B's Compare Runs view.
Do **not** run multiple seeds per condition unless explicitly asked —
that doubles or quintuples wall-clock and isn't the default protocol.
Single-seed comparison is enough to surface a signal; multi-seed is a
follow-up to confirm it.
Do **not** run two trainings in parallel on the same GPU — VRAM/SM
contention slows both unevenly and breaks the comparison.
## Notes For Future Agents
- Prefer `rg`/`rg --files` for search.
- Use `apply_patch` for manual edits.
- Do not commit generated run artifacts from `runs/`.
- Cython-generated `.c` files are gitignored; edit `.pyx`/`.pxd` sources.
- Before committing, run `uv run ruff check .` and at least the relevant pytest
subset. For Deep CFR changes, run
`uv run pytest -q tests/games/classic/test_deep_cfr_trainer.py`.