This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
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.
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.
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.
A new local-first prototype connecting everyday outings with dated public-space proposals and resident suggestions.
Source repository · Download source ZIP
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.
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.
...#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 };
});
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.
Because this app asks people to write down how their own street feels to them.
127.0.0.1. The demo's narration ran locally too.