260924 – Dev update

Six new capabilities and nine improvements reached Implemented, along with fourteen internal items. There were ten bug fixes to features that had already shipped, and sixteen new items were defined and queued. Last week’s UAT batch went to production on 17 September. Everything described below is on the development branch and waiting for the next UAT promotion. Three of the fixes came from field crash reports or production gateway logs rather than from a test

🗺️ The planning page becomes a map, and a route stops being a flight

Route planning now opens on the map. Long-press anywhere for a menu with “fly from here”, which sets a custom departure point, and a per-leg direct. A route is now its own stored object rather than a property of a flight. A seven-step traffic-light bar tracks the pre-flight work, and each step reacts to the others: change the people on board and a completed weight & balance drops back to amber. Fuel compares needed, aboard and liftable side by side (F1320)

Per-leg direct is a hard safety boundary. It suppresses routing at planning time only. In flight, airspace warnings still fire on a direct leg. A non-powered glider gets no fuel step at all, rather than a greyed-out one. The rule that should have hidden it already existed in the codebase, but nothing had ever called it

Two defects came from the first round of owner feedback on the built planner. Dragging to reorder waypoints was fully wired except for one missing connection: nothing ever passed the reorder handler to the list, so the feature could not be reached. It now works, and Move up and Move down accessibility actions mean it no longer depends on a drag gesture, translated into all nine non-English locales. The route screens also read the theme now, so dark mode is legible. A security review of the route object found nine issues, and all nine were fixed before merge

✈️ Route planning, then pre-flight, then fly

The three phases now have names and a visible indicator. Route planning is thinking about your route. Pre-flight is adding the details. Fly is the last chance to finish anything left undone, so every outstanding item can be completed from the fly surface itself, not just reported there. Only two of the five ways to activate a route actually checked the pre-flight brief. All five now go through one function (F1323)

Fly now asks what you are doing. A known flight already in progress still goes straight to the PFD, because that is crash recovery. Everything else lands on a launch dashboard, including a groundspeed that reads airborne with no flight details. A PFD with no aircraft identity would show wrong speed rings, performance and glide figures. Quick fly no longer asks for a registration or a departure ICAO; the departure was overwritten on takeoff by the nearest aerodrome anyway. Activating a plan now lands you on the map, not back on a chooser (F1321)

One readability fix went beyond what contrast ratios can measure. Dark-mode secondary text measured 5.07–7.49:1 against its three surfaces, comfortably clear of the WCAG AA 4.5:1 floor, and it was still reported as unreadable. A ratio does not model glare, small type or grey on an OLED black. It is now roughly double, at 10.7–15.8:1, and deliberately stops short of pure white so the four-state medical-expiry colour ladder still reads

🎯 Recentre keeps your zoom, and a second tap picks one for you

This came straight from pilot feedback: recentring threw away a zoom level the pilot had chosen on purpose. Now the first tap on ⊕ re-centres at your zoom, unchanged. A second tap engages adaptive auto-zoom, driven by groundspeed, height above ground and sustained turns, and it always keeps at least two minutes of flight in view ahead. A circuit at 60 kt and 500 ft shows about 8 km ahead. A glider cruising at 55 kt and 3,000 ft shows about 31 km, and a tourer at 120 kt about 35 km. The view tightens in a sustained turn (above 6°/s for 20 s) and slews at no more than 0.25 zoom levels a second (F1230)

Found alongside it: a pilot who panned while the map style was reloading on a cold launch lost their zoom on the next ⊕ tap or the 30-second auto-return. That code path fell back to a hard-coded default zoom instead of reading the current one (F536). The same investigation found that the follow-mode pan thresholds assume 256-pixel tiles where MapLibre uses 512. Every threshold is therefore twice the intended movement. That is now queued (F1343)

💥 The airspace-sync crash: found in the field, reproduced, and fixed at four causes

One pilot’s iPhone logged about 65 background memory kills between late July and 21 September, plus a foreground one. Device traces showed a single JavaScript task running for 50–92 seconds without yielding, while memory climbed from 191 MB to 1,744 MB in 92 seconds and peaked at 2,911 MB. In the Simulator, forcing a re-download of the 42 largest US airspace cells took the heap from 344 MB to 3,621 MB in 44 seconds, followed by an out-of-memory crash inside the JSON parser (F1257)

Four causes combined. Admission was worldwide and unbounded: the resident limit counted cells, not bytes. A slow JavaScript text decoder had crept back onto this path. Nothing stopped a background relaunch from running a full sync. And the pause between cells had collapsed to nothing. Each cause is fixed. On top of that there is now a tiered memory budget: 256 MiB always reserved, a spare band up to a 768 MiB ceiling that is shed on any memory pressure, and the aircraft’s own cells and alerting cells are never evicted. The same repro now peaks at 1,374 MB and settles around 700 MB. A warm launch settles at ~770 MB, where it used to be 1,442 MB and still climbing after three minutes

