<?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: DEVMO</title>
    <description>The latest articles on DEV Community by DEVMO (@mohamedwael201193).</description>
    <link>https://dev.to/mohamedwael201193</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%2F4203833%2Fbaa9665f-d4f9-43de-b327-49f32e5d6e36.jpg</url>
      <title>DEV Community: DEVMO</title>
      <link>https://dev.to/mohamedwael201193</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohamedwael201193"/>
    <language>en</language>
    <item>
      <title>One wrong day at the EU border. So I made the European Commission grade my tests.</title>
      <dc:creator>DEVMO</dc:creator>
      <pubDate>Sun, 25 Oct 2026 03:44:31 +0000</pubDate>
      <link>https://dev.to/mohamedwael201193/one-wrong-day-at-the-eu-border-so-i-made-the-european-commission-grade-my-tests-4edi</link>
      <guid>https://dev.to/mohamedwael201193/one-wrong-day-at-the-eu-border-so-i-made-the-european-commission-grade-my-tests-4edi</guid>
      <description>&lt;p&gt;&lt;em&gt;By devmo · Built for the #TestSprite Hackathon Season 4 · Live: &lt;a href="https://ninety-coral.vercel.app" rel="noopener noreferrer"&gt;ninety-coral.vercel.app&lt;/a&gt; · Code: &lt;a href="https://github.com/mohamedwael201193/ninety" rel="noopener noreferrer"&gt;github.com/mohamedwael201193/ninety&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One wrong day
&lt;/h2&gt;

&lt;p&gt;A traveller is leaving Lisbon after a long summer in Europe. She has counted carefully: 89 days inside the Schengen area in the last 180. One day of margin.&lt;/p&gt;

&lt;p&gt;Since 10 April 2026, the EU's Entry/Exit System (EES) records every entry and every exit at the border. That record is the one that counts. One missed scan, one exit logged on the wrong day, one stay split in two, and her 89 becomes 91. 91 is an overstay on file.&lt;/p&gt;

&lt;p&gt;The law gives her a way back: Article 52 of Regulation (EU) 2017/2226 lets her ask for her EES data to be corrected. She just has to know the record is wrong, and prove it.&lt;/p&gt;

&lt;p&gt;That is &lt;strong&gt;Ninety&lt;/strong&gt;. It counts your Schengen 90/180 days, compares them with your EES record, and when the border's count is wrong, it drafts the Article 52 correction letter for you. Every rule on &lt;a href="https://ninety-coral.vercel.app/proof" rel="noopener noreferrer"&gt;/proof&lt;/a&gt; cites its article (Reg. 2016/399, Reg. 2017/2226).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdpqyuk3wg015m41cqhu0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdpqyuk3wg015m41cqhu0.png" alt="Ninety home page with the verified numbers" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why one day is everything
&lt;/h2&gt;

&lt;p&gt;A calculator that is off by one day is worse than no calculator, because people trust it. So the real product here is not the UI. It is the proof that the number on the screen is the right one, for every traveller, on every pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: my engine never grades itself
&lt;/h2&gt;

