# Project export: SafeStreets

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: AI spots a street hazard from imagery, proves it with city data, and hands you the fix, the funding, the official to contact, and a before/after render — ready to move the city.
- Devpost: https://devpost.com/software/safestreets-h6yprx
- GitHub: not linked
- Video: https://www.youtube.com/embed/SIy97mAQ4YY?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Best Use of Claude)
- Team: contributor stats unavailable

## Devpost submission (written by the team)

### Overview

There are dangerous intersections all around the world, near our lives. Everyone knows they're dangerous. Cities probably know too. But nothing happens. We looked into why — turns out the data to prove it is all there (crash records, 311 complaints, news articles, council minutes), but it's spread across like 12 different government websites. And even if you find it all, you still have to write the letter, find the right official, and figure out which grant pays for the fix. That's hours of work most people aren't going to do. So we built SafeStreets. Drop a pin, get the whole case in one click. The AI looks at the street before it looks at any complaints. We made Claude analyze satellite and Street View photos without showing it any community data first. Then separately, we check if what it saw matches real crash reports and 311 complaints. That way "the camera spotted it" and "residents reported it" are actually two separate signals — not the model just agreeing with itself. We thought this was important for the results to actually be trustworthy. It can't point at the wrong corner. Instead of using pixel coordinates (which drift), we made Claude assign hazards to named zones like "NW corner" or "north crosswalk." The markers snap to the right spot every time. It finishes the job. Most tools just tell you there's a problem. SafeStreets gives you the specific fix, which grant program funds it, who to email, a before/after render of what the street would look like, and a letter you can actually send. Claude (Haiku) runs the two-stage vision pipeline. First pass is blind — images only. Second pass sees the community data and corroborates. We used Claude Code to build most of it. Browserbase handles the data sources that don't have clean APIs — local news sites and council agenda PDFs that need a real browser to load. Crash records and 311 stay on regular open data APIs. Redis caches everything per intersection. Two layers: raw scraped data and the full analysis result. Only writes to cache when agents actually returned something — otherwise a bad first run would poison every reload. Frontend is React + Mapbox with a retro pixel aesthetic. There's a live feed that shows what each agent is doing as it runs. Keeping the two signals actually independent. It would've been so easy to just show the model the complaints and let it run. We had to be pretty disciplined about the firewall because that's the whole point — if the model just reads the complaints and says "yep looks dangerous," that's not really two signals. Government data is a mess. News and council documents live in JS-rendered pages and PDFs that normal scrapers can't reach. Browserbase saved us there. And even the "clean" APIs (crash records, 311) each have totally different schemas. Substring matching is a nightmare. We had bugs where "king" matched "parking" and "vision" matched "provision" when searching crash records near MLK Way. Had to switch to whole-word matching and actually geocode intersections to real coordinates. Race condition in the cache. The live agent feed and the analysis request both hit the server at the same time on first load. If the analysis request finished first with empty data (rate limit, timeout), those empty results would get cached and break every subsequent load. Fix was to only cache when there's real data, and check if the other concurrent request saved something better. The blind-then-corroborate thing sounds like an implementation detail but it's actually the whole product. If you skip it, you just have a scraper with a chatbot on top. The last mile is genuinely hard. Getting the data is the easy part. Turning it into the exact letter, to the exact person, with the exact grant number — that's what actually gets streets fixed. Pick the right tool per data source. Some things have APIs. Some need a real browser. Don't force one approach for everything.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

No repository was indexed for this project. Claimed technologies below could not be checked against code.
- FastAPI (technology) — claimed on Devpost, not found in the code
- React (technology) — claimed on Devpost, not found in the code
- Redis (technology) — claimed on Devpost, not found in the code
- TypeScript (language) — claimed on Devpost, not found in the code

## Codebase structure

No repository index available.

## Key source files

No repository index available; no source files included.