Chapter 4 ยท verification

Verification โ€” proving the work

Handicap scoring only means something if reps and effort are real. Lane 7 ships two verifiers in the first-party app today: camera rep counting (MoveNet pose detection, 100% on-device) and BLE heart-rate straps (any GATT strap, scored as Karvonen %HRR). Both feed fields the engine already scores.

Camera rep counting

live

Point the phone at yourself side-on, pick the exercise, do the set โ€” the app counts reps live and logs the set as verified:true. No frame ever leaves the device.

Camera verification affordance in the app
app-camera-note.png ยท the camera-verify route offered at logging time

How it works โ€” the pose layer. The verifier lazily injects TensorFlow.js + MoveNet Lightning (~1MB, UMD builds via CDN) the first time a verifier opens โ€” never on app boot, never bundled. Every frame runs through MoveNet locally; the counter consumes only keypoints.

How it works โ€” the counting state machine. Pure logic, unit-tested headlessly in count.ts: one tracked joint angle per exercise, two thresholds (hysteresis) plus a persistence window โ€”

angle < downAngle โ†’ enter "down" (timestamped)
angle > upAngle โ†’ "up"; if down persisted โ‰ฅ 300ms โ†’ 1 rep
ExerciseTracked joint (vertex)down thresholdup threshold
Push-upsshoulder โ†’ elbow โ†’ wrist< 90ยฐ> 160ยฐ
Squatship โ†’ knee โ†’ ankle< 100ยฐ> 150ยฐ

Anti-jitter rules, all enforced in code: keypoint confidence floor 0.35 (both sides averaged when both clear it); the 300ms debounce rejects twitchy half-reps; a tracking dropout longer than the debounce invalidates the phase (the person may have moved anywhere while untracked) โ€” brief dropouts are tolerated. The Good-GYM angle-threshold approach (MIT), kept DOM-free so it can be reused by future verifiers (watch, IMU).

Confirming the counted set logs it straight into the match; the leaderboard's verified % rises with it.

lives: apps/web/src/verify/camera.ts (sheet + MoveNet) ยท apps/web/src/verify/count.ts (pure logic)
proven by: counter unit tests + e2e camera mocks (--use-fake-device-for-media-stream) ยท figma-app surface in e2e.mjs

Heart-rate straps (%HRR)

live

Pair any standard chest/arm strap over Web Bluetooth; the app scores each set's effort as Karvonen %HRR and attaches avgHrrPct to the entry โ€” the exact field the v2 handicap blend consumes.

First-party app match screen where verified entries land
webapp-match.png ยท the /app build where both verifiers live; verified % shows per player

How it works. Connects to the standard GATT Heart Rate Service (0x180D โ†’ measurement characteristic 0x2A37) โ€” Polar H10, Garmin, Wahoo, anything that broadcasts it; no vendor API needed. Per-session it averages notifications into:

%HRR = (bpm โˆ’ restingHr) / (maxHr โˆ’ restingHr) ร— 100, maxHr = 220 โˆ’ age

Settings (resting HR default 60, age default 35 โ†’ max 185) persist in localStorage["rwf.hr.v1"] and are clamped to sane ranges. Graceful by design: unsupported browser (iOS Safari), user cancel, and mid-session disconnect (partial averages are kept) all resolve cleanly โ€” verified reps never block a workout.

The engine side: an entry carrying avgHrrPct plus a learned player baseline switches that entry's multiplier to the 70/30 measured-vs-declared blend (see Game Rules โ†’ tier handicap), with baseline drift โ‰ค10% per update as anti-sandbagging.

lives: apps/web/src/verify/hr.ts ยท engine blend in apps/figma-app/engine.js effortMultiplier ยท baseline logic in game-core/src/baseline.ts
proven by: Karvonen + settings tests; blend math covered in engine tests

The phased verification plan

P2/P3 parked
PhaseLaneStatus
P1In-browser MoveNet camera counting + Web Bluetooth HR strapsBUILT โ€” live in the app, entries log verified:true / avgHrrPct
P2HealthKit / Health Connect history importphased (docs/05) โ€” native wrapper required
P3WHOOP / Garmin cloud cross-checkphased (docs/05)
โ€”Custom ML rep countingdeliberately NOT a blocker โ€” MoveNet covers MVP

Privacy stance: inference is 100% local, no frame ever leaves the device, and HR is averaged per-set rather than streamed anywhere. The wearable research trail (chest-strap accuracy, camera angles) is documented in docs/05_RESEARCH_WEARABLES.md.

plan: docs/05_RESEARCH_WEARABLES.md ยท agents/07-verification-wearables/BRIEF.md ยท docs/04_RESEARCH_OPENGYM.md (the counting approach's lineage)
โ† backBots