Skip to content

fix(#661): the qcat pocket swimmer escapes — floaters never enter the depenetration net, and a blocked swimmer can duck UNDER an obstruction - #767

Open
djhenry wants to merge 3 commits into
mainfrom
fix-661-swim-stepup
Open

fix(#661): the qcat pocket swimmer escapes — floaters never enter the depenetration net, and a blocked swimmer can duck UNDER an obstruction#767
djhenry wants to merge 3 commits into
mainfrom
fix-661-swim-stepup

Conversation

@djhenry

@djhenry djhenry commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Fixes #661 — the qcat spawn-pocket swimmer that mounted the tile floor at −55.9687 (0.009 u above the waterline) and could never get back off. Offline, deterministic; no live client involved.

The mechanism I actually found — the issue's attribution was wrong

#661 names the swimming step-up (#191's haul-out branch) as the writer. I instrumented both the step-up branch and the depenetration net on main @ 269dbbf and drove the exact repro (pocket swim plane −57.978 → shaft, want_swim, horizontal wish only). The step-up never fires — no low_hit even occurs before the mount. The measured chain is:

  1. The depenetration net misreads a floating body as embedded. is_embedded's footprint ring probes at feet + 3; a swimmer floats at surface − 2, so the probe tests the AIR band 1 u above the waterline, where the pocket-rim geometry lives. On alternate frames the swimmer read as "embedded" while its route was physically open — on every frame the wish was allowed to run, the slide made full 0.733 u progress (never blocked).
  2. The ring push-out ate the input and ping-ponged the body (wish frame 0.733 u toward the shaft, push-out frame 1.0 u away, net zero) — the live walker_stalled.
  3. Frame 33: the beach. The first footprint-clear ring candidate fell a fraction of a unit outside the .wtr region's XY extent. Recovery::at_column's afloat arm falls through to the water-blind nearest_floor hunt for a dry candidate column — the trace shows rec=Grounded(-55.96875) cand_wet=false, dxy exactly 1.000 (= PUSHOUT_RADII[0]), dz +2.0092. The body — still swimming, in water — is beached dry, on_ground, want_swim inert. One-way. (This behaviour was even pinned by a test: see "flipped tests" below.)
  4. With 1–3 fixed the swimmer still could not finish: the passage to the shaft is only open below the swim plane, and the controller has a 2.5 u step-up out of water but no mirror to pass under an obstruction. It pressed into the pocket's south face forever, converging asymptotically at (−45.75, 1036.13, −57.978). That asymmetry is the "one-way transition" of the issue title, in its true location.

The mount surface being "a real 14×14 floor indistinguishable from a bank" turned out not to be the crux at all: nothing needs to classify the surface, because the body that got put on it was never in the step-up's hands.

The fix (all in src/movement.rs)

  • A body afloat in water never enters the depenetration net. The medium decides at the door, with the same body_in_water probe step uses. Buoyancy owns a floater's vertical, the collided slide its lateral; every question the net asks about a floater is mis-posed (details in the door comment).
  • Recovery::Afloat is deleted; at_column loses its afloat parameter. The only constructible recovery is Grounded, and only dry bodies can reach the constructor. The qcat spawn pocket: a swimmer at the pocket swim plane is mounted onto the ceiling lid and stranded dry (the actual cause of the #329 live wedge) #649 invariant in its strongest form — not a guard, a state that cannot be expressed.
  • try_duck_under: the swimming step-up's downward mirror. A blocked swimmer first tries sinking its feet (collided, ≤ STEP_UP + GROUND_SNAP_TOL = 2.5 u, the same envelope the step-up can climb) and re-sliding; the duck is taken only when it measures strictly more lateral progress and both the lowered start and destination keep the feet in water. At a bank whose face is solid to the bottom the measurement fails and the haul-out keeps the right of way (Navigation + fall-guard don't handle swimmable water — characters can't cross a surface-level pool (stuck leaving Halas) #191). Gated on wish_vspeed <= 0 so an explicit upward (haul-out) drive is never countermanded. This is what turns "stops being stranded" into "actually escapes": measured end state (-45.796, 1029.938, -44.98247) — the shaft XY on the shaft's swim plane, wet, in ~0.7 s.
  • The underworld fall-through guard's recovering arm is routed through recover() (the issue's "second un-Recovery'd writer" note). Behaviour-identical: the only extra effect (stuck_time = 0) is a no-op on the frames that arm can run.

The one owner-approved boundary I did not touch: haul_out_up stays 2.0 and the planner side is untouched — no file outside src/movement.rs + the three test files changed.

What did NOT work (recorded so nobody re-walks it)

  • A more water-aware net is measurably worse, not better. An intermediate revision kept the net for swimmers but probed the footprint at the submerged torso band (min(feet+3, surf−0.5)). It stopped the strand — and then fired during ordinary bank approaches and tunnelled the body through the bank face to the first clear ring candidate on the far side: walker_sim::p1 failed with the swimmer at [701.0, 24.8, 8.0], ~650 u out to sea. There is no probe height at which "near geometry" is an emergency for a body that floats; that measurement is what pushed the design to "floaters never enter" rather than "floaters are probed differently".
  • Closing only the dry-candidate beach (keep the net, Afloat-or-skip) does not fix the wedge: measured, the swimmer stays wet at its own depth but livelocks in the push-out ping-pong for the full 12 s — the strand becomes a stall.
  • The prompt-era hypotheses (footprint_clear landing guard on the step-up; any rule classifying the mount surface) were moot once the mechanism was measured: the step-up is not the writer here.

Flipped / rewritten tests — called out explicitly

  • tests/water_capability.rs::qcat_pocket_swim_plane_strands_the_swimmer_on_the_tile_floorqcat_pocket_swim_plane_swimmer_escapes_to_the_shaft. Old assertions: end z ≈ −55.9687, !in_water(end), >5 u short of the shaft. New: end on the shaft swim plane −44.982 ± 0.05, within 1.5 u of the shaft XY, still in water — swept over start depths {plane, −59.0, −60.5}, covering the band qcat spawn pocket: the swimming step-up hauls a swimmer onto a tile floor 0.009 u above the waterline that it cannot get back off (the remaining #329 wedge) #661 measured as STRANDED (−58.00…−60.75).
  • movement::tests::depenetration_grounds_a_swimmer_pushed_out_of_the_water_entirelya_swimmer_whose_only_clear_neighbours_are_dry_is_never_beached_by_the_net. The old test pinned the defect (asserted the afloat body ends at z=2.0, grounded, dry — exactly the qcat spawn pocket: the swimming step-up hauls a swimmer onto a tile floor 0.009 u above the waterline that it cannot get back off (the remaining #329 wedge) #661 beach at 1/10 scale). The new one asserts never-beached, still-wet, no hold, and still responsive to swim input. RED on main.
  • movement::tests::depenetration_never_mounts_a_swimmer_onto_the_slab_above_it — first three assertions (never lifted / never grounded / still wet) unchanged; the fourth ("the push-out must have resolved the horizontal overlap") required the net to act on a swimmer and is replaced by a responsiveness assertion (a swimmer between close parallel walls is in a canal, not a trap — swim input moves it).
  • movement::tests::the_nets_water_probe_is_the_BODY_not_the_feetz == 0 exactly was the net freezing the frame; now buoyancy's ordinary ≤0.5 u first-frame rise begins, so the assertion is z < 1.0 && !on_ground, which still fails the pinned mutation (feet-only probe → beached at z=2.0, grounded).
  • tests/water_capability.rs::the_depenetration_push_out_holds_a_qcat_swimmer_at_its_own_depththe_first_frame_never_lifts_a_qcat_pocket_swimmer_toward_the_dry_tile: frame 1 from that wedged start now legitimately ducks (bounded, collided, −2.0 u in the wish direction), so dz ≈ 0 became dz < 1e-3 && dz > −2.6 + not grounded + still wet — the qcat spawn pocket: a swimmer at the pocket swim plane is mounted onto the ceiling lid and stranded dry (the actual cause of the #329 live wedge) #649 teleports (+2.0092 mount, −12 drop) are still rejected.

New tests: movement::tests::a_swimmer_ducks_under_a_hanging_face_instead_of_stranding_at_it (CI, asset-free; RED on main twice over — no duck, and the net eats the approach) and movement::tests::a_swimmer_at_a_solid_bank_still_hauls_out_the_duck_does_not_override_191 (CI companion to walker_sim P1; RED if the swimming step-up is disabled).

Proofs

1. Escape test (was the strand pin), on this branch:

test qcat_pocket_swim_plane_swimmer_escapes_to_the_shaft ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.89s

2. Lid tests stay coherent:

test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (synthetic_water_capability)

3. #191 blast radius, measured (step-up disabled → RED): with the fix in place and only the swimming half of the step-up removed ((self.on_ground || swimming)self.on_ground):

test p1_haul_out_admission_matches_controller_execution ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 10 filtered out; finished in 0.00s
test movement::tests::a_swimmer_at_a_solid_bank_still_hauls_out_the_duck_does_not_override_191 ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 163 filtered out; finished in 0.01s

proving the branch this issue pointed at is still the load-bearing carrier of #191's haul-out guarantee, and this fix did not reroute around it. (With the fix intact, P1 is green: test result: ok. 1 passed; 0 failed — and note P1 also failed RED under the withdrawn submerged-probe design, which is how that design died.)

4. Mutation checks (fix reverted → new tests RED):

# net door reverted (`body_in_water(col, p) ||` removed) + duck disabled:
test the_first_frame_never_lifts_a_qcat_pocket_swimmer_toward_the_dry_tile ... FAILED
test qcat_pocket_swim_plane_swimmer_escapes_to_the_shaft ... FAILED
test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 2 filtered out; finished in 0.46s
test movement::tests::a_swimmer_whose_only_clear_neighbours_are_dry_is_never_beached_by_the_net ... FAILED
test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 161 filtered out; finished in 0.00s

# duck disabled alone (door intact) — the honest kill for the duck-capability tests:
test movement::tests::a_swimmer_ducks_under_a_hanging_face_instead_of_stranding_at_it ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 163 filtered out; finished in 0.00s
test qcat_pocket_swim_plane_swimmer_escapes_to_the_shaft ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 3 filtered out; finished in 0.28s

(Honesty note on the combined revert: under it, `a_swimmer_ducks_under_a_hanging_face_...` came back GREEN — because the revert restores the net's dry `nearest_floor` hunt for floaters, which TUNNELED the swimmer through the test's wall and dropped it `Grounded(-40)` on the far side, whence it "escaped". That is the pre-#649 defect resurfacing inside the mutant, not a weakness of the test; the duck-only mutation above is the meaningful kill, and it is RED.)

Full workspace suite:

Finished `test` profile [unoptimized + debuginfo] target(s) in 28.99s
49 `test result:` lines, all `0 failed`; totals: 1553 passed / 0 failed / 43 ignored

Depenetration corpus over baked zones (asset-gated blast-radius harness):

zones=42 embedded=35244
  changed: dry-body=0 wet-chest-dry-feet=815 submerged=535
  unchanged: dry-body=33444 wet-chest-dry-feet=3 submerged=447
  no recovery: legacy=21167 new=22517
test movement::tests::depenetration_corpus_over_baked_zones ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 163 filtered out; finished in 407.37s

`changed: dry-body=0` is the load-bearing row: dry depenetration is untouched across every baked zone.
The wet-bucket "changed" rows are the point of the fix — those recoveries no longer happen; the
iteration invariant (drove every embedded sample 2 s through the real controller) found zero drifters.

scripts/check-no-local-detail.sh: OK — no forbidden patterns in tracked files.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HQVEpaaKeXsZcW9VT2roeV

…blocked swimmer can duck UNDER an obstruction

The qcat pocket strand's measured writer was NOT the swimming step-up the
issue named (instrumented: it never fires there) but the depenetration
net: its footprint ring probes 1u ABOVE the waterline for a swimmer at
the float plane, so pocket-rim air-band contact read the swimmer as
embedded, the ring push-out ate its input (the live walker_stalled), and
on frame 33 a candidate a fraction outside the .wtr region took
Recovery::at_column's water-blind dry-candidate fall-through and beached
the still-swimming body onto the dry tile at -55.96875 - on_ground, dry,
want_swim inert, one-way.

Fix, three-sided, all in src/movement.rs:
* the net's door: a body afloat in water never enters (buoyancy owns its
  vertical, the collided slide its lateral); Recovery::Afloat is deleted
  and only dry bodies can reach the Grounded constructor;
* try_duck_under: the swimming step-up's downward mirror - a blocked
  swimmer may pass under an obstruction within the same 2.5u envelope
  the step-up climbs, taken only when diving measures strictly more
  progress and stays in water, so bank haul-outs (#191) keep the right
  of way (p1 + a new asset-free bank test both RED if the step-up is
  disabled);
* the underworld guard's recovering arm routed through recover().

The strand test flips to asserting the ESCAPE (pocket plane -> shaft
swim plane -44.982, swept over the whole formerly-stranded start band);
the test that pinned the dry-candidate beach is inverted; the
depenetration corpus over 42 baked zones shows dry-body recoveries
byte-identical (changed: dry-body=0) and zero drifters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQVEpaaKeXsZcW9VT2roeV
@djhenry

djhenry commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Independent adversarial review — REQUEST CHANGES

Reviewed at 617b7d35 in my own detached worktree. Everything below is labelled MEASURED (I ran it and read the output) or REASONED (inference I did not execute). I re-derived the author's figures rather than accepting them, and I list at the end what I could not verify.

The core of this PR is right, and better than I expected going in. The door change is genuinely load-bearing and well pinned, the Afloat deletion holds up, the escape test passes on the real baked qcat assets, and every mutation the author reported reproduced exactly. The blocking findings are all in the new try_duck_under capability, not in the net fix.


🔴 BLOCKING 1 — the duck is a ONE-WAY transition over a shallow far shelf. Measured.

src/movement.rs:668-670 states the reason the duck is tried before the haul-out:

"staying in the water is the reversible move (a swimmer that dives can always surface again; a haul-out onto dry ground cannot be swum back through, which is the one-way transition #661 is about)"

That claim is false. MEASURED, on synthetic geometry that is 3 meshes plus a water slab — the author's own hanging-face scene with one change: the far side of the lintel gets a shelf instead of a 40 u pool.

Scene: near-side pool floor -40 (east < 4), far-side shelf at far_floor (east > 4), lintel wall(4.0, -0.2, 20.0) (bottom edge a hair under the waterline — the author's own value), water slab -40 … 0, swimmer starts at the float plane -2 west of the lintel. Drive east 4 s, then west 10 s:

REV767 PROBE-FG far_floor=-4.6: crossed=true out=[135.0, 0.0, -2.0]  back=[-215.0, 0.0, -2.0]  returned=true
REV767 PROBE-FG far_floor=-3.0: crossed=true out=[135.0, 0.0, -2.0]  back=[   4.33, 0.0, -2.0] returned=false
REV767 PROBE-FG far_floor=-2.5: crossed=true out=[135.0, 0.0, -2.0]  back=[   4.33, 0.0, -2.0] returned=false

With the far shelf at -3.0 the swimmer ducks under the lintel, travels 130 u east, and can never get back: driving west for ten full seconds it converges at east 4.33, pressed against the lintel's west face, for ever. The mechanism is the asymmetry of the envelope: on the near side swim_sink(-2.5) finds 38 u of water and lowers the full 2.5 u (rays at -4.0/-0.5, both clear of the -0.2 lintel bottom); on the far side the shelf clamps the sink to ~1 u, the chest ray stays at +1.0, and the lintel blocks it. REASONED (from the same numbers): the duck's reach is measured from wherever buoyancy has parked the body, so any geometry where the far side's floor is shallower than STEP_UP + GROUND_SNAP_TOL below the local float plane is a trapdoor.

The end state is on_ground=false, in_water=true, hold()=None — no controller-level signal at all. Under the project's top invariant that is the bad shape: a character that is permanently trapped while every observable reads "swimming normally".

Submerged arches, sunken doorways and pool ledges with a shallower far side are ordinary EQ geometry, and #661's own subject zone has two water columns 13 u apart in surface height. Fixing a one-way transition by adding a different one-way transition is the outcome this PR set out to prevent. I do not think the shape of the duck is wrong; I think it needs a returnability condition (or an explicit, owner-visible statement that it is one-way, with hold() telling the truth when the return is shut).

Repro is rev767_probe_fg_duck_return_over_a_shallow_far_shelf — 30 lines, asset-free, drops straight into movement::tests; happy to hand it over.


🔴 BLOCKING 2 — three of the duck's four guards are pinned by nothing at all

The gate standard is "revert the fix → tests go RED". I mutated each guard in try_duck_under individually and ran the full lib suite, walker_sim, synthetic_water_capability, and the asset-gated water_capability with --include-ignored. MEASURED:

# Mutation Result
M4 drop the intent.wish_vspeed <= 0.0 gate (movement.rs:681) ALL GREEN (163 lib / 6 synth / 6 walker_sim / 4 asset-gated)
M5 drop the destination water check — col.in_water(lo).then_some(lo)Some(lo) (:1106) ALL GREEN
M6 drop the lowered-start water check (:1104) ALL GREEN
M8 drop the progress comparison — if hlen(...) > low_prog + 0.05if true (:683) REDa_swimmer_at_a_solid_bank_still_hauls_out_the_duck_does_not_override_191 + a_swimmer_at_the_pocket_swim_plane_holds_its_own_depth_not_the_lid
M7 widen the envelope STEP_UP + GROUND_SNAP_TOL12.0 (:1101) GREEN in CI; RED only in the #[ignore]d the_first_frame_never_lifts_a_qcat_pocket_swimmer_toward_the_dry_tile and stepped_canal_surfaces_are_swimmable_between

Only the progress comparison is protected. The PR body says the wish_vspeed gate exists so "an explicit upward (haul-out) drive is never countermanded" — that is a #191 safety property, asserted in prose, guarded by a line of code, and contradicted by nothing in the tree. Concrete failure scenario (REASONED): the walker sends its haul-out drive (wish_vspeed > 0, water design §4c) at a bank that has any open water below; the gate is deleted in a future tidy-up; the controller dives instead of climbing and the character never leaves the water — and CI is green.

The same for M5: with the destination check gone, a duck whose slide leaves the .wtr region ends with the body dry, on_ground = false, vel_z = 0 — the medium exited downward, which is precisely what the guard's doc-comment says it forbids. Nothing anywhere fails.

Please pin at least the wish_vspeed gate and the destination-water guard. (If a guard turns out to be unreachable, that is also worth knowing — but then it should go, not sit there as unfalsifiable reassurance.)


🔴 BLOCKING 3 (cheap — a comment, but a load-bearing one)

movement.rs:961-962 and :1202-1204 both justify Recovery::Grounded "by construction" with:

"Ring samples are banked only while grounded and non-embedded" / "The ring buffer only ever samples GROUNDED positions (see the !embedded arm above)"

This PR made the second half false, and I measured it. Widening the door to body_in_water(col, p) || !is_embedded(col, p) (:1171) means a wet, embedded, grounded body now takes the banking arm. Fixture: 1.6 u flooded slot (walls ±0.8 vs PLAYER_RADIUS 1.0 → footprint never clear), water -1 … 1, body wading on the floor at z=0:

this branch:  on_ground=true  banked=4  any_embedded=TRUE
main 269dbbf: on_ground=false banked=0  any_embedded=false

So the good ring can now hold positions that is_embedded calls embedded, and both the depenetrate stuck fallback (:1205) and the underworld fall-through guard (:967) will restore a body into one and mark it on_ground. The Grounded half of the claim survives (the sample really was on a floor); the non-embedded half does not, and the #724 comment right above :955 explains why an embedded restore point is exactly the stale-ring re-banking mechanism. Also :2167 repeats the claim in the test module.

Given this repo's record that false mechanism claims in tracked files are the dominant defect class, please correct the three sites and say what the fallback's restore point is actually guaranteed to be. Whether the behaviour also needs changing is your call — I did not construct a live wrong-answer from it, so I am flagging the claim, not asserting a second bug.


Per-flip verdict on all five rewritten tests

I read the pre-image of every one. All five flips are legitimate; four are strictly stronger; one is loose.

1. qcat_pocket_swim_plane_strands_the_swimmer_on_the_tile_floor..._swimmer_escapes_to_the_shaft — NECESSARY.
Old property: the bug still happens (end z ≈ −55.9687, !in_water, > 5 u short of the shaft). That is a defect pin, not a protection; it is logically impossible to keep alongside the fix. New version sweeps three start depths and asserts shaft plane ±0.05, shaft XY within 1.5 u, still wet. MEASURED green on the real baked qcat.glb/qcat.wtr (I verified the builder's copies are byte-identical to the ones this box has), RED under duck-off alone, RED under door-revert alone. Approve.

2. depenetration_grounds_a_swimmer_pushed_out_of_the_water_entirelya_swimmer_whose_only_clear_neighbours_are_dry_is_never_beached_by_the_net — NECESSARY, and the author's claim about it is TRUE.
I checked the pre-image: it started a body wet at z=0 and asserted (pos[2] − 2.0).abs() < 1e-3 && on_ground — a wet body ending dry, grounded, 2 u up. That is the #661 beach at 1/10 scale, and it was pinning it. Could not be kept. The replacement adds two real premise assertions (including "the dry predicate must call this body embedded, or the door is never tested"), and is MEASURED RED under M1. Strictly stronger. Approve.

3. depenetration_never_mounts_a_swimmer_onto_the_slab_above_it — 4th assertion replaced — NECESSARY, with one note.
Old 4th assertion demanded footprint_clear at the end position, i.e. it required the net to act on a swimmer; incompatible with the fix by construction, and in a 1.6 u corridor vs a 1.0 u radius it was only ever satisfiable by leaving the corridor. Assertions 1–3 (never lifted / never grounded / still wet) are untouched, and the whole test is MEASURED RED under M1, so the replacement is not inert. Approve. Note: nothing in the tree now asserts that a swimmer the net declines to rescue is not interpenetrating geometry. That property is simply gone for swimmers. I think that is defensible (the slide is collided) but it should be a recorded decision, not a side effect.

4. the_nets_water_probe_is_the_BODY_not_the_feetz == 0z < 1.0 — NECESSARY but LOOSE. Please tighten.
The z == 0 half really was the net freezing the frame, and with the net gone buoyancy's ordinary first-frame rise begins — the author's reasoning is correct. The pinned mutation (feet-only probe → beached at z = 2.0, grounded) is still killed: MEASURED RED under M1. But < 1.0 is the midpoint between the physical claim and the mutation, not the physical claim. Buoyancy's first-frame rise is bounded at ≤ 0.5 u; assert that. As written, a future change that doubles the buoyancy rate ships green with a test whose name is about something else. This is the one place in the PR where the assertion was weakened further than the fix required.

5. the_depenetration_push_out_holds_a_qcat_swimmer_at_its_own_depththe_first_frame_never_lifts_a_qcat_pocket_swimmer_toward_the_dry_tile — NECESSARY, and better than it looks.
|dz| < 1e-3dz < 1e-3 && dz > −2.6. The upward half is unchanged; the downward half still rejects the old −12 u drop. And the −2.6 bound turns out to be the only thing in the entire tree that pins the 2.5 u duck envelope: MEASURED, widening the envelope to 12.0 turns this test RED (and stepped_canal_surfaces_are_swimmable_between with it) while all of CI stays green. Approve the flip — but see the CI-gate note below, because that pin never runs.


Second focus: the duck as a capability

  • Envelope. STEP_UP + GROUND_SNAP_TOL = 2.5 is borrowed by analogy from the step-up, and the PR says so. MEASURED, it is not arbitrary: widening it to 12 breaks a capability test (stepped_canal_surfaces_are_swimmable_between), so the value has real blast radius. It is just not pinned anywhere CI runs.
  • Guards. One of four is load-bearing-and-pinned; see BLOCKING 2.
  • Return path. Reversible in the author's own scene — MEASURED, PROBE-C: out to east 82.5, back to east −92.5. Not reversible over a shallow far shelf — MEASURED, BLOCKING 1.
  • Navigation + fall-guard don't handle swimmable water — characters can't cross a surface-level pool (stuck leaving Halas) #191 interaction. I attacked this and failed to break it, and I want that on the record. I built an asymmetric bank (surface-only north ledge blocking the swim-plane chest ray but not the ducked rays, with premise assertions proving exactly that) expecting the duck's |displacement| comparison to pre-empt the haul-out with a purely sideways slide. It did not: hauled_out=true, and the end coordinate was identical to the uniform-bank control. The author's Navigation + fall-guard don't handle swimmable water — characters can't cross a surface-level pool (stuck leaving Halas) #191 claim survived my attempt to falsify it. I also reproduced their own control: M3 ((self.on_ground || swimming)self.on_ground) turns p1_haul_out_admission_matches_controller_execution RED (I ran walker_sim in isolation to confirm, because in a combined run the lib fails first and walker_sim never executes). The residual is theory only, REASONED, not reproduced: the comparison is on displacement magnitude, not progress along the wish, so a duck that slides sideways can in principle out-measure a forward surface slide. Same weakness already exists in the step-up comparison, so it is pre-existing in form.
  • Owner sign-off? My read: yes, this warrants owner awareness — not because it is a mantle/climb (it plainly is not, and leaving haul_out_up at 2.0 is the right call), but because BLOCKING 1 makes it a capability-boundary question of the same kind as haul_out_up: "the controller may move itself somewhere it cannot come back from" is an owner decision, not an implementation detail. Non-blocking on my verdict, as instructed.

Third focus: Recovery::Afloat deleted — the claim holds

Verified by construction-search, not by assertion. Recovery is now a one-variant enum. I enumerated every construction site of Recovery::Grounded:

  1. Recovery::at_column (:348) — reachable only past the door at :1171, which returns early for body_in_water. Dry only. ✅
  2. the depenetrate stuck fallback (:1205) — same door. Dry only. ✅
  3. the underworld fall-through guard (:967) — reachable only from the final else of the vertical block, i.e. not swimming and not in_water && (!on_ground || submerged_on_floor). ✅

I could not construct a counter-example. The type genuinely cannot express "afloat body recovered onto a floor", and I agree this is the strongest tier. The one residual is BLOCKING 3: sites 2 and 3 draw from a ring that can now contain embedded samples — which does not break Grounded, but does break the sentence next to it.

The corpus mirror at :2053 (if body_in_water(col, p) { return None; }) correctly restates the production door rather than duplicating the rule. Good.


Mutations — every author-reported one reproduced

MEASURED, all on this branch, --locked, remote builder, Finished \test` profile` present on every run:

Mutation Result
M1 door revert alone (body_in_water(col, p) || removed) lib FAILED 159 passed / 4 faileda_swimmer_whose_only_clear_neighbours_are_dry_is_never_beached_by_the_net, depenetration_never_drops_a_swimmer_to_the_pool_floor, depenetration_never_mounts_a_swimmer_onto_the_slab_above_it, the_nets_water_probe_is_the_BODY_not_the_feet; asset-gated 1 passed / 3 failed
M2 duck-off alone lib 162/1 (a_swimmer_ducks_under_a_hanging_face…); asset-gated 3/1 (qcat_pocket_swim_plane_swimmer_escapes_to_the_shaft)
M3 swimming step-up disabled lib 161/2; walker_sim in isolation → p1_haul_out_admission_matches_controller_execution FAILED
M1+M2 combined 4 lib failures — and a_swimmer_ducks_under_a_hanging_face… comes back GREEN, exactly as the PR body's honesty note says

The combined-revert honesty note is accurate and is in the PR body where a reader will find it. I checked whether disclosure is enough: it is, here — duck-off alone (door intact) is a clean, unrescuable kill for both duck tests, so the duck's coverage does not depend on the ambiguous mutant. Good practice, correctly reported.

Baseline on this branch, for the record: lib 163 passed / 0 failed / 1 ignored; synthetic_water_capability 6/0; walker_sim 6 passed / 5 ignored; water_capability --include-ignored 4 passed / 0 failed (on real qcat assets).


Non-blocking observations

N1 — the sealed-swimmer honesty gap is real but PRE-EXISTING, and this PR improves it. I expected to find that removing swimmers from the net removed their only route to ControllerHoldReason::EmbeddedNoRecovery, and went looking for it. MEASURED, sealed 1.6 u flooded box, swimmer pressing east for 10 s:

this branch:  end=[0.58, 0.0, -2.0]    moved=0.58   hold=None  stuck_time=0.000
main 269dbbf: end=[99.52, 99.99, -2.0]              (the ring push-out walked it ~140 u OUT of a sealed box)

Main does not report the hold either — it teleports the body out through the walls. So this is not a regression; the PR replaces a loud lie with a quiet truth-adjacent stall. I am reversing my own initial hypothesis here. The residual gap — a genuinely trapped swimmer has no controller-level observable — is worth its own issue, and it is the same gap BLOCKING 1's trapped body falls into.

N2 — the CI gate does not run the thing that proves this fix. qcat_pocket_swim_plane_swimmer_escapes_to_the_shaft and the_first_frame_never_lifts_a_qcat_pocket_swimmer_toward_the_dry_tile are both #[ignore]d/asset-gated, and five walker_sim blast-radius corpora are #[ignore]d too. So: the headline user-visible claim of this PR, and the only pin on the duck's 2.5 u envelope, are both invisible to the merge gate. The CI-visible coverage of the duck is exactly two synthetic tests. That is the same shape as the nav-QA problem already on this repo's record, and it is a defect in the gate independent of whether this change is correct. The author did disclose it honestly in tests/synthetic_scenes/mod.rs. Worth an issue on the gate, not a change to this PR.

N3 — try_duck_under uses col.in_water (feet), while this PR's own thesis is that the feet probe lies. :1104 and :1106 use the feet-only probe; depenetrate's door deliberately uses body_in_water. The .wtr volume need not meet the floor (that is the whole the_nets_water_probe_is_the_BODY_not_the_feet story), so a duck near a pool bottom can be refused because the lowered feet fall out of the region while the body is under 20 u of water. That fails safe (a missed duck, not a wrong one), so it is not a bug — but it is an inconsistency with the principle the same PR is enforcing 60 lines away, and I would rather it were body_in_water or carried a comment saying why not.

N4 — the flipped depenetration_never_mounts… responsiveness assertion is a genuinely weaker property than the one it replaced, but it is not vacuous (RED under M1) and the stronger property was unsatisfiable. Recorded so the next reviewer does not have to re-derive it.


What I could NOT verify

  • The 650 u submerged-probe negative result. I tried to spot-check it. My approximation of the withdrawn design (door reverted + ring footprint probed 3 u lower for wet bodies) left walker_sim green — but the author's design probed at min(feet+3, surf−0.5), which is not what I built, so this neither confirms nor refutes the recorded [701.0, 24.8, 8.0] figure. I also measured that a plain door revert leaves p1 green, which tells us p1's sensitivity is specifically to a net that relocates a floater, consistent with the recorded story. Treat the 650 u number as unreproduced-by-review. On the design question: the final shape closes that tunnel by construction for the net path — a floater cannot reach any ring relocation at all — with the one residual being the good-ring path in BLOCKING 3.
  • The 42-zone depenetration corpus (changed: dry-body=0) — not run; 407 s plus a full zone set. The changed: dry-body=0 row is the right load-bearing row to have chosen.
  • The full-workspace 1553/0/43 total — not run; I scoped to --lib, walker_sim, synthetic_water_capability, water_capability.
  • The five #[ignore]d walker_sim corpora — I am told a detached run is in flight elsewhere and was ~5/42 zones in. I did not read or wait on it, and "no failures in 5 of 42 zones" is not a result.
  • Live client behaviour — nothing in this review touched a running client.

Summary

The net fix is correct, well pinned, honestly documented, and the Afloat deletion is exactly the strong form the PR claims. Four of the five test flips are strictly stronger than what they replaced and the fifth (z < 1.0) is merely loose. Every mutation the author reported reproduced, including the awkward one they volunteered.

What I am blocking on is entirely the new capability: a measured one-way trap that contradicts the comment justifying the feature's ordering, three guards that nothing in the tree can falsify, and a "by construction" claim about the good ring that this PR's own door change made untrue.

…d is pinned RED-when-deleted, and the ring's non-embedded invariant is enforced

Review round (PR #767): all three blocking findings accepted and fixed
structurally, none by rewording.

B1: try_duck_under now requires the landing column's floor to exist at
or below the ducked feet (the same ground_below shape as try_step_up's
landing check), so a duck can only cross passages whose far side can be
dived back into at the passage depth. The reviewer's shallow-far-shelf
trap scene is committed as a RED-when-unguarded test, plus a round-trip
positive control; the ordering comment now states the property the code
has instead of the falsified "can always surface again".

B2: each duck refusal clause has an individually-verified
RED-when-deleted pin (up-wish gate, destination water, lowered-start
water, 2.5u envelope now pinned in CI, and the new B1 floor bound).
Pinning the up-wish gate exposed a pre-existing bug: the swim-up clamp
parked feet exactly ON the surface, where the strict in_water probe
reads the surfaced body dry for a frame and the DRY depenetration net
ring-recovered it 40u to the pool floor (measured, one frame, Grounded).
The clamp now stops SKIN under the surface, matching its own comment.

B3: sample banking now enforces "grounded AND non-embedded" with an
explicit is_embedded predicate at the banking site (the widened wet door
had silently let an embedded wading body bank restore points); pinned by
a test in which the stuck fallback must hold-and-disclose rather than
rubber-band into the embedded slot. The three "by construction" comment
sites describe the enforcement.

Also: flip-4 assertion tightened to the physical bound (one buoyancy
step) per review; try_duck_under's deliberate feet-only probes
documented (review N3).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQVEpaaKeXsZcW9VT2roeV
@djhenry

djhenry commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Round 2, addressing the review at 617b7d35. New head: b7c33b0ef58c6e8098be900cb7a441d4304e31d5. Per-finding disposition below; every "measured" is a run on this branch's builder tree with the Finished line verified.

B1 — accepted, and fixed with a structural condition, not a reworded comment

You were right on both counts: the claim was false and the missing piece was structural. try_duck_under now has the same shape as try_step_up's landing check — a destination-occupiability bound:

let floor = col.ground_below(lo[0], lo[1], lo[2] + GROUND_ORIGIN, GROUND_DEPTH)?;
(floor <= lo[2]).then_some(lo)

Re-divability: the landing column's floor must exist and sit at or below the ducked feet, so the passage the duck crosses can always be re-entered at the passage depth from the far side. The explicit <= (not just is_some()) matters: ground_below probes from feet + GROUND_ORIGIN, so a floor up to 1 u above the ducked feet would still be "found" — and a floor above the ducked feet is exactly what shallows the return sink and shuts the door (your −3.0 shelf; also the −4.0 variant that is_some() alone would have admitted).

Your falsifying scene is now a committed test, plus its positive control:

  • a_duck_never_crosses_into_a_column_it_cannot_dive_back_out_of — far floor −3.0: the duck is REFUSED; the body presses at the lip, wet, at its plane, still answering input (a stall nav can see and replan around). With only the floor bound deleted, MEASURED: the body crossed to [174.0, 0.0, -2.0] — your trap — and the test is RED.
  • a_duck_across_a_divable_far_side_is_a_round_trip — far floor −4.6: crosses, and driving back west returns (pos[0] < 0 after the return leg). This is the over-tightening guard on the fix and the measured form of the ordering claim.

The ordering comment is rewritten to state the property the code now actually has — "a duck is only constructible when its landing column is re-divable, so it is the option that keeps options; a dry haul-out is irreversible under every driver, so it is the last resort" — with the residual stated exactly: the return sink stops SKIN = 0.05 u above the floor, so a passage whose outbound clearance was under 0.05 u can still shut; and where the two sides' surfaces differ, the return is guaranteed for a driven dive (the planner's dive-first routes), while the autonomous horizontal-wish duck additionally needs the return column's own float-plane-to-passage depth inside the envelope. Not silent, not overclaimed.

B2 — accepted; every guard now has an individually-RED-when-deleted pin

All mutations run one at a time against the fixed tree, each restored from an md5-verified copy:

Guard (your #) Pin test (all in movement::tests, CI-visible) Mutation Result
wish_vspeed <= 0.0 gate (M4) an_upward_haul_out_drive_is_never_countermanded_by_the_duck gate deleted RED0 passed; 1 failed
destination water check (M5) a_duck_never_exits_the_water_sideways check deleted RED0 passed; 1 failed
lowered-start water check (M6) a_duck_never_dives_out_the_bottom_of_its_own_water_volume check deleted RED0 passed; 1 failed
2.5 u envelope (M7) the_duck_envelope_is_the_step_envelope_not_a_free_dive envelope → 12.0 RED0 passed; 1 failed (now pinned in CI, not only in the #[ignore]d asset tests)
progress comparison (M8) already pinned (your finding) unchanged
B1 floor bound (new) a_duck_never_crosses_into_a_column_it_cannot_dive_back_out_of bound deleted RED — crossed to the trap

Pinning M4 found a real pre-existing bug, which is now also fixed. The first version of the M4 test failed on the fixed controller: the up-wish clamps the feet to exactly surf, where the strict in_water probe reads the surfaced body DRY for the frame — and the dry depenetration net then owns a body that is actually swimming. MEASURED (frame trace): feet reach 0.0, wet=0, and the next frame the net ring-recovers the "dry" body with nearest_floor straight to the pool floor — dz = -40.0, dxy = 1.0 (one ring radius), Grounded — the #649 teleport shape, reachable on main too (the clamp and the strict probe both predate this PR). The clamp now stops SKIN under the surface, making its own long-standing comment ("the feet never leave the water column mid-swim") true in the probe's terms. The M4 pin holds the whole chain in place.

B3 — accepted; the property is restored, not the sentence softened

The banking site now enforces the invariant explicitly instead of inheriting it from control-flow shape:

if self.on_ground && self.good_timer >= GOOD_SAMPLE_SECS && !is_embedded(col, p) { … bank … }

Your fixture is a committed test: an_embedded_wading_sample_never_enters_the_recovery_ring — a wet, grounded, embedded body wades through four banking windows, is relocated over a floorless dry column, and the stuck fallback must find NOTHING: it holds and discloses EmbeddedNoRecovery. With the !is_embedded gate deleted, MEASURED: the body rubber-bands back to [0.0, 0.0, 0.0] — into the embedded slot — and the test is RED. The three comment sites now describe the enforcement (explicit predicate at the banking site) rather than the old shape argument, with the B3 history noted in place.

Flip-4 looseness — accepted, tightened to the physical claim

z < 1.0z <= BUOY_RATE * dt + 1e-3 (= one buoyancy step, 0.5 u), with a comment naming your point: the old bound would have let a doubled buoyancy rate ship green under this test's name.

N3 — kept feet probes, now with the reasoning in the doc

try_duck_under's feet-only probes are deliberate and documented at the function: a duck is a descent into the medium's interior, so "the FEET are in water" is the required condition; in the one geometry where the feet probe lies (a .wtr volume stopping short of the pool floor) these clauses can only REFUSE a duck — a missed shortcut, never a wrong state.

N1 / N2 — follow-up issues filed

State of the tree at the new head

  • movement:: lib: 65 passed; 0 failed; 1 ignored (the corpus).
  • walker_sim: 6 passed; 0 failed; 5 ignoredp1_haul_out_admission_matches_controller_execution green.
  • synthetic_water_capability: 6 passed; 0 failed.
  • asset-gated water_capability --include-ignored: 4 passed; 0 failed (escape sweep green with the B1 bound and the SKIN clamp in place).
  • Full workspace: Finished \test` profileverified; 49test result:lines, every one0 failed; totals 1560 passed / 0 failed / 43 ignored` (+7 vs round 1 = the seven new pins).
  • Depenetration corpus over baked zones: re-run on this head, identical to round 1 — zones=42 embedded=35244, changed: dry-body=0, zero iteration-invariant drifters, test result: ok. 1 passed; 0 failed.

On your "could not verify" list: the five #[ignore]d walker_sim corpora from round 1 were killed (they were running against the round-1 tree and would have been stale evidence for this head); parallel_wall_clearance_blast_radius had completed and passed before the kill. They remain un-run against this head — same status as for prior merges, and the same N2 gate problem.

@djhenry

djhenry commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Round-2 independent adversarial review — REQUEST CHANGES

Reviewed b7c33b0 in my own detached worktree. Every claim below is MEASURED (I ran it and read the output) or REASONED. Where I attacked something and failed to break it, I say so.

All three round-1 blocking findings are genuinely fixed, and I verified each by mutation rather than by reading the test names. B2 in particular is fully discharged — the three guards that were pinned by nothing are now pinned by exactly the tests claimed. B3 is fixed behaviourally, not just test-wise: the number that mattered now matches main.

What blocks is narrower than round 1 and cheap to fix: the replacement for the falsified claim contains two new false universals, and the one refinement the reply singles out as load-bearing beyond is_some() is pinned by nothing.


🔴 BLOCKING 1 — the re-divability universal is still false. The trap moved from the floor axis to the SURFACE axis. Measured.

The floor bound closes the shelf half of round-1 B1. It does not close the asymmetry, because the return duck's envelope is measured from the far column's float plane (surf − float_depth), and floor <= lo[2] says nothing about the far column's surface.

Scene: the author's own lintel (wall(4.0, -0.2, 20.0)), floor −40 everywhere — both columns deep, so the new floor bound admits the crossing — and two adjacent water boxes whose surfaces differ. Drive east 1.5 s, then west 15 s:

far_surf= 0  crossed=true out_wet=true out=[61.0,0,-2.000]  back=[-400.3,0,-2.00] returned=TRUE
far_surf= 2  crossed=true out_wet=true out=[61.0,0, 0.000]  back=[   4.53,0, 0.00] returned=FALSE hold=None
far_surf= 4  crossed=true out_wet=true out=[61.0,0, 2.000]  back=[   4.53,0, 2.00] returned=FALSE hold=None
far_surf= 6  crossed=true out_wet=true out=[61.0,0, 4.000]  back=[   4.53,0, 4.00] returned=FALSE hold=None
far_surf=13  crossed=true out_wet=true out=[61.0,0,11.000]  back=[   4.53,0,11.00] returned=FALSE hold=None

A 2.0 u surface mismatch is enough. The body crosses under the lintel while wet, floats to the far plane (far_surf − 2, visible in the z column), and then presses against the lintel's east face for fifteen seconds — on_ground=false, in_water=true, hold()=None. Permanently trapped, every observable reading "swimming normally". That is round-1 B1 unchanged in kind, in a different coordinate.

This geometry is not exotic. Adjacent water volumes with different surface heights are a first-class modelled case in this repo, with a test named for it: region_map::tests::water_boxes_gives_each_box_its_own_bounded_surface"Two boxes side by side in east, surfaces 16 u apart". And #661's own subject is a zone with two water columns at different surface heights.

The disclosure is real but mis-placed and mis-scaled. The function doc does hedge it (try_duck_under's doc: "…which the same floor bound gives everywhere the two sides' surfaces match"). But:

  • the call-site comment carries no hedge at allmovement.rs:676-678, in a block that explicitly bills itself as "The ordering rationale, stated as the property the code actually has":

    "a duck is only constructible when its landing column is re-divable (try_duck_under's floor bound), so the passage it crosses can be dived back through; the duck is therefore the option that KEEPS options."

    Measured false at a 2 u mismatch. This is a load-bearing comment restating a universal that round 1 falsified, in a PR whose whole point was not to do that.

  • the commit subject says "the duck is re-divable by construction";

  • the hedge sits in the tail of a parenthetical headed "Residual, stated exactly", next to a 0.05 u residual — implying the same order of magnitude. This one is unbounded and produces a permanent trap, not a marginal passage;

  • nothing pins it.

I do not think this needs to stay a residual. There is a two-line structural fix symmetric to the floor bound — require the landing column's own float plane to be inside the duck envelope of the passage depth:

let surf = col.water_surface(lo)?;
((surf - crate::traversability::PLAYER_BODY.float_depth) - lo[2] <= STEP_UP + GROUND_SNAP_TOL)
    .then_some(lo)

With that, "re-divable by construction" is true for the autonomous horizontal-wish driver, which is the driver #661 is about. If you prefer to keep it as a stated limitation instead, then the call-site universal has to go and the magnitude has to be named — but I would rather the code closed it, since the disclosure route leaves a silent permanent trap reachable at a 2 u mismatch.


🔴 BLOCKING 2 — the <= refinement is load-bearing, and pinned by nothing. Measured.

The reply singles this out as the part that matters beyond is_some():

"a floor up to 1 u above the ducked feet would still be 'found' — … (your −3.0 shelf; also the −4.0 variant that is_some() alone would have admitted)"

I mutated exactly that — (floor <= lo[2]).then_some(lo)Some(lo), keeping the ? so a missing floor still refuses:

B1w (<= → is_some):  test result: ok. 174 passed; 0 failed; 1 ignored    ← WHOLE LIB GREEN

And the difference is a real trap, not a stylistic tightening:

fixed head   far_floor=-4.0 / -3.6 / -4.4 : crossed=FALSE (refused), body returns west
B1w mutant   far_floor=-4.0 : crossed=true  back=[4.533,0,-2.0] returned=FALSE hold=None
B1w mutant   far_floor=-3.6 : crossed=true  back=[4.533,0,-2.0] returned=FALSE hold=None
B1w mutant   far_floor=-4.4 : crossed=true  back=[-599.0,0,-2.0] returned=true

Why the committed test misses it: a_duck_never_crosses_into_a_column_it_cannot_dive_back_out_of uses far floor −3.0, which is above ground_below's probe origin (lo[2] + GROUND_ORIGIN = −3.5), so is_some() alone already refuses it. The test pins the ?, not the <=. The author measured the −4.0 variant, named it in the reply, and did not commit it.

This is round-1 B2's exact shape living inside round 2's fix. The remedy is one extra value in the existing loop-free test (or turn it into a loop over [-3.0, -4.0]), which is why I am comfortable blocking on it.


🔴 BLOCKING 3 — the replacement ordering comment contains a second false universal. Measured.

movement.rs:673-675:

"a haul-out that lands DRY is irreversible under every driver — want_swim is inert on dry ground and nothing can sink through a floor — so it must be the last resort"

Tested on the repo's own #191 bank scene (floor(-40,-100,4), floor(0.1,4,100), wall(4,-40,0.1)): haul out, then drive back west.

hauled_out=true  landed=[4.667, 0.0, 0.100]
swim driver (want_swim=true):   back=[-345.3, 0.0, ~0]  wet=true  on_ground=false
walk driver (want_swim=false):  back=[-345.3, 0.0, ~0]  wet=true  on_ground=false

Both drivers return to the water. The mechanism in the sentence is true — want_swim is inert on dry ground, and nothing does sink through a floor. The conclusion does not follow, because the return is lateral: the body walks back off the same lip it just climbed. I reproduced the same result on origin/main (back=[-99.9, 0, ~0], wet), so this is long-standing behaviour that was available to measure.

Taken with BLOCKING 1, the stated rationale is inverted: measured, the haul-out is reversible and the duck can be one-way.

To be explicit about what I am and am not asking for: I am not asking you to change the ordering. #661 needs the duck to win at the pocket mouth, and I did not find a case where trying the duck first is wrong. I am asking that the justification not be two universals that measurement contradicts — this is the third rewrite of this comment and the second to be falsified, which is itself the signal that the honest version is shorter than the confident one.


What I verified as claimed (credit where it is due)

Every guard pin reproduced, individually, with exactly the named test. Each mutation applied alone from an md5-verified copy; Finished \test` profile` present on every run; baseline on my tree is 174 passed (170 + my 4 probes):

Mutation Result Killing test
M4 — delete intent.wish_vspeed <= 0.0 RED 173/1 an_upward_haul_out_drive_is_never_countermanded_by_the_duck
M5 — delete the destination water check RED 173/1 a_duck_never_exits_the_water_sideways
M6 — delete the lowered-start water check RED 173/1 a_duck_never_dives_out_the_bottom_of_its_own_water_volume
B1 — delete the floor bound RED 173/1 a_duck_never_crosses_into_a_column_it_cannot_dive_back_out_of
M7 — envelope 2.512.0 RED 172/2 the_duck_envelope_is_the_step_envelope_not_a_free_dive + a_duck_across_a_divable_far_side_is_a_round_trip
B3 — delete the !is_embedded banking gate RED 173/1 an_embedded_wading_sample_never_enters_the_recovery_ring
CLAMP — revert surf - SKINsurf RED 173/1 an_upward_haul_out_drive_is_never_countermanded_by_the_duck

Round-1 B2 is discharged. And M7 is now genuinely CI-visible — it goes red in a plain --lib run with no assets, which is the specific N2 mitigation claimed.

B3 is fixed behaviourally, not just test-wise. I re-ran the round-1 measurement itself rather than trusting the new test — same wet/grounded/embedded 1.6 u wading fixture:

b7c33b0 : on_ground=true  banked=0  samples=[]        any_embedded=false
617b7d3 : on_ground=true  banked=4  samples=[[0,0,0]×4] any_embedded=TRUE   (round 1)
269dbbf : on_ground=false banked=0                                          (round 1, main)

The branch now matches main's behaviour. That is the number that mattered.

Flip-4's tightening is the physical bound, exactly. BUOY_RATE = 30.0 (movement.rs:100), dt = 1/60 → 0.5 u, and the buoyancy arms take want = (BUOY_RATE * dt).min(target - pos[2]) then swim_rise(want)want, so 0.5 u/frame really is the ceiling. SKIN = 0.05 (movement.rs:33) — the doc's stated constant is correct.

The clamp comment is now true, not merely closer. Measured on the branch: a surfaced up-wishing swimmer ends at z = −0.049993 with in_water = true. True in the probe's own terms, which is exactly how the comment scopes it.

#776 and #777 read faithfully. Neither overstates nor understates the findings they were split from; #776 correctly records that main was louder (the ~140 u sealed-box walk-out), which was the reversal I reported against my own hypothesis. I spot-checked #777's factual claim about the gate: .github/workflows/test.yml runs cargo test --workspace --locked on ubuntu-latest, no assets. Confirmed.


Non-blocking

N1 — I could not construct the SKIN residual, and I think it may not be reachable. The doc says a passage whose outbound clearance was under 0.05 u can shut behind the body. Far floor exactly at the duck depth (−4.5), lintel underside swept through the band:

lintel -0.20 / -0.40 / -0.46 / -0.49 : crossed, and returned
lintel -0.52                          : duck REFUSED outbound (no trap)

REASONED mechanism for why: with matched surfaces the return sink's ray reaches exactly the outbound duck depth, and the bound guarantees the floor is at or below that, so the ray usually finds no hit and the return lands at the same depth it went out at. The doc errs conservatively, which is the right direction — but "stated exactly" is a reasoned figure, not a measured one, and it is the other residual (surface mismatch, unbounded) that needed the magnitude.

N2 — the bonus clamp fix: mechanism CONFIRMED on main, the 40 u figure NOT reproduced. The coordinator asked me to check whether this was a fabricated justification. It is not. On origin/main (44003f7), with a surfaced up-wishing swimmer in a 1.6 u flooded slot, I measured: the feet reach z = 3.7e-7 (i.e. the clamp puts them exactly on the surface), the body is then read dry, and the dry depenetration net takes it — it shoved the body 2.0 u laterally (one PUSHOUT_RADII step) and dropped it 0.033 u. So the chain you describe — strict in_water at the clamp → body probe says dry → dry net owns a swimming body — is real and pre-existing on main, exactly as claimed.

What I did not reproduce is the specific outcome: dz = -40.0, dxy = 1.0, Grounded. My scene gave the net a clear ring candidate at radius 2, so it escaped laterally instead of taking the floor-snap path. My scene is a guess at yours, so this neither confirms nor refutes the 40 u figure. Please publish the fixture, or soften the comment at movement.rs:773-775 from a specific measurement to the mechanism it actually needs to justify — the mechanism is sufficient on its own and I confirmed it.

N3 — keep the clamp fix in this PR. It is one token of code, discovered by the work, and it is required for the M4 pin to be constructible at all (reverting it turns the M4 test red). Splitting it would leave the PR unable to pin its own guard. On its interaction with the duck: the duck now starts 0.05 u lower, which is inside every margin I measured — the #191 haul-out has 2.4 u of margin against 0.05.

N4 — the clamp change has no test of its own name. Its only pin is a test named for a different guard. It works today; the coupling is a trip-wire for a future rename. One line in the M4 test asserting the clamped feet still read wet would fix it.

N5 — the corpora remain un-run against this head, and I think that is acceptable for this PR. Killing round-1's run was correct (it was measuring the wrong tree). The round-2 change is confined to movement.rs and the guards are now pinned in CI, including the envelope pin that was the specific hole. The general gap is #777's problem, not this PR's — and I would rather it be fixed by the enumerable-gap job #777 proposes than by holding this PR hostage to a multi-hour run.


The log figures — independently re-derived, and the 1560 accounted for

My own cargo test --workspace --locked run on b7c33b0 with my probes removed (tree md5-verified identical to the commit, git status --porcelain empty):

Figure Value
Finished \test` profile` present, 1 line, in 15m 12s
test result: lines 49
non-canonical (spliced / truncated / missing a count) 0
well-formed all-zero lines (empty targets — benign, reported separately) 16
totals 1560 passed / 0 failed / 43 ignored / 0 measured
FAILED summaries none

Cross-check against the running N headers, not the binary-invocation lines: 49 running N headers, sum(N) = 1603; passed + failed + ignored = 1560 + 0 + 43 = 1603. Exact. (running N counts ignored tests; passed + failed alone does not match it, and reconciling those two would need a mechanism that does not exist.)

Completeness, not just arithmetic. The equality alone cannot detect a wholly lost binary, so I checked target identity as well: 49 binary-invocation lines = 49 running N headers = 49 summaries, and the set of integration-test target names in the run is an exact match for the 17 tests/*.rs files declared in the tree at b7c33b0 (plus 19 unit-test targets and 13 doc-test targets = 49). No target dropped its pair.

The 49-vs-50 and the ~73-test delta are explained by the base, and I verified it rather than assuming it. origin/main (44003f7) carries exactly one integration-test target that this branch's base does not: crates/eqoxide-renderer/tests/floating_placement.rs (+328 lines, added after 269dbbf). That is the 50th summary line. The passing-count gap is the rest of the same drift — steering.rs +1661, walker.rs +517, collision.rs +362, action_loop.rs +327 between this branch's base and current main. So the delta is the older base, not lost binaries.

One figure does not exist for a test run, and should stop being asked for. rbuild emits its fetched line only when it rsyncs a built eqoxide binary back (rbuild:85, guarded on the fetch path); cargo test produces no such binary, so its absence is expected and is not a truncation signal. The integrity evidence for a test run is the Finished line plus the 49/49/49 correspondence above.

Your reported figures reproduce exactly: 49 summary lines, all 0 failed, 1560 / 0 / 43.


What I could NOT verify

  • the 42-zone depenetration corpus (changed: dry-body=0) — asset-gated, not run;
  • the five #[ignore]d walker_sim corpora — not run against this head by anyone;
  • the specific 40 u / dxy=1.0 / Grounded trace (see N2 — mechanism confirmed, magnitude not);
  • the asset-gated water_capability --include-ignored 4/0 on this head — I verified it at 617b7d3 last round and did not re-run it here;
  • live client behaviour — nothing in this review touched a running client.

Summary

Round 2 does what round 2 should: the fixes are structural, the pins are real, and I checked them by mutation rather than by name. B2 and B3 are closed, and B3 is closed on the measurement, not the test.

What remains is the same failure mode in a new place. The floor bound closed one axis of round-1 B1; the surface axis is still open, still silent, and reachable at a 2 u mismatch in geometry this repo already models on purpose. The comment written to replace the falsified universal contains two more universals, both measurably false. And the one refinement the reply calls load-bearing is the one thing in the new code that nothing tests.

… bound is pinned, and the ordering rationale drops both falsified universals

Review round 2 (PR #767): all three findings accepted.

R2-B1: try_duck_under adds the reviewer's surface bound - the landing
column's own float plane must be inside the duck envelope of the passage
depth - so an admitted duck's crossing is autonomously round-trip-capable
on BOTH axes. Cost measured before adopting: the bound also refuses the
qcat pocket-mouth duck (a ~13u surface mismatch, genuinely one-way for
the autonomous driver), and measurement showed that cost is nearly nil
against the real system: a driven dive crosses with no duck involved,
and the walker's own rise-y steering at that pocket sends an up-wish the
vspeed gate already excludes from ducking. The escape test therefore
became qcat_pocket_swimmer_escapes_to_the_shaft_under_a_driven_dive
(green, swept over the stranded band), with the horizontal driver's
honest outcome pinned as a WET STALL (never dry, never one-way) in
qcat_pocket_horizontal_wish_alone_stalls_wet_at_the_mouth. The same
experiment reproduced the LIVE walker wedge offline for the first time
(planner emits a rise-at-destination swim_surface hop, swim_vspeed turns
the high carrot into an up-wish, body pins under the lid) - that residue
is steering/walker territory, filed separately with the repro.

R2-B2: the floor test now sweeps far_floor in {-3.0, -4.0}; the -4.0 arm
is inside ground_below's probe band and only `floor <= lo[2]` refuses it
(measured RED with the refinement deleted, is_some kept).

R2-B3: the ordering comment's two falsified universals are deleted; the
replacement states the narrow measured basis (banks self-refuse the
duck; an admitted duck is round-trip by the bounds; a haul-out changes
medium) and says outright that the ordering is not a reversibility
argument.

Review N4: the surface clamp has an explicit assertion under the M4
test; reverting the clamp fails it by name. The clamp comment now names
the reproducible fixture for the 40u figure (the M4 scene, clamp
reverted) instead of an unanchored magnitude, and records the reviewer's
independent confirmation of the mechanism on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQVEpaaKeXsZcW9VT2roeV
@djhenry

djhenry commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Round 3, addressing the round-2 review at b7c33b0. New head: 8514aa4d180762cbc70f88d68257c9d09de7d798. All three blockers accepted and fixed structurally; one of them forced a measured decision about this PR's own headline test, described in full under R2-B1 because you will want to attack it.

R2-B1 — accepted; your two-line bound adopted verbatim — after measuring what it costs, which is the interesting part

Before adopting the surface bound I measured it against this PR's own deliverable, because the qcat pocket→shaft crossing is a ~13 u surface mismatch — your far_surf=13 row is the escape this PR shipped in round 2. Three drives from the pocket swim plane on the bounded head:

A horizontal-only (round 2's escape drive): stalls wet at the mouth [-45.75, 1036.15, -57.978]
B driven dive (down-wish + lateral):        ESCAPES, ~60 frames -> [-45.8, 1029.8, -44.98]
C real planner + real steering (carrot_along + swim_vspeed, 150 ms latch):
    route: [start] -> [-42.3, 1036.8, -55.98] -> [-45.75, 1030.06, -42.98]   (rise-at-destination swim_surface hop)
    WEDGED 20 s at [-43.44, 1036.15, -56.03], vs=+17.3 — pinned under the lid

Three consequences, all now in the tree:

  1. The bound is in, verbatim (surf − float_depth − lo[2] <= STEP_UP + GROUND_SNAP_TOL, after the floor bound). With both axes bounded, an admitted duck's crossing is autonomously round-trip-capable — and that sentence is now checkably true rather than asserted: the return sink can reach the passage depth (surface bound), the floor admits it (floor bound), both ends are wet (water checks). Pinned by a_duck_never_crosses_into_a_column_whose_float_plane_is_beyond_the_return_envelope — your 2 u-mismatch scene, water_boxes fixture-asserted. Mutation (bound deleted): RED, mutant crossed to [100.7, 33.4, -39.5]; the floor pin stays green under the same mutant (independent kills).
  2. The escape test changed drives, and says so loudly. The horizontal-only escape was the duck's one-way crossing — exactly the class you were blocking. It is replaced by qcat_pocket_swimmer_escapes_to_the_shaft_under_a_driven_dive (drive B, swept over the whole formerly-stranded band {plane, −59.0, −60.5} — green) plus qcat_pocket_horizontal_wish_alone_stalls_wet_at_the_mouth (drive A: wet, at plane, never the dry strand — the honest disclosure of what the bound forecloses, committed as a test rather than a comment). The bound's real-system cost measured before adopting: nearly nil — the walker's own steering at this pocket sends an up-wish, which the vspeed gate already excluded from ducking with or without the bound.
  3. Drive C reproduced the LIVE walker wedge offline for the first time — the planner emits a rise-at-destination swim_surface hop (correct under the tree's own test(#329/#649): pin what a swimmer can actually do — the #648 rise gate is WITHDRAWN (its premise was false) #648-withdrawal capability pins), swim_vspeed turns the high carrot into an immediate up-wish, and the body pins under the lid; the up-wish also (correctly) gates the duck, so no controller capability can rescue a wrong intent. That is the remaining live qcat spawn pocket: walker strands on an unbounded swim_surface/haul_out rise — water edges have no rise gate (walk_profile_ok cannot reach them) #329/qcat spawn pocket: the swimming step-up hauls a swimmer onto a tile floor 0.009 u above the waterline that it cannot get back off (the remaining #329 wedge) #661 egress residue, it is steering/walker territory outside this PR's file set, and it is now qcat pocket egress: steering turns the planner's rise-at-destination swim_surface hop into an up-wish and pins under the lid — the walker cannot execute the route (offline repro) #783 with the full repro. This PR's scope statement is correspondingly narrower and, I think, now exactly true: the controller-level defects (beach, input-eating net, missing dive mirror) are fixed and pinned; the leg-executor gap is qcat pocket egress: steering turns the planner's rise-at-destination swim_surface hop into an up-wish and pins under the lid — the walker cannot execute the route (offline repro) #783's.

R2-B2 — accepted; the <= is pinned by the value you named

a_duck_never_crosses_into_a_column_it_cannot_dive_back_out_of now sweeps far_floor ∈ {-3.0, -4.0} with the doc stating which half each value kills (−3.0 → the ?; −4.0 → the <=, inside the probe band but above the ducked feet). Mutation (<= deleted, ? kept): RED on the −4.0 arm — mutant crossed to [174.0, 0.0, -2.0], your trap — while the surface pin stays green under the same mutant.

R2-B3 — accepted; both universals deleted, not softened

The ordering comment now opens with "The ordering is NOT a reversibility argument", names both falsified universals as falsified, and states the narrow measured basis: (a) at every measured legitimate bank the duck refuses itself — pinned by the bank test and P1; (b) where both moves genuinely pass, the duck keeps the body in the medium its current driver is steering it through, and an admitted duck is round-trip-capable by the bounds, while a haul-out changes medium and hands the vertical to gravity. No claim in it is universal-shaped except the one the bounds now make true.

The 40 u figure — supplied, not softened

The fixture is the M4 test's own scene with the clamp reverted: flooded_corridor([floor(-40,-100,100), wall(4.0,-0.2,20.0)], water_slab(-40,0)), start [3.0, 0, -2.4], speed 44, wish_vspeed +5. Frame trace at discovery: f28 pos=(3.733, 0, 0.0) wet=0f29 pos=(2.809, 0.383, -40.0) og=1 — one frame, one ring radius, Grounded(-40). Your slot scene shoved laterally because a nearer clear candidate existed; in this scene every candidate column's only floor is the pool bottom. Confirmation that it still reproduces on the round-3 head: the CLAMP mutation of the M4 test fails with end [68.8, 0.38, -32.5] — the body teleported to −40 and carried east — byte-identical to the discovery run. The clamp comment now cites this fixture and your independent mechanism confirmation on main, instead of an unanchored magnitude.

N4 — the clamp now has an assertion under its own name

Last line of the M4 test: ctrl.pos[2] <= -SKIN + 1e-4 with a message naming the clamp; the CLAMP mutation fails it (and the crossing assertion) rather than only a coupled water check.

Mutation matrix for the new code (each alone, restored from md5-verified copies)

Mutation Result Killing test
surface bound deleted RED (1 passed; 1 failed in the pair) a_duck_never_crosses_into_a_column_whose_float_plane_is_beyond_the_return_envelope
<=is_some (? kept) RED (−4.0 arm) a_duck_never_crosses_into_a_column_it_cannot_dive_back_out_of
clamp → exact surf RED an_upward_haul_out_drive_is_never_countermanded_by_the_duck (crossing + named clamp assertion)

State at 8514aa4

  • movement lib: 66 passed; 0 failed; 1 ignored (Finished verified)
  • walker_sim: 6 passed; 0 failed; 5 ignored — P1 green
  • synthetic_water_capability: 6 passed; 0 failed
  • asset-gated water_capability --include-ignored: 5 passed; 0 failed on this head — driven-dive escape, horizontal wet-stall, first-frame, deep-start capability (which survives the bound: the deep crossings never needed the duck), canals
  • Full workspace: two sources.
    • CI at the merge ref (this branch merged onto current main, run 30352477297): Finished \test` profilepresent · **50**test result:lines (the 50th ismain's floating_placementtarget, present by target identity) · 0 non-canonical · totals **1673 / 0 / 46** ·sum(running N) = 1719 = 1673 + 0 + 46`, exact.
    • Branch tree on the builder: Finished \test` profilepresent (50m 40s — the box was at load ~130) · **49**test result:lines · 0 non-canonical · totals **1561 / 0 / 44** ·sum(running N) = 1605 = 1561 + 0 + 44`, exact. The +1/+1 vs round 2's 1560/43 is the new surface pin (passed) and the asset-gated escape/stall split (ignored), accounted by name.
  • Depenetration corpus: re-run on this head — zones=42 embedded=35244, changed: dry-body=0, zero drifters, test result: ok. 1 passed; 0 failed ... finished in 248.80s — identical to rounds 1-2, as expected: the round-3 changes are unreachable under the corpus's idle intent (the duck needs a nonzero wish, the clamp an up-wish), and the run confirms rather than assumes that.

Process note applied: no missing-fetched claims on test runs; totals published in the five-figure shape with the running N cross-check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant