From 0253941d1648e1aa8593b89af5938170b90262f9 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 8 Sep 2026 11:42:25 -0400 Subject: [PATCH] board: lane Y generator gap measured and attributed; BnkEl inversion oracle; divisor defect understated --- campaign/board.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/campaign/board.md b/campaign/board.md index 2a38b2b..6e1cca3 100644 --- a/campaign/board.md +++ b/campaign/board.md @@ -188,3 +188,9 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | WHY NO SWEEP FOUND IT: the virtual edge | meta | verified | high | 100% | 2026-09-08 | ZERO direct calls to 0x00893290 exist in the image; its only reference anywhere is the vtable slot. The dispatch is `call edx` through slot 10 at 0x007d8469, inside DetectEncounters - ONE INSTRUCTION BEFORE the direct call lane I's closure DID follow. LANE I'S INVENTORY IS NOT WRONG: its tempering-scan recall claim is intact and its 22-site list is EXPLICITLY a direct-edge closure, with the indirect-call caveat stated as loudly as the result. This is the demonstration that the caveat was LOAD-BEARING - the single largest RNG consumer of a strategic turn hangs off a virtual edge inside a function the closure already contained. THE LESSON: a closure over direct edges is a lower bound, and saying so is what let the next lane find this in one step | | the repo had already met this function | meta | verified | high | 100% | 2026-09-08 | strategic-turn-internals.md line 153 ALREADY listed 0x00893290 as "raid encounter generation" against these exact three StrategyVars. What was missing was not the identification but THE CONNECTION TO THE LEDGER - nobody knew it was where a turn's RNG goes. Worth remembering when the next "unknown" turns up: search the notes before the binary | | rule 17, third independent instance | meta | verified | high | 100% | 2026-09-08 | Ghidra reports 1532 bytes for GenerateTradeRaidEncounters; the real body is 1546, ending 0x0089389a, and Ghidra's end lands MID-INSTRUCTION. Same failure lane I hit on 0x007aa240 and lane J on the combat resolver. Three lanes, three functions, same defect | +| standalone generator: NO MATCH, residual attributed | verify | mapped | high | 100% | 2026-09-08 | A CLEAN BINARY NO, which is the useful kind. On lane Z's two calibrated pairs: oracle 20 words / standalone 16 / RESIDUAL 4 (left 379 vs 375), and oracle 18 / standalone 16 / residual 2 (left 359 vs 357). mt[624] IDENTICAL both times, block byte-identical (no boundary crossed) - ONLY `left` differs. The residuals 4 and 2 are EXACTLY lane Z's per-site split for those turns (2 detection words both, +2 research on turn 5, +0 on turn 6), so subtracting a statically-bounded 16 from the FILE oracle corroborates the site ledger THROUGH A ROUTE TOUCHING NO HOOK. Stated caveat: a subtraction confirms the SUM, not each term; the static bound carries the 16. New instrument tools/rng_oracle_check.py. MODELLED: GenerateTradeRaidEncounters, 2 Chance rolls per player-vector entry, counted INSIDE the chance helper past both early-outs, so a data file pushing an odds value to 0 or 1 removes the draw and the ledger says so (one test pins that). NOT modelled and why: the 2 detection words (NO DERIVED RULE - fitting 2/turn to three observations is not a model), the 2 research words (downstream of lane N's blocked budget term), the 0-or-1 raid-success draw | +| T36 FinalizeTurnRecords recovered | control-flow | verified | high | 95% | 2026-09-08 | Lane Y: the archive it writes IS ON THE WIRE (/Sim/turnstats/.../stats[], one element per turn per player, every save carries its own frame). Six fields recovered and checked against bytes THE GAME WROTE: 11 saves, 80 player-records, 480 fields, 0 MISMATCHES, and the same check now runs inside every turn as a phase note. TWO CORRECTIONS to combat-done-tail.md 5: `inc`/`tdinc` are THE OPPOSITE WAY ROUND from the obvious reading, and lane K's open population term is `Pop + pbon` (112/112) not `Pop + dcs` (101/112). The block is now a MEASUREMENT: --commit-blocked closes 24 turnstats leaves and REGRESSES 17 (almem x8, inc x3, sav x3, 3 census, nothing outside the unmodelled set) - so it is NOT "committing makes it worse", it is right in six fields and wrong in five, and becomes a clean +24 once S04 and the budget land | +| BnkEl INVERTS: a per-save oracle for lane N's blocker | verify | verified | high | 100% | 2026-09-08 | THE HIGHEST-LEVERAGE FIND OF THE LANE. BnkEl is injective in maxIncome (slope ~6.67) so it inverts: 25 player-records recovered across the corpus (e.g. turn1-state player 16 -> 238592). That value IS `sum of max(ComputeMaxIncome(s),0)` - the exact population->output term lane N is chasing - available as a PER-SAVE ORACLE NEEDING NO VM, read from bytes the game itself wrote. Tool: tools/max_income_oracle.py. The same inversion independently recovers BANKRUPTCY_PROTECTION_LIMIT_FACTOR = 3.3 from saves (all 25 in [3.299994751, 3.299999872]), corroborating lane K's "3.3 is DB-loaded, not a binary constant" WITHOUT READING THE DATA FILES. Routed to lane N mid-flight | +| CORRECTION: the -0.15 divisor defect is much worse than noted | verify | verified | high | 100% | 2026-09-08 | The repo characterised the wrong bankruptcy divisor as roughly "one ulp on large empires". THAT UNDERSTATEMENT IS WRONG - and lane Y corrected its OWN prediction which had repeated it. `-0.15` and the real `-0.15000000596046448` differ for EVERY maxIncome DIVISIBLE BY 3, from m=3 (-20 vs -19), and the rate climbs: 33.3% below ~1.2M, 66.7% at ~1.4M, **100% above 3M**. SIX OF THE 25 CORPUS RECORDS would get a different BnkEl from the engine's current constant than the game wrote. One-line fix in economy.cpp wanting a test at maxIncome=3. Lane Y reported it and did NOT touch the file - it is lane N's | +| re-ranked path to the byte-match | meta | mapped | high | 100% | 2026-09-08 | Lane Y's ranking, superseding lane S2's now that the RNG ledger is closed: (1) PER-SYSTEM MONEY OUTPUT - now has a per-save oracle - unblocks P01/P02/P05/P06/T31 and 5 of 11 record fields, ~82 of 204 leaves and 2 of the 4 missing words; (2) S04 alliance mask - 8 of the 17 leaves stopping T36; (3) encounter detection - the last non-research generator residual, HOOK-SHAPED; (4) ship-design catalogue (hull size + 0x400) - census, ship records, part of T24; (5) ModCount - ONE leaf, but it feeds Summary.Checksum, so NO BYTE-MATCH WITHOUT IT | +| three hook-shaped questions for the VM holder | verify | backlog | — | 0% | 2026-09-08 | Lane Y raised these per rule 18: bracket DetectEncounters with contact/detector counts (settles the last non-research generator residual); hook CreateRaidEncounter to separate "no roll succeeded" from "candidate list empty" (a word count CANNOT); and run one ZUUL End Turn - THE STANDALONE PREDICTS 14 WORDS THERE, NOT 16, which tests what the raid loop actually iterates over. All three routed to lane N mid-flight |