[P3] ZHL deco schedule holds at a fallback stop deeper than the GF anchor until it can reach the anchor directly, skipping the regular stops in between #81
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Priority: P3
Type: bug
Source: full codebase review requested 2026-10-31 (independently reproduced against
2cac7cf)Location:
src/models/zhl16c.ts:765-770,src/models/zhl16c.ts:783-796,src/models/zhl16c.ts:797-811,src/models/vpmb.ts:492Problem and evidence
In decompressionPlan,
nextis set toanchorDepthwhenevercurrent > anchorDepth + 1e-8, and this happens on every loop iteration. The #26 fallback (lines 783-796) can stop the ascent at a regular stop deeper than the anchor, such as 24 m when the anchor is 15 m. That happens when slow tissues load during a slow ascent from a deep bounce dive. On the next iterationnextbecomes the anchor again. The fallback search for the shallowest reachable stop between the anchor and the current depth runs once, when the diver first arrives. At that moment the next stop up is not yet clear, so nothing is found. The hold loop then checks onlycanTravel(anchorDepth)each minute. The diver stays at the deep stop until a direct ascent to the anchor is clear, even though the next 3 m stop was reachable much earlier, and the intermediate stops (21 m and 18 m) are skipped. Every ceiling check still passes, so the schedule is conservative rather than unsafe. The problem is that the stop table breaks the documented 3 m / 10 ft spacing and total ascent time is inflated. VPM-B's projectVpm uses the same kernel.I ran the reviewer's probe /tmp/divesim-review-oQNcyA/probe18.ts with
node --import tsxfrom the worktree. It imports the real HEAD source. zhl16c_mod.ts differs from HEAD in only two ways: astoppedflag that turns off the anchor target once a hold occurs or a stop deeper than the anchor is chosen, and a type-only change.Tracing the code confirms this: at current=24, next=15. canTravel(15) fails, so the fallback checks 18 and 21; both fail on arrival. The hold loop
while (!canTravel(next))then never looks at 21 again.Reachability: the live Ascent input accepts 1 to 18 m/min (App.tsx:2399-2416), and depth goes up to 300 m.
Suggested correction
Use anchorDepth as the travel target only until the first stop has been reached. Keep a
stoppedflag and set it after an iteration whereheld > 0, or wherenext > anchorDepth + 1e-8(meaning the fallback picked a stop deeper than the anchor). After that, computenextas the regular next stop,Math.max(0, (Math.ceil((current - 1e-8) / spacing) - 1) * spacing). This is the change in /tmp/divesim-review-oQNcyA/zhl16c_mod.ts. Another option is to re-run the shallowest-reachable-stop search inside the hold loop each minute.Acceptance criteria
Scope: maintenance of the educational simulator. Preserve historical R1 acceptance and operational-validation limits.