Closes five of lane AI2's open items and corrects two published claims. The stepping order: StrategyServer::ResumePlaying 0x007ddc90 walks the player array in INDEX ORDER and raises SEResumePlaying at each live player; the app callback StrategyApp::OnClientEvent 0x00838e10 delivers it inline for humans and appends it, deduplicated, to the pending queue for AI players. So the AI players are stepped in save player order, each at most once -- computable from a save with no live measurement. AI2's search missed it because 0x00b29f98 is not a pointer to the StrategyApp, it IS the StrategyApp: the enqueue writes the absolute member address 0x00b29fb4 and never materialises the object base. The enqueue also has zero direct callers and no vtable slot -- its address is stored into StrategyServer+0x170 by CreateGame. Lane B6's third blind spot, twice over. The two passes: `pass` is a tier index, not a plan/act switch. 0x006abb2a picks between two per-candidate quota fields -- tier 0 takes +0x10, tier 1 takes +0x14 -- and the hub loops `for (i = 0; i <= pass; ++i)`. Every order-emitting exit is gated pass == 1 (0x006bbd50, 0x006c16c0, 0x006cea50), so pass 0 claims each task's minimum force in priority order and WRITES NOTHING. That halves the ModCount arithmetic. Corrections: - AI2 §4.2: the nine "no order method" tasks are not planners. All nine emit orders at depth 4-9; the depth-4 cut hid it. AITColonize reaches list 7 and AITBuildPoliceShips reaches list 3, which is what their names promise. - AI2 §3: the two priority overrides' flag polarity is inverted. The tunable applies when bit 0 of +0x4 is SET, and "committed" is not a supported name. - AI2 §10.6: the .data invade tunables have no loader. 650 and 750, image constants, exactly one reader each and no writer anywhere. Also: slot 12 named (a preemption permission, 0x006a8d20), slot 13's consumer found (0x00696620, a range budget -- and the Zuul are exempt, a FOURTH independent cross-check on AI2's species reading), slot 11's dispatch located. ServerPlayer+0xf9/+0xfa -- the two bytes that decide whether a player is AI-controlled -- sit in a hole in the serialised layout and are NOT in the save; their writer is unfound and is the biggest remaining hole. Static only; nothing here has run under an instrument. 160 indirect call sites inside the AI closure are unresolved, so reachability is still a lower bound. Four predictions with falsifiers in §7. ghidra/addresses.d/lane-ai3.json: 15 entries, 1,105 -> 1,120, no duplicates, validated to a scratch path. |
||
|---|---|---|
| .. | ||
| lane-a.json | ||
| lane-a2.json | ||
| lane-ai1.json | ||
| lane-ai2.json | ||
| lane-ai3.json | ||
| lane-b5.json | ||
| lane-b6.json | ||
| lane-c3.json | ||
| lane-d2.json | ||
| lane-e1.json | ||
| lane-e3.json | ||
| lane-g2.json | ||
| lane-h.json | ||
| lane-i.json | ||
| lane-j.json | ||
| lane-k.json | ||
| lane-n.json | ||
| lane-o.json | ||
| lane-p2.json | ||
| lane-q.json | ||
| lane-t.json | ||
| lane-t2.json | ||
| lane-u.json | ||
| lane-v2.json | ||
| lane-w.json | ||
| lane-w2.json | ||
| lane-z.json | ||
| README.md | ||
Per-lane address fragments
addresses.json is a single shared file. When several lanes run concurrently they edit the
same lines, and three times on 2026-09-08 one lane's git add swept another's in-flight
entries into the wrong commit. Nothing was lost, but authorship and atomicity were.
A lane may instead drop its own file here:
{ "entries": [ { "name": "...", "addr": "0x...", "convention": "...",
"prototype": "...", "status": "verified", "source": "findings/..." } ] }
Name it after the lane (lane-d.json). tools/gen_addresses.py merges every fragment in
sorted order after addresses.json. A duplicate name across files is a hard error, not
last-wins — two lanes disagreeing about an address is exactly the thing we must not paper over.
The integrator folds fragments back into addresses.json once the lane's work is merged.