Skip to content

Commit 0dc4179

Browse files
wormeymanclaude
andcommitted
Re-score phase 5 against the landed #269, and correct the prediction (#225)
Rebased onto `df3e39e`, which narrowed `basis_noise`'s output scale. Six of the 24 frozen counts moved. The point of this commit is not the counts - it is that this branch predicted, in writing and before it could check, which ones would. hairlineCracks 3 -> 2 directly exposed at output scale 0.6 floodCracksA 15 -> 15 not exposed floodCracksB 40 -> 40 not exposed floodPaths 10 -> 10 not exposed floodBasaltsFunc 8 -> 9 exposed VIA hairline_cracks mountainPlasma 7 -> 11 directly exposed at 125 and 625 elev 113 -> 115 directly exposed at 250 and 150 elevation 113 -> 115 same field as elev in this fixture temperature 196 -> 196 unmoved despite reading elev ## Correction one: exposure is transitive `fixtures.rs` said "do not expect the other four to move", reasoning that eleven of this layer's twelve DIRECT `basis_noise_expr` calls sit at power-of-two output scales and are blind by construction. Three of the four held. `floodBasaltsFunc` did not. The reason is in the layer's own verbatim transcription at the top of `src/noise/expressions/vulcanusCracks.ts`: flood_basalts_func = min(max(flood_cracks_a - 0.125, flood_paths), flood_cracks_b) + 0.3 * min(0.5, hairline_cracks) It READS `hairline_cracks`, so the single exposed term reaches it. The three fields that held are exactly the three that never touch it. The rule is that a field is exposed if it reads an exposed site directly OR through any field it composes - counting call sites misses that, and this is the correction. ## Correction two: hairlineCracks got WORSE, 3 to 2 That is not evidence against #269. The primitive is graded 196/196 against the game at five output scales in `test/basisOutputScale.spec.ts`, which is as settled as this project gets. It is the both-directions movement #273 measured: these are deep composed chains carrying other unported narrowings, so correcting one term shifts values slightly and a position that happened to land exactly right can stop doing so. A count falling by one at 61 positions says the field is still wrong for reasons #269 does not address - the `input_scale` question recorded on that issue is the next term to look at. Recorded rather than smoothed over, because a count that moves the wrong way is the kind of thing a port quietly re-baselines and then cannot explain later. Comments carrying the old numbers are updated in the same pass: the helper, crack, elevation and temperature blocks in `fixtures.rs`, and the phase 5 section of CLAUDE.md. Worst residuals are left labelled as measured before #269, since only the counts were re-measured after it. 175 Rust tests pass. Full gate green at VERIFY_RC=0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X2wz8eFazisYuENEkmBvvG
1 parent 0694ec1 commit 0dc4179

2 files changed

Lines changed: 111 additions & 39 deletions

File tree

CLAUDE.md

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,14 +1308,41 @@ are the distance BOTH ports sit from the game rather than a gap between them.
13081308

13091309
**Four things this phase measured that are worth more than the counts:**
13101310

1311-
- **A second, independent fixture points at #269.** `hairline_cracks` scores
1312-
**3 of 61** and is the shallowest expression in its layer - a bare `plasma`,
1313-
nothing composed on top - so the weakness cannot come from the crack file.
1314-
`plasma` subtracts two `basis_noise_expr` results, and that adapter returns
1315-
the un-narrowed f64 product #269 is about. The crack layer calls it at
1316-
`0.3 * 0.325` and `0.6 * 0.325`, neither exact in f32, which is exactly the
1317-
case #269 records `oracle-basis` as blind to by construction. Do not chase
1318-
the five crack counts before #269 is settled.
1311+
- **A second, independent fixture pointed at #269, and #269 has since landed.**
1312+
`hairline_cracks` is the shallowest expression in its layer - a bare `plasma`,
1313+
nothing composed on top - so its weakness could not come from the crack file.
1314+
`plasma` subtracts two `basis_noise_expr` results, and that adapter returned
1315+
the un-narrowed f64 product. Fixed in `df3e39e`, and this branch re-scored
1316+
against it:
1317+
1318+
| field | before | after | exposed? |
1319+
| -------------------- | ------- | ----------- | ----------------------------- |
1320+
| `hairlineCracks` | 3/61 | **2/61** | directly, at output scale 0.6 |
1321+
| `floodCracksA` | 15/61 | 15/61 | no |
1322+
| `floodCracksB` | 40/61 | 40/61 | no |
1323+
| `floodPaths` | 10/61 | 10/61 | no |
1324+
| `floodBasaltsFunc` | 8/61 | **9/61** | via `hairline_cracks` |
1325+
| `mountainPlasma` | 7/38 | **11/38** | directly, at 125 and 625 |
1326+
| `elev` / `elevation` | 113/434 | **115/434** | directly, at 250 and 150 |
1327+
1328+
**Two corrections came out of that, and both are worth more than the counts.**
1329+
1330+
First, **exposure is transitive.** `fixtures.rs` predicted the four flood
1331+
fields would not move, on the grounds that eleven of the layer's twelve DIRECT
1332+
`basis_noise_expr` calls sit at power-of-two output scales. Three held.
1333+
`floodBasaltsFunc` did not, because it READS `hairline_cracks` -
1334+
`+ 0.3 * min(0.5, hairline_cracks)`, right there in the layer's own verbatim
1335+
transcription. The three that held are exactly the three that never touch it.
1336+
Count composition, not call sites.
1337+
1338+
Second, **`hairlineCracks` went DOWN, 3 to 2.** That is not evidence against
1339+
the fix: the primitive is graded 196/196 against the game at five output
1340+
scales. It is the both-directions movement #273 measured. These chains carry
1341+
other unported narrowings, so correcting one term shifts values slightly and a
1342+
position that happened to land exactly right can stop doing so. A count
1343+
falling by one at 61 positions says the field is still wrong for reasons this
1344+
change does not address.
1345+
13191346
- **A clamp flatters a count, and here it is measurable.** The three clamped
13201347
biomes score 403, 402 and 408 of 434 against their own unclamped sources at
13211348
128, 107 and 127 - the same quantity, times 2, clamped. Nothing improved
@@ -1327,7 +1354,7 @@ are the distance BOTH ports sit from the game rather than a gap between them.
13271354
rather than assumed. `vulcanus_elevation` is `max(-500, elev)` and the
13281355
captured `elev` bottoms out at **-58.77**, so the two columns are the same
13291356
field at all 434 positions - 0 of 434 differ - and a port that dropped the
1330-
`max` would score 113 either way. Both are graded anyway; the clamp's real
1357+
`max` would score 115 either way. Both are graded anyway; the clamp's real
13311358
test lives in the module, constructing the case the fixture does not.
13321359
- **A discrete output scores like one.** `mountain_volcano_spots` at 359 of 434
13331360
is the highest UNCLAMPED count in the Vulcanus port, because it is dominated
@@ -1336,7 +1363,7 @@ are the distance BOTH ports sit from the game rather than a gap between them.
13361363

13371364
**`detailNoise` is the reading to carry out of this phase.** It has the
13381365
SMALLEST residual of its three helper fields (7.778e-5) and the FEWEST exact
1339-
matches (**1 of 38**), where `mountainPlasma` has 2.807e-3 and 7 of 38. A field
1366+
matches (**1 of 38**), where `mountainPlasma` has 2.815e-3 and 11 of 38. A field
13401367
can be uniformly close and almost never right, which is the argument for
13411368
counting matches rather than bounding error, stated in one number.
13421369

crates/fmw-noise/src/fixtures.rs

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,20 +2346,25 @@ fn reproduces_the_vulcanus_helper_layer_at_every_captured_position() {
23462346
detail_out.push(f64::from(detail.eval(x, y)));
23472347
}
23482348

2349-
// Frozen exact counts, measured 2026-08-19 and matched field for field
2350-
// against the TypeScript on the same fixture - 38, 7 and 1, with the same
2351-
// worst residuals. They describe the distance BOTH ports still sit from the
2352-
// game, not a gap between them.
2349+
// Frozen exact counts, matched field for field against the TypeScript on
2350+
// the same fixture - 38, 11 and 1. They describe the distance BOTH ports
2351+
// still sit from the game, not a gap between them.
2352+
//
2353+
// `mountainPlasma` was 7 before #269 narrowed `basis_noise`'s output scale;
2354+
// it reads two basis calls at magnitudes 125 and 625, neither a power of
2355+
// two, so it is directly exposed. The other two are unaffected: `wobbleX`
2356+
// and `detailNoise` are `detail_noise` calls and never touch that term.
23532357
for (key, want_exact, got) in [
23542358
// Bit-exact at all 38, worst residual exactly 0, deep-field point
23552359
// included. Two octaves at magnitude 4 is the shallowest call the
23562360
// fixture grades and nothing accumulates.
23572361
("wobbleX", 38usize, &wobble_x),
23582362
// `abs(A - B)` of two basis calls at magnitudes 125 and 625; worst
2359-
// residual 2.807e-3. The output scales amplify the coordinate floor,
2363+
// residual 2.815e-3 (2.807e-3 before #269). The output scales amplify
2364+
// the coordinate floor,
23602365
// which is why this is the loosest of the three in absolute terms while
23612366
// still beating `detailNoise` on exact matches.
2362-
("mountainPlasma", 7, &plasma),
2367+
("mountainPlasma", 11, &plasma),
23632368
// Four octaves at input scale 0.8; worst residual 7.778e-5. The
23642369
// SMALLEST residual of the three and the FEWEST exact matches, which is
23652370
// the whole argument for counting matches rather than bounding error:
@@ -2425,15 +2430,16 @@ fn reproduces_the_vulcanus_crack_layer_at_every_captured_position() {
24252430
assert_eq!(positions.len(), 61, "fixture size");
24262431
let (cracks, _) = vulcanus_sweep(fixture.get("seed0").as_f64() as u32, positions);
24272432

2428-
// Frozen exact counts, measured 2026-08-19 against the oracle and matched
2429-
// field for field against the TypeScript on the same fixture - 3, 15, 40,
2430-
// 10 and 8, with the same worst residuals. They are the distance BOTH ports
2431-
// sit from the game, not a gap between them.
2433+
// Frozen exact counts, matched field for field against the TypeScript on
2434+
// the same fixture - 2, 15, 40, 10 and 9. They are the distance BOTH ports
2435+
// sit from the game, not a gap between them. Before #269 they were 3, 15,
2436+
// 40, 10 and 8; the block below is the full account of what moved and why.
24322437
//
2433-
// Worst residuals, in the same order: 1.853e-3, 4.440e-4, 1.122e-4,
2434-
// 5.460e-4, 6.387e-4.
2438+
// Worst residuals as measured 2026-08-19, BEFORE #269, in the same order:
2439+
// 1.853e-3, 4.440e-4, 1.122e-4, 5.460e-4, 6.387e-4. Only the counts were
2440+
// re-measured after the fix.
24352441
//
2436-
// `hairlineCracks` at 3 of 61 is the weakest and it is the SHALLOWEST
2442+
// `hairlineCracks` at 2 of 61 is the weakest and it is the SHALLOWEST
24372443
// expression in the layer - a bare `plasma` with nothing composed on top -
24382444
// so the weakness cannot come from anything this file builds. It points at
24392445
// the plasma adapter, and specifically at #269: `basis_noise_expr` returns
@@ -2452,16 +2458,53 @@ fn reproduces_the_vulcanus_crack_layer_at_every_captured_position() {
24522458
// time - the input scale decides which noise value you get, never whether
24532459
// the product is representable.
24542460
//
2455-
// So of the twelve `basis_noise_expr` calls this layer makes, exactly ONE
2456-
// is exposed: `hairline_cracks`'s first term, at output scale 0.6, where
2457-
// 80.10% of products differ. The other eleven sit at 1, 0.5 or 0.25 and are
2458-
// blind by construction. That is a one-term explanation for the one bad
2459-
// count here, and it is why the four fields built only from power-of-two
2460-
// sites score 15, 40, 10 and 8 rather than 3.
2461+
// So of the twelve DIRECT `basis_noise_expr` calls this layer makes,
2462+
// exactly ONE is exposed: `hairline_cracks`'s first term, at output scale
2463+
// 0.6, where 80.10% of products differ. The other eleven sit at 1, 0.5 or
2464+
// 0.25 and are blind by construction.
24612465
//
24622466
// It also explains why `oracle-basis` cannot grade this: that fixture was
2463-
// captured at output scale 1. Do not chase `hairlineCracks` before #269 is
2464-
// settled, and do not expect the other four to move when it is.
2467+
// captured at output scale 1.
2468+
//
2469+
// ## What happened when #269 landed, and where the paragraph above was wrong
2470+
//
2471+
// This comment used to end "do not expect the other four to move when it
2472+
// is". Three of the four held. `floodBasaltsFunc` did not, and the reason
2473+
// is a real correction rather than noise: **exposure is transitive through
2474+
// composition, and counting DIRECT call sites misses that.** This layer's
2475+
// own verbatim transcription says so at the top of
2476+
// `src/noise/expressions/vulcanusCracks.ts`:
2477+
//
2478+
// flood_basalts_func = min(max(flood_cracks_a - 0.125, flood_paths),
2479+
// flood_cracks_b)
2480+
// + 0.3 * min(0.5, hairline_cracks)
2481+
//
2482+
// `flood_basalts_func` READS `hairline_cracks`, so the one exposed term
2483+
// reaches it. `flood_cracks_a`, `flood_cracks_b` and `flood_paths` do not
2484+
// read it, and those are exactly the three that did not move. The rule is
2485+
// therefore: a field is exposed if it reads an exposed site DIRECTLY OR
2486+
// THROUGH ANY FIELD IT COMPOSES.
2487+
//
2488+
// Measured across the #269 fix:
2489+
//
2490+
// hairlineCracks 3 -> 2 directly exposed (0.6)
2491+
// floodCracksA 15 -> 15 not exposed
2492+
// floodCracksB 40 -> 40 not exposed
2493+
// floodPaths 10 -> 10 not exposed
2494+
// floodBasaltsFunc 8 -> 9 exposed VIA hairline_cracks
2495+
//
2496+
// **`hairlineCracks` went DOWN, from 3 to 2.** That is not evidence the fix
2497+
// is wrong - the primitive is graded 196/196 against the game at five
2498+
// output scales in `test/basisOutputScale.spec.ts`, which is as settled as
2499+
// this project gets. It is the both-directions movement #273 measured:
2500+
// these are deep composed chains whose remaining error comes from other
2501+
// unported narrowings, so correcting one term shifts values slightly and a
2502+
// position that happened to land exactly right can stop doing so. A count
2503+
// dropping by one at 61 positions says the field is still wrong for
2504+
// reasons this change does not address, not that the change hurt it.
2505+
//
2506+
// Do not "fix" `hairlineCracks` by reverting anything here. The next term
2507+
// to look at is the `input_scale` question recorded on #269.
24652508
//
24662509
// An earlier draft of this comment blamed the layer's INPUT scales
24672510
// (0.3 * 0.325 and 0.6 * 0.325, neither exact in f32). That was wrong, and
@@ -2471,13 +2514,13 @@ fn reproduces_the_vulcanus_crack_layer_at_every_captured_position() {
24712514
for (key, want_exact, select) in [
24722515
(
24732516
"hairlineCracks",
2474-
3usize,
2517+
2usize,
24752518
&(|f: &C| f.hairline_cracks) as &dyn Fn(&C) -> f64,
24762519
),
24772520
("floodCracksA", 15, &|f| f.flood_cracks_a),
24782521
("floodCracksB", 40, &|f| f.flood_cracks_b),
24792522
("floodPaths", 10, &|f| f.flood_paths),
2480-
("floodBasaltsFunc", 8, &|f| f.flood_basalts_func),
2523+
("floodBasaltsFunc", 9, &|f| f.flood_basalts_func),
24812524
] {
24822525
let got: Vec<f64> = cracks.iter().map(select).collect();
24832526
assert_eq!(
@@ -2700,15 +2743,17 @@ fn reproduces_the_vulcanus_elevation_surface_at_every_captured_position() {
27002743
.map(|p| elevation.eval(p.get("x").as_f64(), p.get("y").as_f64()))
27012744
.collect();
27022745

2703-
// Frozen exact counts, measured 2026-08-19 and matched by the TypeScript at
2704-
// 113 and 113, worst residual 1.332e-1 on both.
2746+
// Frozen exact counts, matched by the TypeScript at 115 and 115 (113 and
2747+
// 113 before #269), worst residual 1.332e-1 on both as measured before it.
2748+
// The chain reads basis calls at output scales 250 and 150 plus both
2749+
// `plasma` pairs, so it is directly exposed.
27052750
//
27062751
// **The two columns are the same field in this fixture, and that is a gap
27072752
// in the fixture rather than a result.** `vulcanus_elevation` is
27082753
// `max(-500, elev)`, and the captured `elev` never goes below -500: its
27092754
// minimum over all 434 positions is -58.77. So `elev` equals `elevation` at
27102755
// every position, the clamp is never exercised, and a port that dropped the
2711-
// `max` entirely would score 113 here too. Checked rather than assumed -
2756+
// `max` entirely would score 115 here too. Checked rather than assumed -
27122757
// 0 of 434 positions differ between the two columns.
27132758
//
27142759
// Grading both anyway, because the cost is nothing and the day a capture
@@ -2723,8 +2768,8 @@ fn reproduces_the_vulcanus_elevation_surface_at_every_captured_position() {
27232768
// the third argument for counting matches instead.
27242769
type E = ElevationFields;
27252770
for (key, want_exact, select) in [
2726-
("elev", 113usize, &(|f: &E| f.elev) as &dyn Fn(&E) -> f64),
2727-
("elevation", 113, &|f| f.elevation),
2771+
("elev", 115usize, &(|f: &E| f.elev) as &dyn Fn(&E) -> f64),
2772+
("elevation", 115, &|f| f.elevation),
27282773
] {
27292774
let got: Vec<f64> = fields.iter().map(select).collect();
27302775
assert_eq!(
@@ -2765,7 +2810,7 @@ fn reproduces_the_vulcanus_temperature_at_every_captured_position() {
27652810
.collect();
27662811

27672812
// Frozen exact count, matched by the TypeScript at 196 with the same worst
2768-
// residual of 1.327e-1. Higher than `elev`'s 113 despite reading it,
2813+
// residual of 1.327e-1. Higher than `elev`'s 115 despite reading it,
27692814
// because temperature scales it by 1/100 above zero - `min(e, e / 100)` -
27702815
// so most of `elev`'s residual is divided away before it lands here.
27712816
assert_eq!(

0 commit comments

Comments
 (0)