This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/mission-liveness-probe.md
Sylpheed RE agent a7cc4d6408 re: sites are entities 1:1 — withdraw "n is craft-per-member"
The suspected confound turned out not to exist. Gaps between consecutive
same-unit definition-pointer sites are all >= 0x1000, with 274 of them exactly
0x1000, so entities are page-spaced and there are no near-adjacent pairs to
merge. Clustering at any threshold below 0x1000 gives ratio 1.00 for every unit
type, and hull is plausible on 298 of 298 clustered bases at delta 0x130. The
player shows two objects because there are two, not because one holds two
pointers.

That removes the excuse the previous iteration had used to keep the reading
alive, and the reading does not survive: sum(n) fits the turret row well (216
against 214, with kills already recorded), but DeltaSaber_T, Player and
Acropolis all come out at exactly twice their sum(n). An undershoot can be
blamed on phases 2-3 not having started; an overshoot cannot. n goes back to 
and the previous 🟡 is withdrawn. All the turret row establishes is that a
roster member expands into many craft, not that n is the factor.

Formation slot count was tested as the alternative and rejected outright: 630
turret slots against 214 live.

Side result worth keeping: a FormationSet record's FrameCount is its slot count,
and the name suffix usually agrees -- Turret07_30 -> 30, ArrowHead03_64 -> 64,
4_Bird -> 4 -- with one exception, AttackerS03_12 having 14 slots, so the suffix
is a label and not a guarantee.

Also recorded: the 298 live entities are not the 116 roster records. Both
structures exist at once, and the rule mapping one onto the other is the real
open question.

Probe caveat noted in the doc: entities2.moving() found no movers this run, so
the delta spectrum was empty and the clustering threshold was a fallback rather
than a measurement. It does not change the conclusion, since every gap exceeds
any threshold below 0x1000.
2026-08-24 13:13:43 +00:00