A quieter defect sat one layer down. When the 128 MB airspace disk cap trimmed cells, it deleted the files but left them marked as current. The pilot lost airspace alerting in the evicted area until the next AIRAC cycle, with no visible warning. On the current cycle, 419 cells were trimmed to 342 files while all 419 were still claimed. The app now never claims a file that is not on disk, and it repairs a damaged state on load (F910)

Two more slow text decodes moved to native code. Loading aerodromes on the Nav page went from 478 ms to 316 ms, JavaScript allocation during it more than halved (18.75 MB to 8.57 MB), and decoding the airspace manifest went from ~8.4 ms to 0.4 ms (F1328)

📦 Offline airspace kept by priority, not by distance

The default offline airspace allowance is now 256 MB, up from 128 MB. A stored 128 that the pilot never chose migrates automatically. A lower cap now keeps what matters: your route corridor, your home field and the areas you have recently flown, in place of an arbitrary far-first trim. Opening a route or saving a plan fetches its corridor straight away. During a recorded flight, a bounded background task fetches the airspace around the aircraft and the corridor ahead, so airspace that was not on the device at takeoff can be there and alerting before you unlock the phone. Nothing is fetched while idle in the background (F1332)

🔋 Four compute states, and terrain fetched in the order you will need it

Background work is now gated from one place, across four states: foreground or background, recording or not. Terrain alerting never switches off and evaluates at least every 10 seconds in the background. Recording, airspace alerting and audio callouts carry on with the screen locked. Synthetic vision, the HUD and prefetching pause. The same review found the compute workers sharing the operating system’s lowest priority with hillshade rendering, so terrain rendering now sits below them (F1311)

Terrain used to download the whole route corridor, departure to destination, in one pass. On a slow connection, bytes went to ground already flown over while the cell ahead waited. While recording, cells are now ranked by time until you enter them, then by corridor ahead, then by distance. Only one speculative download runs at a time, and terrain alerting’s own requests are never made to wait (F1312)

⚡ Wires, obstacles, airways and sensitive areas, from sources we can name

A new pipeline publishes reference data to the CDN once per AIRAC cycle, the same way airspace already is. Each licence source goes in its own file, so an open-licence layer never contaminates an official one. The map can now tell “no publisher covers this area” (amber) apart from “not downloaded yet” (red). Two server endpoints were retired along the way; they had no writer and had only ever returned empty (F1314)

Airways reach a device for the first time. The production airways table turned out to be empty: the US feed had only ever been run by hand. US airways now publish automatically, and French airways are added from the official SIA source. A parser for UK, Spanish and German AIP airways is built and tested against real data, but it stays switched off until we have written licence clearance (F1315)

Power lines are rebuilt from real data. A 12-crossing UK starter set gives way to a full UK extract as the first-launch layer, with a global base and official operator overlays on top. Of 16,984 UK lines, only 117 carry a mapped height; the rest are estimated. Estimates are always labelled as estimates and never shown as surveyed. Where the operator publishes its own lines, they replace the mapped version: in New Zealand that took over ~81% of transmission-line length (F739). One UK operator’s portal returned errors on every attempt, so that importer is recorded as blocked, not guessed at (F1326)

Wire and obstacle alerting, built to DAL Level B. The alerter checks the same 60-second projected track that terrain alerting uses against power lines and obstacles, every two seconds. It gives a caution at 100 m clearance or 60 s, and a warning at 50 m or 30 s. Height margins widen with the quality of the source, and an obstacle with no known height is assumed to be 150 m. If the data is missing, not yet downloaded or late, the app says WIRE ALERTS UNAVAILABLE; it never guesses clear. The alerter runs separately from terrain alerting, so this advisory data can never reach the terrain, proximity or glide paths (F1317)

Low-flying sensitive areas go worldwide. The old UK layer was ten hand-drawn polygons, and its cited AIP source turned out to be chart symbology, not a table of areas. It is now labelled for what it is. The new layer draws on 15 registered sources: the UK AIP (19 entries in the current cycle), European and US protected-area registers, and urban extents. Nine are live, and six are waiting on access. A crash found after merge meant that no sensitive-area layer ever drew on a device. The unit tests could not see it, because the test map never renders its children (F1316)

Profile → Legal → Data contributors now lists the six sources we are required to credit, each with the licence’s exact wording and a line on what it is used for. A new check fails any data source that arrives without a recorded attribution decision (F1318)

🧬 One defect class, closed rather than patched again

An on-device database row does not know which id space its ids belong to. A lookup that uses the server id where the local id was expected returns nothing, silently, instead of failing. The same defect had already shipped three times; the latest was the ownship marker drawing a generic silhouette for flights started on the device. The audit found one row shape declared up to nine times over, 110 unchecked type casts, and no typed ids across 18 tables that hold both kinds (F1146)

