Skip to content

Normalise the four views with no renderer onto the planet's terrain (#227) - #362

Merged
wormeyman merged 1 commit into
mainfrom
227-gate-normalise
Aug 30, 2026
Merged

Normalise the four views with no renderer onto the planet's terrain (#227)#362
wormeyman merged 1 commit into
mainfrom
227-gate-normalise

Conversation

@wormeyman

Copy link
Copy Markdown
Collaborator

The first of three PRs finishing #227's phase 7. This one is the only one that
can change what the app draws, and it is deliberately small: one source file and
one new spec.

The hole

Four (planet, view) pairs have no renderer of their own:

pair what it draws today why
Vulcanus enemies Vulcanus terrain no Vulcanus enemy bases
Vulcanus trees Vulcanus terrain no Vulcanus trees
Vulcanus landmask Vulcanus terrain Vulcanus has no ocean
Nauvis landmask Nauvis terrain the land mask is Fulgora's view

Each one enters the terrain-family branch, matches none of the overlay blocks,
and returns the plain terrain the branch already rendered. Nothing asserted that,
in either direction. The nearest coverage,
test/wasmVulcanusRenderParity.spec.ts's "routes every view the planet has
through the engine", iterates the five ported Vulcanus views and notes the gap in
a comment without covering it.

Why it has to close before the deletion, not with it

The Rust engine refuses all four pairings outright - the supported match in
crates/fmw-wasm/src/render.rs, which pins (Vulcanus, landmask) as unsupported
in its own test. So the fall-through lands on renderTerrain and
renderVulcanusTerrain, and #227 deletes both. Left alone, these four requests
stop rendering and start throwing, on a path whose error message is discarded
three times over (that is PR 2 of 3).

Why normalise rather than widen the gate

Routing them to WASM would ask the module for a render it has deliberately
decided is meaningless. Normalising onto "terrain" reproduces exactly what they
already draw, which is the property this change has to preserve.

Scope

Nothing in the app can reach any of the four. effectiveView
(ElevationPreviewPanel.vue) emits only terrain/resources/cliffs/rocks/all on
Vulcanus, and "landmask" is absent from the panel's view union on every planet.
So this closes a hole in the type surface, not one a user could see. Worth
closing anyway: the type permits all four, and findIslands builds its request
by hand rather than through the panel.

Planted, not predicted

plant result
normalise onto "all" instead of "terrain" RED - vulcanus enemies: pixels
drop the Nauvis landmask arm entirely GREEN, then RED once a fourth block was added

The second plant is the useful one, and it changed the spec. Equality against
terrain cannot see a missing normalisation arm while the TypeScript fallback is
still present: the un-normalised pair falls through to renderTerrain and paints
the same bytes, so the assertion passes. That means the equality block grades the
normalisation target but not its presence - it would go quietly green
again the day someone deleted an arm.

The fourth block checks that the module's own output buffer was written, which is
the idiom test/wasmIslandFinderParity.spec.ts already uses for this question. A
request the engine served writes there; one that fell through to TypeScript
cannot have. With that block in place the second plant fails with
nauvis landmask: module buffer written.

Gate

pnpm run verify green locally, exit code read out of the log rather than off
the pipe.

…227)

Vulcanus `enemies`, Vulcanus `trees`, Vulcanus `landmask` and Nauvis
`landmask` have no renderer of their own. Each has always produced the
planet's plain terrain, because the overlay blocks never match and the
land-mask branch is Fulgora's alone. The request rendered; it just
rendered terrain, and nothing anywhere said so.

That silence is what makes the deletion risky. The Rust engine refuses
all four pairings outright - the `supported` match in
`crates/fmw-wasm/src/render.rs`, which pins `(Vulcanus, landmask)` as
unsupported in its own test - so once #227 deletes `renderTerrain` and
`renderVulcanusTerrain` the four stop rendering and start throwing.

`servedView` makes the fall-through explicit before that happens. It
normalises onto `"terrain"` rather than widening the engine's gate:
`"terrain"` is precisely what these four already draw, so the pixels do
not move, whereas widening the gate would ask the module for a render it
has deliberately decided is meaningless.

Nothing in the app can reach any of the four. `effectiveView` emits only
terrain/resources/cliffs/rocks/all on Vulcanus, and `"landmask"` is
absent from the panel's view union on every planet, so this closes a hole
in the type surface rather than one a user could see. It is still worth
closing, because the type permits all four and `findIslands` builds its
request by hand rather than through the panel.

Nothing covered these four in either direction, so the spec is new.
Planted rather than predicted:

| plant | result |
| --- | --- |
| normalise onto `"all"` instead of `"terrain"` | RED - `vulcanus enemies: pixels` |
| drop the Nauvis `landmask` arm entirely | GREEN first, RED after a fourth block was added |

