Reference pair turn1->turn2: 81 leaves closed, 0 regressed (was 78/0). Pair turn2->turn3: 39 closed, 0 regressed (was 36/0). With --commit-blocked=T31 --ai-player 1: 83/0 and 41/0. game/sim/colony: GrowCivilianPopulations models ServerSystem's civilian growth sub-pass. The whole system's delta is clamped to 20,000,000 -- an int64 column of the population-type table, built in the executable from its own literals -- and on both reference pairs that clamp, not the growth curve and not any carrying capacity, is what decides the value: the uncapped delta is 7.5x it and the capacity headroom 25x it. So the pass commits with no tuning table loaded, and says by how much each unmodelled input would have to be wrong before it mattered. The one input genuinely off the wire is the per-species civilian capacity factor. It is handled by running the pass twice, once with the modelled capacity and once with the system's own wire-known dcs limit, and committing only when the two agree. Imperial growth is deliberately NOT committed: it is a no-op on this corpus and would need a capacity the corpus can bound from below but not from above. game/sim/economy: both interest rates in ComputeBudget are WIDENED FLOAT literals, (double)0.01f and (double)0.15f, and are then truncated -- so a treasury of exactly 50,000 earns 499, not 500. This module used the exact decimals, which left the human's savings one money high on the first reference pair and exact on the second. Sixteen hand-computed test expectations moved by one; they were derived from the model, not measured. The live ComputeBudget compare (4,437 calls, 0 divergences) did not catch this because it presented only 20 distinct states and none sat on a rounding boundary. game/sim/colony: ShipRepairCost, the last unmodelled input of the output turn path. The demand is still 0 -- its two design fields are cached stats the save does not carry -- but the zero is now evidenced rather than silent: S13 reports the candidate set, and the independent colony keeps a ten-ship fleet over a colony whose savings close exactly at zero demand. Gates run as separate commands: clean-room OK, host ctest 49/49, and the CT111 shim cross-build exit 0 (required: game/sim is compiled into the shim). The host build and report were also re-run on CT111 and produced identical numbers. docs/G3-civilian-growth.md; notes repo findings/subsystems/population-growth.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARBgSooAfokKUy6wKUKEyZ
5.1 KiB
Civilian population growth, and a savings-interest correction (lane G3)
The colony sub-pass that runs between the build queue and the resource ledger inside each
system's turn, plus the per-ship repair-cost term that was the last unmodelled input of the
output turn path, plus a one-money defect in ComputeBudget that landing the first of those
exposed.
What decides the value
The whole system's civilian delta is clamped to 20,000,000 per turn. That number is an int64 column of the three-row population-type table, which the executable builds from its own literals; it is not a data-file value and it is not a carrying capacity. On both reference pairs the uncapped delta is 150,000,005 and the capacity headroom is 500,000,000, so the clamp is what produces the answer and neither the growth curve nor the capacity chain can change it without being wrong by more than an order of magnitude.
That is why the pass commits with no tuning table loaded: the growth fraction it computes
under an unloaded table (0.25) and under the shipped one (0.30000001192092896) both land on the
same committed value, and so would anything in [0.04, 1.0].
Ranked by consequence, the float that actually decides the value is neither of those. It is the
rescale, trunc(applied x (clamped / total)). The relative error of the quotient reaches
2^-53 and the product's absolute error can exceed half an ulp of 20,000,000, so a
single-species colony landing exactly on the cap is a measurement, not a theorem. It does, at
both 53-bit and x87 64-bit precision, on both pairs; tests/game_sim/test_colony.cpp pins it.
One ulp the other way costs a whole person and moves the human's savings.
The capacity, and how it is handled without the data files
MaxPopGeneric multiplies Size x 1e8 by a per-species, per-group factor that lives in the
data files and is nowhere on the wire. Rather than assume it, the phase runs the pass twice:
once with the modelled capacity and once with the capacity discarded in favour of the system's
own dcs limit, which is on the wire, and commits only when the two agree. It also reports
the threshold the factor would have to fall below before a committed value moved (0.260 on the
reference pair), against a lower bound of 0.27 that the observed growth itself establishes.
For the imperial group the corpus pins the factor exactly, with no data files at all: Gamma
Cephei's pending population bonus of 1e9 never drains, and the bonus apply returns exactly when
Pop >= MaxPop, so MaxPop <= 1e9; and its Pop never shrinks, and the imperial apply shrinks
exactly when pop > cap, so MaxPop >= 1e9. Two behaviours, one capacity, no assumption.
Imperial growth is deliberately not committed. It is a no-op on this corpus and committing it would need a capacity the corpus can bound from below but not from above; that trades a regression risk for nothing.
The interest literals
Landing growth left the human's savings one money high on the first reference pair and exact
on the second. The residual was not growth. ComputeBudget multiplies a treasury by widened
float literals — (double)0.01f and (double)0.15f — and then truncates, so a treasury of
exactly 50,000 earns 499, not 500. This module used the exact decimals. Corrected, with the
constants named in game/sim/economy.h; sixteen hand-computed test expectations moved by one.
Worth recording why it survived: ComputeBudget has been compared live against the original for
4,437 calls with 0 divergences. That run presented only 20 distinct states and none of them
sat on a rounding boundary. A green behavioural compare is not coverage.
Ship repair cost
ShipRepairCost is max(0, buildTarget - (buildProgress + (allowance ? designAllowance : 0)))
— plain 32-bit integers. The ship's ConCap word is the progress, not a capacity: the repair
apply adds points to it and clamps it to the design's build target.
The demand is still taken as 0, because the two design fields are cached stats the save does not carry. What changed is that the zero is now evidenced: the phase reports the candidate set, and the independent colony keeps a ten-ship fleet in orbit over a colony whose savings close exactly with the demand at zero — which cannot happen if any of those hulls carried a cost.
Result
Reference pair turn1 -> turn2: 81 leaves closed, 0 regressed (was 78/0). Pair
turn2 -> turn3: 39 closed, 0 regressed (was 36/0). The three new leaves per pair are the
two colonies' civilian population and the human's savings. With --commit-blocked=T31 --ai-player 1 the counts are 83/0 and 41/0; the extra two are the human's and the AI's
bankruptcy elimination limits, which that phase could not close before because they move with
the population.
Every gate ran as a separate command: clean-room OK, host ctest 49/49, and the CT111 shim
cross-build (exit 0) — required here because game/sim is compiled into the shim.
The reverse-engineering evidence, the falsification table and the honest list of what no corpus
save exercises are in the notes repo, findings/subsystems/population-growth.md.