164 lines
6.8 KiB
Markdown
164 lines
6.8 KiB
Markdown
# The 116 roster records and the ~300 live craft are not directly linked
|
||
|
||
> ✅ **SOLVED 2026-08-25 — see [mission-phase-membership](mission-phase-membership.md).**
|
||
> The premise below is wrong: **there were never ~300 craft.** The probe counts
|
||
> each craft **twice**. Phase 1 (read out of the `Route_S02.tbl` record names)
|
||
> has Σ`n` = 151, and `300 = 2 × (151 − 1)`, the −1 being the 41-vs-42
|
||
> off-by-one already recorded here. A 2× reading is impossible statically: it
|
||
> needs 16 Delta Sabers for 8 named pilots and breaks the formation capacity
|
||
> bound in 20 of 37 phase-1 squadrons, where the 1× reading has 0 violations.
|
||
> The four refutations below all stand — they were just answering a question
|
||
> built on a bad number.
|
||
|
||
Status: 🔴 a direct pointer link is refuted in both directions; ✅ the two
|
||
structures are in distinct heap regions; ❔ how a roster member expands into
|
||
craft is still unknown.
|
||
|
||
Four attempts have now failed to explain how 116 roster members become ~300
|
||
live craft — `Count` (116), the member field `n` (387), formation slot count
|
||
(630, rejected), and now a pointer link. This records the fourth so it is not
|
||
retried.
|
||
|
||
## The two structures
|
||
|
||
| | vtable / locator | count | VA range (measured) |
|
||
|---|---|---|---|
|
||
| roster records | `0x820AF030` | **116** | `0xbc372c00 … 0xbc9bc720` |
|
||
| live craft | def-pointer site − `0x130` | **296–300** | `0xbdb2fd80 … 0xbdcd1d80` |
|
||
| unit definitions | `0x820AF844` | 14 | — |
|
||
|
||
They are in **different regions**, ~19 MB apart, and neither range overlaps the
|
||
other. The 14 definitions match Stage 02's 14 distinct unit types exactly.
|
||
|
||
The craft count is not fixed: three runs at comparable mission times gave 296,
|
||
298 and 300. That is expected now that kills are confirmed, but it means the
|
||
craft population must be compared *within* a run, never across runs.
|
||
|
||
## 🔴 Refuted: neither structure points at the other
|
||
|
||
`tools/re-capture/link_probe.py` scans the first `0x400` bytes of every object
|
||
for a word equal to a known address of the other kind.
|
||
|
||
* roster record → craft base: **0 of 116** records hold one.
|
||
* craft → roster record base: **0 of 300** craft hold one.
|
||
|
||
Both directions are empty, so whatever associates a member with its craft is not
|
||
a pointer at the head of either object.
|
||
|
||
## ❔ The one loose thread, and why it is not evidence yet
|
||
|
||
The same scan counted pointers into the *ranges* rather than at exact bases:
|
||
|
||
* roster words pointing anywhere into the entity heap: **181**, of which only
|
||
**2** land within `0x400` of a known craft base;
|
||
* craft words pointing anywhere into the roster VA range: **5101**.
|
||
|
||
5101 looks like a lot, but the roster range spans 6.5 MB and certainly holds
|
||
many allocations besides the 116 records, so pointers "into the range" may have
|
||
nothing to do with roster records at all. **This is deliberately not being read
|
||
as a link.**
|
||
|
||
The test that would make it evidence: for each of those 5101 pointers, compute
|
||
the distance to the nearest roster-record base below it. If the association is
|
||
"craft points at `roster_base + X`" for some fixed `X`, that distribution spikes
|
||
at a single delta. If it is flat, the pointers are unrelated allocations and the
|
||
thread is dead.
|
||
|
||
## What this means for the wave question
|
||
|
||
The association may not be a pointer at all — it could be an index, a hash, or
|
||
mediated by a third object (a squadron or formation instance) that neither side
|
||
names directly. Since an arrival is already known to be a *state change* rather
|
||
than an allocation ([mission-liveness-probe.md](mission-liveness-probe.md)),
|
||
finding the mediating structure matters more than finding a flag: it is what
|
||
would let a change be attributed to a named squadron and therefore to a route.
|
||
|
||
---
|
||
|
||
# ✅ FOUND: the link is a pointer at `roster_base + 0x08` (2026-08-24)
|
||
|
||
The refutation above was right that neither object points at the other's **base**
|
||
— and wrong to stop there. Craft point *8 bytes into* a roster record.
|
||
|
||
## How it was found
|
||
|
||
The 5101 "pointers into the roster range" were resolved by distance to the
|
||
nearest roster-record base below. The distribution is not flat and not diffuse:
|
||
|
||
```
|
||
+0x00008 x300 5.9% <- exactly the number of live craft
|
||
+0x034b2 x2
|
||
+0x02d97 x2
|
||
... every other delta appears at most twice
|
||
(4780 of 5104 land >0x4000 from any base -- unrelated heap traffic)
|
||
```
|
||
|
||
One delta, hit exactly as many times as there are craft.
|
||
|
||
## ✅ Verified by an independent cross-check, 300/300
|
||
|
||
A count matching a count is the sort of coincidence that has already misled this
|
||
corpus three times, so the link was tested on something a coincidence cannot
|
||
survive: **each side's unit type is resolved by a completely different chain**,
|
||
and they must agree.
|
||
|
||
* the craft's type comes from its definition pointer at `+0x130`;
|
||
* the roster record's type comes from its `+0x04` name chain
|
||
([mission-wave-arrivals.md](mission-wave-arrivals.md)).
|
||
|
||
Result:
|
||
|
||
| check | result |
|
||
|---|---|
|
||
| craft fan-out at `base+0x08` | **`[(1, 300)]`** — every craft has exactly one |
|
||
| craft with no link | **0** |
|
||
| craft with more than one | **0** |
|
||
| **unit type agrees** | **300** |
|
||
| unit type disagrees | **0** |
|
||
|
||
Two independently-derived labels agreeing on all 300 objects settles it.
|
||
|
||
## ✅ What the fan-in shows: 41 of 116 members have craft
|
||
|
||
Only **41 of the 116** roster records are referenced by any craft. The rest have
|
||
none at all. The distribution of craft per referenced record is strikingly
|
||
discrete:
|
||
|
||
| craft per record | records |
|
||
|---|---|
|
||
| 2 | 24 |
|
||
| 4 | 1 |
|
||
| 8 | 4 |
|
||
| **18** | **12** |
|
||
|
||
24·2 + 1·4 + 4·8 + 12·18 = **300**, exactly.
|
||
|
||
The twelve records holding 18 craft each are all `UN_e007_ADAN_Turret`, giving
|
||
216 turret craft — which is also what `sum(n)` predicted for turrets, though
|
||
that agreement is not being promoted again on its own after the last withdrawal.
|
||
|
||
## Why this matters for the wave question
|
||
|
||
This is the observable that four iterations have been looking for:
|
||
|
||
* a roster member with **zero** craft has not been deployed;
|
||
* a member's **craft count is its live strength**;
|
||
* therefore an **arrival is craft appearing for a record that had none**, and a
|
||
**kill is that count decreasing** —
|
||
|
||
and, crucially, both are **attributable to a named squadron**, because the roster
|
||
record is one `UnitGroup` member and `UnitGroup` names the squadron, which
|
||
`Route_S02.tbl` ties to a phase and an arrival time. The chain from a memory
|
||
observation to a static route entry is now complete.
|
||
|
||
75 of 116 members being undeployed at this point in Stage 02 is consistent with
|
||
phases 2 and 3 not having started, but that is an interpretation, not a
|
||
measurement — the run only shows they have no craft.
|
||
|
||
## Next
|
||
|
||
Watch the per-record craft counts over a mission, not the global total. A record
|
||
going 0 → n is an arrival; n → n−1 is a kill. That is the kill-versus-no-kill
|
||
experiment the event-gated model needs, and it now has a signal with squadron
|
||
resolution.
|