Found triaging clang-tidy's bugprone-branch-clone Phase D (see .claude/phase-d-branch-clone-remaining-plan.md). app/bin/elev.c:1197 and :1199 -- inside the endpoint-elevation-mode reconciliation if chain (mode0/mode1 priority cascade around line 1180-1207).
Two identical-body pairs exist: the mode0==ELEV_GRADE / mode0==ELEV_COMP branches both no-op (;), and the mode1==ELEV_GRADE / mode1==ELEV_COMP branches both do mode0=mode1.
Deliberately NOT merged: the chain's ordering encodes which endpoint's mode 'wins' when both ends have a special mode simultaneously (checked in mode0-then-mode1 pairs, per mode type, in a specific sequence). A same-value merge across non-adjacent conditions risks silently changing that precedence -- not confirmed safe without a deeper read of every mode-combination's intended precedence, not attempted in this pass. This ticket exists purely to track the decision (per project policy: leave-alone findings get a ticket) so it doesn't keep resurfacing as 'new'. No fix planned. A regression fixture exercising a joined track pair where one endpoint is ELEV_GRADE and the other ELEV_COMP (and the reverse ordering) would give this cascade real coverage and clarify the intended precedence -- currently believed untested.
Anonymous
Diff:
Investigated. Precedence in SetTrkElevModes() (elev.c:1171-1210) is actually already well-defined and consistent — STATION > GRADE > COMP, independent of which endpoint
(trk0/trk1) holds which mode, because it's a single fixed-order if/else-if chain. So the "which endpoint wins" framing isn't quite the issue; elev correctly stays 0.0 in
the GRADE/COMP branches since those heights are computed dynamically elsewhere (fork-propagation engine, elev.c:632+), not stored at join time.
The actual gap: when both ends are GRADE (or both COMP), trk1's specific grade/computed state is silently discarded in favor of trk0's, with no check for whether they
actually agree. This is inconsistent with the DEF/DEF case immediately above (line 1183-1185), which explicitly compares heights and raises MSG_JOIN_DIFFER_ELEV if they
differ by more than 0.1.
Fix: keep the STATION > GRADE > COMP precedence as-is, but add the same diff-check/warning for the mode0==mode1==GRADE and mode0==mode1==COMP cases that DEF/DEF already
has, in SetTrkElevModes() itself (the conflict can only be detected once a join is attempted, not earlier in the UI).
Fixed on branch bug-707-elev-join-mode-merge-gtk3 (changesets 6b70d18317e4 and c7aea294eb58).
Findings
The elevation-mode precedence in SetTrkElevModes() (elev.c) was reviewed. When two endpoints are joined, the resulting endpoint mode is chosen by this order: Defined
elevation beats Station name beats Grade beats Computed; within the same tier the first endpoint wins. That ordering was already correct and is unchanged.
Two real problems were found in the code that implements it:
applied the FIRST endpoint's (non-Defined) mode to both ends. Consequence: joining, for example, a Grade endpoint to a Defined-elevation endpoint discarded the
defined elevation entirely and left both ends as Grade. Joining in the other order worked. This is now fixed - the defined elevation always wins and propagates to
both endpoints regardless of join order.
(MSG_JOIN_DIFFER_ELEV). There was no equivalent when both endpoints carried an elevation Station and the names differed - one name was silently dropped. A new
message, MSG_JOIN_DIFFER_STATION, is now shown in that case, keeping the first endpoint's station name (consistent with the existing precedence rule).
Changes
elevjoin.h. It takes the two endpoints' elevation state and returns the merged mode, height, and station plus flags for "heights differ" and "stations differ". The
precedence order is now stated once, in a small rank helper, instead of being implicit in the ordering of the if/else arms.
averaging and height-mismatch flag, the Station/Station name-mismatch flag, tie-breaking, and the regression case for the latent bug above. The test links the
extracted logic directly with no stubs.
Verification: full build clean, AStyle clean, all unit tests pass. The GUI demo regression suite was run; its pre-existing unrelated failures are unchanged.