Inspiration
The gap between seeing something broken in Glendale and getting it reported — searching the city site for the right department, finding a phone number that may or may not work, leaving a voicemail describing what and where — is so high-friction that most people don't bother and the pothole, graffiti, or downed branch just stays.
What it does
You take a photo of a pothole, graffiti, broken streetlight, or downed branch and Claude Haiku 4.5 vision categorizes the issue against a 20-type Glendale taxonomy, infers severity, drafts a polite service request you can edit, auto-resolves location from EXIF GPS → live geolocation → typed address (with a Glendale bounding-box check that rejects out-of-region coordinates), routes the report to one of 8 verified Glendale department contacts, and gives you one primary CTA that either opens email with the subject + body prefilled or — for departments without a published email — copies the full report to your clipboard and opens the dialer in one tap.
How we built it
Next.js 16 + React 19 + TypeScript on Vercel, with the Anthropic SDK calling claude-haiku-4-5 through a report_civic_issue tool whose schema declares category as a 20-value enum (no JSON parsing, no string matching), routing as a flat Record lookup enforced by TypeScript exhaustiveness, a client-side image pipeline (exifr → heic2any → Canvas resize), a Glendale bounding-box geofence layered over EXIF + browser geolocation, Vercel Blob + Postgres for persistence, and 158 Vitest tests across routing, prompt schema, image pipeline, geofence, and the analyze endpoint.
Challenges we ran into
iOS Safari fought back at every turn (HEIC handling, capricious file-picker behavior, soft keyboard collapsing the viewport over the Submit button), the original substring-based routing chain broke immediately on rephrased categories and forced a rewrite to constrained-enum tool use, @vercel/blob/client direct uploads were structurally CORS-blocked in production until the upload moved server-side, and a real production bug — a photo with malformed EXIF reverse-geocoded into Xinyi City, Jiangsu (same magnitude, flipped longitude sign) — forced a defense-in-depth Glendale bounding-box check on every coordinate from any source.
Accomplishments that we're proud of
A 20-category × 8-department routing layer with zero string matching that's structurally impossible to break with rephrasing, a one-tap end-to-end "photo → ready-to-send report" path that adapts to whether the routed department even has an email, and shipping it solo with 158 Vitest tests in roughly eight hours.
What we learned
Tool use with a JSON Schema enum is the right answer for "I want structured output from a vision model" — the constrained-decoding guarantee converts an entire class of "the model rephrased the field" bugs into compile errors — and writing a plan + engineering-review pass before each change felt absurd at hour 1 of a solo hackathon but was paying back hours of avoided debugging by hour 6.
What's next for Spot-It Fix-It
Persistent reports + status, direct CitySourced API integration to close the loop into the city's real ticketing queue, voice-memo input ("there's a downed branch behind 1100 Verdugo"), and multi-city rollout to Pasadena and Burbank — the schema (city → categories → departments → contacts) generalizes cleanly.
Log in or sign up for Devpost to join the conversation.