For agents, on top of README.md, which they read first.
- Never edit the bot. A behaviour the host needs is a change here, in
shim/host.c, or a compiler flag in../../third-party/c/BUCK- with the evidence in../../research/zbanks-alttp.md. Only a change no host can make from outside becomes a carried patch, by the rules in../../third-party/c/CLAUDE.md. - A new
rando::PATCHESrow changes the cartridge CRC: everyhomestate made before it is refused. Remake them (apps/zbanks --make-home, the window'sroms/<rom>.states/included). - One bot per process. Its state is C globals;
Bot::startpanics on a second call. Do not try to run two for comparison in one process. - The pointers
base()returns are cached by the bot for ever (ap_snes.c:60-61). The WRAM mirror and ROM copy are leaked on purpose and must never move or be freed; everything reaches the console by copying through them intick,loadandsave. - A new ROM table read by the bot is a Japanese address. Find the table's
bytes in
~/src/github.com/spannerisms/jpdasm, locate them in the USA ROM, check the whole table matches, and add aJP_TO_USrow. rando::patch_romchanges the cartridge's CRC, which every snapshot records: states for a bot must be made from the patched image.- Not wasm: the C and the signal handler make it native-only, and
src/outcomes.rswritesgoal_outcomes.jsonl. States still come through theStatestrait, neverstd::fsinlib.rs. - The given-up watch (
zb_watch_goals) reads goals after they leave the list. That is safe only because upstream never frees one:ap_goal_failandap_goal_completeboth justLL_EXTRACT(ap_plan.c). A refresh from upstream that starts freeing goals turns this into a use-after-free; check those two functions on every refresh. - Permafailed vs completed is
attempts > 3, upstream's own threshold inap_goal_fail. Three things test it in the shim and the crate now: the given-up watch above,zb_completed_count(recovery::Recovery's progress signal), and the outcome ringzb_note_outcomefills (Outcome::completed). If a carried patch or a refresh changes that threshold, change all of them. src/stall.rsandsrc/recovery.rsare shared by the window and headless. They depend on//packages/alttp:alttp(forstall.rs'salttp::Place) but nothing app-specific - no window, no MCP, no filesystem.apps/nativeused to keep its own copy ofstall.rs; it now doesuse zbanks::stall;, so there is one definition of "stalled" for bothapps/nativeandapps/zbanks, not two that can drift.- A goal's identity string is duplicated by convention in three places:
decisions::goal_choice::identity,shim/host.c'szb_note_outcome, and whatoutcomes.rsstores ("{kind}|{node}|{screen}"). This crate cannot depend ondecisions(the dependency runs the other way). Change all three together or a restart's outcomes stop matching the goals offered.