You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Found while measuring PR #767's round-3 change (the duck-under's surface bound) against the real system, and reproduced OFFLINE for the first time: the residual live #329/#661 wedge at the qcat spawn pocket is a steering/execution gap, not a controller one, and it reproduces deterministically with the real planner + real steering + real controller, no client.
The measurement (offline, baked qcat.glb + qcat.wtr, controller at PR #767 round-3 head)
Three drives from the pocket swim plane (-42.3, 1036.8, -57.978) toward the shaft (-45.75, 1030.06):
A horizontal-only wish: stalls wet at the mouth [-45.75, 1036.15, -57.978] (by design after #767)
B driven dive (down-wish+lateral): ESCAPES in ~60 frames -> [-45.8, 1029.8, -44.98] (shaft swim plane)
C real planner route + real steering (carrot_along + swim_vspeed, 150ms latch):
route: [start] -> [-42.3, 1036.8, -55.98] -> [-45.75, 1030.06, -42.98]
f0..f1200: pos pins at [-43.44, 1036.15, -56.03], carrot [-43.44, 1034.6, -51.7], vs=+17.3
WEDGED under the pocket lid for the full 20 s
The mechanism (each step verified in the trace)
The planner joins the pocket to the shaft with a rise-at-destination swim_surface hop (+13 u). That admission is CORRECT under the tree's own capability pins (a_swimmer_rises_to_the_destination_columns_surface_not_its_own: buoyancy lifts a swimmer to the DESTINATION column's plane; the test(#329/#649): pin what a swimmer can actually do — the #648 rise gate is WITHDRAWN (its premise was false) #648 rise gate was withdrawn for exactly this reason). The edge means "go LATERAL, the rise happens at the far column".
swim_vspeed knows nothing of that convention: it turns the carrot's high z into an immediate up-wish (vs=+17), so the controller rises IN THE SOURCE column, clamps under the pocket surface (the lid is 0.009 u above the waterline), and presses against the mouth geometry at a height where the passage is closed.
So the planner's swim_surface edges into higher-surface columns are not executable by the current steering: the executor needs lateral-first-then-rise (or the route needs to be dive-first through the corridor), and the steering produces rise-first. This matches the live record exactly — #329's triage measured 4/4 walker_stalled at [-42.3, 1037, -56] with complete routes and no planner timeout.
the strand (dry beach at −55.9687) and the input-eating net ping-pong are gone — the wedge is now a WET, visible stall, and replans keep working;
a driven dive executes the crossing (drive B): if the route (or the leg executor) expresses the descent explicitly, the controller side is already sufficient;
controller-level pins for everything the fix here would rest on.
Possible shapes (not prescribed)
steering: for a swim_surface leg whose carrot is above the CURRENT column's surface, hold depth (vs ≈ 0 / plane-hold) until the lateral crossing is done, and let destination buoyancy do the rise — i.e. teach swim_vspeed the edge's own convention;
or planner: emit the corridor crossing as an explicit dive-first pair (descend under the lip, then lateral) so the existing steering executes it;
either way, drive C above is the deterministic offline acceptance test.
Files involved are crates/eqoxide-nav/src/steering.rs / walker.rs / possibly collision.rs route synthesis — outside #767's file set (held by other work at the time), which is why this is an issue and not part of that PR.
Found while measuring PR #767's round-3 change (the duck-under's surface bound) against the real system, and reproduced OFFLINE for the first time: the residual live #329/#661 wedge at the qcat spawn pocket is a steering/execution gap, not a controller one, and it reproduces deterministically with the real planner + real steering + real controller, no client.
The measurement (offline, baked
qcat.glb+qcat.wtr, controller at PR #767 round-3 head)Three drives from the pocket swim plane
(-42.3, 1036.8, -57.978)toward the shaft(-45.75, 1030.06):The mechanism (each step verified in the trace)
swim_surfacehop (+13 u). That admission is CORRECT under the tree's own capability pins (a_swimmer_rises_to_the_destination_columns_surface_not_its_own: buoyancy lifts a swimmer to the DESTINATION column's plane; the test(#329/#649): pin what a swimmer can actually do — the #648 rise gate is WITHDRAWN (its premise was false) #648 rise gate was withdrawn for exactly this reason). The edge means "go LATERAL, the rise happens at the far column".swim_vspeedknows nothing of that convention: it turns the carrot's high z into an immediate up-wish (vs=+17), so the controller rises IN THE SOURCE column, clamps under the pocket surface (the lid is 0.009 u above the waterline), and presses against the mouth geometry at a height where the passage is closed.So the planner's
swim_surfaceedges into higher-surface columns are not executable by the current steering: the executor needs lateral-first-then-rise (or the route needs to be dive-first through the corridor), and the steering produces rise-first. This matches the live record exactly — #329's triage measured 4/4walker_stalledat[-42.3, 1037, -56]with complete routes and no planner timeout.What #767 already provides
Possible shapes (not prescribed)
swim_surfaceleg whose carrot is above the CURRENT column's surface, hold depth (vs ≈ 0 / plane-hold) until the lateral crossing is done, and let destination buoyancy do the rise — i.e. teachswim_vspeedthe edge's own convention;Files involved are
crates/eqoxide-nav/src/steering.rs/walker.rs/ possiblycollision.rsroute synthesis — outside #767's file set (held by other work at the time), which is why this is an issue and not part of that PR.