Skip to content

Fix takeoff slice bug on duplicate GPS timestamps#164

Open
pokle wants to merge 3 commits into
masterfrom
claude/dazzling-clarke-c2d99b
Open

Fix takeoff slice bug on duplicate GPS timestamps#164
pokle wants to merge 3 commits into
masterfrom
claude/dazzling-clarke-c2d99b

Conversation

@pokle
Copy link
Copy Markdown
Owner

@pokle pokle commented May 11, 2026

Summary

  • detectFlightEvents used fixes.findIndex(f => f.time.getTime() === takeoffEvent.time.getTime()) to locate the takeoff fix. Cheap GPS loggers stall and emit consecutive fixes with identical timestamps, so the lookup can land on an earlier fix and silently feed pre-takeoff data into thermal/glide/altitude detection, producing spurious events. Read the index from takeoffEvent.details.fixIndex instead, which detectTakeoff already stores when it emits the event.
  • Two defensive cleanups in the same area:
    • evaluateTakeoffCriteria guarded the upper bound (index < fixes.length - 1) while reading fixes[index - 1]. Harmless today because the only caller starts at i = 1, but the guard now protects the correct boundary (index >= 1).
    • scoreTask replaces Math.max(...arr) / Math.min(...arr) with maxBy / minBy from array-utils, matching the policy already documented in array-utils.ts.

Why

Surfaced during a code-quality review of web/engine. The takeoff-slice bug is the only one with an observable failure on real-world IGC data — when triggered, it lets pre-takeoff climbs (e.g. a chairlift ride logged before launch) leak into thermal detection. The other two are consistency / robustness with no behavior change at realistic inputs.

Test plan

  • New regression test web/engine/tests/event-detector-duplicate-timestamp.test.ts — constructs a track with a pre-takeoff 0.6 m/s climb and a duplicate timestamp at fix 0. Fails on the previous code (thermal_entry emitted at fix 9) and passes on the fix.
  • Full engine suite: bun test web/engine/tests/ → 407 / 407 pass.
  • bunx tsc --noEmit in web/engine clean.

🤖 Generated with Claude Code

pokle and others added 3 commits May 11, 2026 07:47
Adds GET/PUT /api/auth/preferences in the auth-api worker for syncing
per-user app preferences and custom theme across devices. Storage is
opaque JSON in a new D1 table; partial updates are atomic via a single
ON CONFLICT statement so concurrent PUTs from two devices can't lose
each other's writes.

Bootstraps a vitest+miniflare test harness for auth-api (none existed)
and covers the route with 21 cases including auth gating, validation,
size cap, partial updates, CASCADE on user delete, and a concurrent-PUT
test pinning the atomicity guarantee.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a frontend sync layer that backs the existing localStorage-based
preferences and theme with the cloud API. localStorage remains the
synchronous read cache so startup has no flicker and anonymous users
keep working as before. When signed in, the layer hydrates from cloud
on import, debounces mutations (2s) into PUTs, and flushes any pending
writes on pagehide via keepalive fetch. Existing users' localStorage
prefs are uploaded once on first cloud-synced session.

mapLocation is allowlisted as device-local: stripped on upload and
preserved when cloud values overwrite localStorage during hydrate.
Different devices have different screens; viewport sync would also
generate noisy writes from continuous pan events.

Bootstraps a vitest+jsdom harness for the frontend (none existed) with
a localStorage polyfill that bypasses Node 22+'s broken built-in web
storage shim. Covers the sync layer with 23 cases across hydrate
quadrants, debounce coalescing, retry/4xx/keepalive behaviour, and
the pagehide flush path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
detectFlightEvents re-derived the takeoff fix index by scanning fixes
for one whose timestamp matched the takeoff event. Cheap GPS loggers
stall and emit consecutive fixes with identical timestamps, so the
lookup could land on a fix earlier than the real takeoff and silently
feed pre-takeoff data (e.g. a stalled climb) into thermal/glide
detection, producing spurious events. The takeoff event already
carries the correct fix index in its details — use that directly.

Also two defensive cleanups in the same area:
- Fix the lower-bound guard in evaluateTakeoffCriteria (was guarding
  the wrong boundary; harmless today because callers start at i=1).
- Replace Math.max(...arr) / Math.min(...arr) in gap-scoring's
  scoreTask with maxBy/minBy from array-utils, matching the policy
  documented in array-utils.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant