Security audit (5 phases) → reports under _docs/05_security/. AZ-501 (F-SAST-1, HIGH): Externalize hardcoded Google Geocode key from mission-planner/src/config.ts to VITE_GOOGLE_GEOCODE_KEY via new GeocodeService.ts; fail-soft warn when unset; STC-SEC1D static deny-list gate; +5 unit tests in tests/mission_planner_geocode.test.ts. AZ-502 (F-DEP-1, HIGH): Force vite>=6.4.2 and postcss>=8.5.10 via package.json overrides in both roots; clean reinstall clears all bun audit advisories. Test-spec sync (Step 12) + Update Docs (Step 13) deltas: AC-43, AC-44, NFT-SEC-09b, FT-P-61, FT-N-17, ripple log, batch_12 report. Pending user actions: revoke Google + OWM keys (AC-6 / AZ-499 AC-7). 229 PASS / 13 SKIP / 0 FAIL on static + fast suites. Co-authored-by: Cursor <cursoragent@cursor.com>
13 KiB
05 — Flights & Mission Planning
1. High-Level Overview
Purpose: One logical component covering everything mission/flight related — flight CRUD, waypoint editing, altitude profile, wind/battery calc, raw mission JSON I/O, and GPS-Denied operations (incl. an end-to-end test mode that simulates a real flight from a tlog + video pair). It is currently physically split across two codebases that the project intends to converge.
Architectural Pattern: Page composition (FlightsPage) wiring three sibling panels — FlightListSidebar, FlightParamsPanel, FlightMap — plus modals (AltitudeDialog, JsonEditorDialog) and a GPS-Denied sub-page.
Implementation status — two trees, one component:
| Tree | What it is | Deployed? |
|---|---|---|
src/features/flights/ (15 modules, React 19 + Tailwind) |
The target implementation. Mostly a mechanical port-in-progress of the tree below. | Yes — Dockerfile builds src/ only. |
mission-planner/ (37 modules, React 18 + MUI 5) |
The port source / reference. The richer, more battle-tested mission planner that the new SPA is being adapted from. | No. Disjoint dependency island. Deletion candidate after parity. |
The two trees are intentionally disjoint at the file level (no cross-imports — 00_discovery.md §1) but they are one component in the design: same domain, same data model, same intent. Findings, port plan, and architecture decisions are tracked in this single component spec. Mission-planner files are listed in §"Module Inventory" below alongside the new SPA files; per-finding origin is preserved.
Upstream dependencies (target tree): 00_foundation, 01_api-transport, 03_shared-ui (FlightContext, ConfirmDialog).
Downstream consumers: 10_app-shell (routes /flights and the GPS-Denied sub-page).
2. Internal Interfaces
Page entries (target tree, src/features/flights/)
| Export | Notes |
|---|---|
FlightsPage() |
Top-level route component for /flights. Uses useFlight(), fetches flight detail by id, exposes save/delete/duplicate. Wires react-leaflet, leaflet-draw, and chart.js. |
GpsDeniedPage() (planned route, see §6) |
Sub-page for GPS-denied operations and test mode. Today a partial inline panel inside FlightsPage (finding #25) — slated to become its own route. |
Internal modules — target tree (src/features/flights/)
| Module | Role |
|---|---|
FlightsPage.tsx |
Orchestrator (route component) |
FlightMap.tsx |
Leaflet map + draw control + waypoint markers + minimap |
FlightListSidebar.tsx |
Left panel: flight list, search, new/duplicate/delete |
FlightParamsPanel.tsx |
Right panel: name, aircraft, takeoff/landing, altitude chart, waypoint list, wind effect |
WaypointList.tsx |
DnD-sortable waypoints (@hello-pangea/dnd) |
AltitudeChart.tsx |
chart.js altitude profile |
WindEffect.tsx |
Wind-vector visualisation |
MiniMap.tsx |
Inline overview map |
MapPoint.tsx |
Single waypoint marker |
DrawControl.tsx |
leaflet-draw integration |
AltitudeDialog.tsx |
Per-waypoint altitude/purpose modal |
JsonEditorDialog.tsx |
Raw mission-JSON editor |
flightPlanUtils.ts |
Distance / battery / weather computation helpers |
mapIcons.ts |
Leaflet icon factory |
types.ts |
Local feature types (waypoint shape, mission JSON shape) |
Internal modules — port source (mission-planner/)
| Group | Modules | Role / what the target should learn |
|---|---|---|
| Entry | main.tsx, App.tsx (vestigial CRA stub) |
Composition root + LanguageProvider. The CRA stub App.tsx is a deletion candidate post-port. |
| Page composition | flightPlanning/flightPlan.tsx, LeftBoard.tsx |
Canonical page shape (sidebar + map). |
| Map | flightPlanning/MapView.tsx (cycle-paired with MiniMap.tsx), MiniMap.tsx, DrawControl.tsx, MapPoint.tsx |
Reference Leaflet integration. Cycle: MiniMap imports the named helper UpdateMapCenter from MapView; MapView imports MiniMap as JSX child. Document the contract precisely if porting both at once. |
| Panels | flightPlanning/PointsList.tsx, AltitudeChart.tsx, AltitudeDialog.tsx, WindEffect.tsx, TotalDistance.tsx, JsonEditorDialog.tsx, LanguageSwitcher.tsx, Aircraft.ts |
Reference panel shapes. Several have richer behaviour than the current SPA siblings. |
| Services | services/calculateBatteryUsage.ts, AircraftService.ts, WeatherService.ts, calculateDistance.ts |
Authoritative battery / weather / distance logic. The target's flightPlanUtils.ts is still an inferior port on remaining axes (silent errors, sequential await). The hardcoded-API-key gap was closed by AZ-448 / AZ-449 (main SPA) and AZ-499 (mission-planner — env-resolved + fail-soft). |
| i18n | flightPlanning/LanguageContext.tsx, constants/translations.ts, constants/languages.ts |
Local translation pattern. The port should converge to 00_foundation/i18n instead. |
| Constants | constants/{actionModes,maptypes,tileUrls,purposes}.ts |
Reference constant tables. |
| Icons | icons/{MapIcons,PointIcons,SidebarIcons,PhoneIcon}.tsx |
Reference icon factory. |
| Utilities | utils.ts, config.ts, types/index.ts |
Reference helpers + types. |
| Test (vestigial) | test/jsonImport.test.ts, setupTests.ts |
One Jest test that cannot run today (Jest not in package.json). Out of scope for the live SPA test plan. |
3. External API Specification
Endpoints consumed (target tree + planned for GPS-Denied):
| Method | Path | Purpose |
|---|---|---|
| GET | /api/flights |
List + page (pageSize=1000 ceiling from FlightContext) |
| GET | /api/flights/{id} |
Detail + waypoints |
| POST | /api/flights |
Create |
| PUT | /api/flights/{id} |
Update flight metadata |
| DELETE | /api/flights/{id} |
Delete |
| POST/DELETE | /api/flights/{id}/waypoints |
Add / remove waypoints |
| POST | /api/gps-denied-desktop/... |
GPS-denied desktop service (mission setup, plan upload) — partial today |
| POST | /api/gps-denied-onboard/... |
GPS-denied onboard service (frame + IMU consumer) — target of test-mode SITL output |
| GET | OpenWeatherMap (third-party, direct from browser) | Wind/temperature lookup. Will be proxied via suite as part of Step 4 (hardcoded key removal). |
Concrete GPS-Denied endpoint shapes are not yet finalised in the suite spec — flagged for confirmation in autodev Step 3 (Test Spec) and Step 6 (Problem Extraction).
5. Implementation Details
State Management: Page-local React state for the active flight; FlightContext (in 03_shared-ui) for the list of flights.
Save model — current shape, target tree (finding #19): on Save, the UI deletes all existing waypoints and POSTs each one again. N delete + M POST round-trips. Lossy — concurrent edits race; if a POST fails halfway through, the saved flight is left with truncated waypoints. The mission-planner/ reference does this differently (single mission-JSON PUT) and is the recommended target shape.
Waypoint POST shape mismatch (PRIORITY, finding #20): target tree sends {name, latitude, longitude, order}; suite spec wants {Geopoint:{Lat,Lon,MGRS}, Source, Objective, OrderNum, Height}. Strict server returns 400. Owner of fix: this component + 00_foundation/types/index.ts::Waypoint.
Battery / weather calc (target tree flightPlanUtils.ts):
- Hardcoded OpenWeather API key in source (
'335799082893fad97fa36118b131f919') — committed secret. Step 4 fix. - Sequential
awaitper segment — perf trap on long missions. - Silent
try/catchswallows weather errors. - Mixes km / m altitudes; ambiguous battery-capacity unit (Wh vs Ws).
- Port source (
mission-planner/services/) has a cleaner, more correct implementation — use it as the reference.
Map / icons:
- Target tree
mapIcons.ts::defaultIconCDN URL pinned toleaflet@1.7.1whilepackage.jsonhas 1.9.4. MiniMapmap-tile licence attribution missing in some configurations.
Modal a11y (AltitudeDialog, JsonEditorDialog) — no aria-modal / role="dialog" / focus trap. Step 4. Same gap on the port-source counterparts.
Tech-stack divergence between the two trees (carry into the port plan):
| Concern | mission-planner/ |
src/features/flights/ (target) |
|---|---|---|
| React | 18 | 19 |
| UI library | MUI 5 | Tailwind 4 + custom az-* tokens |
| i18n | local LanguageContext |
react-i18next (Foundation) |
react-leaflet |
4.2 | 5 |
@hello-pangea/dnd |
16 | 18 |
Key Dependencies (target tree): leaflet 1.9, react-leaflet 5, leaflet-draw, leaflet-polylinedecorator, chart.js 4, @hello-pangea/dnd 18.
6. GPS-Denied sub-feature
Today a partial UI panel exists inside FlightsPage (target tree finding #25). The component design promotes this to a first-class sub-page with two tabs.
6a. Operations tab — current intent (already present, partial)
- Upload / select a mission plan.
- Configure GPS-denied desktop parameters.
- Hand off to onboard.
The exact endpoints are partially wired today and tracked under "External API Specification" above.
6b. Test mode — new subitem (per _docs/how_to_test.md)
Source of truth:
_docs/how_to_test.md. Architecture Vision artifact (/documentStep 4.5) MUST surface this verbatim.
Goal: enable end-to-end testing of the GPS-denied onboard system without a real flight, using a recorded telemetry log + video pair.
User journey:
- Upload tlog file (MAVLink telemetry log).
- Upload video synced with the tlog (the two are usually not time-aligned at the file level — the system aligns them).
- The system:
- Extracts timestamps, IMU, and GPS samples from the tlog.
- Auto-syncs video to tlog by detecting the take-off moment in the IMU stream and matching it to the corresponding frame in the video. Most test sessions are quadcopter "ground-to-ground" flights; the start-on-ground signature in the IMU is the alignment anchor.
- Spawns a SITL (Software-In-The-Loop) instance.
- Feeds IMU samples + video frames into the GPS-denied onboard system in lockstep.
- The user observes the onboard system's outputs (position estimate, drift, GPS-recovery moments) on the same map / chart components used for live flights — reusing
FlightMap,AltitudeChart, and a results overlay.
Scope notes for downstream skills:
- This sub-feature is not implemented today. It is a planned addition that this component will own.
- The tlog parser, IMU/video sync, and SITL controller are suite-side services; this component contributes the upload UI, the run controller, and the result overlay.
- Test-mode I/O endpoints will live under
/api/gps-denied-desktop/test/...(proposed; confirm inautodevStep 3 / Step 6). - The existing
/documentStep 1 finding "GPS-Denied panel partial" remains valid for the Operations tab and is broadened to track the Test mode tab as well.
7. Caveats & Edge Cases
26 numbered findings consolidated in src__features__flights.md. Highest-priority Step 4 items:
- Hardcoded OpenWeather API key — rotate + remove + proxy via suite.
- Waypoint POST shape mismatch + delete-then-recreate save model — likely-broken on a strict suite server. Reference port-source's PUT-mission-JSON model.
flightPlanUtils.tsunits / silent errors / sequential awaits. Replace with port-source services.- Modal a11y + flight dropdown a11y (overlap with Shared UI).
- Leaflet 1.7.1 CDN URL drift.
Mission-planner-side caveats (port-only):
mission-planner/src/test/jsonImport.test.tscannot run (Jest absent). Do not add Jest just for this one test.mission-planner/src/App.tsxis a CRA stub. Delete only after parity.MapView.tsx ↔ MiniMap.tsxnamed-handle cycle. Document precisely.
GPS-Denied-side caveats:
- Operations tab is partial (finding #25 unchanged).
- Test mode is unimplemented — no risk of regression today, but the design must accommodate the eventual test-mode entry point in routing and in
FlightContext.
8. Dependency Graph
Must be implemented after: 00_foundation, 01_api-transport, 03_shared-ui.
Can be implemented in parallel with: every other feature page.
Blocks: 10_app-shell (routes to /flights as default authenticated landing; will also host the GPS-Denied sub-route).
Internal port order (within this component): port services/ (battery/weather/distance) → port flightPlanning/ panels (AltitudeChart, WindEffect, PointsList) → port MapView/MiniMap cycle group → fold the mission-planner/ tree out as the target reaches parity.
Module Inventory
| Tree | Module Doc |
|---|---|
src/features/flights/* (15 modules) |
_docs/02_document/modules/src__features__flights.md |
mission-planner/* (37 modules) |
_docs/02_document/modules/mission-planner.md |