You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Narrow basis_noise's output_scale the way the game does, 196/196 (#269) (#288)
`basisNoiseExpr` returned `output_scale * basis` as an un-narrowed f64. The game
evaluates `f32(f32(output_scale) * basis)`. Graded against the game at 196
positions and five output scales using the fixture captured in #287:
f32(f32(output_scale) * basis) 196/196 at all five scales <- the game
f32(output_scale * basis) 196, 110, 151, 196, 196 <- #269's proposal
output_scale * basis (shipped) 196, 28, 6, 96, 1
f32(output_scale) * basis 196, 0, 0, 0, 1
Every count is an exact f64 equality, never a bound. Both sides produce f32
values, so a tolerance would pass for all four models at once and measure
nothing (#162).
**Narrowing the product is necessary and NOT sufficient, which is the half the
issue does not say.** The `output_scale` CONSTANT is held at f32 as well - the
same shape as #273 - so this is both cases of the two-case rule in
`src/noise/eval/f32.ts` at a single call site. The two forms coincide only where
`output_scale` is already f32-exact, which is why the second row above is
complete at 1 and 125 and short at 0.6, 0.51 and 0.75.
The assertion in `test/basisOutputScale.spec.ts` that froze `[196, 28, 6, 96, 1]`
was written to go red when this landed, and it did. It is now all-196 rather
than loosened.
## Re-scored, because a green gate cannot see this change
This alters shipped behaviour in five expression files that each pass their own
oracle spec, and #273 measured that this class of change moves counts in BOTH
directions. So every affected field was re-scored by exact f32 matches, before
and after, by reverting only `basisNoiseExpr` on this same tree:
vulcanus mountain_plasma 7 -> 11 of 38
vulcanus elev 114 -> 116 of 434
vulcanus elevation 114 -> 116 of 434
nauvis elevation_lakes 13 -> 13 of 17 (control)
nauvis elevation_nauvis 3 -> 3 of 17 (control)
cliffiness gate seed=123456 1024 -> 1024 (control)
cliffiness gate seed=777771 1024 -> 1024 (control)
Nothing regressed. `test/basisOutputScaleCallers.spec.ts` freezes those counts
as a live spec rather than leaving them in a commit message, for the reason
`fixtures.rs` carries #273's planted fix as a test: a measurement nobody runs
goes stale. Reverting `basisNoiseExpr` takes it red at 7 and 114 - watched, not
assumed - while its control test stays green.
Which sites the fix can reach is decided by the output scale alone. A
power-of-two scale is a pure exponent shift and can never leave the f32 grid, so
the eleven `plasma` calls the crack layer makes at 1, 0.5 and 0.25 are blind by
construction and did not move. `cliff_fields` (0.51), `nauvis_shared` (0.6),
`elevation_lakes` (1.5), `vulcanus_elevation` (250, 150) and the `plasma`
magnitudes routed into it (125/625, 0.15/0.75) are the exposed ones.
## A measured instance of #256
The full TypeScript suite passed with ZERO failures under this change. Every
oracle spec covering these five callers asserts a combined abs/rel bound wide
enough to swallow the whole difference, so `pnpm run verify` going green was not
evidence of anything. Only the two frozen-count specs could see it. That is
recorded in CLAUDE.md next to the three port findings, because the next person
to change a shared op needs to know the gate will not catch them.
## Rust
`basis_noise_expr` now returns `f64::from((output_scale as f32) * basis)`. An
f32 multiply IS `f32(f32(os) * basis)`: the exact product of two f32s fits in an
f64 mantissa, so there is no double rounding and it matches
`Math.fround(Math.fround(os) * basis)` bit for bit.
Its docblock argued at length that NOT narrowing was deliberate, because no
fixture could grade the difference and a unilateral change would read as a port
bug in tier 2. That reasoning was sound and is now spent: #287 captured the
fixture, and both ports change in this commit, so tier 2 stays honest. The
docblock records that rather than quietly dropping it.
The `the_output_scale_multiplies_the_result_once` test asserted the old reading
and is replaced by `the_output_scale_and_the_product_are_both_narrowed`, which
keeps the original's discriminating scan and adds a second counter for points
that separate narrowing the product alone from also holding the constant at f32
- so the scan can see the half #269 omits. A new
`a_power_of_two_output_scale_is_blind_to_the_narrowing` asserts the immunity
rule directly. 137 Rust tests, up from 136.
`engine.wasm` rebuilt: 84,160 bytes, 4031d1d7. No POISONED_TESTS entry names the
renamed test, and no op was added, so none is owed.
Full gate green at VERIFY_RC=0, including the committed-wasm byte compare.
Claude-Session: https://claude.ai/code/session_01X2wz8eFazisYuENEkmBvvG
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments