<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonathan Lee</title>
    <description>The latest articles on DEV Community by Jonathan Lee (@junkfeathers).</description>
    <link>https://dev.to/junkfeathers</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4025446%2F1f5d7d47-13e5-4283-905f-f4fc23604716.png</url>
      <title>DEV Community: Jonathan Lee</title>
      <link>https://dev.to/junkfeathers</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/junkfeathers"/>
    <language>en</language>
    <item>
      <title>The Local Agora: A Retro AI Machine for Finding Local Events</title>
      <dc:creator>Jonathan Lee</dc:creator>
      <pubDate>Mon, 13 Jul 2026 04:37:45 +0000</pubDate>
      <link>https://dev.to/junkfeathers/the-local-agora-a-retro-ai-machine-for-finding-local-events-2155</link>
      <guid>https://dev.to/junkfeathers/the-local-agora-a-retro-ai-machine-for-finding-local-events-2155</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;The Local Agora&lt;/strong&gt;, a retro-inspired Android event discovery machine for finding local &lt;strong&gt;music, comedy, and theater&lt;/strong&gt; performances.&lt;/p&gt;

&lt;p&gt;The basic interaction is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose a city or ZIP code.&lt;/li&gt;
&lt;li&gt;Choose when you want to go.&lt;/li&gt;
&lt;li&gt;Choose music, comedy, or theater.&lt;/li&gt;
&lt;li&gt;Scan for current public event information.&lt;/li&gt;
&lt;li&gt;Open the original source to verify the details.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The interface is designed like a strange monochrome civic machine: part CRT terminal and part mythological signal receiver.&lt;/p&gt;

&lt;p&gt;Behind that visual identity is a grounded Google AI discovery system called the &lt;strong&gt;Keryx Engine&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I Built It
&lt;/h3&gt;

&lt;p&gt;I am a musician and help operate a small DIY performance space. I have seen how much passion exists in local creative scenes—and how easily events disappear inside scattered social posts, flyers, venue pages, and ticket listings.&lt;/p&gt;

&lt;p&gt;A great local show can be happening only a few miles away while the people who would love it never learn that it exists.&lt;/p&gt;

&lt;p&gt;The Local Agora is my attempt to help those scenes become easier to discover without turning them into another social network, advertising feed, or map covered in promoted pins.&lt;/p&gt;

&lt;p&gt;Its guiding phrase is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find your scene. Grow your scene.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/shorts/Spib3RoKJSY" rel="noopener noreferrer"&gt;Watch the Android video demo on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video shows an earlier physical Android build of The Local Agora.&lt;/p&gt;

&lt;p&gt;Since recording it, I completed several additional parts of the challenge build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real grounded event discovery through a protected Firebase callable&lt;/li&gt;
&lt;li&gt;Current public source links inside event results&lt;/li&gt;
&lt;li&gt;Improved text-only CRT results&lt;/li&gt;
&lt;li&gt;Bottom-anchored controls and improved phone layout&lt;/li&gt;
&lt;li&gt;A protected Add Event contribution flow&lt;/li&gt;
&lt;li&gt;Server-side submission validation&lt;/li&gt;
&lt;li&gt;Pending Firestore storage for community-submitted events&lt;/li&gt;
&lt;li&gt;Deny-all direct client access to Firestore&lt;/li&gt;
&lt;li&gt;Additional automated testing&lt;/li&gt;
&lt;li&gt;Android and Flutter web builds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The protected contribution backend is deployed. Its final Android-to-Firestore physical verification is the next acceptance test, so I am not claiming that final device proof as completed in this submission.&lt;/p&gt;

&lt;p&gt;During physical testing, the live discovery system successfully returned sourced events for locations including Springfield, Missouri and Mountain Home, Arkansas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/JonathanEdwardLee/the-local-agora" rel="noopener noreferrer"&gt;View The Local Agora on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Challenge snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Repository: JonathanEdwardLee/the-local-agora
Branch: pass-03-agora-discovery-vertical-slice
Commit: 4408b9fa53a9b5648ae626a650246344c17d6d0e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project was developed during the challenge window.&lt;/p&gt;

&lt;p&gt;The latest build produced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working Android debug APK&lt;/li&gt;
&lt;li&gt;A successful Flutter web build&lt;/li&gt;
&lt;li&gt;131 passing Flutter tests&lt;/li&gt;
&lt;li&gt;39 passing Firebase Functions tests&lt;/li&gt;
&lt;li&gt;Passing Flutter analysis&lt;/li&gt;
&lt;li&gt;Passing TypeScript compilation&lt;/li&gt;
&lt;li&gt;Security tests confirming unauthorized Firestore reads and writes receive &lt;code&gt;403 PERMISSION_DENIED&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Flutter and Dart Client
&lt;/h3&gt;

&lt;p&gt;The application is built in &lt;strong&gt;Flutter and Dart&lt;/strong&gt; so the same product can support Android, web, and a future iOS release.&lt;/p&gt;

&lt;p&gt;Android was the primary physical test target for this weekend.&lt;/p&gt;

