<?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: Samir Yuja</title>
    <description>The latest articles on DEV Community by Samir Yuja (@syuja).</description>
    <link>https://dev.to/syuja</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%2F3962741%2Fabbed9c0-d180-4f25-92ce-aaccbcb845b7.png</url>
      <title>DEV Community: Samir Yuja</title>
      <link>https://dev.to/syuja</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syuja"/>
    <language>en</language>
    <item>
      <title>I Gave Claude the Keys to My Airbnb for Two Weeks. Here's What It Found.</title>
      <dc:creator>Samir Yuja</dc:creator>
      <pubDate>Wed, 10 Jun 2026 03:28:18 +0000</pubDate>
      <link>https://dev.to/syuja/i-gave-claude-the-keys-to-my-airbnb-for-two-weeks-heres-what-it-found-3peo</link>
      <guid>https://dev.to/syuja/i-gave-claude-the-keys-to-my-airbnb-for-two-weeks-heres-what-it-found-3peo</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://samiryuja.dev/blog/hospitable-mcp-airbnb-audit" rel="noopener noreferrer"&gt;samiryuja.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I connected the official Hospitable MCP server to Claude, pointed it at my Airbnb, and asked it to take a look. Here's what I found.&lt;/p&gt;

&lt;p&gt;A language model is only as good as the context it's given. On its own it can talk about short-term rentals in the abstract; it knows nothing about &lt;em&gt;mine&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;MCP&lt;/strong&gt; closes that gap — it gives the model live, authenticated access to my actual reservations, messages, reviews, and calendar. So instead of guessing, it can look at the real thing. I run a two-room rental I've managed for over a year, and I figured I had it under control. Two weeks of letting an agent actually look turned up a surprising amount that was quietly broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hospitable &lt;strong&gt;Professional&lt;/strong&gt; plan (the tier that includes MCP access)&lt;/li&gt;
&lt;li&gt;Claude Pro&lt;/li&gt;
&lt;li&gt;Connect them: &lt;strong&gt;Settings → Connectors → add custom connector → OAuth&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;~50 tools load: reservations, messages, calendar, reviews, the Knowledge Hub, tasks, smart locks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions were monitored the entire time&lt;/strong&gt; — no messages were ever sent to guests. Everything guest-facing stayed read-only or draft-and-paste: the agent proposes, I decide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What an AI audit of a real business actually surfaces
&lt;/h2&gt;

&lt;p&gt;Here are the highlights of what it found, in four buckets.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Liability
&lt;/h3&gt;

&lt;p&gt;The registered guest booked from overseas, but the person staying was his wife, who had no verified profile of her own. The agent caught it by reading the message thread. That matters because AirCover and host protections are written around the &lt;em&gt;registered&lt;/em&gt; guest, and nothing in the structured reservation data reveals a substitution. It only surfaces if someone actually reads what the guest wrote.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Revenue opportunities
&lt;/h3&gt;

&lt;p&gt;This bucket changed how I look at my inquiry inbox. Every inquiry is revenue waiting on a response, and the agent treated it that way: it read the full inquiry history and identified exactly where a faster or better-matched reply would have won the booking. One guest asked about renting the whole place; by the time a reply went out hours later, those dates had gone to another property. Another inquiry came in written in Spanish, and the automated rule answered in English because no multilingual template existed. Across the history, it flagged six inquiries where response time or language likely cost the &lt;strong&gt;conversion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;None of this is visible on any dashboard. What the agent produced was effectively a conversion punch list: respond faster to whole-home inquiries, add a Spanish template, and answer the recurring questions (parking, check-in) before guests have to ask. Questions are demand — the goal is turning them into bookings.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Things quietly broken
&lt;/h3&gt;

&lt;p&gt;The theme in this bucket: the business changed, and the documentation didn't keep up.&lt;/p&gt;

&lt;p&gt;The big one was &lt;strong&gt;stale door codes.&lt;/strong&gt; When I first set up the Airbnb, I hardcoded the entry codes into the Knowledge Hub — the reference Hospitable's AI uses to answer guests. Later I synced my Schlage lock with Hospitable, which generates a fresh code for every reservation. The original setup-era codes stayed behind in seven different Knowledge Hub entries, wrong for every guest since. If anyone had asked the AI "what's my door code?", it would have confidently handed them a code that opens nothing.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;typo&lt;/strong&gt; had been sitting in an active message rule for months: a small formatting error in the review request that fires after every checkout, the final touchpoint of every stay. Easy to fix, impossible to spot without going line by line through every rule, which no one does.&lt;/p&gt;

