Skip to content

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

Description

@djhenry

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)

  1. 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".
  2. 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.
  3. The up-wish also (correctly) gates the controller's autonomous duck-under (an explicit upward drive must not be countermanded by a dive — pinned in fix(#661): the qcat pocket swimmer escapes — floaters never enter the depenetration net, and a blocked swimmer can duck UNDER an obstruction #767), so no controller capability can rescue the execution: the intent itself is wrong for the edge.

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.

What #767 already provides

  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions