<?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: Alan Maizon</title>
    <description>The latest articles on DEV Community by Alan Maizon (@alanmaizon).</description>
    <link>https://dev.to/alanmaizon</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%2F1466829%2F6e863d71-3b45-4c83-b0d6-ea868450c8db.png</url>
      <title>DEV Community: Alan Maizon</title>
      <link>https://dev.to/alanmaizon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alanmaizon"/>
    <language>en</language>
    <item>
      <title>Beat the Oracle</title>
      <dc:creator>Alan Maizon</dc:creator>
      <pubDate>Sat, 06 Jun 2026 20:47:36 +0000</pubDate>
      <link>https://dev.to/alanmaizon/beat-the-oracle-nbp</link>
      <guid>https://dev.to/alanmaizon/beat-the-oracle-nbp</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/june-game-jam-2026-06-03"&gt;June Solstice Game Jam&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Beat the Oracle&lt;/strong&gt; is a free-to-play World Cup 2026 prediction game where you go head-to-head with an algorithm.&lt;/p&gt;

&lt;p&gt;An "Oracle" — a prediction engine built on 125 years of international football results — has already simulated the entire tournament. It rates all 48 teams, decides every match with a single probability formula, and runs the bracket thousands of times to estimate who lifts the trophy. It always backs the favourite.&lt;/p&gt;

&lt;p&gt;Your job is to find where it's wrong. You predict the real 2026 group stage and knockouts yourself. Match the Oracle and you keep pace — but call an upset it never saw, and reality obeys you instead of the math, and you bank &lt;strong&gt;Defiance&lt;/strong&gt; points the machine can't earn. The whole game is a duel between human intuition and cold expected value.&lt;/p&gt;

&lt;p&gt;It connects to the jam through the World Cup itself — the June celebration the challenge calls out, running live (June 11–July 19) right through the jam window. The tournament reaches its turning point at the height of summer, and the game is about the moment an underdog refuses the position the numbers assigned it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployed Live to Google Cloud Run
&lt;/h2&gt;


&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://beat-the-oracle-frontend-490238296903.us-central1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Code in GitHub
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/alanmaizon/beat-the-oracle" rel="noopener noreferrer"&gt;https://github.com/alanmaizon/beat-the-oracle&lt;/a&gt;                                                                   &lt;/p&gt;

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

&lt;p&gt;This started as my capstone for the Google Data Analytics certificate, then grew into a game.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data.&lt;/strong&gt; I worked from four datasets: historical World Cups, every World Cup match, player records, and an Elo rating series spanning 1901–2026. I cleaned and explored them in a Jupyter notebook (pandas) — and hit an interesting wall. The Elo dataset only contained the 48 teams that qualified for 2026, which made it useless for the historical question I'd started with (it silently dropped half of all past participants, including four-time champion Italy). But that limitation was the unlock: a dataset of exactly the 48 qualifiers is &lt;em&gt;perfect&lt;/em&gt; for a game about &lt;em&gt;this&lt;/em&gt; tournament. The "bias" became the feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The engine.&lt;/strong&gt; Every team carries an Elo rating. One match is one formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;P(A beats B) = 1 / (1 + 10^((Eloᴮ − Eloᴬ) / 400))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Hosts get a small home boost. From that primitive, three layers stack up: group tables come from a round-robin simulation with a draw-and-goals model; the knockout uses single-draw resolution; and pre-tournament title odds come from a Monte Carlo over the full 48-team bracket (the real format — 12 winners, 12 runners-up, 8 best third-place teams into a Round of 32). The whole thing runs client-side in plain JS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest simplification.&lt;/strong&gt; Knockout matchups are seeded by Elo for clean favourite-vs-underdog drama. The real 2026 bracket is seeded by group position via a fixed placement map. I traded a little fidelity for playability, and say so in-game.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production.&lt;/strong&gt; The deployed version runs on Firebase — Hosting, Google sign-in via Firebase Auth, and Firestore to store each player's locked prediction. Predictions and the Oracle both freeze at first kickoff so everyone forecasts before reality. A scheduled Cloud Function then polls live match results, maps them onto the bracket, and re-scores every player against the Oracle automatically — feeding a public leaderboard that fills in as the real tournament unfolds. I scaffolded and deployed it with the &lt;strong&gt;Antigravity CLI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Google AI Usage.&lt;/strong&gt; Two ways. First, the build itself: I used the &lt;strong&gt;Antigravity CLI&lt;/strong&gt; (Gemini 3.5 Flash) to scaffold, refactor, and deploy the Firebase app — auth, Firestore, scheduled scoring functions, and hosting. Second, embedded in the product: each team's &lt;strong&gt;scouting report&lt;/strong&gt; is generated by the &lt;strong&gt;Gemini API&lt;/strong&gt; (gemini-3.5-flash) through a Cloud Function, with the key kept server-side and responses cached to keep it free. Gemini is both how it was made and a live feature inside it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Ode to Alan Turing.&lt;/strong&gt; The Oracle &lt;em&gt;is&lt;/em&gt; the tribute. It's a transparent probability engine — Elo expectation plus Monte Carlo resolution — and the game frames you as the human trying to out-reason the machine. That's not a costume on top of the mechanics; it's the core loop. Turing, born this June in 1912, used sequential Bayesian methods at Bletchley Park to turn uncertainty into decisions under time pressure. &lt;em&gt;Beat the Oracle&lt;/em&gt; is a small, playable argument about the same thing: where human judgement still beats the expected value, and where it doesn't.&lt;/p&gt;

