[P2] The replay uses the departure GF even when GF was changed mid-dive, so its decompression verdict, NDL and ceilings contradict the live dive #109
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: P2
Type: bug
Source: full codebase review requested 2026-10-31 (independently reproduced against
2cac7cf)Location:
src/App.tsx:801-804,src/App.tsx:884-889,src/App.tsx:2902-2904,src/App.tsx:2952-2990,src/App.tsx:728-737,src/replay/diveReplay.ts:186-190,src/replay/diveReplay.ts:216,src/replay/diveReplay.ts:274,src/replay/diveReplay.ts:314-315,src/replay/diveReplay.ts:340-341,src/replay/diveReplay.ts:367-368,src/replay/diveReplay.ts:412,src/replay/diveReplay.ts:427,src/replay/DiveReplayComparison.tsx:439-447Problem and evidence
For Bühlmann models, the Conservativeness slider and the GF low/high inputs (App.tsx:2952-2990) stay enabled underwater. The VPM conservatism slider is disabled once the dive has started (App.tsx:2902-2904). The live ceilings, NDL and the 'Uncleared decompression at surface' warning use the current gfLow/gfHigh (App.tsx:728-737, 874).
The replay seed is a ReplayContext holding a single low/high pair (App.tsx:801-804). It is captured once, from the frame before departure (
replayStart.current = previousReplay.current, App.tsx:888). DiveSample does not record GF.replayDive applies seed.low/seed.high to the whole dive: per-point tissueCeilings and ceiling (diveReplay.ts:186-190, 216, 274), the final decompressionRequired verdict (314-315), the VPM tail plan (340-341), the Complete decompression ascent plan and its final check (367-368, 412), and bottomNdl (427). The unmodified 'Original reconstruction', and the 'Modified replay' built from it, therefore evaluate the dive with a GF the diver had stopped using. The comparison UI never shows which GF it used.
Effects:
Browser reproduction on the HEAD production build (/tmp/divesim-review-dist-SqbiNx), served on 127.0.0.1 and driven by Playwright, using a copy of the reviewer's pw-gf.mjs in /tmp/divesim-verify-ozE7jk:
Reverse direction, from the reviewer's node harness (/tmp/divesim-review-TPAVti/gf.ts, run from the worktree with tsx against the real replayDive). This harness models the App recorder rather than driving the real App:
Code trace confirms every replay calculation reads seed.low/seed.high, and the seed is frozen at departure (App.tsx:884-889).
Suggested correction
Simplest consistent fix: disable the Bühlmann Conservativeness slider and the GF low/high inputs while underwater (
dive.minutes > 0 || dive.history.some(p => p.depth > 0)), the same way VPM conservatism is already locked. Then the departure seed matches the GF used for the whole dive.If mid-dive GF changes must stay possible:
Acceptance criteria
Regression test, ZHL-16C live dive in the browser or an App-level test:
Reverse case:
Scope: maintenance of the educational simulator. Preserve historical R1 acceptance and operational-validation limits.