&lt;p&gt;No expected value in Ninety's tests comes from Ninety's own engine. They come from outside:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The European Commission's own short-stay calculator.&lt;/strong&gt; 14 public scenarios match it &lt;strong&gt;14/14&lt;/strong&gt;, live at &lt;a href="https://ninety-coral.vercel.app/parity" rel="noopener noreferrer"&gt;/parity&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Commission's calculator script, run directly.&lt;/strong&gt; 400 inputs compared field by field: &lt;strong&gt;399/400&lt;/strong&gt; match on every printed field, and the one difference is documented line by line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5,000&lt;/strong&gt; fuzzed travel histories on every PR, &lt;strong&gt;20,000&lt;/strong&gt; every night.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An independent reference implementation&lt;/strong&gt;, written straight from Art. 6(1) of Reg. 2016/399: &lt;strong&gt;1,200&lt;/strong&gt; cases per PR, &lt;strong&gt;20,000&lt;/strong&gt; nightly, half of them on the edges (day 180 and 181, 29 February, the year change, the clock changes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;408&lt;/strong&gt; Vitest tests and a &lt;strong&gt;94.47%&lt;/strong&gt; mutation score.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The European Commission grades my tests. TestSprite proves a real person clicking through the real app sees that same right number.&lt;/p&gt;

&lt;h2&gt;
  
  
  How TestSprite is wired in
&lt;/h2&gt;

&lt;p&gt;Every pull request gets a Vercel preview, and TestSprite tests it twice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;TestSprite V3 (GitHub App): 54 end-to-end UI tests&lt;/strong&gt;, each asserting exact values. One test checks all 14 parity counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestSprite backend (CLI in GitHub Actions): 30 API + MCP tests&lt;/strong&gt; across the 3 API endpoints, which are also 3 MCP tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;84 TestSprite tests on every PR.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both sit inside &lt;strong&gt;7 required checks&lt;/strong&gt; on &lt;code&gt;main&lt;/code&gt;. No bypass. Admins included. The 7th is the &lt;strong&gt;TestSprite N/N gate&lt;/strong&gt;: it reads the real counts from both suites on the same commit and accepts nothing short of N/N.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ivqz1dawo0fzk3lmxgs.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ivqz1dawo0fzk3lmxgs.png" alt="Branch protection: 7 required checks, admins included" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It earned its place on the final PR. &lt;a href="https://github.com/mohamedwael201193/ninety/pull/71" rel="noopener noreferrer"&gt;#71&lt;/a&gt;'s first commit read &lt;strong&gt;52/54&lt;/strong&gt;, and the gate held the merge. The next commit read &lt;strong&gt;54/54 + 30/30&lt;/strong&gt;, the gate went green, and it merged.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3d6uft3ixx6ivp0eoc04.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3d6uft3ixx6ivp0eoc04.png" alt="PR #71: the N/N gate holds the merge at 52/54" width="800" height="464"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxfgigc2a98y1752h2cj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxfgigc2a98y1752h2cj.png" alt="PR #71: 54/54 + 30/30, every check green" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Triage on red.&lt;/strong&gt; A red backend run posts a comment on the PR: each failing test, the likely cause, the run id, and the exact command to rerun just those tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nightly on production.&lt;/strong&gt; Every night 5 TestSprite tests run against the live site. On a red night the workflow opened &lt;a href="https://github.com/mohamedwael201193/ninety/issues/66" rel="noopener noreferrer"&gt;issue #66&lt;/a&gt; by itself, and &lt;a href="https://github.com/mohamedwael201193/ninety/pull/67" rel="noopener noreferrer"&gt;PR #67&lt;/a&gt; closed it. The loop runs even while I sleep.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F851ontff3nmhv4qku422.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F851ontff3nmhv4qku422.png" alt="The N/N gate explained on the live /proof page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop: 14 PRs went red, then green
&lt;/h2&gt;

&lt;p&gt;I didn't write the loop story by hand. A script reads the GitHub API (every merged PR, every commit, every TestSprite status) and generates &lt;a href="https://github.com/mohamedwael201193/ninety/blob/main/LOOP-ITERATIONS.md" rel="noopener noreferrer"&gt;LOOP-ITERATIONS.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Across &lt;strong&gt;64 merged PRs&lt;/strong&gt; and &lt;strong&gt;99 TestSprite V3 results on 60 PRs&lt;/strong&gt; (as of #70), &lt;strong&gt;14 PRs went red on TestSprite, then green&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PR&lt;/th&gt;
&lt;th&gt;Red run&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/8" rel="noopener noreferrer"&gt;#8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;9/11&lt;/td&gt;
&lt;td&gt;App defect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/14" rel="noopener noreferrer"&gt;#14&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;12/14&lt;/td&gt;
&lt;td&gt;App defect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/18" rel="noopener noreferrer"&gt;#18&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13/14&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/22" rel="noopener noreferrer"&gt;#22&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13/14&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/32" rel="noopener noreferrer"&gt;#32&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;15/17&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/36" rel="noopener noreferrer"&gt;#36&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13/14&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/39" rel="noopener noreferrer"&gt;#39&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;14/15&lt;/td&gt;
&lt;td&gt;App defect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/41" rel="noopener noreferrer"&gt;#41&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;14/15, then 16/17&lt;/td&gt;
&lt;td&gt;App defect (2 bugs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/43" rel="noopener noreferrer"&gt;#43&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;16/17&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/55" rel="noopener noreferrer"&gt;#55&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;34/35&lt;/td&gt;
&lt;td&gt;App defect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/57" rel="noopener noreferrer"&gt;#57&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;34/43 → 42/43 → 43/43&lt;/td&gt;
&lt;td&gt;App defect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/60" rel="noopener noreferrer"&gt;#60&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;42/43&lt;/td&gt;
&lt;td&gt;UX loading state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/68" rel="noopener noreferrer"&gt;#68&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;52/54&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mohamedwael201193/ninety/pull/69" rel="noopener noreferrer"&gt;#69&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;52/54&lt;/td&gt;
&lt;td&gt;Test plan / CI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;7 real bugs, 1 UX loading state, 7 sharper test plans. 0 planted.&lt;/strong&gt; Every one is logged in &lt;a href="https://github.com/mohamedwael201193/ninety/blob/main/docs/DEFECTS.md" rel="noopener noreferrer"&gt;docs/DEFECTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 7 real bugs TestSprite caught
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;#8:&lt;/strong&gt; the parity table silently dropped a scenario.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#14:&lt;/strong&gt; the planner ignored a stay still in progress, the exact error that becomes an overstay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#39:&lt;/strong&gt; a loaded scenario was lost between pages, so the comparison and the letter used the wrong history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#41 (a):&lt;/strong&gt; &lt;code&gt;?fresh=1&lt;/code&gt; wiped saved trips again on reload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#41 (b):&lt;/strong&gt; the evidence table's difference cell read as blank.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#55:&lt;/strong&gt; Escape left keyboard focus on the page body instead of "Add trip".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#57:&lt;/strong&gt; /parity could not confirm the Commission's calculator from production. It now runs a stored, hash-checked copy of the Commission script.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  PR #57: red first, on purpose
&lt;/h2&gt;

&lt;p&gt;Code review found 8 bugs. I wrote 8 TestSprite plans for them (TS45–TS52) and pushed them &lt;strong&gt;before&lt;/strong&gt; the fixes. TestSprite read &lt;strong&gt;34/43&lt;/strong&gt;. The fixes took it to &lt;strong&gt;42/43&lt;/strong&gt;, and that last red exposed the /parity bug above. Fixed: &lt;strong&gt;43/43&lt;/strong&gt;. Tests first, real bugs found, merged green.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl70l7uhzm9p954jm7vph.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl70l7uhzm9p954jm7vph.png" alt="TestSprite V3: 54/54 on PR #70" width="800" height="464"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fabwppp0i8iyneew7uf01.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fabwppp0i8iyneew7uf01.png" alt="GitHub Actions: backend suite and N/N gate on PR #70" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why TestSprite made this possible
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It tests what users actually see.&lt;/strong&gt; Unit tests prove arithmetic. TestSprite proves the right number reaches the screen, on a real preview of every PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It turns a claim into a gate.&lt;/strong&gt; With the N/N gate as a required check, "all tests pass" is no longer a promise in a README. It is a rule GitHub enforces, on admins too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It closes the loop.&lt;/strong&gt; Plan written first, red on the PR, fix pushed, green, merged. 14 times, generated straight from the GitHub API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It never sleeps.&lt;/strong&gt; The nightly production run opened issue #66 on its own, and the fix closed it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plans in plain English.&lt;/strong&gt; 54 UI plans that read like a checklist a traveller would write, each asserting exact values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A traveller standing at a border should never have to wonder whether her count is right. With the European Commission grading the math and TestSprite guarding every pull request, Ninety can tell her: it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://ninety-coral.vercel.app" rel="noopener noreferrer"&gt;ninety-coral.vercel.app&lt;/a&gt; · &lt;strong&gt;Proof:&lt;/strong&gt; &lt;a href="https://ninety-coral.vercel.app/proof" rel="noopener noreferrer"&gt;/proof&lt;/a&gt; · &lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/mohamedwael201193/ninety" rel="noopener noreferrer"&gt;github.com/mohamedwael201193/ninety&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built by devmo for the TestSprite Hackathon Season 4. #TestSprite #TestSpriteHackathon&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
