# Which squadrons fly in which phase — and the ~300 craft are ~150 Status: ✅ the phase map, read out of `Route_S.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__p` — **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.