The owner's live-GUI report was correct on all four counts, and all four are
one bug: the commitment is cancelled by our own tile-boundary crossing
(96.1% of picks, 3793/3946, mean hold 5.06 ticks) while the bot is still
accelerating, and the picker is an unconstrained uniform draw over every
safe tile, so the new target can land in the mirror direction at |speed| < 4.
New knobs, all env-gated and default = today's behaviour (byte-for-byte
default parity guard re-run and green, 51 checks):
TR_TFIL_COMMIT_ARRIVAL hold the committed tile until we are ON it; the
tick knob becomes a MINIMUM dwell. 0 = shipped.
TR_TFIL_COMMIT_MARGIN leave only if the best alternative is at least
this much cooler on the same pathMaxHeat scale.
0 = shipped.
TR_TFIL_NOREV_SPEED while |speed| is below this, a mid-flight switch
may not take a tile >90 deg off the travel
direction. 0 = shipped. norevPool() never returns
an empty pool: with every candidate behind us it
takes the least-bad turn.
Offline gate (recorded DrussGT fixture, 20026 ticks): mean hold 4.1 -> 24.0
ticks, abandoned-before-arrival 92.8% -> 40.5%, committed tile actually
reached 3.3% -> 17.2%, opposite-direction slow mid-flight switches 394 -> 64
(-84%). 'TR_TFIL_TILE_REPLAN=off' alone - what cc11ede's arm B already tried -
only gets the hold to 13.6, which is why that A/B could not find this.
strafe is untouched: it imports only heatDecay/bulletMagScale/Pillar*, none
of which this touches. TR_MOVEMENT default stays strafe. Registered in
env_report.nim + knownEnvNames() + .env.example. Arms pre-registered in
docs/movement_campaign.md and tools/ab/arms_tfil_commit.txt.