test(cliffs): the RUNTIME DESTROY PROBE - the cascade reproduces EXACTLY (#84) - #139
Merged
Conversation
…TLY (#84) #127 asked for a runtime probe that destroys a cliff outside map generation, because the connection rules cannot be scored from map-generation output at any number of regions: the game's output is always connection-consistent, so there is never a dangling end for the rules to act on. #135 read all four of Cliff::onDestroy's cascade gates on the Lua path and showed such a probe is safe. This builds it. RESULT: the cascade is confirmed, and the port's destroyEnd model reproduces the game CELL FOR CELL AND ORIENTATION FOR ORIENTATION - zero discrepancies across 16 targets, in both a border and an interior arm. Trims look exactly like the model says: south-to-west -> none-to-west, east-to-south -> east-to-none. arm targets removed orientations changed border, correction ON 8 9 13 border, correction OFF 8 8 0 interior, correction ON 8 10 12 interior, correction OFF 8 8 0 This matters for the residual: #114/#115 identified it as "31 destruction decisions", and the destruction cascade is no longer a suspect for those - it is now validated against the game rather than only against the disassembly. Three things nearly made this a false result, and all three are recorded: do_cliff_correction DEFAULTS TO FALSE. A probe calling a bare destroy() gets untouched neighbours, and that null reads exactly like "the game does not cascade". Both values are captured; the OFF arms are that near-miss kept as the control. The comparison was clamped and looked like a defect. The first target set was picked in scan order, so all eight landed on the region's top edge and the port appeared to under-destroy by 7 cliffs. Every mismatch was an edge artifact - a cliff at y = 1498.5 is in the dump only through bounding-box overlap while its own neighbours are outside the dump, so the game cascades through cliffs the comparison cannot see. With 48 tiles of margin the agreement is exact. The counts would have misled. Removals said 8 extra at the border against 1 in the interior, which reads like a border-only cascade; orientations say 13 and 12. A cliff is only removed when a trim leaves it with nothing, so removals measure how many neighbours were single-ended, not where the rule runs. A fourth was caught by the probe's own instrumentation: the first run FOUND only 4 of 8 targets, because find_entities_filtered selects on the per-orientation rotbb bounding box, which is offset from the cell centre and need not contain it. The search box is now wider than the largest half-extent with an exact position match, and `found` is dumped per target so a lookup miss can never read as a destruction. What this does NOT settle: updateConnections. It is not reachable from Lua at all, so that gate is still unscored. A cheaper route was tried and failed first and should not be retried - #137's chunk-order lever produces arms where a border chunk is applied with its neighbour chunk PROVABLY ungenerated, which is exactly the gate's input, but on the [1500,1500] west seam all five cliffs carrying a west end have a neighbour that isCliffConnected accepts, so there is still nothing to drop. The exact-match assertion was confirmed non-vacuous by planting a destroyEnd that never force-destroys: both ON arms fail. Verification: pnpm run verify green - 1534 app (up 10) + 13 worker + 3 container tests, 0 warnings, exit 0. Measurement only; shipping accuracy unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VRsPwmSwjnFKVSS6jx9Mo
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VRsPwmSwjnFKVSS6jx9Mo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#127 asked for a runtime probe that destroys a cliff outside map generation, because the connection rules cannot be scored from map-generation output at any number of regions — the game's output is always connection-consistent, so there is never a dangling end for the rules to act on. #135 read all four of
Cliff::onDestroy's cascade gates on the Lua path and showed such a probe is safe. This builds it.Result
The cascade is confirmed, and the port's
destroyEndmodel reproduces the game cell for cell and orientation for orientation — zero discrepancies across 16 targets, in both a border and an interior arm. Trims look exactly like the model says:south-to-west → none-to-west,east-to-south → east-to-none.This matters for the residual. #114/#115 identified it as "31 destruction decisions". The destruction cascade is no longer a suspect for those — it is now validated against the game rather than only against the disassembly.
Four things that nearly made this a false result
do_cliff_correctionDEFAULTS TO FALSE. A probe calling a baredestroy()gets untouched neighbours, and that null reads exactly like "the game does not cascade". Both values are captured; the OFF arms are that near-miss, kept as the control.y = 1498.5is in the dump only through bounding-box overlap while its own neighbours are outside the dump, so the game cascades through cliffs the comparison cannot see. With 48 tiles of margin the agreement is exact.find_entities_filteredselects on the per-orientationrotbbbounding box, which is offset from the cell centre and need not contain it. The box is now wider than the largest half-extent with an exact position match, andfoundis dumped per target so a lookup miss can never read as a destruction.What this does NOT settle
updateConnections. It is not reachable from Lua at all, so that gate remains unscored. A cheaper route was tried and failed first, and is recorded so it is not retried: #137's chunk-order lever produces arms where a border chunk is applied with its neighbour chunk PROVABLY ungenerated — exactly the gate's input — but on the[1500,1500]west seam all five cliffs carrying a west end have a neighbour thatisCliffConnectedaccepts, so there is still nothing to drop.Also measured, and the reason there is no unconnected-target control arm: exactly one cliff of the 885 has no connected neighbour at all — itself a restatement of the connection-consistency #127 measured.
Non-vacuity
The exact-match assertion was confirmed by planting a
destroyEndthat never force-destroys: both ON arms fail. Target selection is re-derived from the port's realonChunkBorder/isCliffConnectedin the spec, so the capture's Node-safe duplicate cannot drift silently.Verification
pnpm run verifygreen — 1534 app (up 10) + 13 worker + 3 container tests, 0 warnings, exit 0. Measurement only; shipping accuracy unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_012VRsPwmSwjnFKVSS6jx9Mo