&lt;p&gt;Thanks for reading — now go beat the Oracle.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gamechallenge</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Turning a wedding gift list into a charity-giving platform</title>
      <dc:creator>Alan Maizon</dc:creator>
      <pubDate>Sat, 06 Jun 2026 12:53:08 +0000</pubDate>
      <link>https://dev.to/alanmaizon/turning-a-wedding-gift-list-into-a-charity-giving-platform-kh5</link>
      <guid>https://dev.to/alanmaizon/turning-a-wedding-gift-list-into-a-charity-giving-platform-kh5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;A real wedding asked guests for charity donations instead of gifts. That single-couple prototype became &lt;em&gt;Love That Gives Back&lt;/em&gt;: a multi-tenant platform where anyone can spin up a registry for any celebration, guests donate to &lt;strong&gt;verified&lt;/strong&gt; charities through Stripe, leave a moderated public message, and every euro is tracked on an append-only ledger. The original wedding is the live flagship campaign.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it started
&lt;/h2&gt;

&lt;p&gt;A year ago, instead of a traditional gift list, a couple — Anna &amp;amp; Alan — asked their wedding guests to donate to three charities that mattered to them: &lt;strong&gt;Mary's Meals&lt;/strong&gt;, &lt;strong&gt;Operation Smile&lt;/strong&gt;, and &lt;strong&gt;Xingu Vivo&lt;/strong&gt; (a grassroots Amazon-basin movement). Each choice had a story: a visit to the Scottish Highlands where Mary's Meals began; a brother born with a cleft palate; a wedding ring "paid for" with a donation instead of cash.&lt;/p&gt;

&lt;p&gt;The v0 site did the job, but it was held together with tape:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guests transferred money manually over Revolut/bank, and an admin clicked &lt;strong&gt;"confirm"&lt;/strong&gt; by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bank details were stored in plaintext&lt;/strong&gt; on a model.&lt;/li&gt;
&lt;li&gt;The guestbook was a static CSV.&lt;/li&gt;
&lt;li&gt;Charts were server-rendered matplotlib PNGs dragging in ~8 heavy dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It worked for &lt;em&gt;one&lt;/em&gt; couple. The interesting question was: what if &lt;strong&gt;anyone&lt;/strong&gt; could do this — for a birthday, a memorial, any celebration — without the manual money handling and without a platform ever touching a bank number?&lt;/p&gt;

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

