Inspiration
Moving to a new city is more than finding an apartment. Generic "best of" lists don't help you rebuild the small routines that make a place feel like yours: a morning coffee spot that suits you, a bookshop to browse, a place to eat that feels familiar, or a cultural outing for a free afternoon. We wanted a companion that starts from what you already love, and is honest about what it knows and doesn't.
What it does
You tell First 90 Days a few things you enjoy ("indie bookstores, Korean food, coffee shops, quiet mornings"), where you're settling in Raleigh, and how far you'll go.
It returns a staged plan of real places—three for each of First Week, First Month, and Months 2–3—framed as routines to try, never scheduled events.
- Grounded Explanations: Each card says why it fits in plain words built only from Qloo's data ("Qloo ranked it using your picks… Qloo tags it: Cozy, Quiet, Morning"), states what remains unverified ("Check current hours, prices, and details before visiting"), and links out only when Qloo returned an official website.
- Reasoned Feedback & Revision Loop: You can Save, Replace (with a structured reason: Too far, Too expensive, Too lively, or Not interested), or inspect the full evidence for any place. Replacing one place changes only that slot: saved places stay locked, constraints tighten visibly (e.g., halving the search radius), and if Qloo has no verified alternate, the app says so honestly instead of inventing one. Undo is one tap.
- Demonstrable Personalization: Change an interest, the travel radius, or the dining budget and the plan visibly reshapes.
How we built it
- Qloo as the Cultural & Place Grounding Layer:
/v2/tagsand/searchresolve user text to exact Qloo tags or named entities (and prompt the user to disambiguate when multiple entities share a name)./v2/insightsqueries places per category using the user's other picks as taste signals (signal.interests.tagsandsignal.interests.entities), applying explicit geographic coordinates (filter.location+ mandatory radius), price ceilings, and exclusions as hard filters, alongsidefeature.explainability=trueto surface per-pick attribution. - Bounded, Observable Agent Loop: The engine executes five explicit steps:
resolve_interests→discover_places→apply_constraints→construct_plan→explain, plusrevisePlanfor reasoned feedback. Each step streams real progress to the Building view as it completes. - Deterministic by Design: Candidate ranking uses a documented, testable fit score with stable tie-breaking by Qloo entity ID—identical inputs and Qloo responses always produce the exact same plan. No language model can manufacture a place, URL, or fact.
- Truth Rules Enforced in Code & Tests: Every suggested venue traces to a Qloo
entity_id. An automated live audit script repeats the Qloo queries and verifies that every rendered name, address, price level, website, distance, and tag matches Qloo's response verbatim. - Engineering Stack: Next.js (App Router) + strict TypeScript, Zod boundary validation, Vitest (180+ unit/integration tests), Playwright E2E suite across desktop and mobile viewports with axe WCAG 2.2 AA accessibility scans, HMAC-signed session state, per-visitor and global rate-limiting, GitHub Actions CI, deployed on Vercel.
Challenges we ran into
- Auditing Real Coverage First: Before writing application code, we ran a bounded live feasibility audit against the hackathon environment. Climbing gyms—one of our initial persona's interests—had no usable venue coverage in Raleigh, so we revised the demo persona to match verified data rather than fabricating inventory.
- Noisy Category Tags: Qloo's broad category tags occasionally include off-category businesses (for example, a discount variety store tagged under bookstores, or a roller rink tagged under cafés). We built a transparent post-filter using Qloo's own
primary_genreallow/deny rules. - Missing Price Metadata on Cultural Venues & Bookstores: Qloo returns no price level for museums and most Raleigh bookstores. Sending a server-side
filter.price_level.maxquery parameter silently drops all unpriced venues. We engineered category-aware price filtering that keeps unpriced museums and bookstores in the pool and explicitly labels them "Price not available — check current details". - Signal Attribution Differences: Qloo returns per-input explainability scores for named entity signals (
signal.interests.entities), whereas tag signals return overall affinity without per-tag attribution. We implemented three distinct, honest explanation templates (input_attribution,taste_signals, andcategory_and_distance) so the UI never overclaims. - Rate-Limit Resilience: Burst queries can trigger HTTP
429responses on the event key. We added request pacing, bounded concurrency, private in-memory caching, zero retry storms on429, and route-level visitor throttling.
Accomplishments that we're proud of
- A Plan You Can Trust: 100% of places, addresses, price levels, and URLs come directly from Qloo, verified by an automated live grounding audit.
- Feedback That Actually Respects Constraints: Saved places never shift, replacement reasons translate into visible hard constraints, dismissed places stay excluded for the session, and tight filters return an honest "no verified match" state rather than silently relaxing user preferences.
- Production-Grade Verification: 180+ unit tests, 46 Playwright E2E tests across desktop and phone viewports (testing
429, API outages, empty results, missing keys, and tampered sessions), and 100 Lighthouse / axe WCAG 2.2 AA accessibility compliance.
What we learned
Cultural and place intelligence is most compelling when the product is transparent about both why a place matched and what remains unverified. Running a live data audit before locking product scope prevented us from promising categories the dataset couldn't support, and combining Qloo's /v2/insights signals with deterministic constraint solving makes an agentic workflow both explainable and repeatable.
What's next for First 90 Days
Expanding beyond Raleigh to support multi-city comparison, broader cuisine tags, verified live opening hours and transit-time routing, cross-device plan syncing, and an optional LLM phrasing layer governed by our existing fact-validation guardrails.
Built With
- axe-core
- css3
- github-actions
- html
- lucide
- nextjs
- nodejszod
- playwright
- qloo-api
- react
- typescript
- vercel
- vitest

Log in or sign up for Devpost to join the conversation.