Every table now has one canonical row type (47 in all), the 110 casts are gone, there is a single resolver, and two new lint rules keep it that way at zero tolerance. Two things are visible to pilots: the ownship marker is right, and aircraft fuel figures you never entered now show as unknown rather than a wrong zero. Aircraft envelope, route and aerodrome data now travel end to end in SI units

📓 Sign-in codes, syndicate summaries and flight imports

Email sign-in codes were being generated, stored and then never sent. One missing call into the email path that already worked meant nobody could sign in by code. The same review found that no issued code was ever deleted, so a garbage collector now purges them 24 hours after expiry. Codes are valid for 20 minutes, up from 10, to allow for real-world mail delays (F90)

The logbook’s date column carried the same model-versus-database mismatch as last week’s currency fix, and it is now fixed at the model. The monthly syndicate summary had nine places that turned any error into zero, so every member was reported as “0 hours, never flew”. A summary now sends correct numbers or does not send at all. No email beats a wrong one (F1134)

An internal tool can now replay a real IGC trace into a test account. Building it found that IGC files using the long-form date header, which third-party recorders write, were dated 1 January 1970 (F1084)

📰 Release notes, in the app

Notes like this one now appear as a card in the dashboard feed and open in full inside the app. They are cached on the device for 24 hours and refreshed when the app updates. They are kept separate from pushed notices, which remain reserved for stop-press safety alerts (F1324)

🛰️ Traffic feeds and production operations

New ADS-B, FLARM and Vela specific receivers integrated, the office ADS-B receiver gets the same tunnel route. The tunnel container itself was being throttled on 83% of its CPU periods, now down to 0.06% (F787)

The two aggregator pollers carried duplicate copies of the same decoder. That duplication is how a renamed field that blanked one feed for 46 hours in July stayed unfixed in its sibling. There is now one shared decoder. Two further aggregators are allowlisted, but neither feeds the app until we have reviewed their terms (F1360)

Database connection-pool bookkeeping made up 98.9% of our tracing volume: 15,116 of 15,284 spans a day, about 2.18 GB a month, none of it attached to anything a user did. It is now dropped at the collector. On production on 23 September the volume fell to zero immediately, with real query spans unaffected (F1363). The database servers now report their own health, including replication lag, connections and load, where before there was no view of it at all (F1359)

🧪 Test runs were calling production

Production gateway logs showed unauthenticated requests carrying test-fixture ids. One endpoint took 2,000 rejected calls against 22 real ones in a week. The mobile test setup stubbed the browser-style fetch, but the app’s HTTP client does not use it, so any unmocked call opened a real connection. Every unmocked call is now answered locally before it reaches a socket (F795). The same trail led to the contour download screen, which fetched its manifest in a tight loop whenever the session had expired: 8,054 rejected requests against 10 successful ones in seven days. It is fixed at the source (F705)

📐 Defined and queued

Airspace when zoomed out, and airspace that follows the camera. A pilot at Cranwell zooming into Tarbes sees no airspace today, and zooming out far enough empties the map. Two server-built overview levels and route- and viewport-aware loading are planned. Coverage reporting has to come first, because the app cannot yet tell an uncovered area such as Greenland from a genuinely empty one such as the mid-Atlantic. Greenland, the Faroes and oceanic FIRs are a separate gap (F1355, F1356, F1357)

A gridded weather platform. Open data would be ingested four times a day and served as pre-built tiles, with offline weather tiles along a saved route. The soaring grid would move onto the same data, replacing the placeholder values it uses when data is missing. A long-distance expedition-planning capability is also scoped. Two older items were re-scoped onto the platform instead of duplicating it (F1364–F1367, F429, F946)

Also raised: mobile and web switch airspace labels to flight levels at different altitudes (F1336). The bundled font needs its licence notice (F1337). The altitude profile declares quadrantal altitude, wind-optimised altitude and groundspeed but never fills them (F1361). Three smaller defects came out of this week’s reviews (F1341, F1342, F1344)

🚀 Production

Last week’s UAT batch went to production on 17 September: water routing, the mobile memory chain, the hangar and aircraft checklists, the MapLibre 6 web map fix and the currency-dashboard fix (F1289, F1254–F1265, F1284, F1285, F935, F69). Tracing and database monitoring changes went live on 23 September (F1363, F1359)

No app code from this window has reached UAT yet. 31 new device or UAT sign-off checks are queued for the next promotion. The most important are the airspace-sync crash rate over a week of real flying, the background airspace safety fetch, the wire alerts in flight, the three planning phases walked outdoors, and airways and power lines rendering from a live published pack (F1257, F1332, F1317, F1323, F1314, F739)

192 commits over eight days, 2,060 files, ~1.39M insertions (~20.6k deletions). ~1.21M lines are generated data, mostly the new power-line datasets plus parser fixtures and schema snapshots. Leaving ~62k lines of code, ~90k lines of tests and ~29k lines of engineering documents.