&lt;p&gt;The agent also caught a &lt;strong&gt;time-sensitive review window.&lt;/strong&gt; A recent guest had smoked inside and brought unregistered visitors, and my window to review them was about to close. Once that window shuts, future hosts get no warning, so that was the catch that mattered. It also surfaced a backlog of guest reviews I'd never responded to — lower stakes, but review responses are public and responsiveness factors into Airbnb's ranking, so the backlog went on the list rather than in the trash.&lt;/p&gt;

&lt;p&gt;Parking was in this bucket too. It's the single most common question guests ask, and the Knowledge Hub had no car parking entry at all. The Parking section existed, but it only covered bicycles.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Features I wasn't using but should be
&lt;/h3&gt;

&lt;p&gt;Some of what it surfaced wasn't broken, just sitting there unused. The Knowledge Hub was thin and partly wrong; now it's corrected and actually filled in. Tasks was completely empty, so I registered myself as a teammate and set up text alerts for upcoming cleanings. Right now that's redundant since it's just me. But the structure exists now, and the day I bring on a housekeeper, I swap in their number and nothing else changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I actually use it
&lt;/h2&gt;

&lt;p&gt;The dramatic catches are one thing; the reason it's still connected is the boring daily stuff.&lt;/p&gt;

&lt;p&gt;The first is &lt;strong&gt;complaint clustering&lt;/strong&gt;, because it goes straight at my biggest pain point: a new guest arrives and starts complaining, and in the moment it's hard to tell a one-off from a pattern. I had the agent group every review across Airbnb and Booking.com by theme. The runaway top three:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Bedroom doors didn't lock from the inside&lt;/strong&gt; — four guests, both rooms. The clear #1. Guests wanted keyed locks, not the privacy locks the rooms came with. The main entrance already had a Schlage deadbolt; the fix was recently replacing the bedroom privacy locks with keyed door knobs — and the clustering confirmed the complaints dropped off after the change.&lt;/li&gt;
&lt;li&gt;Location and transit — guests underestimating how far things were.&lt;/li&gt;
&lt;li&gt;Surprise at the shared-home setup — a listing-clarity issue, not a property one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The clustering also corrected an assumption I'd been carrying: I was sure Booking.com guests complained more than Airbnb guests. Totally wrong. The data showed the gap was a scoring-norm artifact, not a real satisfaction difference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9m0us7rkdc8ad449fto2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9m0us7rkdc8ad449fto2.png" alt="Review dashboard across Airbnb and Booking.com" width="800" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second is the &lt;strong&gt;weekly turnover outlook.&lt;/strong&gt; One question and I get a clean in-chat calendar of which days have a checkout and a check-in stacked up — the days I can't schedule anything else. Combined with the task alerts, I stopped double-booking myself — committing to a day in the office only to find out it was also a turnover day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvykbmni88srxdpyfyzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvykbmni88srxdpyfyzx.png" alt="In-chat turnover calendar" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives and rough edges
&lt;/h2&gt;

&lt;p&gt;If I only told you the good parts you shouldn't believe me. The honest column:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It flagged a problem that wasn't one.&lt;/strong&gt; Reviewing my message templates, the agent called out messages where the &lt;code&gt;%smartlock_code%&lt;/code&gt; shortcode hadn't resolved. The real explanation: those guests had already checked out, and their codes had expired with the reservation. The lesson is that the agent doesn't automatically know the lifecycle of the data it's reading — what looks broken on a past reservation can be perfectly normal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writes can fail silently.&lt;/strong&gt; Adding an item to a Knowledge Hub that had never been initialized succeeded with no error at all — but nothing actually appeared until I generated the hub in the dashboard. A success response that doesn't result in the thing existing is the kind of surprise that bites you later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The obvious next step is automating the loop I ran by hand these two weeks — issue comes in, gets detected, a fix gets drafted for me to approve. I'm still working out exactly what that should look like, so I'll leave it there for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that stuck with me
&lt;/h2&gt;

