Every colored ribbon is trying to tell you something about the terrain and how you
rode it. Here's what each means — plus the honest story of how we got to today's definition, and
the math under it. Two eras live here: the classic ribbons (GNAR, Flow, Boom, Jordan)
come from the shared gnar-overlays engine on every ride; the v4 ribbons are a
research rebuild; v-next scoring is now live on every ride (#291) (the old v4* demo rides were retired in the #246 cleanup). Where a ribbon is a
prototype and not what production computes, we say so.
The data under everything datasets & provenance
Every ribbon is computed from a 1-second windowed timeline.parquet built
by the pipeline from two recorders:
| Column | Sourced from | What it really is |
| acc_* / gyro_* / impact_count / kurtosis | GNAR logger (fork-mounted IMU, 400 Hz) | impacts, vibration texture, spikiness — the terrain signals |
| altitude_baro_m | GNAR logger barometer | device altitude; one of the two grade witnesses |
| altitude_fit_m | Garmin Fenix FIT file | FusedAlti — a second barometer, correlated with the device baro (≈0.99 on clean rides; collapses when one baro phantoms — why provenance is classified by jitter, not correlation). NOT independent GPS, despite its old altitude_gps_m alias (#242) |
| lat / lon / distance_m / speed_mps | Garmin Fenix FIT (GPS) | position, distance, speed; gap-interpolated across smart-recording gaps (#215) |
| grade_pct / grade_confidence / grade_source | computed ONCE: compute_foundation_grade() (#254/#255) | honest-low grade: high = both baros agree; low = abstain, with the reason in grade_source (abstain-divergence / -gap / -slow / -interpolated). No GPS fallback on this hardware — there is no independent altitude witness (E-034) |
| heart_rate / cadence / power … | Garmin Fenix FIT | physio passthrough |
The honest part: roughly half of each ride carries a trusted grade;
on the other half every grade multiplier simply switches off. Scores got lower in the 2026-07 release
because the old fallback was quietly enhancing them with a fabricated “GPS” grade —
being dark beats being wrong.
GNAR v-next · production
How hard, punishing, and technical was the trail under you?
GNAR is the difficulty ribbon. High GNAR means the ground was hitting you hard
and in a way that took skill to absorb — a rock garden, a chundery ledge line, a steep
technical pitch. It's about the terrain's demand on you, not your speed.
How we got here. The old GNAR was grade-blind and magnitude-blind.
It had no idea which way the hill went (same trail up or down scored nearly the same,
even though the descent was twice as violent), and because vibration scales with speed, a
fast, mellow-feeling "plow" down a smooth Colorado descent hammered the sensor harder
than a slow Austin rock garden — and wrongly out-scored it. v-next measures impacts
per meter (speed stops inflating it), adds a spikiness gate that
rejects continuous buzz while keeping real hits, and folds grade in as a two-way multiplier
so a technical climb finally gets credit, not just descents. Validated against ground-truth:
buzz killed, the Colorado plow no longer beats real gnar, tech-climb credit preserved.
Then we found the catch (#183): barometric altitude is directionally unreliable —
it disagrees with GPS on up-vs-down for ~40% of a ride, so the two-way multiplier was
crowning rough climbs as the gnarliest “descents.” So (2026-07-16) the multiplier was
gated on a confidence signal (then: baro vs “GPS” sign agreement): where the grade direction
can’t be trusted, the climb/descent boost is switched off and the score falls back to pure
technicality. 2026-07 (#254/#255): grade went single-source and honest. We then
discovered the “GPS” channel is really the Fenix’s FusedAlti — a
second barometer that tracks the device baro (corr ≈0.99 on clean rides), not an independent witness
(#242). Falling back to it was fabrication, so that fallback was retired (E-034). Grade is now computed
once in the pipeline — honest-low: high where the two baros agree,
low = abstain where they diverge, altitude data is missing across a recording
gap, the baro window is interpolated, or you’re too slow to resolve slope — with a ±2% dead-band so near-flat noise never
counts as disagreement (E-037). Every overlay reads that stored grade; none recomputes its
own. About half of each ride carries a trusted grade; the rest runs multiplier-off —
dark rather than wrong.
Under the hood — signals, weights & formula
| Signal | Source | Role |
| impacts / meter | impact_count / speed | terrain technicality, speed-invariant |
| spikiness | kurtosis_mean | gate: rejects low-kurtosis buzz/plow |
| grade | timeline grade_pct — single-source honest-low grade (#254/#255) | two-way multiplier (climb & descent) |
| grade confidence | timeline grade_confidence (honest-low high/low) | gate: multiplier off on abstain — divergence, gap, or slow (#254/#255; was #183) |
dens = clip((impact_count / max(speed,1)) / 22.0, 0, 1) # DENS_REF
kfac = clip(kurtosis_mean / 0.8, 0, 1) # K_REF, floor 0 / exp 1
tech = dens * kfac
conf_gate = 1 where grade direction is trustworthy, else 0 # #183: 0 on baro-vs-GPS sign disagreement, slow, or interpolated
climb_s = clip( grade / 10, 0, 1) * conf_gate desc_s = clip(-grade / 10, 0, 1) * conf_gate
gnar = tech * (1 + 0.6*climb_s + 0.9*desc_s) # b/a = 1.5; multiplier -> 1.0 when gated
score = clip(gnar * 134, 0, 255) # SCALE: max ~1.9 -> 255
Per-distance atom removes the speed confound. Spikiness gate
(no floor, exp 1) drives the low-kurtosis plow toward zero — the EX2 fix. Grade
multiplier (1.0 flat → ~1.9 steep descent) multiplies tech, so a smooth
steep road stays near zero while a technical climb earns credit.
Validation (2026-07-06): median×100 — descent 61.6 >
true-gnar 37.3 > Colorado-plow 30.9 > tech-climb 24.3 > Bentonville 2.5 > buzz 0.0. Ordering
correct, EX2 fixed, buzz annihilated, climb credit retained.
Source: tools/make_v4_ride.py :: gnar_vnext(). #175 Items 1–3;
grade + confidence are READ from the timeline — computed once by
research/pipeline/grade_utils.py :: compute_foundation_grade() (#254/#255,
supersedes the #183 gate); production port = gnar-overlays#40.
Flow (trail) v4 = v3.1
Were you flowing — carrying speed and rhythm on the dirt?
Flow lights up when you're in the groove: descending or holding momentum, linking
corners, riding textured trail without stalling or grinding. Marginal flow renders faint on purpose,
so a trail with just a little flow doesn't look like flow heaven.
How we got here. The original flow score rewarded
"smooth + steady + rhythmic" — exactly the signature of a slow uphill grind or cruising
pavement (it counted 74% of uphill as flow). We rebuilt it grade-aware, made climbs require
real carried speed (pumping up a riser is flow; grinding isn't), read cornering only
as a fast berm, and bridged brief braking dips (a scrub is still flow; a stall breaks
it). The hard lesson lives here: the sensor cannot separate Austin's rocky flow from
Bentonville's berms — when you flow, all terrain pounds about the same — so we
decided Flow just means "you're flowing," and moved terrain character into TECHNICAL,
FLOHGNAR, and Cruise.
Under the hood — signals, weights & formula
| Term | Weight | Definition |
| band | 0.25 | roughness bell, center 1.2g (pavement & chunder both low) |
| descent | 0.25 | clip(-grade,0,15)/15 |
| corner | 0.15 | clip((gyro-10)/50) × atspeed (fast berm, not switchback) |
| consist | 0.15 | speed consistency |
| momentum | 0.20 | speed / rolling-max(12s) |
atspeed = clip((speed - 1.5)/(4.0-1.5), 0, 1)
base = .25*band + .25*descent + .15*corner + .15*consist + .20*momentum
gate = max( 0.15 + 0.85*clip((8-grade)/10,0,1), # grade gate (floor .15)
clip((momentum-0.5)/0.3, 0, 1) ) # OR momentum gate
flow = clip(base * gate, 0, 1)
flow *= 1 - clip(grade/2,0,1)*(1-atspeed) # climb needs real speed
# then: bridge braking dips <= 5s bounded by flow, no stall (> 1.2 m/s)
The decision baked in: Flow is terrain-agnostic by choice (accept (a),
2026-07-04). Two probes proved the IMU can't separate terrains on flow windows — even direct
magnitude has Austin ≈ Colorado. Kurtosis is the one metric that fingers
Austin (1.3–1.6×), kept as a monitored lever, not adopted.
Source: tools/make_v3flow_ride.py :: compute_v3_flow(); the grade it
gates on is the stored single-source honest-low grade (#254/#255) — see the GNAR panel for
the full grade data-source story.
FLOW sustained v-next · production
Not just flow — sustained flow, the kind that rides like a ribbon.
Flow weighted by continuity. Long unbroken runs stay bright; short scattered
flashes fade but don't disappear. The difference between a Colorado ribbon you settle into and
Austin's brief flow "confetti."
How we got here. Flow is scored second-by-second, so on a
gnar-dominant trail it colonizes the lulls between hits and paints short flow everywhere — making
a scant-flow ride look flowy. The fix rewards duration, but floors rather than
erases, because Austin's rare genuine flow moments ("flohments") shouldn't vanish.
Under the hood — formula
mask = (trail >= 0.45) AND (speed > 0.8)
L = seconds of the flow run this window belongs to
w = 0.35 + 0.65 * min(L / 20, 1) # FLOOR 0.35, LFULL 20s
flow_sustained = trail * w
A lone window keeps 0.35× its value; a window inside a ≥20s run keeps full
value. On the sustained ribbon Austin drops ~19%→8% (fragmented) while Colorado holds
~26%→16% (sustained).
Source: tools/make_v4_ride.py :: sustained_flow().
FLOW bridge v-next · production
A pumped roller between two flow sections — flow through a rise.
Gold marks a spot where you had flow, hit a roller, pumped through holding speed,
and came out into flow again. It looks like a gap, but it's the connective tissue between two
ribbons, not an interruption.
How we got here. Operator's lens: "flow → brief roller
→ flow is still flow; a stall or real feature breaks it." A bridge only counts if it lives
between two real flow runs, stays short, keeps its speed (not hard-braked), and has
no consequential rock/root/ledge — a test that reads the GNAR score.
Under the hood — the six tests
| Test | Constant |
| lives between two flow runs | — |
| both flanks real | MIN_FLANK 3s |
| short gap | MAX_GAP 12s |
| always moving | speed > 0.8 |
| not hard-braked | DECEL 0.78 × flow-speed |
| no consequential feature | GNAR < 100 |
Cross-overlay coupling: the GNAR threshold was rescaled from
the operator's original 150 (old 200-max scale) to 150×134/200 = 100 when
GNAR's scale changed. A GNAR decision directly forced this FLOW-bridge change.
Source: tools/make_v4_ride.py :: roller_bridges().
Cruise v-next · production
You were flowing — but on a smooth surface (road), not dirt.
Road flow feels completely different from singletrack flow, but the raw flow number
can't tell them apart. Cruise is the tan ribbon for flow on a smooth surface, so fast pavement
stops masquerading as rowdy trail.
How we got here. Splitting by shape (corner/descent) failed —
roads are curvy and downhill too. The fix splits on surface texture: speed-normalized
roughness smoothed over ~20s. Pavement is sustained-smooth; dirt sustained-textured. It cut road-flow
mislabeled as trail from ~38% to ~14%.
Under the hood — formula
texc = (acc_rms - 1.0) / max(speed, 1)
roll = rolling_mean(texc, 21s, centered)
engaged = roll > 0.06 # dirt ~0.23, pavement ~0.02
trail = flow where (engaged AND moving)
cruise = flow where (!engaged AND moving)
Source: tools/make_v4_ride.py :: split_flow().
FLOHGNAR v-next · production
Flowing the rough — downhill, at speed, through the gnar.
The Austin kind of flow: carrying speed downhill on rocky, consequential trail and
flowing it anyway. Flow that requires gnar, not flow in its absence.
How we got here. The smooth-flow model kept failing Austin
because it treated rocks as flow-killers — but Austin's best flow is rocky descents ridden fast.
Naming it resolved the contradiction: FLOW = flow without gnar; FLOHGNAR = flow through gnar; TECHNICAL
= gnar you fight. Three coexisting overlays instead of one number carrying everything.
Under the hood — formula
gnar_density = clip((impact_count / max(speed,1)) / 22, 0, 1) # UN-guarded
climb_gate = grade-based where grade confidence is "high", else neutral 1.0 # #183 gate
flowing = clip( momentum * climb_gate * atspeed, 0, 1 )
descent = clip( (-grade + 2) / 10, 0, 1 )
flohgnar = gnar_density * flowing * (0.5 + 0.5*descent) # 0 if speed <= 0.8
Staleness: gnar_density here is the
un-guarded atom (impacts/meter only, no kurtosis gate). GNAR v-next added that gate to
reject the low-kurtosis plow — FLOHGNAR lacks it, so a plow GNAR now rejects could still
register here.
Source: tools/make_v4_ride.py :: overlays().
TECHNICAL v-next · production
The rough you had to fight — gnar while climbing, slow, or breaking momentum.
High-gnar terrain you were not flowing through: technical climbs, slow
deliberate moves, momentum-breaking spots. Of all the ribbons, this one ranks terrain gnar-ness the
most cleanly — Austin far above Bentonville above Colorado, matching lived experience.
How we got here. It fell out of the same three-way split as
FLOHGNAR: take the speed-normalized roughness and keep the part you were not flowing. It
turned out to be the strongest terrain signal in the whole project.
Under the hood — formula
technical = gnar_density * (1 - flowing) # 0 if speed <= 0.8
# same gnar_density & flowing as FLOHGNAR
Complement of flowing — captures technical climbs and momentum-breaking terrain.
Ranks Austin 34% >> Bentonville 24% > Colorado 11%.
Staleness: same un-guarded gnar_density caveat
as FLOHGNAR.
Source: tools/make_v4_ride.py :: overlays().
Boom production engine verify
The big hits — individual impacts hard enough to register as an event.
Boom marks discrete, heavy impacts: a drop landing, a square-edge slam, a case.
Where GNAR is continuous "how rough overall," Boom is "there was a specific big hit here,"
graded thud → boom → mega-boom by how hard.
Status. Boom comes straight from the shared
gnar-overlays engine and wasn't part of the recent rework. It flags a window when peak
acceleration crosses a threshold, then clusters nearby hits. It currently logs
unknown-config-key warnings nobody has cleaned up — see Staleness.
Under the hood — detection
is_event = acc_peak >= 6.0 g # DEFAULT_MIN_PEAK_G
THUD 6.0g BOOM 9.0g MEGA_BOOM 13.0g
cluster if gap between events <= 2.0 s # GAP_THRESHOLD
Staleness: unknown-config-key warnings for
boom_threshold_g, mega_boom_threshold_g, thud_threshold_g,
zone_severity_weight. Config surface has drifted; not revisited.
Source: gnar-overlays deep-read (2026-03-08). Verify against current
gnar-overlays v0.5.0.
Jordan production engine verify
Airtime — the moments you left the ground.
Jordan (as in "air Jordan") is the airtime ribbon. When the sensor goes near
free-fall — the force it feels drops toward zero — that's a jump, drop, or launch. It scores
both how long you were up and how clean the float was.
Status. Like Boom, Jordan is the shared engine's algorithm and
wasn't touched in the recent grade/speed work. It's a genuinely different signal (free-fall, not
roughness), so most speed-confound fixes don't apply — but it hasn't been re-reviewed against the
grade fix, and deserves a verification pass.
Under the hood — detection & scoring
airtime if acc_rms_g < 0.8 # AIRTIME_THRESHOLD_G
guard: skip if not moving OR speed <= 1.0
depth_factor = 1.0 - (acc_rms_g / 0.8)
score = (0.6*duration + 0.4*depth) scaled 0..255
Staleness / verify: not re-reviewed against the canonical
grade fix (#172). Display note: viewer ramps Jordan 0–100 while the engine scores 0–255
— confirm the scaling.
Source: gnar-overlays deep-read (2026-03-08). Verify against current
gnar-overlays v0.5.0.
GNAR orig (pre-v-next) A/B reference
The old difficulty score, kept side-by-side so you can see the change.
This grey ribbon is the previous GNAR — grade-blind and speed-confounded.
It's here purely as a before/after so you can judge whether v-next got it right on your own trails.
As of #291 the v-next GNAR is what EVERY ride gets from the deployed pipeline; this grey ribbon is the retired pre-v-next score, kept only for before/after comparison.
Why keep it. Trust is earned by comparison. If v-next calls a
section harder or easier than the old score did, you should see exactly where and decide for yourself.
Under the hood — the old formula
score = (0.35*roughness + 0.25*impact + 0.25*technical + 0.15*saturation) * speed_factor * 255
roughness = norm(acc_rms, 1.0..4.0g) impact = norm(acc_peak, 8g) # peak-only
technical = norm(gyro_rms, 200dps) # pure rotation, direction-blind
speed_factor = clip(5.0/speed, 0.5, 1.5)
Three flaws: grade-blind, magnitude/count-blind (impact is peak-only, 1 rock = 50), and
speed-confounded. v-next fixes all three.
Source: gnar-overlays deep-read, GnarAnalysisService.
Fork attitude & terrain interaction mag rides only
A different family of ribbons: not "how hard was the trail" but "what was the front end
physically doing." Present only on magnetometer-instrumented rides.
The device sits on the unsprung lower fork, below the suspension, at the
tire–terrain interface. These ribbons reconstruct its full earth-frame orientation — roll, pitch,
yaw — plus the vertical load it takes, and then relate that to the GPS path. The magnetometer's job is
narrow: it's the drift-free absolute-attitude anchor that makes the fork's yaw trustworthy over a
whole ride (gyro alone random-walks). Everything below is built on the same 1-second window grid as the
scoring ribbons.
How we got here. Landed across #293 (mag-anchored drift-free yaw),
#302 (AHRS coast-latch fix), #301 (bike-frame pose) and #310 (the terrain-interaction feature layer). The
four attitude/load ribbons are validated; three of the four interaction features are
provisional v0.1 — read them directionally until more labelled rides land (#311).
Fork roll / lean validated
How far the bike is leaned over — blue = left, red = right.
The bike-frame roll angle: lean into a berm or a turn and this lights up on the side you're
leaning toward. Zero when the bike is upright.
Status — validated (E-058). Correlates 0.89 with an independent
physics-lean reference, sign correct on 96–98% of windows, reads upright (−1.5°) at stops.
Known limit: under-reads peak lean by up to ~30% in fast coordinated turns (gravity capture),
and low-speed tight switchbacks decouple from the turn — the roll is real bike attitude there, just not
turn-coupled.
Under the hood — derivation
bike_roll = attitude expressed in the BIKE frame via R_mount
R_mount = mount-up + a per-ride twist fitted against physics lean
published fork_roll_deg = −bike_roll # sign-flipped so + = RIGHT
Depends on: L1 AHRS gravity solution (imufusion), the fitted mount transform. Falls back to null when
the twist fit is unresolved (pose withheld rather than shown wrong).
Source: scripts/build_l1_motion.py v1.8.0, foundation.py.
Fork pitch validated
Nose-down vs nose-up — the front end diving into a drop or climbing.
The bike-frame pitch angle. Nose pitches down going over a drop or steep descent, up when
climbing or lofting the front. Marks every drop-in and every climb.
Status — validated. From the same bike-frame solution as roll;
tracks the bulk GPS grade (correlation ~0.69) while also capturing the sub-grade pitching the trail imposes.
Under the hood
bike_pitch = pitch in the bike frame (R_mount applied)
Depends on: L1 gravity solution + mount transform. Same null-fallback as roll.
Source: scripts/build_l1_motion.py, foundation.py.
Fork heading (yaw) validated
Which way the fork points in absolute terms — drift-free, 0 = magnetic north.
The fork's absolute earth-frame heading, anchored by the magnetometer so it doesn't drift
over a long ride the way gyro-integrated heading does. This is the enabling signal — a trustworthy
absolute yaw is what the terrain-interaction features are built on.
Status — validated (E-053/E-054, #293). Drift-arrest confirmed on
a 2.7-hour ride with stops: on accel-still spans (bike physically stationary) true yaw is constant, and the
mag-anchored yaw stays bounded where gyro-only yaw walks away. Requires a magnetometer and a
per-mount calibration; rides without either show this ribbon null.
Under the hood — the anchor
imufusion AHRS with magnetic_rejection ON
framed + hard/soft-iron-calibrated mag fed to ahrs.update(gyr, acc, mag)
gravity fixes roll/pitch; mag fixes yaw drift
Depends on: gnar_mag.parquet + a mount-specific calibration
(mag_calibration_fork-crown-v1.json). Mount-specific — recalibrate on any mount change.
Source: scripts/build_heading.py, build_l1_motion.py.
Fork vertical load validated
How hard the front wheel is being driven into the ground, g RMS.
The earth-frame vertical acceleration at the unsprung fork — the raw impact the wheel
takes. Spikes on every landing, square-edge, and hard compression. This is a direct measurement, not a score.
Status — validated. Earth-frame vertical load from the AHRS
solution, per-window RMS. Nulled on sustained-coast windows where the attitude solution is untrustworthy
(#302 guardrail).
Under the hood
fork_load_rms = RMS(world_vertical_accel) per 1-s window # g
Depends on: L1 earth-frame vertical acceleration.
Source: scripts/build_l1_motion.py.
Absorption validated
The compression–rebound intensity of a hit — how much the terrain loaded the front end.
Where fork load is the raw force, Absorption is the event: it measures the
compression-then-rebound signature of a drop or square-edge, so a real drop reads far higher than continuous
chatter. The strongest, most trustworthy of the terrain-interaction features.
Status — validated (#310). Against operator-labelled drops it
separates cleanly: labelled drops median ~8.7g vs chatter baseline ~5.0g. Reuses the verified
build_load_transition lineage (#287) — not re-derived.
Under the hood
absorption_intensity = load_transition per-window intensity
(compression_peak → rebound_trough signature)
Depends on: foundation/load_transition.parquet (50 Hz vertical accel). Caveat:
the ribbon shows the per-window intensity; the deeper span-based drop measurement is a prereg-v2 refinement.
Source: scripts/build_load_transition.py, build_terrain_interaction.py v0.1.1.
Lean vs turn provisional v0.1
The fork roll set beside the GPS turn direction — how the rider works a corner.
Intended to show whether the rider is leaning into a turn or fighting it. Read it as
lean/muscle for now — the turn-direction interpretation is not yet proven.
Status — PROVISIONAL. v0.1 is the raw fork roll passed through,
masked to windows where GPS curvature is defined; it does not yet compute a roll-vs-curvature
relationship. Its ability to track turn direction is under-powered — 5/9 on one
labelled ride is a coin flip at that sample size. Expected to validate on volume (pooled turns across
~2–3 more labelled rides, #311).
Under the hood
lean_vs_turn_deg = fork_roll_deg # passthrough; gps_curvature emitted separately
Depends on: fork_roll + GPS-derived curvature. Turn-sign interpretation deferred to Stage C / #311.
Source: scripts/build_terrain_interaction.py v0.1.1.
Micro-terrain provisional v0.1
Fork pitch minus the bulk trail grade — the terrain within the slope.
Subtracts the GPS grade from the fork pitch to isolate the steps, roots and rollers the
bulk slope doesn't explain. Plausible, but unproven.
Status — PROVISIONAL. No ground-truth reference exists for
micro-terrain yet, and it does not currently gate on grade confidence (so low-confidence grade windows can
leak in). Progress depends on labelled micro-features, not just more miles.
Under the hood
micro_terrain_deg = fork_pitch_deg − atan(grade_pct/100)
Depends on: fork_pitch + GPS grade. Grade-confidence gating is a prereg-v2 refinement (#310 F4).
Source: scripts/build_terrain_interaction.py v0.1.1.
Steering deflection provisional v0.1
Fork heading minus GPS travel direction — where the front end points vs where the bike goes.
Blends the rider's steering input with the terrain deflecting the wheel. Read only its
swings, never the absolute value — the number carries a constant offset.
Status — PROVISIONAL. The absolute angle is frame-offset-dominated
(fork yaw is magnetic-north with an unpinned zero; GPS bearing is true-north), so only its variation is
meaningful today. Separating the rider's steering from the terrain's deflection of the wheel is genuine new
science — the committed next thread (#311).
Under the hood
steering_deflection_deg = wrap(fork_yaw_deg − gps_bearing_deg)
Depends on: fork_yaw (mag) + GPS travel bearing. Per-session offset removal + steering/deflection
separation are #311.
Source: scripts/build_terrain_interaction.py v0.1.1.
Staleness & cross-impact
What's honest to know — where the overlays are out of sync, still prototypes,
or where a decision on one rippled into another.
v-next GNAR is now PRODUCTION for every ride (#291)
The v-next scorer was promoted into the deployed pipeline (#291): every ride is now scored by it,
no _vnext suffix. The old speed-confounded GNAR is retired (kept only as the grey A/B reference).
FLOHGNAR & TECHNICAL use an un-guarded atom GNAR no longer trusts
GNAR v-next added a kurtosis gate to reject the low-spikiness plow. FLOHGNAR/TECHNICAL still build
on the older gnar_density (impacts/meter, no gate), so the plow GNAR ignores could
inflate them. Worth reconciling.
A GNAR scale decision rippled into the FLOW bridge
GNAR's display scale changed (max → 255). The FLOW-bridge "consequential feature?" test reads
GNAR, so its threshold was rescaled 150 → 100 to preserve the operator's original call.
Boom logs unknown-config-key warnings
boom_threshold_g, mega_boom_threshold_g, thud_threshold_g,
zone_severity_weight warn as unknown keys. Harmless today, but config has drifted; not
revisited.
Jordan hasn't had a review pass
Not part of the recent rework. Free-fall signal, so speed fixes mostly don't apply — but never
checked against the grade fix (#172). Also a viewer 0–100 vs engine 0–255 scale note.
Flagged for review.
The grade fix (#172) is the foundation under everything grade-aware
One canonical grade signal (measured over distance, not a single noisy sample) feeds GNAR v-next and
FLOW. Overlays compute grade themselves so they're correct; the grade shown on hover has
only been reprocessed for some sessions and can lag on older rides.
Older v3FLO* demo copies over-report Austin flow
They reflect a pre-decision flow model that over-reports flow on rocky Austin trails. The
every ride uses the accepted terrain-agnostic flow (#291).
Sources: OVERLAY_REDESIGN_HISTORY.md, SESSION_STATE_OVERLAYS.md,
tools/make_v4_ride.py, tools/make_v3flow_ride.py. Tracked #175 / #172.
Overlay definitions are one rider's lens, not universal truth.