Trestate
Trestate scores any point in London or Paris for residential fit or for a specific kind of business — bakery, optician, supermarket — based on what actually surrounds it: amenities, schools, transit, competitors, demographics, cost. Every hex on the map precomputes to a single ~200-feature vector, so a score, a chat answer, or an export is always a read against real data, never a live computation. It's deliberately narrow about what it's willing to claim: the product is licensed to say a position resembles where a trade is already established, never that it will succeed there — a discipline that came out of killing more hypotheses than it kept.

The Context
Site selection for a small business — where to put a bakery, a laundromat, an optician — mostly still runs on gut feel and a handful of demographic PDFs. The data to do better exists (amenities, transit, schools, demographics, competition, cost), but it's split across incompatible national schemas, and turning it into a single number for a single address is a real engineering problem, not a spreadsheet exercise.
Trestate answers a narrower question than "will this succeed" — a claim nothing in the data actually supports — and answers it precisely: does this position resemble where this kind of business already thrives in this city.
What it does
- Precomputed location feature vector. Every H3 hex in London and Paris — 72,863 of them (32,182 in London, 40,681 in Paris) — carries a ~200-feature vector covering amenities, healthcare, schools, transit, demographics, cost and competition. A score, a chat answer, a comparison, or an export is a read against that vector, never a live computation.
- Archetypes as data, not code. A residential fit or a business type (bakery, optician, supermarket) is a YAML spec, not a deploy. A household is just another archetype, which is why adding a new one costs almost nothing.
- Two countries, one schema. Overture Maps, OpenStreetMap, ONS/VOA/Land Registry and INSEE/DVF/BAN all normalise into the same feature vector — the actual engineering work is making a UK dataset and a French dataset agree with each other, not the modelling on top.
- An interactive map, not a report. A React + deck.gl + MapLibre frontend renders every scored hex directly, so a location's grade is something you explore rather than read off a PDF.
- Says only what it can prove. Every grade is licensed to say one thing: this position resembles where the trade is established here — never a success prediction. Three states stay separate on purpose: scored, gated (not eligible, with the reason named), and not yet assessed.
Built to be disprovable
Before anything shipped as a claim, it had to survive being pre-registered and then attacked: the model was tested against a version of the city with every business deleted and asked to redraw it from scratch, and it separated cleanly from a simple density count for the first time. A separate test tried to predict how long a business survives at a location — after four independent attempts with progressively better data, the honest answer was that location alone doesn't explain it, and that's reported as a closed question rather than left ambiguous.
The same discipline caught a licensing problem before it became a legal one: one dataset turned out to be restricted to a purpose site selection doesn't qualify for, and it was excluded before anything downstream depended on it.
High-calibre architecture decisions.
Technical Highlights
- Go monolith API over a ~200-feature H3 res-9 vector; every question is a precomputed read, never a live computation.
- features.yaml as the single schema source of truth, with codegen emitting matching SQL, Go and TypeScript.
- Python + DuckDB + Dagster pipeline normalising Overture Maps, OSM, ONS/VOA/Land Registry and INSEE/DVF/BAN into one cross-country schema.
- Valhalla-based batch isochrone routing, computed offline and never on the request path.
- React 19 + Vite 6 + deck.gl + MapLibre GL frontend rendering every scored hex directly on an interactive map.
- Archetypes (residential fit, business types) defined as YAML specs with a JSON Schema and a fair-housing validator — no deploy required to add one.
Key Decisions & Rationale
Score, chat, compare and export all becoming reads against the same ~200-float vector means the hard problem — normalising two countries of incompatible data — only has to be solved once, and every new question on top of it is nearly free.
A location score that implies "you will succeed here" is a claim the data cannot support. Restricting every grade to "this resembles where the trade is established" keeps the product honest about the difference between a structural resemblance and a prediction.
A business type or a household profile is a spec, not a deployment. That is what makes adding new use cases — a new retail vertical, a new demographic profile — cheap enough to actually do.
By the numbers.
Live Scoring Map
Real sessions from the running app — every hex is a live score, not a mockup.
Paris, scored for Laundromat fit — 40,681 hexes, purple-to-yellow for weak-to-strong, red for gated out entirely.



If we worked together.
I bring high-leverage product engineering and absolute operational discipline: shipping weekly, pressure-testing user inputs, and automating delivery pipelines.
ScrollCollector
ScrollCollector is a privacy-first Chromium browser extension that intercepts, sanitizes, and categorizes social feeds in real time. Running entirely on-device, it leverages Chrome's built-in Gemini Nano model or local keyword filters, storing items locally without network dependencies.