Split out of PR #767's review (N1, and the residual named in its B1). Since #661, a body afloat in water never enters the depenetration net — which removed the pre-existing LOUD failure mode (the review measured main walking a swimmer ~140 u OUT of a sealed box through the walls, one ring radius at a time) and replaced it with a quiet stall. But the residual is real and now the only gap of its kind:
A genuinely trapped swimmer has no controller-level observable. A swimmer wedged in a sealed pocket, or pressing against a passage the duck-under refuses (e.g. a far side that cannot be dived back out of — PR #767's re-divability refusal), simply stops making progress: on_ground=false, in_water=true, hold()=None, stuck_time never accrues (the net's clock is the only one, and the net no longer runs for floaters). Every observable reads "swimming normally". The walker sees walker_stalled and can replan, so nav is not blind — but the CONTROLLER-level honesty surface (player.hold) says nothing, and a driver that is not the walker (manual, WASD-agent) gets no signal at all.
Why this was NOT bolted onto #661's ControllerHold (recorded in movement.rs's neutral-buoyancy comment for the analogous case): the state is not locally distinguishable from correct behaviour — a swimmer holding at its float plane beside a wall is also stationary, unsupported, wet. Reporting the shape naively raises player.hold on every ordinary floating character, and a false alarm in an honesty observable is the same defect as a silence. A real fix needs its own signal (e.g. sustained zero progress against a nonzero wish while afloat), its own reason enum variant, and a way to distinguish "trapped" from "resting".
The agent-honesty label applies: this is a silent-wrong-answer gap (missing disclosure), the class this project ranks above crashes.
Split out of PR #767's review (N1, and the residual named in its B1). Since #661, a body afloat in water never enters the depenetration net — which removed the pre-existing LOUD failure mode (the review measured
mainwalking a swimmer ~140 u OUT of a sealed box through the walls, one ring radius at a time) and replaced it with a quiet stall. But the residual is real and now the only gap of its kind:A genuinely trapped swimmer has no controller-level observable. A swimmer wedged in a sealed pocket, or pressing against a passage the duck-under refuses (e.g. a far side that cannot be dived back out of — PR #767's re-divability refusal), simply stops making progress:
on_ground=false,in_water=true,hold()=None,stuck_timenever accrues (the net's clock is the only one, and the net no longer runs for floaters). Every observable reads "swimming normally". The walker seeswalker_stalledand can replan, so nav is not blind — but the CONTROLLER-level honesty surface (player.hold) says nothing, and a driver that is not the walker (manual, WASD-agent) gets no signal at all.Why this was NOT bolted onto #661's
ControllerHold(recorded inmovement.rs's neutral-buoyancy comment for the analogous case): the state is not locally distinguishable from correct behaviour — a swimmer holding at its float plane beside a wall is also stationary, unsupported, wet. Reporting the shape naively raisesplayer.holdon every ordinary floating character, and a false alarm in an honesty observable is the same defect as a silence. A real fix needs its own signal (e.g. sustained zero progress against a nonzero wish while afloat), its own reason enum variant, and a way to distinguish "trapped" from "resting".The agent-honesty label applies: this is a silent-wrong-answer gap (missing disclosure), the class this project ranks above crashes.