The second plant is the reason that fourth block exists. Equality against
terrain cannot see a missing arm while the TypeScript fallback is still
there: the un-normalised pair falls through to `renderTerrain` and paints
the same bytes, so the assertion stays green. Checking that the module's
own output buffer was written tells the two apart, and the plant then
fails with `nauvis landmask: module buffer written`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgrxV4zcQLSsWu4XymFMBV
@wormeyman
wormeyman merged commit b68f010 into main Aug 30, 2026
9 checks passed
@wormeyman
wormeyman deleted the 227-gate-normalise branch August 30, 2026 20:40
wormeyman added a commit that referenced this pull request Aug 31, 2026
Phase 7's deletion. The app has rendered from WASM since #362, so the
TypeScript math under `src/noise/` has no live caller left. This removes it and
rewires the twelve specs that were still reading it.

42 source files go, not the 43 in `deadlist.txt`:
`src/noise/resources/vulcanusResourceCatalog.ts` stays and is trimmed in place,
223 lines to 113. Its two math functions (`makeVulcanusOreFootprint`,
`sulfuricAcidGeyserProbability`), the placement threshold and the three
closure-valued fields are all owned by Rust now; what is left is the order and
the map colours, which `wasmVulcanusRenderParity` grades the engine's pixels
against. #227's done-when is that `src/noise/` holds orchestration and catalogs
only, and that file is now a catalog. It has no imports at all.

`src/noise/preview/elevationRenderRequest.ts` loses its TypeScript fallback
arms, 309 lines out and 97 in. The four views that used to fall through to
plain TypeScript terrain were normalised onto their planet's terrain code in
#362, and the error path and the `startingLakePositions` guard landed in #365,
so nothing reaches the fallback any more.

## The parity specs are converted, not deleted

`tier2Frozen.ts` and `tier3Frozen.ts` were built in #345 and #360 for exactly
this step: `expectFrozen` takes its reference arm as an optional last argument,
so a spec whose TypeScript is gone drops that argument and keeps grading the
engine against a value captured while the two ports demonstrably agreed.
Deleting the specs instead would take the port's arithmetic out of
`wasm32-unknown-unknown` altogether, which is the one thing `cargo test` cannot
cover and the reason the freeze exists.

Tier 2: 68 of Vulcanus's 74 fields keep both arms, and the six whose reference
implementation went (`geyserProbability`, `cliffinessBasic`,
`decorativeKnockout`, `rockHuge`, `rockBig`, `rockDensity`) drop to
frozen-only. `NO_TS_ARM` names them and a new test asserts the list matches what
`tsFields` actually withholds, so a seventh field losing its arm fails rather
than downgrades quietly. Nauvis loses its arm entirely - its whole expression
core was in the dead set.

Tier 3 needed the same treatment and the notes had cleared it. Those specs have
no dead-set imports, which is what was checked; they get their reference arm by
calling `runRenderRequest(req)` with the engine argument left off, which the
rewrite above now refuses. That was 76 failing tests across seven files, and
`tier3Frozen.ts` had written down the trap in advance: after the deletion the
no-engine call "is not a weaker arm, it is the SAME arm - so the comparison
would pass while grading nothing".

`test/tiledEquality.spec.ts` is the one that changes character rather than
shrinking. It passed no engine at all, so it graded the TypeScript renderers
and could not see the WASM gate; it now runs every render through the engine,
which closes a gap the #227 notes had already identified.

## Anti-vacuity guards were re-based, not dropped, wherever the claim survived

Vulcanus's slider guard (still 50 of 74 fields) and window guard (still all 74)
now read the engine; the counts are unchanged because every field with both
arms is pinned to the same frozen value. The off-grid sweep's "these really are
different points" check moved the same way. The routing tests in both render
specs became the sharper statement they always stood in for: with no fallback
left, a view that reaches the engine is exactly a view that REFUSES to render
without one.

Two of those rewrites failed on the first run, and both were findings rather
than test bugs. Recorded where they were measured:

- `checksum_pow` takes its exponent as an `f32`, so the 2.0000001 that used to
  prove the dispatcher leaves the squaring branch narrows to exactly 2. The
  perturbation is now one f32 ULP, asserted to survive the boundary.
- `checksum_distance_from_nearest_point` folds
  `f64::from(distance_from_nearest_point(...))`, and that function returns an
  `f32`, so an f64 ULP on the cap comes back as the same number.

Three tests flipped because they said in their own comments that they should.
`viewNormalisation`'s last block was labelled "the arm #227 deletes";
`elevationRenderRequest.spec`'s KNOWN HOLE row asked to "flip to asserting the
refusal" if `runRenderRequest` ever refused a Nauvis-only view on another
planet, which is what the rewrite does; and `wasmNauvisRenderParity`'s ABI-cap
row said it "belongs to the carve-out, and the #227 deletion removes both
together". The cap is now a refusal, with a companion case at eight points so
the refusal is about the list's length rather than its presence.

Seven Nauvis tier-2 guards could not be re-based: each counts how many swept
positions satisfy a predicate, and the export returns an order-sensitive fold
that cannot be decomposed back into counts. They are deleted here and their
frozen numbers recorded in a follow-up issue, so restoring them behind a
predicate-counting export re-measures rather than re-derives.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant