The map was flat
We route motorcycles through the Alps. This week we discovered that, as far as the software was concerned, the Alps were not there.
Elevation, everywhere at once
An audit of the routing graph found that most of Europe's tiles carried no elevation at all — the majority of the graph's one-degree cells had no DEM behind them, and a missing DEM yields silently zero grade, which looks exactly like flat ground. Then the same check on the road-score database: every one of its million European roads had NULL in all seven elevation columns. The "epic road" score had never seen a mountain. Same class of bug, found the same way: by looking instead of assuming.
Both are fixed from the same source — the 1-arcsec DEM we already host, resampled into the exact grid Valhalla reads. The routing tiles now carry grade, and the riding knobs that were quietly inert came alive: prefer or avoid hills, avoid tunnels, cap route difficulty, with a difficulty number reported beside "% unpaved" rather than folded into it. Steep tarmac stays easy; a rider who asked for no rough stuff did not ask to avoid mountain passes.
The road score grew a climb axis — a second number beside curvature, because "twisty but flat" and "high but straight" are different roads and one blended score cannot say which you got. Ranked by climb, the Alps now answer Gavia, Stelvio, Timmelsjoch. The Netherlands answers dike roads at 8.
Don't teach the solver what "good" means
The tempting design was to mix "fun" into Valhalla's cost model. Measurement killed it: discount steep edges and the solver buys more steep kilometres, not more altitude — one test route went 23 km further and 300 m lower the harder we asked it for hills. An optimisation solver optimises what you price, not what you meant.
So scoring stays in the curvature database, and a new scenic bias hands the router per-edge discounts for the best-ranked roads in the corridor — by curvature or by climb, off by default, and every failure path returns an ordinary route. The loop generator learned the same trick: it can now weave in a road chosen for its climb, not just its bends, with a hard elevation floor so a flat region says so instead of offering its least-flat lane.
Dirt-first routing shipped the same lesson in miniature. The first cut inflated the cost of pavement, which made ferries relatively cheap and sent a "dirt" route across a fjord. Discounting dirt instead fixed the ferry, fixed the inert middle preset, and the badge now says "no unpaved roads on this route" rather than a bare 0% that reads as broken.
The desktop caught up — and then got audited
The desktop planner had been dark for a week while the server kept shipping, so it got a catch-up sprint: the dirt-first axis, grade knobs, climb loops, the POI seeker reaching all 39 categories instead of 2, corridor search along the route, our own basemap, live view, reach contours, and a wildcamp planner that says honestly when the analysis backend is not on this server. The first fix of the sprint was humbling: the slope-coloured terrain strip had never rendered on prod — a graceful fallback had hidden a missing gateway rule for weeks.
Then we did something new: an independent audit of our own sprint. It
found a real boundary defect — the gateway allowlist could be walked out of
with encoded .. segments, turning every prefix rule into a door. Browsers
normalise those before sending, which is exactly why browser testing could
never have caught it. The matcher now refuses dot segments outright, the
client got its first-ever unit tests, and all seven follow-ups were acted on
the same day.
The first real browser pass found the rest: every vector layer on the desktop was blank (relative tile URLs, which a worker cannot resolve — no request, no error, nothing in any log), the proxy told browsers a gzipped tile was plain, raster basemaps silently dropped every text label, and the caching was exactly backwards — the mutable document was cacheable and the immutable assets were not. An audit that reads code and a pass that drives a browser find different bugs. You need both.
One more honest note: tier gating is now off by default. The tier line-up is not decided, the current accounts are all development fakes, and the pilot will run fully featured so that anything we might later gate still gets tested. The draft bundles are kept as drafts, clearly marked as not a product decision.
The phone stopped freezing, forensically
The tab-bar freeze finally gave up its causes, on device, with a watchdog: duplicate navigator trees stacked by deep links, a frozen scene left on top still eating taps, sensor readings written to the store 48 times a second, and one environment load firing ~100 parallel GeoJSON parses that blocked JS for 24 seconds. Each got a targeted fix rather than a workaround.
Also this week, at speed: the rally roadbook grew a 137-glyph symbol library in four dialects and FIA-layout print — A5 pages and a continuous moto-roll with cut marks; search learned the famous passes, so three keystrokes surface Stelvio instead of ten villages named Colle; the TET got a Europe-wide tap-a-trail overview map; and Settings was regrouped around how riders actually think, in ten languages.
Dig deeper: the desktop client and the rider app guide.
