Skip to content

Phase E: UI — map, planner, live view, via-waypoints#23

Merged
DocGerd merged 15 commits into
mainfrom
worktree-phase-e-ui
Jul 15, 2026
Merged

Phase E: UI — map, planner, live view, via-waypoints#23
DocGerd merged 15 commits into
mainfrom
worktree-phase-e-ui

Conversation

@DocGerd

@DocGerd DocGerd commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

Phase E delivers the complete user interface — eight tasks, each implemented and reviewed with fix loops, browser-verified end-to-end against real data, closed by a whole-branch gate review (verdict: ready to merge).

  • E1 MapLibre + PMTiles map, fully offline style assets (self-hosted glyphs/sprites, network-audited), formatting lib
  • E2 Planner panel: diacritic-insensitive harbor search ('aero' → Ærøskøbing), option bounds with the 2.2 m safety-depth floor (draft 2.1 + margin — closes the PR Mask: reconnect all harbors (2x resolution, center-sampled land, connectivity gate) #8 blend-residual exposure), DST-safe departure picker
  • E3 Plan flow: wind → worker → auto-saved plan; typed error mapping to actionable i18n copy; singleton client with poisoning-proof recovery; cancellation resolved as guarded no-op
  • E4 Route rendering: Okabe-Ito board colors, WMO wind barbs, rig-comparison summary with GPX export; stale-forecast detection (spec §4)
  • E5 Saved plans list — loads render against their STORED wind grid (structurally proven: no fetch path exists)
  • E6 Live GPS guidance: heading-to-steer, next-maneuver distance, ETA drift (no re-routing, per spec); GPS state local (1 Hz never re-renders the app)
  • E7 Assembly: three-tab shell, banner system (offline/stale/GPS/persistence), About dialog with prominent disclaimer + live data attributions from mask.meta.json
  • E8 Draggable via-waypoints with stored-wind re-route — Closes Movable via-waypoints on planned routes with automatic re-route #4 (B13 delivered the router half in PR Phase B: routing domain core #5)

Verification

330 tests across 38 files (~235 s incl. the real-mask solver acceptance suite), strict + exactOptionalPropertyTypes typecheck, lint, build — all clean. Browser-verified complete real flows (17+ screenshots): plan Flensburg→Glücksburg with real Open-Meteo wind, route on map, rig tabs, reload → saved plan loads with ZERO new wind requests (network-log proven), via add/drag/remove replanning from stored data (request count pinned at 2 for the whole session).

Gate hardening (whole-branch review findings, fixed pre-merge)

Stale via-replan clobber guard (planIdRef; watched-vs-background asymmetry documented); stationary-GPS NaN heading → null (the berth is the most common Live-view start); initial-run coincident-via dedupe (B13 gap); Escape priority; canPlan during replan.

Phase F inheritances (tracked in ledger)

F1 precache list (incl. mask.meta.json for offline About); F2 E2E flows (via dedupe, multi-via drag); F3 BASE_URL sub-path verification + documented map-label language limitation.

Closes #4

🤖 Generated with Claude Code

Comment thread app/src/state/usePlanFlow.ts Outdated
Comment thread app/src/state/usePlanFlow.ts
Comment thread app/src/components/PlansList.tsx
Comment thread app/src/App.tsx
Comment thread app/src/state/replan.ts Outdated
Comment thread app/src/components/PlannerPanel.tsx Outdated
Comment thread app/src/components/PlannerPanel.tsx Outdated
Comment thread app/src/state/AppState.tsx Outdated
Comment thread app/src/services/geolocation.ts
Comment thread app/src/components/ViaMarkers.tsx Outdated
Comment thread app/src/components/AboutDialog.tsx
Comment thread app/src/components/MapView.tsx
Comment thread app/src/App.tsx Outdated
Comment thread app/src/lib/storage.ts Outdated
Comment thread app/src/lib/windBarbs.ts Outdated
@DocGerd

DocGerd commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

[errors / Medium — no inline anchor: file enters the diff with the fix] routing/workerClient.ts: no timeout on plan() — a hung worker suspends the promise forever; finally never runs, so replanning stays true permanently (markers disabled, spinner stuck, no recovery short of reload). openMeteo got its request timeout in the Phase D review; the worker path never did. Resolution: fix in flight — 120 s fake-timer-testable race treating timeout as a targeted fatal.

@DocGerd

DocGerd commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

[tests / High, product bug — no inline anchor: file enters the diff with the fix] services/db.ts: recommendedResult's intentional throw poisons listPlans' entire map — ONE corrupt stored plan blanks the whole saved-plans list into a misleading empty state (console-only trace). Garmin import (#3) will be a source of externally-built plans. Resolution: fix in flight — per-row isolation (skip + log corrupt rows, return valid ones), db + PlansList tests.

@DocGerd

DocGerd commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

The two non-inline findings (worker plan timeout; listPlans corrupt-row isolation) also landed in 2162bd2 — both files are now in the diff with tests. All 15 inline threads resolved; suite 356/356; the five-lens cycle is complete.

@DocGerd
DocGerd merged commit 3aec885 into main Jul 15, 2026
1 check passed
@DocGerd
DocGerd deleted the worktree-phase-e-ui branch July 15, 2026 20:39
@DocGerd

DocGerd commented Jul 16, 2026

Copy link
Copy Markdown
Owner Author

Record correction: the fix-wave report for 2162bd2 claimed all 22 items landed with none skipped, and the map-error-banner thread was resolved on that basis — but item 14 (MapView map.on('error') → one-shot banner) was not actually implemented (verified post-merge: no such wiring exists in the codebase). Caught by F2's E2E implementer looking for the banner to test. The item is now queued for the Phase F gate fix wave; the misreport is recorded in the project ledger.

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.

Movable via-waypoints on planned routes with automatic re-route

1 participant