This is a submission for the AI Agents Challenge powered by n8n and Bright Data.
**UPDATED**
I just noticed my n8n trial is officially expired 😢 So I've turned the chat back off so it doesn't seem like it should be doing something. I have bigger plans for this guy though, so stay tuned. More updates from future me will follow. 🫶
Also, the "prequel" I wrote up a couple of days later. If you're curious about the whole "no hackathons" rule and want the whole story, it's here.
🦄 FYI: I updated this after the fact—I did plan two full weeks down to the minute—but I didn’t plan for not having the data or not having a solid backup when it didn’t arrive. Also, I lost half a day to Figma’s decorative quicksand. The rushed thing I pasted to make the buzzer didn’t sound like me, so this is the version I can live with (and you can actually read for fun). 🪄
What I Built
Underfoot (my stubborn little ChatPot) is a chatbot that finds locals-only, off-the-map places you won’t see on Yelp or TripAdvisor. You give it a location and intent (music, coffee, hiking, spider farms…) and it returns unique results with a Stonewalker rating. Each query also adds anonymous whispers to the cache, so the dataset grows as people use it. 🎯
GitHub Repository — docs, flows, screenshots
Underfoot Chat — The Stonewalker Agent (try it)
Underfoot — the locals-only treasure hunter 🎒
I didn’t try to cram this into a weekend; I mapped two weeks like a flight plan. The part that snapped first was the one I trusted too much: a data preload that was “ready” on paper and nowhere in reality. My beautiful schedule grew a crater where the testing window used to be, and from there it was just me, the agent, the caches, and a lot of swearing said lovingly at my own ambition. 🧭
The whisper-network: project background 📼
Late ’90s / early 2000s Georgia, when cell phones were for emergencies and you rationed the battery like it owed you money. My brother and I chased off-the-wall spots through a whisper network: backyard mini-festivals with passwords in the backs of free magazines, odd museums, ghost tours that were either hilariously fake or so real you quietly recalibrated your cosmology on the drive home. Too public and the cops came; too secret and the field stayed empty. We loved that balance.
Underfoot bottles that energy. You give the resident Stonewalker an intent you actually feel (“live music,” “odd museums,” “strange gardens,” “spider farm”) plus a location. It crawls the not-obvious places first, normalizes the messy HTML, then returns a ranked list that says, “this is probably your kind of weird—go now.” 🕵️♀️
The “catch”: sometimes these events haven’t been discovered yet— that's how these things work. Besides, a chatbot can’t politely wait for scraping, so every {intent, location}
pair sends out separate whispers that build the cache for that combo. The next person’s list is a little louder and a little wilder than the last. 🔁
I can’t do simple (and I stopped pretending otherwise) 🧶
I promise “tiny demo,” and five minutes later I have: an ADR (revised twice), a color palette in Leonardo, three caches, and a Future Enhancements page that looks like a subway map someone cried coffee onto. It’s not scope creep; it’s scope pre-planning. I need to see the whole creature to ship a slice.
Vibe-coding lasted maybe two minutes. Copilot drafted the UI, I took a scenic (unnecessary) tour through Figma, flirted with Stitch, sprinted to n8n because hosted chat looked fast, missed my UI and dragged it back, and somewhere in there I wrote the agent I actually wanted—decisions like a product, not a science-fair trifold. 🧪
Why I don’t do hackathons (and why I did this one anyway) ⏱️
Hackathons want a snapshot; I build ecosystems. They want “Friday by midnight”; I want to label the stations, run one line today, and the others next week. I joined anyway because this is the exact agent I’ve wanted to build for months. It was fun—and it reminded me why trusting a green checkbox you don’t control is a dice roll. 🟩
What it looks like — the sprint with real nouns 🧵
Two mouths, one brain:
- n8n Hosted Chat (discovered late, used constantly)
- A tiny Copilot-assisted React UI once I get this deployed and working the way it does in my head. I will update with this link too.
Both funnel free-form requests into { intent, location }
.
Then:
- Fetch from the non-mainstream first (Facebook events that behave, Reddit posts that map to places or pop-ups, a growing “weird web” list living in Obsidian).
- Parse with minimal markup assumptions (HTML changes when you blink).
- De-dupe with URL + fuzzy geo (and strip tracking sludge).
- Score with the decimal Stonewalker blend: uniqueness • locality • recency • intent-fit.
- Cache to Supabase (scraping the same planet twice is a sin).
I hit n8n’s memory ceiling right when the first end-to-end run smiled at me. Reflex: vectors + RAG + tidy GCP attic. Reality: the clock. I did the boring, correct thing—pull less, trim sooner, cache earlier. Two caches unlocked; the third is a miniboss I’ll beat when I’m not running on midnight. 🧱
AI agents: saints on Monday, goblins on Tuesday 👾
Agents in the system and agents helping build the system—both truths can be true in the same hour:
- ChatGPT yanked me out of holes, then cited docs from a neighbor universe ten minutes later.
- Copilot went license-dark mid-sprint—peak front-end day timing.
- Leonardo kept momentum by letting me see the brand when the plumbing got gnarly.
- Various chat boxes (n8n, Bright Data, Supabase) were great rubber ducks until they hallucinated syntax.
Rule that saved me: no agent writes straight to storage. The normalizer is the law. Drafts can be flammable; data cannot. 🧯
The cache: Sheets vs Supabase (be a database, not a vibe) 🗄️
I tried to keep the prototype in Google Sheets because it was there and felt simple. Simple lied. As soon as you care about de-dupe, TTLs, hit/miss tracking, and “please don’t melt anyone’s hobby site,” you need grown-up storage. I pivoted to Supabase, met Row-Level Security (RLS) and grants like a mini-boss in a fresh dungeon (v17 feels spicier), learned just enough to stop tripping over my new underfoot
schema, and came out with two caches behaving—and a third that now knows the sound of my footsteps. 🪪
Underfoot belongs in your pocket (laptops were the proving ground) 📱
It runs on a computer because that’s where the tools live, but Underfoot is meant for your phone—a “you’re already out, what’s weird nearby?” companion. One-tap location, swipeable cards you can skim at a red light (responsibly), save/share that doesn’t fight you, and a quiet toggle: “nudge me when something odd appears within five miles.”
Path: PWA now (installable, offline last results, Share Target) → push later (saved intents → “new oddities near you”) → Expo wrap with haptics and deep links like underfoot://place/:id
so maps feel native. 🧭
What I did wrong (say it out loud) 🧨
- Trusted an external preload without a Plan B. The curated 2.3M rows never landed; my testing window vanished; I rewrote for “fetch + cache” and paid the polish tax. 🔌
- Spent a half-day in Figma I couldn’t spare. Respect to UX; for me it was decorative quicksand. 🎨
- Let “Cloudflare will be easy” live rent-free. It wasn’t. Keep the UI small; add a tiny Worker proxy only where needed. ☁️
- Left Copilot tied to work. Running out of credits mid-sprint felt like knitting with oven mitts. 🧤
- Underestimated n8n memory. Fixed by budgeting data like money: pull less, trim sooner, cache earlier. 🧮
- Structured output optimism. Agents can do it, but the normalizer must be the bouncer from hour one. 📇
🦄 I still had a blast, learned a ton, and might even do it again—after a nap and a vacation from permissions dialogs. 🌙
Future plans (also in the repo) 🧱
High-impact next: dataset preload I control (seeded batches), vector search via pgvector once the pipeline is boring, Google Places (Text/Nearby + Details) through the same normalizer, and a curated “weird web” seed list (40–50 URLs you wouldn’t know to google, scheduled fetcher, quality scoring so the noisy ones get benched). 🧰
Cache-first response: the Whisper Network runs in the background, but the agent should hit the cache first. Update the prompt/flow to enforce cache → live tools.
Cache management planning: I put a lot of design work into how the cache could be structured to allow for future RAG-type navigation, along with simpler embeds for straight vector search. However, I put zero thought (asides from "I need to think about that at some point") into actually managing it after the data is there. For now, it's all dependent on a merge that's unique by URL.
- That "unique" URL isn't very reliable and needs a better "smart" dedupe system
- I took a limited hour-ish aside through the land of TTL in Google Sheets (not at all recommended, by the way) and learned that any way I tried to make it work it always had the same result = terrible idea. That's how Supabase got involved, but ultimately broke things worse than fixing them. I probably should have left it alone.
Localization: right now it returns the first geocoder result for speed. Flesh this out so results match what people meant, not just what a geocoder guessed.
Experience: transparent Stonewalker scoring you can peek and tweak; richer cards (images, tags, map snaps, save/share lists, quick filters); a quieter Discord bot that delivers value, not noise; mobile that respects thumbs and daylight. ✨
Reliability: Normalization 2.0 is the law; a tiny “repair” agent only when validation fails; better cross-source entity linking so “The Old Mill” is one place—not fourteen. 🔐
Technical Stuff
- System Instructions: Stonewalker persona — hybrid nature/tech guide with a maps + travel focus.
- Model: GPT-4.1-mini (full model as backup).
- Memory/Cache: normalized results for reuse (v2.0); prototype began in Google Sheets.
- Nodes: HTTP Request, Function (normalize/de-dupe), Discord notifier. Bright Data Verified node was great when it fit; I often swapped to plain HTTP for unusual flows.
-
Central AI agent: The Stonewalker.
- I had a separate data-only agent; it took too long for mediocre results. After cache reliability + maps data, I’ll revisit.
- Triggers: Webhook (custom UI) + n8n Chat (convenience).
- Sources: internal cache (in progress), Google Maps SERP (fallback), Google SERP, Reddit, Facebook events, pre-approved secondary sources (future).
-
Output: structured parser + secondary normalizer.
- I went back and forth on structured output; docs warn against strict schemas with agents. The normalizer stays the final authority.
- Error handling: Discord notifier—useful, loud; needs formatting + routing love.
- Planned: Bright Data Google Maps dataset (delivered 2025-09-01 03:43 PDT — thanks, team).
Screenshots 🖼️
Last-minute solution instead of pre-cached data
The Whisper Network (background caching)
Discord notifications baked in
Quick links 🔗
Repo: https://github.com/CheckMarKDevTools/underfoot-underground-travel-planner
Live link: https://checkmarkdevtools.app.n8n.cloud/webhook/d92bc454-8f78-4471-ae25-ffa0c9bb87b3/chat
🛡️ R.A.I. — Responsible AI (how this stays good on purpose)
- Transparency: model-generated text is obvious in context; ranked results cite sources; Stonewalker scores are explainable (uniqueness • locality • recency • intent-fit) with tunables coming. 🔍
- Privacy: no PII required; anonymous intent + coarse location only; one-click “forget me” clears local cache and server traces tied to your session. 🫥
-
Respect: honor
robots.txt
, rate limits, and site TOS; prefer official APIs; link back, don’t lift; no paywalls or wholesale republishing. 🧑⚖️ - Safety: filter obviously harmful/illegal content and adult venues by default; opt-in gates for edgier categories; down-rank chains; spotlight genuinely local listings. 🧯
- Hallucination guard: agents don’t write to storage; a deterministic normalizer validates/repairs/rejects before anything lands; uncertainty is surfaced in the UI. 🧪
- Security: secrets live server-side; least-privilege keys; redacted logs; regular rotation. 🛡️
- Crawl with manners: staggered schedules, backoff, polite concurrency so nobody’s hobby site gets melted. 🐢
🦄 Confession: Yes I cheated — I let ChatGPT write the initial submission, which honestly I didn't even have time to read. There is no telling what was on it, but I know it wasn't mine and it was making me crazy. 🤪 So please, do not judge this writing as part of whatever scoring happens with the project. It was all done after the deadline.
Last updated: 2025-09-01. If I miss the mark on any of this, file an issue and I’ll fix it._ ✅
Epilogue 📝
Half of what I attempted was a bad idea—which is why I deferred the other half on purpose—and somehow I still had the best time. I’m tired in the satisfying way. I want a nap and I want to open my phone in a random city and hear Stonewalker whisper “turn left.” That’s how I know I’m not done—just paused long enough to tell you what really happened. 🌆
🧠💥 The Oomph (mic-drop)
The clock ran out; the curiosity didn’t. Underfoot / Stonewalker already finds the odd little places that make a town feel alive, and the desk was just a proving ground because your pocket is the point—one tap, one intent, a handful of cards that feel like a friend leaning in to whisper, psst, go here. 🌙
Top comments (5)
I completely understand! The "I can't do simple" problem is very real. I often start with a basic MVP, and before I know it, I'm already building a full-scale system instead of a working prototype.
Finding the balance between perfectionist tendencies and actually shipping something functional within reasonable timeframes is an ongoing struggle.
Thanks for the honest account of your development journey!
This feels like the kind of project that crawled out of a cave with a flashlight and a map nobody else had. Love the Stonewalker persona and the way whispers build the cache feels like folklore meets tech. The n8n chaos, Discord screams, and last-minute dataset hacks? Classic indie dev energy. Can’t wait for the full write-up. Underfoot deserves its own genre.
For me, the best projects are always based in some form of I lived it already. All those field concerts and underground without the ground type of setup? All true stories 😊
Impressive
Some comments may only be visible to logged-in visitors. Sign in to view all comments.