Both defects from the previous iteration are fixed by measuring instead of assuming, and the fix immediately promotes a 🟡 result to ✅. Labelling: the previous probe assumed object+0x04 -> name_record+0x10 -> char* and resolved 0 of 116. wave3_probe.py searches for the chain per record instead, the way unit_discover.py does, and resolves 116 of 116 -- every one through the pointer at +0x04 with the string at delta 0x00, not 0x10. The 0x10 belongs to the definition object (vtable 0x820af844); the spawned-entity record (0x820af030) uses 0x00. Carrying one over to the other cost the last run. Stride: measured, not assumed. Gaps between consecutive records are min 32, median 800, with common values 800, 640, 608, 576, 416 and 32. There is no fixed record size, so the old RECLEN=0x200 window truncated large records and overran small ones -- which is why its busiest fields were the last words of the window. Future diffs must bound each record by the next record's address. With labels available, the "116 records == 116 roster members" claim was tested properly and is promoted from 🟡 to ✅. The multiset of unit types matches the static roster exactly: Turret 21/21, e106 Destroyer 19/19, f106 Destroyer 14/14, f105 Cruiser 11/11, ASFrigate 9/9, ISCMissile 9/9, Attacker_S 9/9, e105 Cruiser 7/7, DeltaSaber_T 7/7, ArrowHead 6/6 -- 10 of 10 exact. A coincidental total is possible; a coincidental distribution over ten unit types is not. The game allocates one record per roster member at mission load. Not settled: REMAINING OB at 0xbdb59668 held 95748078 unchanged all run. That address is known to be run-dependent, and this was one of the misses, so the run cannot say whether the pilot killed anything. Re-hunting it is a precondition for the kill-versus-no-kill test, not an optional extra.