&lt;p&gt;Instead of using conventional cards and map markers, I designed a text-first interface around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A monochrome CRT display&lt;/li&gt;
&lt;li&gt;Dial-like time and category selectors&lt;/li&gt;
&lt;li&gt;Source actions presented as terminal text&lt;/li&gt;
&lt;li&gt;A shared procedural Junkfeathers Tech startup animation&lt;/li&gt;
&lt;li&gt;A portrait-oriented machine layout&lt;/li&gt;
&lt;li&gt;Accessible controls beneath the retro presentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Keryx Engine
&lt;/h3&gt;

&lt;p&gt;The most important technical decision was separating discovery from factual normalization.&lt;/p&gt;

&lt;p&gt;Keryx uses a two-stage process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grounded discovery&lt;/strong&gt; searches current public information using the Gemini API and Google Search grounding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fact-constrained normalization&lt;/strong&gt; converts supported findings into structured event records.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The normalization stage is not allowed to freely invent missing information.&lt;/p&gt;

&lt;p&gt;When an event time, price, age restriction, address, performer, or ticket link cannot be supported by a public source, it remains unknown.&lt;/p&gt;

&lt;p&gt;Every displayed event preserves a path back to its original source.&lt;/p&gt;

&lt;p&gt;This was important to me because an event finder becomes harmful very quickly if it confidently invents details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protected Google Cloud Architecture
&lt;/h3&gt;

&lt;p&gt;The Google AI work happens on the server rather than exposing secrets inside the Flutter application.&lt;/p&gt;

&lt;p&gt;The current stack includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flutter and Dart&lt;/li&gt;
&lt;li&gt;Firebase Cloud Functions, second generation&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Node.js 22&lt;/li&gt;
&lt;li&gt;Firebase App Check&lt;/li&gt;
&lt;li&gt;Gemini API with Google Search grounding&lt;/li&gt;
&lt;li&gt;Cloud Firestore&lt;/li&gt;
&lt;li&gt;Server-side schema validation&lt;/li&gt;
&lt;li&gt;Firebase Admin SDK writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The live Keryx route has been proven end to end on a physical Android phone through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Android app
→ Firebase App Check
→ Protected callable function
→ Gemini with Google Search grounding
→ Fact-constrained normalization
→ CRT event results
→ Original public sources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployed event-submission callable is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submitEventContribution
Region: us-central1
Runtime: Node.js 22
Memory: 256 MiB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The existing &lt;code&gt;keryxScanDebug&lt;/code&gt; and &lt;code&gt;keryxStatus&lt;/code&gt; functions were preserved rather than modified unnecessarily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Community Event Contributions
&lt;/h3&gt;

&lt;p&gt;I also built &lt;strong&gt;Add Event Lite&lt;/strong&gt; so community members can contribute a missing public event.&lt;/p&gt;

&lt;p&gt;The form accepts only public event information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event name&lt;/li&gt;
&lt;li&gt;Music, comedy, or theater&lt;/li&gt;
&lt;li&gt;Date and start time&lt;/li&gt;
&lt;li&gt;Venue&lt;/li&gt;
&lt;li&gt;City and state&lt;/li&gt;
&lt;li&gt;Public source or ticket URL&lt;/li&gt;
&lt;li&gt;Optional short description&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application does not request a submitter name, email address, or phone number.&lt;/p&gt;

&lt;p&gt;Submissions travel through the App Check-protected &lt;code&gt;submitEventContribution&lt;/code&gt; callable. The server validates the information and creates a Firestore record with server-controlled values including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status: pending
origin: community_submission
schemaVersion: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Flutter client cannot approve its own submission or write directly to Firestore.&lt;/p&gt;

&lt;p&gt;Firestore rules deny direct client reads and writes, including access to the &lt;code&gt;eventSubmissions&lt;/code&gt; collection.&lt;/p&gt;

&lt;p&gt;I verified those rules with unauthenticated REST read and write attempts. Both were rejected with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 PERMISSION_DENIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server-side negative tests also confirmed that the following inputs are rejected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalid URLs&lt;/li&gt;
&lt;li&gt;Invalid event types&lt;/li&gt;
&lt;li&gt;Past event dates&lt;/li&gt;
&lt;li&gt;Attempts to submit a client-controlled approved status&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost and Abuse Control
&lt;/h3&gt;

&lt;p&gt;A public AI event finder could become expensive if every visitor caused a new discovery request.&lt;/p&gt;

&lt;p&gt;The production direction is therefore &lt;strong&gt;cache-first&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Return useful recent stored events when available.&lt;/li&gt;
&lt;li&gt;Invoke grounded AI discovery only when information is missing or stale.&lt;/li&gt;
&lt;li&gt;Normalize and store useful results.&lt;/li&gt;
&lt;li&gt;Reuse those results for later visitors.&lt;/li&gt;
&lt;li&gt;Accept missing events from the community into pending review.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The shared cache and event index are the next production milestone. I intentionally did not rush them into this weekend build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy and Source Honesty
&lt;/h3&gt;

