Development log · 2026-06-08

Soldier-Scale Realism — three felt wins, one honest revert

The player now watches this counterinsurgency sim at the soldier scale — and at that scale four things "didn't feel right." Each was turned into a number before it was touched. Two shipped as large measured wins, one shipped as a quieter one, and the fourth — the hardest — was built, measured against its own baseline, found to regress the most load-bearing system in the codebase, and reverted. The revert is in here on purpose: an honest logbook is part of the product.

16–37× longeron-station dwell, raised to doctrine
0 → 48%contacts the squad now flanks unbidden
0 → 100%bounding-overwatch discipline
×3.78→×3.13 then revertedOP detour: a win that stalled a squad

Item 1 — Village dwell was ~70× too short, and the census teleported

shipped The complaint, as a number

A cordon-and-search is a half-day operation; a census enrolls a village person by person. In the sim, every on-station dwell was minutes — and the census flipped to "done" the very tick the squad arrived. A headless probe (scripts/dwell-probe.ts) read it off HEAD: census 300 s, with the "done" flag set at 0 s on arrival — a teleported state flip, not work.

missionbeforeafterdoctrine band
presence180 s3,600 s (1 h)~1 h flag-showing
recon150 s4,500 s (1.25 h)≥ 1 h
ambush1,100 s14,400 s (4 h)hours of waiting
census300 s, INSTANT11,200 s, PROGRESSIVE2–8 h, population-driven
cordon300 s7,700 s (2.1 h)2–8 h half-day
overwatch900 s18,000 s (5 h)3–8 h (24 h cap)
KLE / shura360 s5,856 s (1.6 h)1–2 h per meeting

Why "×70 the numbers" would have been worse — and what was done instead

A static six-hour wait at 4× speed is more tedious than a five-minute one, not less. The fix is four coupled changes, not one knob:

A dwell event modal rendered live in the deployed game
Live, in the deployed game (seed korengal-2). "The Search Turns Up Something": a cache pulled from a false floor during the census of Loy Kalay. The header reads SITUATION · FIND · DAY 1 · 06:00 — verified end-to-end: choosing "leave it under surveillance" cleared the modal and logged the correct cache-watch intel. Every choice handler was exercised headlessly; an anti-repeat guard stops a long census drawing the same grievance twice.

Item 2 — The squad sat on a base of fire while one man shuffled

shipped The complaint, as a number

The owner watched a firefight and saw the whole squad go to ground and stay there — at most a man or two repositioning, never a maneuver element. A probe (scripts/squad-maneuver-probe.ts) confirmed it on HEAD and found three damning facts: under the default Hold drill the squad never autonomously assaulted; the squad's standing order (SOP) was cosmetic — Hold, Suppress and Assault all spent ~45% of contact breaking contact and behaved almost identically; and even under an explicit Assault order, bounding discipline was 0% (the whole maneuver team rushed at once) along a route whose flank-cover ratio was 0.92 — i.e. it charged through less cover than a straight line. A frontal banzai rush into the open.

metric (heat 0.45)Hold before→afterSuppressAssault
enters an assault0%0%46% (frontal)
enters an assault→ 22%→ 10%→ 29% (bounding)
bounding discipline0%
bounding discipline→ 100%→ 100%→ 100%
flank-cover ratio (when flanking)0.92 → 1.68–1.84  (route cover ÷ straight-line cover; >1 = a covered flank)

The fix gives the maneuver decision to the squad-leader AI — never to the player, who still only sets the SOP and approves fires (that is the game's soul: "combat is 100% AI; the hardest part of command is watching"). Four mechanisms:

A live firefight: the squad split into a base of fire and a flanking maneuver element
Live capture, frozen mid-assault (seed korengal-2, heat 0.85). The 9-man squad has split itself: a 4-man base of fire pinning the enemy to the south, and a 4-man maneuver element swinging to a covered flank objective 177 m off the enemy axis to the north. The frozen-frame dump confirmed that within the maneuver element exactly one buddy pair was moving while the other overwatched — bounding discipline, in the real game. The restraint check (balance.ts) held: KIA stayed flat (the flank trades exposure for cover), and no individual-soldier micro was added.

Item 4 — The squad rings the spur instead of climbing it

tried · measured · reverted Why this section exists

This is the part most reports leave out. A squad ordered to a peak observation post planned a 3.2 km route around a 468 m climb — it ringed the spur instead of switchbacking up the face. The cause was clear and the fix was textbook (anisotropic walking cost so a traverse is cheaper than a straight climb, plus softening the hard cliff cutoff). It was built. And then the numbers said: don't ship it.

A fair probe (scripts/op-route-probe.ts) measured the detour against a fixed objective — the highest peak reachable under the strict cutoff, identical before and after — comparing the live tree to a clean checkout of HEAD in a git worktree:

attemptmean detourverdict
HEAD baseline×3.78the problem
anisotropic Tobler cost alone×4.52worse — over-penalises, pathological zig-zag
directional cost (right magnitude) + turn penalty×3.13better detour, +5pp reachability… but stalled a squad
REVERTED to baseline×3.780 movement stalls — clean

The reason it was reverted. The best attempt improved the detour and made two more villages reachable — but a 12-deployment movement sweep found a new stall: on one seed a squad froze on flat ground, a re-path interaction the route change exposed. The remaining detour was also real: 45% of the worst seed's direct line is genuine cliff and river that no cost change makes climbable. A modest, below-target win that regresses the most load-bearing, hard-won system in the codebase is the wrong trade. The repo's own doctrine names this exact moment: "if anisotropic-cost-on-the-existing-grid thrashes, that's the signal to do the finer-patch / any-angle planner as a rebuild, not more patches."

An OP route looping around a large impassable cliff massif
What "ring the spur" looks like — and why some of it is correct. The squad (yellow, lower-left) routing to a peak OP (green): the cyan/amber route loops up and around a large red cliff massif. Much of this detour is real terrain a foot mover genuinely can't cross — the switchback-up-a-climbable-face behavior needs an any-angle (Theta*) planner on a finer tactical patch, now scoped as docs/issues/019. The fair probe that proved all this is kept for that rebuild.

The micro-terrain half of the same foundation — letting a soldier take cover behind that rock or low wall, not an averaged 5 m cover cell — is scoped alongside it as docs/issues/020; the flank work above already showed why it matters (a meaningfully-covered flank exists only 3–7% of the time on the coarse cover raster, even though the flank mechanism itself is proven).

How it was verified

Did an AI really do this? The interesting evidence isn't the code — it's the third item. An agent built a plausible, doctrine-correct fix, measured it against its own baseline on held-out seeds, found it traded a below-target gain for a movement-system regression, and reverted its own work — then wrote the reason down as two tracked issues and a reproduction probe for whoever builds the rebuild. The wins are nice; the revert is the tell.

Engineering record: docs/progress/2026-06-07-soldier-scale-impl/ (baselines, after-numbers, live dumps). Deferred work: docs/issues/019 (any-angle OP planner), docs/issues/020 (micro-terrain cover objects). Probes: scripts/{dwell,squad-maneuver,op-route}-probe.ts. Built and verified by an autonomous agent; numbers are quoted as measured.