&lt;p&gt;A &lt;strong&gt;modular monolith&lt;/strong&gt; that treats money with the seriousness it deserves.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;v0 prototype&lt;/th&gt;
&lt;th&gt;v2 platform&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tenancy&lt;/td&gt;
&lt;td&gt;one hardcoded couple&lt;/td&gt;
&lt;td&gt;many &lt;strong&gt;Campaigns&lt;/strong&gt; + &lt;strong&gt;Charities&lt;/strong&gt;, row-scoped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money&lt;/td&gt;
&lt;td&gt;manual transfer, admin clicks "confirm"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Stripe Connect&lt;/strong&gt; destination charges → verified charity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bank data&lt;/td&gt;
&lt;td&gt;plaintext &lt;code&gt;account_number&lt;/code&gt; on a model&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;never stored&lt;/strong&gt; — a Stripe account id + capability flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split&lt;/td&gt;
&lt;td&gt;50% couple / 50% charity&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% to charity&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source of truth&lt;/td&gt;
&lt;td&gt;the &lt;code&gt;Donation&lt;/code&gt; row&lt;/td&gt;
&lt;td&gt;append-only &lt;strong&gt;&lt;code&gt;LedgerEntry&lt;/code&gt;&lt;/strong&gt;, reconcilable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guestbook&lt;/td&gt;
&lt;td&gt;static CSV, unmoderated&lt;/td&gt;
&lt;td&gt;moderated &lt;code&gt;Message&lt;/code&gt; API (approved-only public)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;signature-verified, &lt;strong&gt;idempotent&lt;/strong&gt; (deduped by event id)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PII&lt;/td&gt;
&lt;td&gt;donor email in public responses&lt;/td&gt;
&lt;td&gt;stripped for non-staff; JSON-only API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Django 5 + DRF (Postgres in prod, SQLite locally), React 19 + Vite on the front, Stripe-hosted Checkout + Connect for payments, all deployable to a deliberately lean AWS footprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The invariants I refused to break
&lt;/h2&gt;

&lt;p&gt;Money software is mostly about what you &lt;em&gt;won't&lt;/em&gt; let happen. I wrote these down on day one and built guardrails around them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Money can only ever reach a &lt;strong&gt;verified&lt;/strong&gt; &lt;code&gt;Charity&lt;/code&gt;. No payouts to individuals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never&lt;/strong&gt; store raw bank/card data — payout identity is a Stripe account id, full stop.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LedgerEntry&lt;/code&gt; is the source of truth for money — &lt;strong&gt;append-only&lt;/strong&gt;, reconciled daily against Stripe. You never mutate money state by editing a &lt;code&gt;Donation&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;All public user content is &lt;strong&gt;moderatable&lt;/strong&gt; and consent-gated.&lt;/li&gt;
&lt;li&gt;Row-level multitenancy enforced in DRF &lt;code&gt;get_queryset&lt;/code&gt;, not just in serializers.&lt;/li&gt;
&lt;li&gt;No PII (donor email) in public API responses.&lt;/li&gt;
&lt;li&gt;PCI: stay &lt;strong&gt;SAQ-A&lt;/strong&gt; — Stripe-hosted Checkout only; card data never touches the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency&lt;/strong&gt; on every payment op; verify &lt;em&gt;and&lt;/em&gt; dedupe webhooks by event id.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The one that shaped the most code is #3. A donation isn't "real" because a row says &lt;code&gt;confirmed&lt;/code&gt; — it's real because a signed Stripe webhook arrived, was deduped, and wrote a ledger entry inside the same transaction as an &lt;code&gt;OutboxEvent&lt;/code&gt;:&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="c1"&gt;# same DB transaction: ledger + outbox, drained later for receipts/emails
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;LedgerEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;donation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
    &lt;span class="n"&gt;OutboxEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;donation.confirmed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{...})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A worker (&lt;code&gt;manage.py drain_outbox&lt;/code&gt;) drains the outbox to send receipts and thank-you emails. If email delivery is down, the money record is still correct and the side effects retry — no lost receipts, no double charges.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hardest lesson: don't test someone else's UI
&lt;/h2&gt;