157 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# A motion-independent live roster — and what `n` probably is
Status: ✅ the enumeration method; ✅ the hunting pilot gets kills; 🔴 "no
births" cannot distinguish the wave models; 🟡 the member field `n` looks like a
craft count, with a confound that must be removed before believing it.
## ✅ Enumerate by definition pointer, not by motion
`entities2.moving()` finds entities *by displacement between two samples*, so
anything stationary in the window is invisible. That is the whole explanation
for the ±10 swing that made the previous run's entity count useless
([mission-wave-arrivals.md](mission-wave-arrivals.md)).
`tools/re-capture/liveness_probe.py` scans the entity heap
(`0xBD0000000xBE000000`) for aligned words equal to a known unit-definition VA
instead. Moving or not, an entity is counted. Hull is `f32` at
`position + 0x154` (`HULL_OFF`, already confirmed in `pilot.py`), so a death is
a hull crossing to ≤ 0.
The difference is immediate — the series is monotone instead of oscillating:
```
t= 15s 294 alive born 0 died 0 gone 4
t= 36s 290 born 0 died 0 gone 4
t= 57s 289 born 0 died 1 gone 0 <- e010_ADAN_Attacker_S
t= 78s 286 born 0 died 0 gone 4
t=100s 286 born 0 died 0 gone 0
t=143s 282 born 0 died 0 gone 4
t=164s 280 born 0 died 0 gone 2
```
Total decline 298 → 280 = 18, matching the 18 "gone" events exactly.
## ✅ The hunting pilot kills
One hull crossing was caught directly — an `e010_ADAN_Attacker_S` at t = 57 s —
plus 18 disappearances. The previous run's worry that `SYLPH_HUNT` shoots but
never destroys anything is settled: it does.
## 🔴 "No births" does **not** separate the two wave models
Zero entities appeared in 164 s. That looks like evidence against clock-driven
arrivals at t = 90/120 s — **and it is not**, because the roster finding already
established that all of a mission's entities are allocated at load. If every
participant exists from the first frame, then *neither* model produces a "birth",
and the observation is consistent with both. Recorded so the next run does not
mistake it for a result: **an arrival must be a state change on an existing
entity, not an allocation.**
## 🟡 `n` looks like the number of craft per roster member
The member tuple's third field `n` has been ❔ since the roster was decoded.
Comparing the static `sum(n)` per unit type against what is resident:
| unit | members | `sum(n)` | sites found |
|---|---|---|---|
| `UN_e007_ADAN_Turret` | 21 | **216** | **214** |
| `UN_e105_ADAN_Cruiser` | 7 | 7 | 6 |
| `UN_e106_ADAN_Destroyer` | 19 | 19 | 14 |
| `UN_f105_TCAF_Cruiser` | 11 | 11 | 4 |
| `UN_f106_TCAF_Destroyer` | 14 | 16 | 10 |
| `UN_e010_ADAN_Attacker_S` | 9 | 45 | 32 |
| `UN_f001_TCAF_DeltaSaber_T` | 7 | 7 | **14** |
| `UN_f001_..._Player` | 1 | 1 | **2** |
The turret row is striking: 216 predicted against 214 found, with the count
already falling before the first sample. `Count` alone predicts 21, which is
off by an order of magnitude. So `n` scaling a member into a flight of craft
fits the dominant unit type well.
**It is not promoted, because the measurement has an unquantified confound.**
The player is one member and yields **two** sites, and `DeltaSaber_T` yields
exactly double its `sum(n)` — so at least some entity types hold more than one
pointer to their definition. What the probe counts is *definition-pointer sites*,
not entities, and until those are collapsed into distinct entities the turret
match could be a coincidence between a ×1 multiplicity and a ×1 ratio. The
capital-ship rows undershoot instead, which is separately explained by phases 2
and 3 not having started.
## Next
Collapse sites into entities before re-testing `n` — cluster sites by their
implied entity base and take distinct bases. Then the table above becomes a real
test rather than a suggestive one. The same de-duplication is a precondition for
using this probe to watch an arrival, since an arrival is now known to be a
state change rather than an allocation.
---
# Sites really are entities — and `n` does not survive the fix (2026-08-24)
## ✅ The multiplicity confound is refuted
The worry was that some entity types hold several pointers to their definition,
so "sites" over-counted entities. Measured, it does not happen:
* **Gaps between consecutive same-unit sites are all ≥ `0x1000`**, and 274 of
them are *exactly* `0x1000`. Entities are page-spaced; there are no pairs
sitting a few bytes apart that a clustering pass could merge.
* Clustering at any threshold below `0x1000` therefore gives **ratio 1.00 for
every unit type** — 298 sites, 298 entities.
* **Hull is plausible (`0 < h ≤ 200000`) on 298 of 298** clustered bases at
delta `0x130`. These are real objects, not stray pointer words.
So the previous run's counts were right after all, and the reason the player
shows two is that there genuinely are two such objects — not two pointers to one.
*(The delta spectrum came back empty this run: `entities2.moving()` found no
movers in its sample window, so `find_delta` had nothing to vote on. It does not
affect the conclusion — every gap is ≥ `0x1000`, so any threshold below that
gives the same clustering — but the threshold was a fallback, not a measurement,
and that is worth knowing if this probe is reused.)*
## 🔴 With the confound gone, the `n` = craft-per-member reading fails
| unit | members | `sum(n)` | formation slots | **live entities** |
|---|---|---|---|---|
| `UN_e007_ADAN_Turret` | 21 | 216 | 630 | **214** |
| `UN_e010_ADAN_Attacker_S` | 9 | 45 | 126 | **32** |
| `UN_f001_TCAF_DeltaSaber_T` | 7 | 7 | 23 | **14** |
| `UN_e106_ADAN_Destroyer` | 19 | 19 | 23 | **14** |
| `UN_f106_TCAF_Destroyer` | 14 | 16 | 16 | **10** |
| `UN_e105_ADAN_Cruiser` | 7 | 7 | 7 | **6** |
| `UN_f105_TCAF_Cruiser` | 11 | 11 | 11 | **4** |
| `UN_f001_..._Player` | 1 | 1 | 3 | **2** |
| `UN_f101_TCAF_Acropolis` | 1 | 1 | 6 | **2** |
`sum(n)` fits the turret row well (216 against 214, with kills already recorded
before the sample). It does **not** generalise: `DeltaSaber_T`, `Player` and
`Acropolis` all come out at exactly **twice** their `sum(n)`, and an undershoot
can be blamed on phases 23 not having started but an *overshoot* cannot.
Two different multipliers are at work and only one of them is explained. **`n`
goes back to ❔** — the 🟡 in the previous section is withdrawn. What the turret
row shows is only that a roster member expands into many craft; it does not show
that `n` is the expansion factor.
## 🔴 Formation slot count is not the expansion factor either
Tested because the formation names carry a size suffix. `FormationSet_S02.tbl`
gives 630 turret slots against 214 live — an overshoot of 3×. Rejected.
## ✅ Side result: `FormationSet` `FrameCount` is the slot count
The frame count of a formation record is the number of slots, and the name
suffix usually agrees: `Formation_ADAN_Turret07_30` → 30,
`Formation_TCAF_ArrowHead03_64` → 64, `Formation_4_Bird` → 4. One exception
found: `Formation_ADAN_AttackerS03_12` has 14 slots, not 12, so the suffix is a
label rather than a guarantee.
## Also worth recording
The live entity population (**298**) is not the 116 `0x820af030` roster records.
Both structures exist simultaneously: 116 records, one per `UnitGroup` member,
and 298 heap objects, one per actual craft. Whatever maps one onto the other is
the expansion rule, and it is still unknown.