Files
Sylpheed/docs/re/mission-phase-membership.md
Sylpheed RE agent 82ce0cb9ad re: Turret-biased run verified live -- 38 kills, still no phase advance
Checked liveness before investing this time (animating, max_delta=254) and the
run was real: 823 distinct speeds, hull 1500->1485, escort 100%->30.7%, ADAN
population 127->89 (38 destroyed) over ~1064s, no GUEST FROZEN banner. The phase
mirror stayed 0 throughout.

But this still does not test the prediction. The claim is that phase 1 clears
when ADN110/111/112 -- 27 specific Turrets -- die; what was measured is that 38
enemies of some kind died. SYLPH_PREFER biases target choice but the pilot went
to DEFEND and spent the run on e010_ADAN_Attacker_S protecting the escort.

The missing instrument is per-squadron liveness: nothing maps a live craft back
to its roster squadron, the same gap roster-to-craft-link.md records as unsolved.
So 'no advance after 38 kills' is compatible with both 'the condition is wrong'
and 'the right 27 were never killed'. The decisive experiment is blocked on
attribution, not on flying time.
2026-08-25 14:11:50 +00:00

277 lines
13 KiB
Markdown
Raw Permalink 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.
# Which squadrons fly in which phase — and the ~300 craft are ~150
Status: ✅ the phase map, read out of `Route_S<NN>.tbl` record names;
✅ the 387-vs-300 gap explained — **the live-craft probe double-counts**;
❔ what *advances* a phase (nothing static encodes it).
## ✅ The phase map is in the route names
`stage\Route_S02.tbl` has **121 records: 120 routes + 1 roster**, and every route
name matches `Route_<squadron>_p<N><kind>`**120/120**:
```
Route_ADN101_p1F Route_TCN003_p2S Route_ADS351_p3M
```
The phase lives **in the name and nowhere else**; a route record's only named
field is `FrameCount`. So the squadron→phase mapping was on the disc all along,
one string-parse away, in a table that was being read for fly-in geometry.
**108 of 111** S02 squadrons match a route name exactly. The three that do not —
`TCN207`, `TCN208`, `TCS252` — are **typos in the route table** (`Route_TCT207_p2S`,
`Route_TCT208_p2S`, `Route_TCN252_p2F`), plus two orphan routes naming no
squadron (`Route_ADS352_p3F`, `Route_kichi_p3F`). All three near-misses are
**phase 2**, so phase 1 is unaffected.
Phase partition of Stage 02 (Σ`n` = 387 overall):
| phase set | squadrons | Σ`n` |
|---|---|---|
| {1} | 33 | 142 |
| {2} | 31 | 110 |
| {3} | 39 | 118 |
| {1,3} | 2 | 5 |
| {2,3} | 1 | 1 |
| {1,2,3} | 2 | 4 |
| none (the 3 typos) | 3 | 7 |
| **per phase** | 37 / 34 / 44 | **151 / 115 / 128** |
## ✅ The 387-vs-~300 gap: the probe counts each craft TWICE
[roster-to-craft-link](roster-to-craft-link.md) records four failed attempts to
explain how the roster becomes "~300 live craft". The premise was wrong — **there
were never 300 craft.**
Phase 1's **member-level** `n` multiset against the measured craft-per-record
fan-in (41 records, 300 craft):
```
phase-1 members (42): n=1 x25 n=2 x1 n=4 x4 n=9 x12 sum 151
observed craft/record: 2 x24 4 x1 8 x4 18 x12 sum 300
observed / 2: 1 x24 2 x1 4 x4 9 x12 sum 150
```
Every bucket matches shape for shape. The only discrepancy is 24 vs 25 records
at the bottom — **the already-documented 41-vs-42 off-by-one, and it is an `n=1`
record.** So `300 = 2 × (151 1)`. Unit type corroborates independently: the 12
records showing 18 craft are all `UN_e007_ADAN_Turret`, and phase 1 has exactly
12 turret squadrons at `n=9`.
### ✅ The factor of 2 is an artifact of the probe, not real units
Two static arguments, and the second is decisive because it is a hard capacity
bound:
1. **Named pilots.** Phase 1's eight `f001` members all have `n=1` and carry
distinct identities — `Character_Player_Test, ELLEN, RAYMOND, GENE, SANDRA,
BILLY, ANTONIUS, CARL`. A 2× reading means 16 Delta Sabers for 8 named people.
2. **Formation slots.** Squadrons cannot exceed their formation's `FrameCount`
(see [unit-group-table](structures/unit-group-table.md)):
| reading | violations among the 37 phase-1 squadrons |
|---|---|
| `Σn ≤ FrameCount` (1×) | **0** |
| `2·Σn ≤ FrameCount` (2×) | **20** |
Including 16 squadrons on `Formation_1_only` — one slot, `n=1` — which a 2×
reading would have placing two craft in a one-slot formation.
So the true phase-1 population is **~150**, and the 296 / 298 / 300 spread is
2 × a 148 / 149 / 150 population after kills. That all three measurements are
**even** is a further tell a 1:1 count would not produce half the time.
**Best explanation of the missing 42nd record:** the player's own tuple
(`TCN001` member 0, `Character_Player_Test`), which plausibly is not in the
AI-craft pool the `+0x130` def-pointer scan walks. That makes the chain exact:
`300 = 2 × (151 1 player)`.
⚠️ **This is a static argument about a dynamic measurement.** It says the probe's
number cannot mean what it was taken to mean; it does **not** identify *what* the
probe is double-counting. That needs a run — the counting rule in the craft scan
is the place to look, not the game.
## 🔴 Refuted along the way
* **`UnitGroup` carries no phase, spawn time or arrival delay.** The raw field
dump accounts for every slot: `1019 = sum(Count)·4 + 5·111`, with positional
entries tagged by sequential index (`0x0,0x1,0x2,…`), not hashes. There is no
spare field.
* **`DisableInterval` cannot explain Stage 02** — it is `No` for all 111
squadrons. Corpus-wide its 31 `Yes` records sit in just two stages, S04 (5) and
S14 (26), all on `GNN***` squadrons.
* **`stage\EnumSquadron_Test.tbl` does not exist** (`0x213f4a7e` is not in the
TOC). It was listed as a candidate for the arrival-interval values; it is dead.
* `AIParams_S02.tbl` is keyed by AI profile, not squadron, and carries only
combat tuning. `SUBObjectiveSettings_S02.tbl` is keyed `SUBOBJ_005..014` with
only points/unlock/HUD strings — no squadron, phase or trigger.
## ✅ Bonus: the `UnitGroup` record key, confirmed corpus-wide
A squadron record's key is `tag_hash(squadron_id)` — **1160/1160 across all 28
stages** (`ADN101``0x659aff47`). Same hash as
[idxd-tag-hash](structures/idxd-tag-hash.md); this is an independent
confirmation of it on a second kind of table.
## ❔ Still open: what advances a phase
Nothing static encodes a trigger. Swept the stage record and every table it
names — `Route`, `AIParams`, `SUBObjectiveSettings`, `FormationSet`, `nameplate`,
`EnumUnit`, `EnumCharacter`, `EnumLocalString`, `EnumWeapon` — plus the
`message\` family, for field names matching
`interval|time|phase|delay|wave|spawn|arrival|trigger|start|appear|event|condition`.
The only hits anywhere are `FrameCount` and `PresetMessage_Phase1/2/3` in
`message\UnitMessageSet_S02.tbl`. The trigger is almost certainly in the
executable, not the data.
## Falsifiable predictions for the next run
* Per-record craft counts, **in probe units (2×)**: 4 records at 8 =
`ADS151, ADT102, ADT107, ADT113` (all `UN_e010_ADAN_Attacker_S`); 1 record at
4 = `TCN105` (`UN_f106_TCAF_Destroyer`); 24 records at 2 = the remaining
phase-1 `n=1` members.
* A phase advance should move the deployed set toward phase 2: 34 squadrons,
36 members, Σ`n` 115 (≈230 in probe units).
* `TCN207`, `TCN208` and `TCS252` may **fail to arrive** in phase 2, because
their routes are misnamed.
## 🟡 2026-08-25 — first long live observation: no phase advance, and a GAME OVER
Flew Stage 02 with an actively hunting pilot for **936 s** while polling the
runtime phase mirror `[*(0x828F35F8) + 236]` every 3 s (`phase_probe.py`).
**The mirror never left 0.** That is what the static work predicts rather than a
contradiction: `CScriptInterpreter::ChangePhase` (opcode 995) is only posted when
the ordinal is **> 1** (the guard in mission-bytecode opcode `0x83`), so a run
that stays in phase 1 never writes the mirror. It does mean the mirror is
**useless as a "which phase am I in" readout for phase 1** — it reads 0, not 1.
`[ScriptMission+40]` is the real counter and needs a debugger.
**The run ended in `GAME OVER`** — and the pilot's hull was still **1500/1500**,
so the player did not die. A **script lose-branch fired**, and the pilot log says
which one is likely: the tracked **asset fell from 100.8 % to 23.4 %**, with
`ASSET-HIT` events logged, while the pilot repeatedly `AVOID`ed
`f101_TCAF_Acropolis` — a friendly capital ship — 2588 times.
So the escort was being destroyed throughout, and the mission failed with the
player untouched. **"Escort lost" is now a measured candidate rather than a
guess**, though the final asset value was 23.4 %, not 0, so the exact threshold
(or whether a different unit's loss ended it) is **not** established.
**So a phase advance still has not been observed.** Two runs now — 530 s and
936 s — neither reached phase 2. Combined with the static reading that phase 1
polls `unit_state` on **ADN110/ADN111/ADN112**, the likeliest explanation is that
those three squadrons were never destroyed; the pilot hunts whatever is nearest,
not the objective. **Next: bias the pilot at those squadrons' craft and re-run.**
## 🔴 Refuted: `sub_8226D740` does not read the flag register file
Chasing what *reads* the 32-entry flag file at `[phase+120]`, the promising hit
was three `addi rN, r31, 120` inside `sub_8226D740`, the per-frame engine→script
trigger edge. **It is not the flag file.** In that function `r31` is a local
**trigger-record buffer** whose fields are handed to `sub_8226E220` as
out-parameters (`+80, +84, +88, +92, +112, +120`), and `lfd f1,120(r31)` reads a
`double` from the record. The phase is in `r26`/`r29`.
Offsets 88 and 120 are far too common (2091 and 5228 instructions image-wide,
mostly stack frames) for an offset search to isolate the register files.
**Who reads the flags is still unknown**, so the chain
`set_flag(8) → … → END_PHASE` has a gap in the middle.
## ✅ 2026-08-25 — Stage 02 phase 1: destroy 27 Turrets (ADN110/111/112)
Resolving the three squadrons the phase-1 condition polls
([isl-builtins](structures/isl-builtins.md), `unit_state` at `0xF524`) against
the roster:
| squadron | phases | AI | unit | `n` |
|---|---|---|---|---|
| ADN110 | **1** | `AI_ADAN_CraftSquadron_Rookie` | `UN_e007_ADAN_Turret` | 9 |
| ADN111 | **1** | `AI_ADAN_CraftSquadron_Veteran` | `UN_e007_ADAN_Turret` | 9 |
| ADN112 | **1** | `AI_ADAN_CraftSquadron_Rookie` | `UN_e007_ADAN_Turret` | 9 |
All three are **phase 1** (from the `Route_S02` name map), all fly the same
craft, all `n = 9` — so the condition watches **27 Turrets**, and all three
squadrons share `Formation_ADAN_Turret09_30`.
Three independent layers agree here, which is the point worth recording: the
**route names** say these are phase-1 squadrons, the **roster** says what they
fly and how many, and the **script** polls exactly these three before latching
`set_flag(8)`.
### ✅ Confirms "Turret" is a craft type, not ship AA
The user's correction stands on the data: `UN_e007_ADAN_Turret` is flown by
`AI_ADAN_CraftSquadron_Rookie`/`_Veteran` — a *craft squadron* AI, in a
30-slot craft formation — never `AI_Structure`. It is an enemy the player fights,
not a gun bolted to a capital ship.
## 🔴 2026-08-25 — the Turret-biased run was VOID: the guest was frozen
Ran it: `SYLPH_PREFER=Turret`, escort intact (`ast=100.0%`), 1070 s. The phase
field read **0** throughout and the pilot logged **4029** Turret-targeting
samples. That looked like a clean negative — "hunting the objective squadrons
does not advance the phase" — and it is **worthless**.
`frozen.py` says `max_pixel_delta=0`: **the guest was frozen**, and the pilot's
very first sample at `t=0.0` already shows `spd=0` with the same yaw, pitch,
target and `d=7186` that it still had at 1070 s. The mission froze on entry to
flight; nothing was ever shot. `screen_id` said `flight` the whole time, which is
precisely the trap [mission-freeze-resume-spin](mission-freeze-resume-spin.md)
documents.
**I was one step from writing this up as a result about the game.** What saved it
was running the freeze test rather than trusting a plausible-looking log — the
same lesson that note already carries, arrived at again from the other side.
### ✅ Fixed so it cannot recur silently
`phase_probe.py` now calls `frozen.py` every 60 s and prints
`*** GUEST FROZEN ***` inline, so a dead-world reading is labelled in the data
rather than discovered afterwards. ⚠️ `frozen.frozen()` returns a **tuple**
`(is_frozen, max_delta)` — testing it directly is always truthy and would have
made the witness fire on every check; it must be unpacked. Verified against the
frozen guest: the banner fires, and the unpacked value is `(True, 0)`.
**The pilot log is itself a witness** and was not being read as one: identical
yaw/pitch/target across thousands of samples means a dead world, not a patient
pilot.
## 🟡 2026-08-25 — Turret-biased run, VERIFIED LIVE: still no phase advance
Re-ran it with the freeze witness armed, and checked liveness **before**
investing in the run this time (`frozen.py``animating, max_delta=254`).
This one was real: **823 distinct speeds** logged, the player's hull moved
1500 → 1485, the escort fell 100 % → 30.7 %, and the ADAN population dropped
**127 → 89 — 38 enemies destroyed** over ~1064 s. The probe printed no
`GUEST FROZEN` banner.
**The phase mirror stayed 0 throughout.** So: 38 enemy losses, escort still
alive, no advance.
### 🔴 But this still does not test the prediction
The prediction is that phase 1 clears when **ADN110/ADN111/ADN112** — 27
specific Turrets — are destroyed. What was measured is that *38 enemies of some
kind* died. `SYLPH_PREFER=Turret` biases target choice, but the pilot switched to
`DEFEND` and spent most of the run on `e010_ADAN_Attacker_S`, protecting the
escort.
**The missing instrument is per-squadron liveness.** Nothing currently maps a
live craft back to its roster squadron — that is the same gap
[roster-to-craft-link](roster-to-craft-link.md) records as unsolved. Until a
craft can be attributed to `ADN110`, a kill count cannot say whether the
condition was met, and "no advance after 38 kills" is compatible with both
"the condition is wrong" and "the right 27 were never killed".
**Honest state: three live-run attempts, no phase advance observed, and the
decisive experiment is still blocked on attribution rather than on flying time.**
### 🟡 The prediction to test
If phase 1 clears on those three squadrons dying, then destroying **27 Turrets**
while the escort survives should push `[ScriptMission+40]` to 2 and make
`ChangePhase` write the mirror at `[*(0x828F35F8)+236]`. Two runs so far killed
whatever was nearest and never advanced.