&lt;p&gt;My end-to-end test originally drove &lt;strong&gt;Stripe's hosted Checkout page&lt;/strong&gt; in CI with Playwright — typing the &lt;code&gt;4242…&lt;/code&gt; test card into Stripe's iframes. It was chronically flaky, and eventually I understood &lt;em&gt;why&lt;/em&gt;: Stripe flags headless/datacenter traffic with an agent-identity challenge and never completes the charge. Automating that page in CI tests &lt;strong&gt;Stripe&lt;/strong&gt;, not my app — and it's PCI SAQ-A territory I don't own.&lt;/p&gt;

&lt;p&gt;So I decoupled it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI test&lt;/strong&gt; fills my donate form, asserts the backend created a valid &lt;code&gt;checkout.stripe.com&lt;/code&gt; session (proving params + idempotency), then confirms the donation through the &lt;strong&gt;real webhook code path&lt;/strong&gt; via a &lt;code&gt;DEBUG&lt;/code&gt;-and-&lt;code&gt;E2E_TEST_HOOKS&lt;/code&gt;-gated endpoint, and finishes the browser flow: pending guestbook → host approves → public.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live UI walk&lt;/strong&gt; (the full card entry) stays a local-only &lt;code&gt;@live&lt;/code&gt; test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CI went from a multi-minute flake-fest to a deterministic ~4s run. The principle generalizes: &lt;strong&gt;at your trust boundary, assert the contract you control, not the third party's UI.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping it without lighting money on fire
&lt;/h2&gt;

&lt;p&gt;The deploy target is intentionally cheap. Using the AWS pricing tooling against my Terraform, the whole stack lands around &lt;strong&gt;$50–60/month&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ECS Fargate (0.25 vCPU / 0.5 GB) &lt;strong&gt;in public subnets — no NAT Gateway&lt;/strong&gt; (that alone saves ~$32/mo)&lt;/li&gt;
&lt;li&gt;RDS &lt;code&gt;db.t4g.micro&lt;/code&gt; PostgreSQL, Single-AZ ($0.017/hr)&lt;/li&gt;
&lt;li&gt;One ALB, S3 + CloudFront for the SPA, SSM Parameter Store for secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The budget guardrails are explicit non-goals in the repo: no NAT, no Aurora Serverless v2, no second ALB. I wrapped the whole go-live into one idempotent script (&lt;code&gt;scripts/phase1-golive.sh&lt;/code&gt;) with subcommands — &lt;code&gt;infra&lt;/code&gt;, &lt;code&gt;acm&lt;/code&gt;, &lt;code&gt;ssm&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;migrate&lt;/code&gt;, &lt;code&gt;frontend&lt;/code&gt;, &lt;code&gt;webhook&lt;/code&gt;, &lt;code&gt;verify&lt;/code&gt; — that read straight from Terraform outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;

&lt;p&gt;With the flagship seeded, the "real money plumbing" is visible end to end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Home&lt;/strong&gt; — hero + a guestbook carousel of 27 &lt;em&gt;real&lt;/em&gt; guest messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt; — Money raised, per-charity bars, goal progress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin&lt;/strong&gt; — Donations, &lt;strong&gt;read-only Ledger entries&lt;/strong&gt;, Webhook events. (These two tables are the whole thesis in one screen.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd do next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Self-serve &lt;strong&gt;cover image upload&lt;/strong&gt; and &lt;strong&gt;co-host invites&lt;/strong&gt; in the registry wizard.&lt;/li&gt;
&lt;li&gt;Move the platform-admin &lt;strong&gt;charity verification queue&lt;/strong&gt; out of Django admin and into the SPA.&lt;/li&gt;
&lt;li&gt;Per-campaign analytics for hosts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it / read it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/alanmaizon/love" rel="noopener noreferrer"&gt;https://github.com/alanmaizon/love&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="http://www.lovethatgivesback.com" rel="noopener noreferrer"&gt;www.lovethatgivesback.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you take one thing from this: when you build on top of a payment processor, let &lt;em&gt;their&lt;/em&gt; hosted surface own the card data and the compliance — and make your own system provably correct with an append-only ledger, idempotent webhooks, and a transactional outbox.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
