1@README.md 2 3- **A new Jev call is a new module implementing `Decision`, never an ad-hoc 4 request built somewhere else.** If a call site wants to ask Jev something 5 and there is no decision kind for it yet, the fix is a new 6 `src/<kind>.rs` (Facts/Answer/Memory/Key/Decision impl) plus one 7 `Engine<YourKind>`, not a request built inline against `jev-http` 8 directly. 9- **Off must stay upstream.** With no chooser installed the goal-choice 10 hook is NULL and the bot is byte-for-byte upstream's; a change here must 11 never install one implicitly. The apps default to off. A future decision 12 kind that drives some other part of the bot owes the same guarantee. 13- **Every failure is upstream's pick, never a stop.** `jev_http::Jev::ask` 14 refuses on the self-clearing guards (`Failure::Throttled`) and on what 15 only a human can clear (`Failure::Budget`: the vendor said out of credit, 16 or the ledger cannot be read; there is no self-imposed lifetime cap since 17 2026-09-22); `Engine::ask` always falls back to 18 `Decision::fallback` and the bot plays on. Do not retry around either, and 19 do not sleep for a throttle: `Engine` records `retry_in` and simply does 20 not ask again before it (`throttled_until`). 21- **`goal_choice`'s option order is the C shim's**: index 0 is upstream's 22 own pick, the rest cheapest first, and `IDS` is fixed at six because the 23 shim never offers more. The panel, the log, and `Chooser::overrides` 24 (goal `!= 0` means Jev's pick differed) rely on this. 25- Native only (this crate links the C bot and a socket, through `zbanks` 26 and `jev-http`); never add it to a wasm build. A `packages/` crate that 27 needs the network or a window is the exception here, same reasoning as 28 `zbanks`/`mcp`/`replay` - see `../CLAUDE.md`. 29- The key only ever arrives through `op-env-run`; see the repo CLAUDE.md. 30- **`Record<A>`'s JSON shape IS the log's schema** - there is no second, 31 hand-written mapping to keep in sync. Adding or renaming a field on a 32 decision's `Answer` or on `How` changes what a line looks like on disk; 33 `Engine::load_history` already treats a line it cannot parse, or whose 34 `kind` does not match, as belonging to some other kind or an incompatible 35 shape (skipped, not an error), so this is safe to evolve - but a field 36 rename silently stops matching every line written before it, which is a 37 real (if harmless) loss of that window's seeded history, not a compile 38 error to catch it. 39- **`kind`'s default (`default_kind` in `src/lib.rs`) is hardcoded to 40 `"goal_choice"`, not left generic.** Every line ever written before this 41 crate had more than one decision kind WAS a goal choice, so that is the 42 one sound default for an old line with no `kind` field. Do not change it 43 to something "more neutral" - there is no neutral answer for what an old 44 line meant, only the correct one. 45- **`Engine::history()`/`load_history()` are bounded by `HISTORY_CAP`; the 46 log they read from and write to is not.** Raising the cap only changes 47 how much a restart re-seeds into memory; it does not recover anything, 48 the full past is already on disk in the log either way. 49- **`goal_choice::Chooser::overrides` is not part of the shared `Totals`.** 50 Whether an `Answer` differs from "upstream's own pick" depends on that 51 decision kind's own convention (goal index 0 here); `Engine`/`Totals` 52 stay honestly generic and do not guess at it. A future decision kind with 53 its own notion of "differed from the default" keeps that count the same 54 way - beside its own `Chooser`-equivalent, not bolted onto `Totals`. 55- **`Decision::Memory` is deliberately not always `Decision::Answer`.** 56 `goal_choice` keeps a probability per underlying goal IDENTITY 57 (`kind|node|screen`), not per the sentence built for one particular call, 58 because the next call's offer grouping can name the same goals with 59 different wording once Link has moved. A decision kind whose reuse really 60 is "the exact same answer, unchanged" can set `Memory = Answer` and have 61 `reuse` ignore the sampling closure. 62- **`goal_choice::Facts` reaches SRAM/WRAM without touching `packages/zbanks`** 63 (`goal_choice::Snapshot`, read from `console.wram()`): the CALLER 64 (`apps/native`'s tick, `apps/zbanks`'s loop) calls `Chooser::set_snapshot` 65 once a frame, BEFORE `zbanks::Bot::tick`, since the C shim's goal-choice 66 hook can fire synchronously inside it. This is deliberate - the 67 `zbanks::GoalChooser` trait's signature is not this module's to change, 68 and a second, purely-additive channel avoids ever needing to. A caller 69 that forgets to call `set_snapshot` is not a compile error (the field 70 defaults to all-zero), so a NEW caller of `goal_choice` must be checked 71 against this, not assumed to inherit it. 72- **`Facts::sentences()`, not `words::offers()`'s own sentences, is what 73 goes to Jev and into `Answer`.** It appends vanilla dungeon knowledge 74 (`DUNGEON_LORE`) when an offer's sentence already names a place the bot 75 resolved - never for "somewhere never visited", which is honestly all 76 that is known about an unexplored door. Extend `DUNGEON_LORE`, not 77 `words.rs`, for a new named place: the wording change stays isolated 78 here (user, 2026-09-22). 79- The behaviour-preservation test in `goal_choice.rs` 80 (`the_request_sent_to_jev_is_unchanged_by_the_rewrite`) compares against a 81 request captured from the pre-rewrite goal-choice code (the crate 82 `packages/zbanks-jev`, which this crate replaced on 2026-09-22), byte 83 for byte. Any future edit to the wording or shape of the goal-choice request 84 should update that golden deliberately, in the same commit, with a note 85 saying why the wording changed - never adjust the test to match new 86 output without reading the diff. 87- **`MODEL` is pinned (`jev-1.13.0`), not an alias.** `FAVOURITE_THRESHOLD` 88 (0.70) was tuned against it; moving the pin is a change to re-measure, not 89 a version bump.