A squad shouldn't string out across a draw because one man snagged on a wall while the lead kept marching. Fixing that meant first proving what the men actually snag on — and the obvious answer was wrong.
Two asks, and a causal theory tying them together: men snag → the lead doesn’t wait → the file spreads. The house rule here is no fix without a number, so before touching a line I built a probe that attributes every stalled tick to the exact cell that blocked the man, and ran it on 21 near-village patrols — the ones a player actually watches.
Blocked-seconds per patrol, by the land type that actually stopped the man.
Whatever a man snags on — a wall, a b-hut, the wire, a cliff — the point man barely reacts. While a follower is genuinely stuck he runs at 0.51 m/s (full march) and is fully halted just 1% of the time. He marches on; the file opens to 94 m ahead of centroid, and stretches to 355 m on the worst seed. That is the “unrealistically far forward.”
Two guards that were supposed to catch this turned out to be dead code — both keyed on
blockedTimer > 6, but the stall watchdog resets blockedTimer at 2 seconds,
so it can never exceed ~2. One was a mis-aimed exclusion in the pace governor; the other was the
follower-strand harness’s maxWedge column, which had read 0.0s on every seed —
the very “nobody is stuck” reassurance a prior investigation had leaned on. Both fixed.
The point man now takes a knee — a genuine stop — when a follower is blocked and trailing, or when the rearmost man’s along-wake lag exceeds a file-length. It’s clock-latched and, critically, capped at 45 seconds of halt per leg.
Every dot is a soldier at the instant the point man is furthest ahead of the element centroid (×). Same seed, same route — only the wait differs.
A/B on identical code (a runtime switch disables the halt), near villages, march phase only.
| metric | baseline | fix | read |
|---|---|---|---|
| halt % while a man is wedged | 0% | 26% | waits |
| nav speed while wedged (m/s) | 0.58 | 0.45 | slower |
| seeds with peak forward > 40 m | 9 / 16 | 5 / 16 | tighter |
| new strandings introduced | — | 0 | none |
squad-arrival) — the issue-031 arbiter| metric | baseline | fix | read |
|---|---|---|---|
| point man arrived | 25 / 27 | 25 / 27 | identical |
| …within tactical window (≤1500 s) | 15 / 27 | 15 / 27 | identical |
| squad cohesion @ objective | 99% | 98% | noise |
| elements stranded mid-route (balance) | 0 | 0 | none |
Byte-for-byte the same arrival profile — the 45s/leg budget makes the halt provably window-neutral. Combat is untouched (the halt only runs during the patrol move; contact resets it): KIA 0.83 / WIA 8.75 / civilian casualties 0, within the noise floor.
Reproduce. npx tsx scripts/squad-wedge.ts (cause attribution + halt %;
ITM_NOWAIT=1 for the baseline) · npx tsx scripts/squad-arrival.ts (the window
gate) · held-out A/B on survey-40…55. Full evidence, verbatim baselines, and per-seed
tables in docs/progress/2026-07-03-squad-village-cohesion/; the mechanism and residuals in
docs/issues/036.
Code: lib/sim/world/formation.ts (the wedge/cohesion halt), tasks.ts
(per-leg budget reset), types.ts (task latch fields), scripts/squad-wedge.ts
(the probe), scripts/follower-strand.ts (dead-metric fix).