It was emitting '### Like This' (my prompt's literal example). Give several varied
title examples + 'make up your own, do not reuse these' so it invents one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Narration: kill label-parroting at the source + reduce gate false positives
Traces showed two remaining issues: the model parroted 'park garden'/'place of
worship' from the 'Weights extracted:' prompt line (removed it + dropped the now
dead _weights_summary), and the gate kept rejecting lone sentence-starters
('Stepping'). Add a comprehensive common-word set instead of a blanket single-word
pass (which had let the planted 'Eiffel Tower' through). Guarantee intact: all
multi-word invented venues still caught.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Narration: stop category-label parroting + force flowing prose
Traces showed the 1B model echoing the raw category ('It's a park garden, exactly
as it sounds') and reverting to a numbered list that cut off mid-walk. Fix: feed
stop names only (no '(park garden)'), instruct one-heading flowing prose with no
numbering, and raise the token budget 480->600 so longer walks finish.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Narration: stop the gate rejecting the LLM's own title + 'Just' (traces-driven)
Live traces showed the LLM writes great flowing prose but gets rejected over false
positives: its evocative title ('### The Quiet Canvas'), the word 'Just' (sentence
starter), and a few off-route NYC landmarks. Fix: exempt markdown heading/title
lines from grounding, allow 'just'/'main'/evocative prose words, add NYC landmarks
to the gazetteer. Planted-hallucination guarantee still holds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trace: create the dataset repo before pushing (upload_file does not auto-create)
Root cause of the missing trace dataset: upload_file 404s when the repo doesn't
exist; only the CLI auto-creates. Add an idempotent create_repo(exist_ok=True) on
first push + in the selftest, so the dataset is created and traces actually flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The push errors were swallowed by a bare except, so a misconfigured HF_TOKEN
secret made the trace dataset silently never appear. Now log the real error and
run a boot-time selftest that prints whether the token is present and can write
to the trace dataset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Evocative route-title headline per walk (template + LLM)
- Rotating reason variants so repeated categories never read identically
- LLM prompt rewritten for a titled, flowing first-person story
- Loosen the zero-hallucination gate (token-subset match + more allowed
descriptive words) so vivid output survives; no-invented-venue guarantee kept
- Gazetteers for San Francisco, Tokyo, Mumbai, Shanghai, Berlin
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- City <select> now styled like the text inputs (cream, rounded, custom caret)
- Detour + Adventurousness sliders condensed to single compact rows with inline captions
- Replace static 4-step loader with an animated live-map teaser (route threads between popping pins + a walking dot)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Docs/UI: reflect multi-city + Off the Grid; Thousand Token Wood track
Tristan Leduc+1: Claude Opus 4.8 (1M context)
2ab78dc
- README/Space card: cities (Paris/London/Barcelona/NYC), offline no-cloud-API
framing, track tag → thousand-token-wood, badge tags → off-the-grid/off-brand/
tiny-titan/field-notes/openbmb.
- Start-field help now names the supported cities so judges find multi-city.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pre-baked offline cities (London, Barcelona, New York) + endpoint-stop fix
Tristan Leduc+1: Claude Opus 4.8 (1M context)
6881784
Keeps the "Off the Grid" badge while going multi-city: each extra city is baked
as a bounded walkable core (centre + radius) committed via Git LFS, so it routes
fully offline — no live OSM at request time.
- data/build_city.py + config.CITIES registry; data/cities/<slug>_{walk.graphml,
pois.parquet} for london/barcelona/newyork (+ cities_manifest.json).
- routing/area.py: resolve_area now picks Paris → a pre-baked city (both points
in its core) → (online only) on-demand fetch. In offline mode, anywhere else
is refused with an honest "WanderLust covers Paris, London, …" message.
- routing/geocode.py: offline name index now spans Paris + every pre-baked city,
with city-hint disambiguation ("…, London") so a landmark resolves to the
right city; suggest()/local_geocode() are city-aware.
- pipeline.py: drop candidate POIs within 80 m of the start/destination so an
endpoint (e.g. the square you start from) is never offered as a "discovery
stop" — it isn't a detour and read as …
Mobile UI: bound the autocomplete dropdown + denser drawer + keyboard handling
Tristan Leduc+1: Claude Opus 4.8 (1M context)
d34ffe8
The "typing narrows the menu" issue: the start/destination autocomplete list
had max-height:none, so a 9-item dropdown grew unbounded and buried the rest of
the menu (Destination/Mode/sliders), with its tail clipped by the CTA. Cap it at
min(46vh,300px) with internal scroll so it stays compact.
Also for mobile readability:
- Tighten drawer density (panel padding, control margins, chip gap) so more of
the menu is visible at once — the brand + vibe header no longer clip at the top.
- Add a visualViewport handler: when the on-screen keyboard opens, size the
bottom drawer to the visible area above it and scroll the focused field into
view (dvh alone gets crushed on Android / overlaid on iOS).
Verified in the mobile preview (375px): dropdown now 300px + scrollable; full
menu (vibe → Plan button) scrolls cleanly; no console errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The walk/bike segmented control set its labels to flex:1 + justify-center,
but the .wrap container was display:block, so the labels never expanded —
each stayed ~content-width and clustered at the left of the tan box instead
of splitting it in half. Add display:flex; align-items:center.
Verified live: both labels now split evenly (332px each in a 680px box);
click still toggles selection (handler was never broken).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add on-demand worldwide cities (any city via live OSM)
Tristan Leduc+1: Claude Opus 4.8 (1M context)
1a502dd
Paris stays pre-baked (instant, offline). Any other city is now fetched from
OpenStreetMap at request time — only the bounding box spanning the two endpoints
plus a margin, so a cross-city route downloads a few MB, not the whole metropolis.
- routing/area.py: new Area (graph + POIs + projection origin + timezone + CSR);
resolve_area() returns the cached Paris area for in-Paris requests, else builds
an on-demand bbox area (bounded LRU cache), with a max-endpoint-distance guard.
- pois.py: projection origin is now per-area (index_table/to_metres take origin),
not the Paris constant; corridor_pois accepts an area table+origin.
- graph.py: build_csr(graph) extracted so the travel matrix uses the area's own
network; geocode_point now resolves worldwide (Paris offline index for Paris-
hinted queries, Nominatim otherwise) instead of rejecting non-Paris.
- matrix.py: build_matrix accepts the area CSR.
- pipeline.py: resolve + thread the area; open-now uses the area timezone.
- confi…
Round-5 fix: relabel bakery_food_shop "food shop" (no false 'bakery' claim)
Tristan Leduc+1: Claude Fable 5
2577e90
Both round-5 reviewers converged on the SOLE remaining MAJOR: a wine shop
("Trois Fois Vin", OSM shop=wine → bakery_food_shop) was narrated "another
bakery along the way" — a false, verifiable label. The category bundles
bakery/patisserie/wine/cheese/deli, so render it "food shop" (always true).
Sole change to pretty_category.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-4 fixes: drop overclaiming match-tag, dedup stops, hidden excludes famous
Tristan Leduc+1: Claude Fable 5
c16ccf9
Adversarial round 4 (NOT-CONVERGED) — root-caused to 3 issues:
1. The per-stop "a match for your vibe" tag overclaimed: any route that
backfills a lower-ranked category (bakery on wine, church on jazz, statue
on street-food/photography) falsely labelled those stops a match. Removed
the tag entirely — each stop's reason text + the interpretation panel carry
the honest framing; the model can't guarantee per-stop relevance.
2. No within-route dedup → the same place ("UP /SIDE / DOWN / TOWN") appeared
twice in one route. Dedup the shortlist by osm_id AND name.
3. "hidden gems" still surfaced Notre Dame — the attraction-zeroing missed it
(it's viewpoint). Discovery cues now drop famous, well-documented POIs by
tag-richness (confidence >= FAMOUS_CONFIDENCE 0.85), category-independent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adversarial round 3 (NOT-CONVERGED) found 3 MAJOR issues:
1. The discovery-cue affinity adjustments (zeroing "attraction" for "hidden
gems") were COMPUTED then DISCARDED — the pipeline scored with
effective_weights() while interp.weights was thrown away. Now the vibe
path uses interp's own affinity (blended with profile when present), so
"hidden gems" actually drops famous attractions.
2. OSM ref-code names ("PA_1570") surfaced as unfindable stops labelled "a
match". Added taxonomy.is_real_name(); such POIs now self-demote
(UNNAMED_SCORE_FACTOR) and render as "a <noun>".
3. "attraction" gloss ("famous landmark... worth seeing") embedded near
"tour/spots/photo", landing Hôtel de Ville on a coffee tour. Tightened to
"a major crowded tourist attraction or world-famous monument".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- "hidden gems / off the beaten path / like a local" now suppresses the
famous-"attraction" category (its semantic opposite) and bumps
adventurousness to 0.8 so the route favours under-documented places.
- When the user's #1 requested category has zero stops on the corridor
(e.g. "jazz and live music" with no venues between A and B), the
interpretation panel says so instead of silently tagging churches "a
match for your vibe".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-2 route-fit: top-N category cap + weak-match honesty
Tristan Leduc+1: Claude Fable 5
a4c45c1
Adversarial round 2 (NOT-CONVERGED) found two systemic issues, both fixed:
- AFFINITY_FLOOR let all 17 categories keep weight, so ranks 5-17 silently
backfilled routes with off-vibe filler (same statues/churches bled into
10+ unrelated routes). Now cap to TOP_AFFINITY_CATEGORIES=6; the tail is
zeroed (sparse routes end honestly short, not confabulated). Applied in
embed + centrally in resolve_affinity (covers llm/keyword tiers too).
- min-max forces a top category to 1.00 even for weak/out-of-vocabulary
vibes, so "brutalist architecture" got churches confidently labelled "a
match for your vibe". Added raw-cosine confidence (WEAK_MATCH_SIMILARITY
=0.55; real vibes 0.66-0.85, brutalist 0.51, nonsense ~0.49); weak vibes
now get honest framing ("no strong match — here's my closest guess") and
drop the per-stop "a match for your vibe" tag.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route-fit fixes from adversarial review (span, glosses, attraction gate)
Tristan Leduc+1: Claude Fable 5
6c11e60
- MIN_AFFINITY_SPAN 0.18 -> 0.10: 0.18 neutralised real evocative vibes
("romantic", "beautiful", "brutalist" all span ~0.14-0.15) into an
identical generic grab-bag; gibberish spans 0.081, so 0.10 separates.
- Gloss keyword-bleed: drop "coffee roaster" from bakery gloss (was tying
cafe 1.00 / bakery 0.97 on "specialty coffee tour") and "quiet" from
worship gloss (was pulling churches into "quiet to read").
- classify(): admit a bare tourism=attraction only if notable
(wikidata/wikipedia/heritage) — drops commercial venues like escape
rooms that polluted monuments/worship/romantic routes (conf can't filter
them: an escape room has conf 1.0). Rebuilt parquet: 290 -> 58 attractions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Design system, live freshness, autocomplete, and clay-pin markers
Tristan Leduc+1: Claude Fable 5
b34c92a
UI/UX (from the design handoff):
- Clay-sticker theme, hero, framed Voyager map with warm grade, loading
state ("Scouting your wander…"), route-draw + staggered marker pop-in
- 14-piece clay-pin marker family (vendored SVGs) with color-by-meaning
mapping for all 17 categories, start/dest pins, legend in the family's
color language
- Start/Destination autocomplete over the local 30k POI-name index
- Force-light mode (design is light-only; Gradio dark vars made text
unreadable), per-event js fixed to pass inputs through
Freshness:
- Open-now awareness from OSM opening_hours (9,461 POIs): conservative
evaluator (abstains on exotic grammar, per-rule PH/SH handling),
plan-time demotion of closed places, night realism for unknown-hours
daytime categories, 🟢/🔴 itinerary badges
- Optional Google Places live-verify of final stops (GOOGLE_MAPS_API_KEY;
ToS-clean: per-request, never stored)
- Embedder switched to fastembed/ONNX (no torch in the request path;
warms in ~9s), bo…
Wanderlust: taste-aware Paris discovery routing
Tristan Leduc
f00d669
Build Small Hackathon submission - v1 complete
All P0 requirements + P1 features:
- OSM-based routing with detour budget control
- POI extraction with confidence scoring (30k places, 17 categories)
- Submodular orienteering solver with diversity optimization
- Vibe interpretation via sentence embeddings (bge-small)
- Grounded narration (0% hallucination gate) with optional Qwen3.5-9B
- Persistent taste profile (browser-based)
- Alternative routes (3 options)
- Custom UI (clay/sticker design with animations)
- Offline-first architecture (no runtime cloud APIs)
- Fully tested (42 tests passing)
Stack: OSM + networkx + scipy + Gradio 6 + HuggingFace
Ready for HF Space deployment.
See HACKATHON_DEPLOYMENT.md for push instructions.