Commit graph

14 commits

Author SHA1 Message Date
lane-l4
7e6bda3699 L4: the AI's command block, read out of the running game
findings/subsystems/ai-order-capture.md -- two workloads on VM145, every submitted
TurnCommands block dumped at the batch applier, plus sixteen pass-attributed entry
probes on the task bodies and the emission gates.

Headline: our emission model reproduces both real blocks exactly, list for list and
element for element, and both turns land on the measured ModCount of 12. What it did
not have: a list-23 element on every AI turn (the first element ever observed in the
free half of the cost table, and it really is free), client-allocated ids travelling
inside the commands (design 18, fleet 34 -- neither in the input save), and build,
rates and population all naming the same home system.

Two predictions falsified, both usefully: the batch is n = playerCount = 8 with the
four monster factions holding untouched slots, not n = 4; and the fleet order names
the fleet the client has ALREADY created, not the one in the save.

AITRaid's pass-0 question stays open and now says why -- the task never ran on either
board, so the list-16 zero is 'never entered', not 'entered and silent'.

Rule 19: the ref-turn2 control passed byte-identical to the published oracle with all
seventeen detours installed. Separately, the turn-1 workload is NOT reproducible --
three runs, three autosaves, differing in exactly one field: the research target of
the AI player that owns nothing.

ghidra/addresses.d/lane-l4.json: 9 entries (8 IAITask::Execute bodies + the list-16
order method). Raw logs and the two divergent autosaves in verify/results/shim/aiorders/.
2026-09-08 18:01:45 -04:00
alex
e4195d47d1 board: lane L5 - reference pair non-deterministic; interest literals verified with a failing control; verified column held at 0 on principle 2026-09-08 17:55:47 -04:00
alex
471d6cb973 W3: NVO.TShn's writer trapped live, its gate named, and 158/158 on the corpus
TShn is refreshed to Frame for every (system, player) that satisfies ServerSystem::IsKnownTo
0x00746390 -- IsVisibleTo OR (2-bit sensor contact at ServerSystem+0x24 AND CCC_AdvSens). Not
AFlags alone, which is why lane E3 could not fit it. The writer chain was confirmed live, frame by
frame, from a hardware watchpoint on Spica's NVO node; the refresh runs twice per End Turn (driver
phase 24 and combat-done phase 25).

Also: the trade and spy containers read out of a live game for the first time (both empty, zero
traps -- the workload confirmation two lanes lacked); Player.Status's predicate named as
ReqCL != 0 && Elim == 0, with Species != 4 shown to be a corpus coincidence; and all ten command
ModCount handlers named, two of them only reachable on a turn-1 workload.

Corrects objects/layouts.md's ServerSystem PID@0x274 row and closes system-visibility-record.md 7.

Runs: build w3tshn-bb81d3d-20260908T2007Z, configs shim.cfg.w3tshn / w3mod / w3control.
Rule 19 control passed -- the armed run reproduced the determinism oracle byte for byte.
2026-09-08 16:41:09 -04:00
alex
92ad44efb0 lane W2: multiplayer Tier 0 played end to end with no server; ModCount/Frame/Status watchpoints; rcex explained
Multiplayer: two clients on one guest, joined by typed IP and by LAN browse, launched a
2-player game and played two turns in lockstep. 44,319 packets captured outside the guest,
ZERO to any GameSpy port. Availability check fails open exactly as lane G2 read it; Join
Manually is enabled with every gamespy.com name dead, falsifying G2's own caveat (a). The
shipped MOTD is fetched live from kerberos-productions.com and tells players to host in LAN
mode -- the developer's own statement of the same finding.

Watchpoints: a new shim module arms DR0-DR3 from StrategyServer::ApplyAllTurnCommands.
ModCount takes exactly 12 writes per End Turn (lane A2's prediction, confirmed twice, both
predicted addresses exact); Frame takes exactly one, from BeginProcessTurn+0x2a, settling the
ModCount-vs-Frame naming in A2's favour. The Player.Status writer between tail phase 31 and
the autosave EXISTS and is StrategyNetworkClient::OnMessage+0xa15 -- correcting lane T2.
Rule 19 control: the armed run reproduced the determinism oracle byte for byte.

