jevsnes.git / packages / zbanks / CLAUDE.md
1@README.md
2
3- **Never edit the bot.** A behaviour the host needs is a change here, in
4  `shim/host.c`, or a compiler flag in `../../third-party/c/BUCK` - with the
5  evidence in `../../research/zbanks-alttp.md`. Only a change no host can
6  make from outside becomes a carried patch, by the rules in
7  `../../third-party/c/CLAUDE.md`.
8- **A new `rando::PATCHES` row changes the cartridge CRC**: every `home`
9  state made before it is refused. Remake them (`apps/zbanks --make-home`,
10  the window's `roms/<rom>.states/` included).
11- **One bot per process.** Its state is C globals; `Bot::start` panics on a
12  second call. Do not try to run two for comparison in one process.
13- **The pointers `base()` returns are cached by the bot for ever**
14  (`ap_snes.c:60-61`). The WRAM mirror and ROM copy are leaked on purpose and
15  must never move or be freed; everything reaches the console by copying
16  through them in `tick`, `load` and `save`.
17- **A new ROM table read by the bot is a Japanese address.** Find the table's
18  bytes in `~/src/github.com/spannerisms/jpdasm`, locate them in the USA ROM,
19  check the whole table matches, and add a `JP_TO_US` row.
20- **`rando::patch_rom` changes the cartridge's CRC**, which every snapshot
21  records: states for a bot must be made from the patched image.
22- Not wasm: the C and the signal handler make it native-only, and
23  `src/outcomes.rs` writes `goal_outcomes.jsonl`. States still come through
24  the `States` trait, never `std::fs` in `lib.rs`.
25- **The given-up watch (`zb_watch_goals`) reads goals after they leave the
26  list.** That is safe only because upstream never frees one:
27  `ap_goal_fail` and `ap_goal_complete` both just `LL_EXTRACT` (ap_plan.c).
28  A refresh from upstream that starts freeing goals turns this into a
29  use-after-free; check those two functions on every refresh.
30- **Permafailed vs completed is `attempts > 3`**, upstream's own threshold in
31  `ap_goal_fail`. Three things test it in the shim and the crate now: the
32  given-up watch above, `zb_completed_count` (`recovery::Recovery`'s progress
33  signal), and the outcome ring `zb_note_outcome` fills (`Outcome::completed`).
34  If a carried patch or a refresh changes that threshold, change all of them.
35- **`src/stall.rs` and `src/recovery.rs` are shared by the window and
36  headless.** They depend on `//packages/alttp:alttp` (for `stall.rs`'s
37  `alttp::Place`) but nothing app-specific - no window, no MCP, no
38  filesystem. `apps/native` used to keep its own copy of `stall.rs`; it now
39  does `use zbanks::stall;`, so there is one definition of "stalled" for
40  both `apps/native` and `apps/zbanks`, not two that can drift.
41- **A goal's identity string is duplicated by convention in three places**:
42  `decisions::goal_choice::identity`, `shim/host.c`'s `zb_note_outcome`, and
43  what `outcomes.rs` stores (`"{kind}|{node}|{screen}"`). This crate cannot
44  depend on `decisions` (the dependency runs the other way). Change all
45  three together or a restart's outcomes stop matching the goals offered.