This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
Somewhere in a city PDF, someone has already redrawn the corner you walk past every day. In March 2026, NYC DOT presented a plan for Court Square in Long Island City, Queens: wider sidewalks on Thomson Avenue, new pedestrian crossings, a raised shared street. The people who know whether that corner feels crowded are the people walking it. Almost none of them will ever open that PDF.
Sidewalk Senate is a small, local-first notebook that sits on either side of an ordinary outing.
- Before you go (optional). Type one line about where you're headed. Press I'm heading out, and the app answers: "That's enough screen time." The map disappears. There is nothing to record and nothing tracking you.
- Go outside. Get coffee, walk with a friend, sit somewhere. No GPS, no photos, no check-ins. The outing is the point.
- When you're back. Write down where you spent time and how it felt, in your own words. If the place matches one of the curated locations, you confirm it yourself. The app never guesses where you were.
- Find the connection. A local open-weight model (Gemma 4) reads your reflection next to a dated public proposal and suggests where your experience fits. It cites the exact page, and everything it writes is labelled AI interpretation · Review required. Your original words are shown separately and never rewritten.
- Shape your suggestion. Edit the AI's suggested question or write your own, tick "I've reviewed these words," and format an unsent draft. Nothing is ever sent. That part stays with you.
If the place you visited has no reviewed proposal, it says so plainly ("Your thought still belongs here") instead of borrowing a nearby project to look helpful.
Who it's for: residents who already walk their neighborhood and have opinions about it, but who will never attend a community board meeting or read a long design presentation. The screen is the shortest part of the experience: a line before you leave, a few sentences when you get back.
The first pilot area is ZIP 11101 (Long Island City, Queens), with one curated, page-cited source: NYC DOT's Court Square Pedestrian Improvements, March 2026.
Demo
Watch the 2-minute Sidewalk Senate demo on YouTube
Prefer a file? The same video and its captions are attached to a GitHub release.
The video is a fictional outing run through the real app: every app screen is the real local website, and the connection, page citation and draft are real output from Gemma 4 running on my own computer. The outing and the reflection are simulated, which the video says at the start and on the end card. The outdoor shots are stock footage from Pexels (Keira Burton and Coverr), not my walk. The model's 21-second wait is shown faster, with the real time on screen. Even the soundtrack stayed local and open: narration by Kokoro-82M and music generated with Meta's MusicGen-small.
Code
antunishdPursuit
/
SidewalkSenate
A local Gemma-powered civic notebook connecting everyday outings with dated public-space proposals and unsent resident suggestions.
Sidewalk Senate
A new local-first prototype connecting everyday outings with dated public-space proposals and resident suggestions.
Source repository · Download source ZIP
Current scope
A local website with two notebook workspaces: My outing and Connection & draft. A local API and an optional interactive terminal session support the same source-backed workflow. Current coverage is one curated Court Square project with three reference intersections. Other places can still support a resident-written suggestion without an invented proposal match.
No tracking, Gmail, email sending, account connection, live search, analytics or persistent reflection storage is implemented. Nothing here establishes present street conditions or an open consultation. The website is intended to be used briefly before or after an ordinary outing; it requires no phone use or recording while outside.
Requires Node.js 24+. Run npm ci to install the frontend dependencies. No paid API is required.
npm ci
npm test
npm run check
npm…Sidewalk Senate source code on GitHub (MIT-licensed; the README covers setup, the pinned model, privacy boundaries and known limits).
Run it locally (Node.js 24+, no paid API):
npm run setup
npm run local
Then open http://127.0.0.1:4178. npm run doctor checks dependencies and the model service; npm run simulate runs a labelled two-stop outing through the real HTTP handlers with mock AI, and npm run simulate:real runs the same outing against real local Gemma.
How I Built It
The model. Gemma 4 E4B Instruct, the Q4_0 GGUF from ggml-org/gemma-4-E4B-it-GGUF (pinned revision and SHA256 in the README), imported into Ollama as local-gemma with a 4096-token context. The app calls Ollama's OpenAI-compatible chat endpoint on 127.0.0.1:11434. That's it: no cloud endpoint is accepted. The URL validator rejects anything that isn't a loopback HTTP address.
The architecture.
Browser notebook (React + Vite + TypeScript, Leaflet map)
│ same-origin, loopback only
â–¼
Local Node API (/api/cases, /api/reflect, /api/draft)
│ validated JSON in, validated JSON out
â–¼
Ollama · Gemma 4 E4B (open weights, on this computer)
Keeping a small model honest. The interesting work wasn't the prompt; it was everything around it.
- Schema-constrained output. Gemma answers through a strict JSON schema (summary, connections, suggestion, open questions), at temperature 0.2 with a bounded output budget. A response cut off by the length limit is rejected instead of half-parsed.
-
The model can only cite what a person reviewed. Each curated source is broken into short claims with a page number. Gemma may only refer to claims by ID; the server rejects any unknown or duplicate ID and builds the citation link (
...#page=17) itself, so the model can't invent a page.
const allowed = new Set(context.claims.map(c => c.id));
const connections = raw.connections.map(c => {
if (!c || !allowed.has(c.claimId) || seen.has(c.claimId))
throw new InputError('Unsupported or duplicate source claim.');
const claim = context.claims.find(item => item.id === c.claimId);
return { claimId: c.claimId, explanation: text(c.explanation, 'Connection', 1200),
sourceClaim: claim.text, citation: context.source.url + '#page=' + claim.page };
});
- The resident confirms the place, not the model. A ZIP code or a nearby pin never decides relevance. If you pick a curated location, you have to tick "I visited this location" before any connection is attempted. An unconfirmed or unknown place returns a no-match without calling the model at all.
- Feelings stay feelings. In testing, one response turned a resident feeling hurried at a crossing into a claim about inadequate signal timing. So the model-written rationale is now replaced with a fixed line: based on your personal reflection, not verified street conditions or an engineering assessment. The prompt also tells it to keep satisfaction as satisfaction and not manufacture a problem.
- Dated means dated. The UI keeps saying what a proposal is not: "A proposal is not proof of today's conditions, an open comment period, or completed construction."
Privacy by default. No accounts, no analytics, no GPS, no route recording, and no persistent storage of reflections: they live in browser memory for the session. The API binds to loopback and rejects cross-origin requests. The only third party is the OpenStreetMap tile server, which sees the map area you're viewing, never your words.
Testing. 32 automated tests cover footprint matching, citation validation, the loopback-only inference boundary, review gates, HTTP behavior, and real React component interactions in JSDOM (including resetting an outing while the model is still thinking). A real-model mode runs the same React flow against local Gemma.
AI assistance, disclosed. I used AI coding assistance and independent source reviews (GPT-6 Astra and Claude Opus 5.5) while building, and Claude Code with HyperFrames and FFmpeg to produce the demo video from a scripted capture of the real app. At runtime, the only AI is local Gemma; nothing calls a hosted model.
Why Does Open Innovation Matter?
Because this app asks people to write down how their own street feels to them.
-
Your words stay on your machine. Where you walk, who you walk with, what made you uncomfortable: with a closed API, every reflection would leave your computer before you'd even decided whether to share it. With open weights, the whole loop (reflection, connection, draft) runs on
127.0.0.1. The demo's narration ran locally too. - Civic tools should be inspectable. If an AI is going to tell you that your experience relates to a city proposal, you should be able to read exactly how it decides. Here the prompt, the claim list, the validators and the model hash are all in the repo. A community group can audit them, change them, or swap the model.
- No meter running. There is no per-request bill, so a library, a tenants' association or a classroom could run it without a budget line or an account.
- Reproducible behavior. Pinning an exact open model file means the behavior you test is the behavior you ship. A hosted model can change underneath you; this one can't.
I took it outside
Today I went for a real walk through ZIP 10001 in Manhattan, with the app closed. When I got back I wrote what I noticed, in my own words: "i think that there should be more repairs on the sidewalk, i saw cracks when i walking."
Here's how it went:
- It said no, honestly. Sidewalk Senate has one reviewed source so far (Court Square, Queens). For 10001 it answered "No reviewed match. Your thought still belongs here." It didn't borrow the Queens project, and it didn't call the model at all, because only confirmed, reviewed locations go to Gemma.
- My words stayed mine. The reflection and the suggestion went into an unsent draft exactly as I typed them.
- It found a bug, which I fixed the same day. On a no-match stop, the suggestion panel still said "Edit the AI suggestion... You don't have to agree with the proposal," when there was neither. It now says there's no reviewed proposal for this place and asks for your own suggestion or question (commit).
- The real gap is coverage. What I actually wanted to know, which public record or plan covers sidewalk repairs here, is exactly what the app can't answer yet. That's the next step: more reviewed sources and a verified place to send a letter.
What's honest to say about it
- One curated source so far. Coverage is one Court Square project with three reviewed intersections. Live search and other cities are future work.
- No recipient is verified. The draft says it's unsent and asks who the right recipient is. The app never sends anything.
- The model is still a model. Validation guarantees citations point at reviewed claims; it can't guarantee every paraphrase is right. In testing, a reflection where the resident forgot the exact location still got too confident a connection, and positive feedback sometimes got a weak, location-only link. That's why every AI line sits next to your original words and needs your review.
- One real outing so far. It's described above. It's one person's walk, not a survey.
Prize Categories
- Best Use of Gemma: Gemma 4 E4B runs locally through Ollama and is central to the core workflow: connecting a resident's reflection to dated, page-cited public proposals under deterministic validation.
Top comments (0)