rcex: closed from the corpus alone. It is sixteen 4-bit per-player counters; nibble p is set
to 1 on the turn the system enters player p's AFlags and ticked to 0 the next turn. 7/7
across two different games.
2026-09-08 15:23:52 -04:00
alex
8ad9971f6e lane H: five live probes, the oracle restored, and a harness perturbation bisected to one hook
- determinism oracle regenerated and byte-identical (bb4fd9ac / 978041ac)
- the four phase-23/33 draw-bearing tail callees run EVERY turn; the three inner
  functions holding the draws run zero times -- the gate is inside each outer body
- CreateRaidEncounter is called (2 on one turn) and draws nothing: candidate list empty
- Zuul: 7 calls / 7 words per trade-raid Chance site, 14 not 16, as predicted
- EncounterDetect_Run receives an EMPTY record vector, so ProcessTeamRecord and
  AssignContacts never run; the 2-word detection residual is in 0x007d5150's subtree
- a MinHook detour on 0x00893290 changes the game's output; bisected over six runs.
  The un-instrumented game and lane Z's instrument agree, so lane Z's numbers stand
- lane AI1 insert: P2 held across two fresh processes, Rung B stays as written
2026-09-08 13:53:36 -04:00
alex
da823d3b36 Z: a second calibrated oracle pair -- three instruments agree on 20 words for turn 5 2026-09-08 10:45:18 -04:00
alex
1d50f1edda lane Z: the RNG ledger for one strategic turn, measured end to end
We consume 18-22 generator words per turn and model none of them as a count.
All of it is inside StrategyServer::ProcessTurn; OnAllCombatDone_Tail costs 0
on every turn observed; the residual outside the two drivers is exactly 0. The
generator does not move between turns at all, so the interval a standalone has
to reproduce is closed at both ends.

The instrument reads generator STATE, not calls, and that choice paid: the
image has four draw entry points, not three (NextUInt 0x004f7670 is in no
lane's primitive set) plus inlined draws in twelve functions, two reachable
from the turn roots. A primitive-counting hook would have undercounted
silently.

Checked against the save files independently: the turn-6 autosave pair gives
18 words read from the two Sim.RNG blobs, and with twists == 0 that number
never passes through a twist implementation -- so the two instruments do not
share the hidden assumption they could have.

Corrections to combat-done-tail.md, in place:
  * the node-line 0x20000-fleet check runs AFTER the Chance(0.5f) call and
    cannot gate the draw; the expiry test is NodePath::RemainingLife 0x006e2130
    and is now a formula rather than a description
  * StrategyHost::Autosave is ret 8 and returns the std::string* in EAX
  * SNMAllCombatDone IS delivered every End Turn (8 of 8) -- lane K's inference
    was right; the stronger no-encounter reading is narrowed, not closed
  * S+0x8 advances 12-14 times per turn, not twice

Node-line decay still has not fired. The hook reports the distance instead of
the absence: 51 of 53 lines are permanent, the mortal ones are dug ~1/turn by
the Zuul, each ~40 turns from expiry. It stays a labelled hypothesis.
2026-09-08 09:45:06 -04:00
Alex
5a3f986f38 lane U: the unlock cascade, implemented and live-verified
Reads: TechTree::PrereqsMet 0x0057d8e0 (AND of ORs; zero groups TRUE, an empty
group FALSE), the prerequisite layout at TechDef+0x88/+0x98, the tail collector
0x00587cc3, and the head of OnTechResearched (RecordObservedTech unconditional;
the research-event roll gated on ResT == def && ResearchRollPending).

Live: 35 compared calls over three workloads, 0 divergences, tracecmp exit 0,
End-Turn oracle hashes unchanged. The EVENT_TECHS_UNLOCKED residual lane P
predicted and lane V measured is closed by running the cascade, not by posting
on completion.

12 new addresses in ghidra/addresses.d/lane-u.json (header 615 -> 627).
2026-09-08 06:57:37 -04:00
alex
7eabfefff2 lane V: live verification of the event-posting model against an advance prediction
Lane P wrote the expected numbers into sots-engine docs/P-events-wiring.md §4 before
this run existed. Every one held.

First End Turn (ref-turn2 -> Launch -> End Turn, shim.cfg.recapb3 unchanged, build
eventlive-dd38117-20260908T0916Z, main dd38117 with no source change): 3 calls, 3
compared, 0 divergent, tracecmp exit 0 — where lane R's run exited 1 on
side.events.after.v.next_id orig=4 ours=3. Call 0 read turn=3,
events_turn_bucket_exists=true, events_next_id_in=3, events_in_turn_bucket=1,
events_dedup_risk=0, no events_scan_truncated, next_id 3->4 on both sides,
turns/turns_bytes 2/48 unchanged, node[144].progress 2879->5768, flag 1->2, rng
identical — the whole predicted list.

Five End Turns: exit 1 with 2 divergent calls instead of 3, each short by exactly 1
(next_id orig 7 ours 6, orig 12 ours 11) = the deliberately unmodelled
EVENT_TECHS_UNLOCKED. A 0-divergence result there would have been suspicious.

sizeof(Game::ObservedTech) = 44 measured live: observed_techs.bytes grew by exactly
44 on both completion calls (440->484, 484->528), confirming lane X's static pin
behaviourally. Non-researching players measured 880 = 20 x 44 and never moved.

One deviation from lane R, and it is a workload effect, not a defect: rng diverged on
call 9 because the completed tech had research_roll_pending set and the
OnTechResearched callback drew one word ours does not (left 374 vs 375). Lane R's
"RNG matched 15 of 15" was workload luck; the honest statement is that the rng region
matches on every call that does not complete a roll-triggering tech.

Zuul double roll CLOSED. No species-5 save existed, so this lane made one: a custom
game with only Zuul in the Available Species pool, 4 End Turns under the same compare
config. 8 calls, 8 compared, 0 divergent, exit 0; all four researching calls have
species=5 and advance the generator by two, not one (left 540->538, 522->520,
504->502, 485->483), and ours reproduced every post-state bit-for-bit. Save added as
verify/results/saves/zuul-turn5-species5.sav.

End-Turn oracle byte-identical to lane R's on the first turn, so none of this
perturbs the game. clean_room_check OK and host ctest 33/33, run as separate commands.
VM140 restored to the recap build at the main menu and released.
2026-09-08 05:59:22 -04:00
alex
9d385a7683 board: lane C state-checksum landed; fpu_cw experiment queued; event-posting lane 2026-09-08 03:47:21 -04:00
alex
05095e93da lane R: golden-trace recapture with guards on the live game
Five hooks recaptured, build recap-7584bad-20260908T0615Z, no source change.

- ProcessResearch: the over-budget event is now a compare divergence
  (side.events.after.v.next_id orig=4 ours=3) instead of a 609 KB save diff.
  15 calls over 5 turns, 3 diverged, RNG matched 15/15.
- ComputeBudget: 4284 compared, 0 diverged, guard quiet - but only 20 distinct
  states and 13 of 22 slots always zero.
- MoveFleet: 8 of 45 calls diverge by 1 ULP of position. B4's clean verdict was
  a one-sample verdict.
- First guarded captures for OnTechResearched (RollResearchEvent draw fired and
  matched), the colony turn and MoveFleet.
- Guards mapped SetResearched live and found an undeclared ObservedTech append.

VM140 released at the main menu, hooks=trace.
2026-09-08 03:06:43 -04:00
alex
f146a7616a m2 code merged (VM window pending); record the 3-word vector struct hazard 2026-09-07 22:43:05 -04:00
alex
8116f9e0c8 P2-M1 verified on the live game: golden trace, 0-divergence compare, replace passes the End-Turn oracle 2026-09-07 22:18:22 -04:00
alex
5614940650 p2-m0 evidence: shim log + menu screenshot; Initialize signature caution 2026-09-07 17:22:21 -04:00