&lt;p&gt;The Local Agora does not infer or expose private venue addresses.&lt;/p&gt;

&lt;p&gt;Supported location information can remain at levels such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact public address&lt;/li&gt;
&lt;li&gt;Venue only&lt;/li&gt;
&lt;li&gt;General area&lt;/li&gt;
&lt;li&gt;City only&lt;/li&gt;
&lt;li&gt;Ask the organizer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unknown information stays unknown.&lt;/p&gt;

&lt;p&gt;The app does not claim that its results are a complete list of every event. It presents sourced public signals and lets the user inspect the original source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building With AI Tools
&lt;/h3&gt;

&lt;p&gt;I built this as a solo founder using AI tools as specialized collaborators.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; was the primary repository implementation agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT&lt;/strong&gt; helped with product planning, architecture review, risk analysis, acceptance criteria, and continuity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google AI and Gemini&lt;/strong&gt; power the actual event-discovery system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I remained responsible for product direction, scope decisions, physical-device testing, privacy boundaries, source-honesty rules, and final acceptance.&lt;/p&gt;

&lt;p&gt;One major lesson from this weekend was that AI-assisted development works best when each agent receives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A bounded assignment&lt;/li&gt;
&lt;li&gt;An exact starting commit&lt;/li&gt;
&lt;li&gt;Clear acceptance tests&lt;/li&gt;
&lt;li&gt;Protected systems it may not alter&lt;/li&gt;
&lt;li&gt;A required technical handoff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That approach helped me make significant progress without allowing multiple AI systems to overwrite each other’s work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Progress During the Weekend
&lt;/h2&gt;

&lt;p&gt;The project moved from a product blueprint to a functional Android application with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A custom Flutter interface&lt;/li&gt;
&lt;li&gt;Physical Android testing&lt;/li&gt;
&lt;li&gt;Grounded current-event discovery&lt;/li&gt;
&lt;li&gt;Supported event normalization&lt;/li&gt;
&lt;li&gt;Working public source links&lt;/li&gt;
&lt;li&gt;Protected Firebase communication&lt;/li&gt;
&lt;li&gt;App Check integration&lt;/li&gt;
&lt;li&gt;A deployed contribution function&lt;/li&gt;
&lt;li&gt;Server-side validation&lt;/li&gt;
&lt;li&gt;A new Firestore database&lt;/li&gt;
&lt;li&gt;Pending community submissions&lt;/li&gt;
&lt;li&gt;Default-deny database rules&lt;/li&gt;
&lt;li&gt;Android and web builds&lt;/li&gt;
&lt;li&gt;More than 170 combined passing tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I deliberately did &lt;strong&gt;not&lt;/strong&gt; add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User accounts or public profiles&lt;/li&gt;
&lt;li&gt;Social feeds&lt;/li&gt;
&lt;li&gt;Embedded maps&lt;/li&gt;
&lt;li&gt;Ticket sales&lt;/li&gt;
&lt;li&gt;Billing&lt;/li&gt;
&lt;li&gt;Paid placement&lt;/li&gt;
&lt;li&gt;Nationwide background crawling&lt;/li&gt;
&lt;li&gt;Automated flyer extraction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to build one useful and trustworthy vertical slice instead of a large collection of unfinished features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Limitations and Next Steps
&lt;/h2&gt;

&lt;p&gt;The Local Agora is still an early challenge build.&lt;/p&gt;

&lt;p&gt;Current limitations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The newest Add Event backend still needs its final physical Android submission proof&lt;/li&gt;
&lt;li&gt;The Flutter web build is not yet publicly deployed for production use&lt;/li&gt;
&lt;li&gt;Pending community submissions require a future review process&lt;/li&gt;
&lt;li&gt;The shared cache-first event index is not built yet&lt;/li&gt;
&lt;li&gt;Flyer image extraction is intentionally deferred&lt;/li&gt;
&lt;li&gt;Android is currently the primary tested platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next milestone is a shared Firestore event index that lets one grounded discovery serve many visitors.&lt;/p&gt;

&lt;p&gt;After that, I plan to securely release the Flutter web version and continue turning The Local Agora into a useful public tool for local creative communities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Best Use of Google AI
&lt;/h3&gt;

&lt;p&gt;Google AI is not a decorative chatbot inside this project. It performs the central product function.&lt;/p&gt;

&lt;p&gt;The Gemini API with Google Search grounding discovers current public event signals. A separate fact-constrained normalization stage converts those findings into structured records while preserving original sources and refusing to invent unsupported details.&lt;/p&gt;

&lt;p&gt;This allows The Local Agora to search the fragmented public web while remaining honest about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What it knows&lt;/li&gt;
&lt;li&gt;What it does not know&lt;/li&gt;
&lt;li&gt;Where each event came from&lt;/li&gt;
&lt;li&gt;Which details require verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google AI makes the product possible, while the surrounding Firebase and Flutter architecture makes that AI safer, reusable, and practical for a public application.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>googleai</category>
      <category>flutter</category>
    </item>
  </channel>
</rss>
