Commits
- descriptors: extract query_variants() as the single source of truth for
the fwd/rev/mirror curve set; query_descriptors() now delegates to it
- matching: reuse query_variants() for both the ANN and DTW passes,
deleting the duplicated _variant_xy() helper and its function-level
import; tidy import ordering. Spiral query verified bit-identical to the
pre-cleanup top-15 (behavior preserved)
- main.js: remove write-only activeIdx; set the '<n> matches' status AFTER
showResults() (clearResults inside it was blanking the count immediately)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CARTO dark_all rendered the street network as near-invisible dark-gray on
black. Swapped to Esri World Dark Gray Base: streets are clearly legible
while the theme stays dark (validated by a headless-Chrome A/B of 5 dark
options + a Gemini visual review — Esri unfiltered won on legibility-while-
staying-moody vs contrast-filtered CARTO/Esri, which read as muted daytime).
- basemap → Esri World_Dark_Gray_Base (maxNativeZoom 16, upscale beyond),
attribution updated to Esri
- map bg #0e0f13 → #26292d so tile-load / edges don't flash black against
the gray basemap
- match polylines factored to shared STYLE_SELECTED/STYLE_IDLE; idle line
brightened (#4a90d9@0.35 → #5a9fe0@0.6) so non-selected matches read on
the lighter basemap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each result gets a maps link that drops a pin at the road midpoint
(google.com/maps/search?api=1&query=lat,lng), target=_blank, and
stopPropagation so it does not also re-select the card. Full "maps ↗"
label on desktop; collapses to just the ↗ icon on the narrow mobile
cards (title/aria-label keep it accessible).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- <=720px: stack header/pad/controls, results become a horizontal card
strip, map fills the remaining height (100dvh); pad sizes to
min(58vw, 280px)
- pad backing store now fits its CSS box x devicePixelRatio (fitPad on
load/resize/orientation) and pointer coords map through the scale, so
the canvas can be CSS-sized freely and strokes are crisp on hi-dpi;
desktop pins 320px CSS so behavior there is unchanged
- result thumbnails render at dpr resolution; active card scrolls into
view; fitBounds padding tightens on narrow screens
- deploy stages web/ into dist/ and stamps ?v=<content-hash> via
tools/cache-bust.py (foal pattern): a stale cached style.css against
fresh main.js broke canvas sizing during rollout, and Caddy's
file_server sends no Cache-Control
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prod (Pi, faiss-cpu 1.14.3 aarch64) returned garbage ANN candidates for
every query: top-k was a single id duplicated k times with distances
~= |query|^2, so the DTW re-rank only ever saw junk and every drawing
matched generic curly roads ('extra little curve' user report). Index
bytes verified identical (md5) to local; fresh small indexes were fine;
self-queries were fine. The trigger is the default parallel_mode=0 with
OMP threads > 1 over the large IVF (nlist=12930, 10.45M vectors) —
single-thread, mode 1, and mode 3 are all bit-identical to the x86
results. Pin mode 1 (parallelizes across the 2-4 query variants).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploys 333-335 all died at ~200s: compose tarred /srv/roadsketch including
index/ into the build context every time, and ctrl's log reader hit its
64KB spinner limit long before the tar finished. With the context at 16KB
the whole cached build+up completes in seconds. Deploy 336 went clean:
https://roads.cee.wtf
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pip's \r-based progress output accumulates into one multi-MB log line with
no newline; ctrl's deploy-log reader (aiohttp readline) overflows and fails
the deploy ('Separator is not found, and chunk exceed the limit'). Deploy 332
died this way with a healthy build. PIP_PROGRESS_BAR=off + pip -q keeps
build log lines short. The ctrl-side robustness fix is tracked separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New service moved into the monorepo. FastAPI + FAISS shape search over a
prebuilt 3.1GB US road index (turning-function descriptors, IVF ANN +
banded-DTW re-rank), static Leaflet frontend.
Changes on top of the moved code:
- matching: DTW re-rank vectorized across candidates (_dtw_batch) —
bit-identical scores, ~7x faster on the rerank stage (~22ms/query
in-process on the full US index)
- app: 1MB body cap + 20k point cap + (x, y) tuple validation, /api/healthz,
stats no longer leaks the server filesystem path
- Dockerfile + compose (fullstack .deploy, host 8925 -> container 8899,
index volume-mounted ro from /srv/roadsketch/index, mem_limit 3g,
OMP_NUM_THREADS=4)
- .pushignore keeps the 3.1GB index and .venv out of the deploy tarball
- deploy.conf registration + DEPLOY.md production notes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- descriptors: extract query_variants() as the single source of truth for
the fwd/rev/mirror curve set; query_descriptors() now delegates to it
- matching: reuse query_variants() for both the ANN and DTW passes,
deleting the duplicated _variant_xy() helper and its function-level
import; tidy import ordering. Spiral query verified bit-identical to the
pre-cleanup top-15 (behavior preserved)
- main.js: remove write-only activeIdx; set the '<n> matches' status AFTER
showResults() (clearResults inside it was blanking the count immediately)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CARTO dark_all rendered the street network as near-invisible dark-gray on
black. Swapped to Esri World Dark Gray Base: streets are clearly legible
while the theme stays dark (validated by a headless-Chrome A/B of 5 dark
options + a Gemini visual review — Esri unfiltered won on legibility-while-
staying-moody vs contrast-filtered CARTO/Esri, which read as muted daytime).
- basemap → Esri World_Dark_Gray_Base (maxNativeZoom 16, upscale beyond),
attribution updated to Esri
- map bg #0e0f13 → #26292d so tile-load / edges don't flash black against
the gray basemap
- match polylines factored to shared STYLE_SELECTED/STYLE_IDLE; idle line
brightened (#4a90d9@0.35 → #5a9fe0@0.6) so non-selected matches read on
the lighter basemap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each result gets a maps link that drops a pin at the road midpoint
(google.com/maps/search?api=1&query=lat,lng), target=_blank, and
stopPropagation so it does not also re-select the card. Full "maps ↗"
label on desktop; collapses to just the ↗ icon on the narrow mobile
cards (title/aria-label keep it accessible).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- <=720px: stack header/pad/controls, results become a horizontal card
strip, map fills the remaining height (100dvh); pad sizes to
min(58vw, 280px)
- pad backing store now fits its CSS box x devicePixelRatio (fitPad on
load/resize/orientation) and pointer coords map through the scale, so
the canvas can be CSS-sized freely and strokes are crisp on hi-dpi;
desktop pins 320px CSS so behavior there is unchanged
- result thumbnails render at dpr resolution; active card scrolls into
view; fitBounds padding tightens on narrow screens
- deploy stages web/ into dist/ and stamps ?v=<content-hash> via
tools/cache-bust.py (foal pattern): a stale cached style.css against
fresh main.js broke canvas sizing during rollout, and Caddy's
file_server sends no Cache-Control
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prod (Pi, faiss-cpu 1.14.3 aarch64) returned garbage ANN candidates for
every query: top-k was a single id duplicated k times with distances
~= |query|^2, so the DTW re-rank only ever saw junk and every drawing
matched generic curly roads ('extra little curve' user report). Index
bytes verified identical (md5) to local; fresh small indexes were fine;
self-queries were fine. The trigger is the default parallel_mode=0 with
OMP threads > 1 over the large IVF (nlist=12930, 10.45M vectors) —
single-thread, mode 1, and mode 3 are all bit-identical to the x86
results. Pin mode 1 (parallelizes across the 2-4 query variants).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploys 333-335 all died at ~200s: compose tarred /srv/roadsketch including
index/ into the build context every time, and ctrl's log reader hit its
64KB spinner limit long before the tar finished. With the context at 16KB
the whole cached build+up completes in seconds. Deploy 336 went clean:
https://roads.cee.wtf
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pip's \r-based progress output accumulates into one multi-MB log line with
no newline; ctrl's deploy-log reader (aiohttp readline) overflows and fails
the deploy ('Separator is not found, and chunk exceed the limit'). Deploy 332
died this way with a healthy build. PIP_PROGRESS_BAR=off + pip -q keeps
build log lines short. The ctrl-side robustness fix is tracked separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New service moved into the monorepo. FastAPI + FAISS shape search over a
prebuilt 3.1GB US road index (turning-function descriptors, IVF ANN +
banded-DTW re-rank), static Leaflet frontend.
Changes on top of the moved code:
- matching: DTW re-rank vectorized across candidates (_dtw_batch) —
bit-identical scores, ~7x faster on the rerank stage (~22ms/query
in-process on the full US index)
- app: 1MB body cap + 20k point cap + (x, y) tuple validation, /api/healthz,
stats no longer leaks the server filesystem path
- Dockerfile + compose (fullstack .deploy, host 8925 -> container 8899,
index volume-mounted ro from /srv/roadsketch/index, mem_limit 3g,
OMP_NUM_THREADS=4)
- .pushignore keeps the 3.1GB index and .venv out of the deploy tarball
- deploy.conf registration + DEPLOY.md production notes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>