&lt;p&gt;What surprised me wasn't any one finding — it was the &lt;strong&gt;speed of the audit.&lt;/strong&gt; Catching a months-old typo without reading every message rule by hand. Clustering a year of reviews into three themes in seconds. Fewer complaints, less of my time spent firefighting, more of it back. The most useful thing an agent did here wasn't anything clever — it was simply &lt;em&gt;looking&lt;/em&gt;, systematically, at data I already had, and sorting it into the four things that actually matter: liabilities, &lt;strong&gt;revenue opportunities&lt;/strong&gt;, the things that had quietly snuck past me, and the features I should have been using all along.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with the official Hospitable MCP server connected to Claude. Real two-room property, real findings. Guest details kept deliberately vague.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>airbnb</category>
      <category>hospitable</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Futbol Report — building a multi-model LLM comparison on AWS Lambda</title>
      <dc:creator>Samir Yuja</dc:creator>
      <pubDate>Mon, 01 Jun 2026 13:17:21 +0000</pubDate>
      <link>https://dev.to/syuja/futbol-report-building-a-multi-model-llm-comparison-on-aws-lambda-1n1l</link>
      <guid>https://dev.to/syuja/futbol-report-building-a-multi-model-llm-comparison-on-aws-lambda-1n1l</guid>
      <description>&lt;p&gt;A few months ago I set up a soccer-digest bot that sends me a Telegram message every few days with fixtures, results, transfer news, and manager changes. It started as a &lt;code&gt;tmux&lt;/code&gt; session running Claude Code on a small Linux server, fired by a cron job. It worked. It also went down occasionally, and I had no good way to inspect what it was producing.&lt;/p&gt;

&lt;p&gt;I wanted to do two things at once: make it more reliable, and turn it into something more interesting than "one bot sending one report." The result is &lt;a href="https://samiryuja.dev/projects/futbol-report" rel="noopener noreferrer"&gt;Futbol Report&lt;/a&gt; — a scheduled job on AWS that sends the &lt;em&gt;same prompt and search context&lt;/em&gt; to four different language models (Claude, Kimi, Qwen, Gemma) every three days, stores all four reports in Redis, and renders them side-by-side on this site with live voting.&lt;/p&gt;

&lt;p&gt;This post is about how it works, what I learned from running it, and the deployment war stories — the bits that are usually edited out of "here's my side project" writeups.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the comparison shows
&lt;/h2&gt;

&lt;p&gt;Same input, four models, no editing. The page has a dropdown to switch between past runs and a vote button under each report. Vote tallies persist.&lt;/p&gt;

&lt;p&gt;The point isn't to crown a "best" model. It's to make differences visible on a &lt;em&gt;real, repeated task&lt;/em&gt;. Placed next to each other, the models diverge in ways that are easy to see — how faithfully they follow the requested format, what they choose to include or filter out, and how long their reports run.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the pipeline works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EventBridge Scheduler  (every 3 days)
        ↓
   AWS Lambda  (Python 3.12)
        ├── Brave Search    (~13 queries: fixtures, results, transfers, manager changes)
        └── OpenRouter      (same context → 4 models)
        ↓
   Redis  (Vercel)
        ↓
   Next.js page  (server-rendered comparison + voting)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every three days EventBridge fires a Lambda function. The Lambda calls Brave Search with about a dozen queries, then sends the same compiled context to four models through OpenRouter. Each model's report goes into Redis under a timestamped key. The Next.js site, deployed on Vercel, reads from the same Redis and renders the comparison page.&lt;/p&gt;

&lt;p&gt;A few decisions worth surfacing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenRouter as the inference layer.&lt;/strong&gt; One API instead of four, and adding or swapping a model is a one-line change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-rendered comparison page.&lt;/strong&gt; The data only changes every few days, so there's no point fetching it from the browser. The server reads Redis and sends back the finished page. Only the vote button runs in the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redis with a 90-day TTL on report keys.&lt;/strong&gt; Redis fit the access pattern — small payloads (a few KB per report) and pure key lookups by timestamp, no queries. The TTL means old reports expire automatically; votes and the run index have no TTL, so voting history is never evicted even if memory fills.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned from running it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Search results aren't deterministic.&lt;/strong&gt; Running the same query thirty minutes apart returns different result sets — that's just how live ranking works. So context has to be held fixed &lt;em&gt;within&lt;/em&gt; a run for the comparison to be fair (one Brave call feeds all four models).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A simple anti-hallucination clause worked across all four models.&lt;/strong&gt; After the first run hallucinated fixtures, I added &lt;em&gt;"use ONLY facts present in the provided search results"&lt;/em&gt; to the prompt. None of the four models invented data after that — same clause, same effect, across four different labs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Models filter context differently.&lt;/strong&gt; One run, Brave's results included an out-of-scope Indian Super League match. Three models filtered it out; one led its report with it. Same prompt, same data, different prioritization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Output length varies wildly with model size.&lt;/strong&gt; Claude and Kimi used most of the available context. Gemma — by far the cheapest model — collapsed the same input into one-line summaries. Cost and level of detail are correlated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Format adherence varies too.&lt;/strong&gt; Claude followed the prompt's structure most faithfully. Gemma dropped most of it. Qwen and Kimi were in between.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. The pipeline survived the off-season without code changes.&lt;/strong&gt; When Serie A ended, fixture queries returned nothing useful. I added two new search categories (transfers, manager changes) and one line to the prompt — &lt;em&gt;"if fixtures are sparse, lead with transfer news."&lt;/em&gt; Reports stayed substantive: Allegri sacking at Milan, Guardiola leaving City, World Cup buildup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deployment war stories
&lt;/h2&gt;

