# `pilot.py` never pulls the trigger — the proximal cause, measured **Status: ✅ the gate that blocks it is identified and measured; 🔴 the root cause is not, and one attempt to find it was invalidated by a freeze.** ## The observation Over a 900 s Stage 02 run, `fire=1` appears in **0 of 13 521** logged samples. The loop flies, chases, evades and retires; the guns are never fired. Every "kill something and watch X" experiment in this corpus has therefore been leaning entirely on the wingmen. ## Which gate closes `pilot.py`'s trigger needs all of: mode `ENGAGE`/`DEFEND`, a target, the aim error inside the firing cone, range < `FIRE_RANGE` (`SHELL_MAX_RANGE` = 4 000), and a small avoidance push. Over the 3 004 samples that had a target: | quantity | median | min | inside its gate | |---|---|---|---| | `\|aim yaw\|` | **90.0°** | **90.0°** | 0 of 3 004 below 25° | | `\|aim pitch\|` | 179.2° | 166.4° | 0 of 3 004 below 25° | | range | 49 634 | 45 078 | 0 below 4 000, 0 below 10 000 | `|aim yaw|` being **exactly** 90.0 in every sample is not a coincidence, it is a branch: `sticks()` contains ```python if ez < 0: # target behind: commit to a full turn yaw = math.copysign(math.pi / 2, ex if ex else 1.0) ``` So the committed target was **behind the ship in 100 % of samples**, with a pitch error near 180° — directly astern — and the range to it grew monotonically: ``` t= 0 s 22 265 t= 231 s 33 632 t=510 s 49 475 t= 884 s 48 200 (plateau) ``` The craft flies *away* from the thing it is chasing for fifteen minutes and the turn never completes. Nothing else needs to be wrong for the trigger to stay cold. ## What is NOT established Three explanations fit that shape and this pass did not separate them: 1. the attitude matrix the pilot reads is **stale**, so the ship turns and the controller cannot see it; 2. the matrix is live but `fwd_row`/`fwd_sign` name the wrong axis; 3. both are fine and the **yaw stick sign** is inverted, so the loop turns away from the error it is nulling. 🔴 **An attempt to separate them was invalidated and is withdrawn.** `aim_probe.py` watched the forward vector under neutral, full-left and full-right stick and reported it pinned at `[-1, 0, 0]` with `|turn| = 0.00 °/s` in every phase — which looks like explanation (1). It is not evidence: **the guest had frozen** partway through, confirmed immediately afterwards by `frozen.py` (max pixel delta 0) and by the player's position being byte-identical across 3 s. A dead world holds every matrix still. The probe is committed because it is the right experiment; its result is not. ## One confusion resolved on the way Today's repeated `entities2.py self` failures — `# 0 moving triples`, "player entity not found" — are **the freeze**, not a tool defect. `moving()` types entities by their position *changing* between two samples, so a frozen world yields nothing by construction. Runs where the bind failed were runs that had already stopped. ## Next step Re-run `aim_probe.py` on a run confirmed to be animating **at the end of the probe as well as the start** — the tool should check `frozen.py` itself and discard the phase otherwise. If the matrix does move under stick, the question becomes the sign; if it does not, the binding is what to chase.