diff --git a/guides/method-rules.md b/guides/method-rules.md index da903a7..beccb84 100644 --- a/guides/method-rules.md +++ b/guides/method-rules.md @@ -186,3 +186,28 @@ coverage 89% -> 100%. Ghidra's size is also simply wrong sometimes — it gave 7 that is 7,641 and ends mid-instruction. **Never trust a function's end. Disassemble to the next function start and find the real boundary.** + +## 18. Measure first. The lab exists. + +We have the game running under a hook framework, a debugger, 11 curated saves, a byte-identical +autosave oracle and a state-checksum tool that localises to named leaves. **Use them before deriving +anything.** + +The RNG gap is the case study. Static analysis spent several lanes on it and produced a complete +draw-site inventory with an unexplained count — genuinely good work that could not close. The answer +came from ten minutes of live hooking: return-address capture on the entry points attributed every +word in one turn, and the dominant consumer turned out to hang off a **virtual** edge that no +direct-edge closure could ever have reached. + +The general shape: **static reading is for explaining what you measured, not for predicting it.** +Derivation is the fallback when the path cannot be reached, not the default. + +A question of the form *"what writes this?"*, *"how many times does this run?"*, *"does this branch +ever fire?"* is a **watchpoint or a hook**, not a week of reading. Open items that fit that shape +right now: the `Player.Status` writer between tail phase 31 and the autosave; the `TShn`/`ltis` +writer; `Summary.Checksum`'s inputs; whether a trade-raid roll ever succeeds (a word count cannot +separate "no success" from "empty candidate list" — a hook on the callee separates them instantly). + +Corollary, from the same session: **search the notes before the binary.** The dominant RNG consumer +was already identified in `strategic-turn-internals.md` months of lanes earlier. What was missing was +never the identification — it was the connection.