&lt;p&gt;The interesting part of moving the generator to Lambda was the several hours of debugging in the middle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python runtime mismatch.&lt;/strong&gt; AWS defaulted the new Lambda to Python 3.14. My deployment package was built for 3.12. The error didn't say "wrong Python version" — it said &lt;code&gt;No module named 'pydantic_core._pydantic_core'&lt;/code&gt;, because the compiled C extension is a &lt;code&gt;cpython-312&lt;/code&gt; &lt;code&gt;.so&lt;/code&gt; that won't load under 3.14. Fix: match the runtime to the build target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mac vs Linux binaries.&lt;/strong&gt; Even after pinning the Python version, &lt;code&gt;pydantic-core&lt;/code&gt; kept loading the macOS binary into my zip. I was using &lt;code&gt;uv&lt;/code&gt; for packaging — &lt;code&gt;uv pip install --only-binary&lt;/code&gt; is supposed to fetch a Linux wheel but reliably didn't here. Switching to vanilla &lt;code&gt;python3 -m pip install --platform manylinux2014_x86_64 --only-binary=:all:&lt;/code&gt; finally pulled the right artifact. The newer tool I trusted was the problem; the older boring tool worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional imports for dual environments.&lt;/strong&gt; &lt;code&gt;python-dotenv&lt;/code&gt; is great locally — reads &lt;code&gt;.env&lt;/code&gt;, populates &lt;code&gt;os.environ&lt;/code&gt;. On Lambda, environment variables come from AWS directly, and &lt;code&gt;python-dotenv&lt;/code&gt; is just dead weight that doesn't ship in the runtime. Wrap the import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
    &lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# dotenv not needed on Lambda — env vars are set directly
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same code works in both environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default timeout.&lt;/strong&gt; Lambda's default timeout is 3 seconds. My pipeline needs about 4 minutes — 13 sequential Brave calls plus four sequential model generations. Bump it to 900 seconds (Lambda's max).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reaching for the CLI over the browser.&lt;/strong&gt; I couldn't get the AWS console's "Upload .zip file" button to reliably refresh the deployed code — the SHA-256 hash kept matching the previous upload even after I selected a new file. It probably works fine most of the time; in my case &lt;code&gt;aws lambda update-function-code&lt;/code&gt; from the CLI was faster and easier, and it's the path I use now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets are easy to leak when copy-pasting console output.&lt;/strong&gt; I rotated three API keys during this project — twice because I pasted command output that included environment variables. Painful in proportion to how preventable it is. Wiring &lt;code&gt;gitleaks&lt;/code&gt; into a GitHub Actions workflow is next on the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;In priority order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Telegram delivery from the Lambda&lt;/strong&gt; — restore the original use case so the digest pings me when a new run finishes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI security scanning&lt;/strong&gt; — &lt;code&gt;gitleaks&lt;/code&gt; for secrets and &lt;code&gt;osv-scanner&lt;/code&gt; for dependency CVEs, both wired into a GitHub Actions workflow on each repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelize the model calls&lt;/strong&gt; — sequential right now (~4 minutes); concurrent would cut runtime by ~75%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full page content for grounding&lt;/strong&gt; — Brave returns 1-2 sentence snippets, so thin context yields thin reports. Firecrawl or a readability extractor would fix this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side vote dedup&lt;/strong&gt; — votes are deduped per-browser via &lt;code&gt;localStorage&lt;/code&gt;; clearing storage or going incognito gets around it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://samiryuja.dev/projects/futbol-report" rel="noopener noreferrer"&gt;The live comparison&lt;/a&gt; — latest run, history, voting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/syuja/futbol-report" rel="noopener noreferrer"&gt;Generator repo&lt;/a&gt; — Python pipeline, Lambda packaging, EventBridge schedule&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/syuja/samiryuja.dev" rel="noopener noreferrer"&gt;Site repo&lt;/a&gt; — the Next.js side, comparison page, vote API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;To Ryan — for letting me run the original bot on his machine over Tailscale, and for the steady stream of articles and ideas that shaped a lot of the thinking behind this project.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>serverless</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
