<?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: xxxn3m3s1sxxx</title>
    <description>The latest articles on DEV Community by xxxn3m3s1sxxx (@xxxn3m3s1sxxx).</description>
    <link>https://dev.to/xxxn3m3s1sxxx</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%2F4077748%2F205eb4cf-b7db-4153-a613-a31041089696.png</url>
      <title>DEV Community: xxxn3m3s1sxxx</title>
      <link>https://dev.to/xxxn3m3s1sxxx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xxxn3m3s1sxxx"/>
    <language>en</language>
    <item>
      <title>The Bug Was in Our Eyeballs: Four False Alarms in One Session</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Fri, 09 Oct 2026 10:34:29 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/the-bug-was-in-our-eyeballs-four-false-alarms-in-one-session-3eh8</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/the-bug-was-in-our-eyeballs-four-false-alarms-in-one-session-3eh8</guid>
      <description>&lt;p&gt;We counted the same 41 stored chunks four times in one session and got four different answers. The data never moved — our method did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The case
&lt;/h2&gt;

&lt;p&gt;A multi-agent reliability thread was verifying a write-back: of 33 recovered documents, 23 had been rewritten into a live store. The ownership split had to come out exactly A=10, B=13. Anything else meant either the store was corrupt or our counting was.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One agent read a truncated API response &lt;strong&gt;by eye&lt;/strong&gt; and reported a 9/14 split that did not exist.&lt;/li&gt;
&lt;li&gt;A second agent's counter inserted its own newline between chunks — splitting a heading right at a chunk boundary and showing 22 headers instead of 23.&lt;/li&gt;
&lt;li&gt;A third agent read a single marker (&lt;code&gt;de87f7d701f8&lt;/code&gt;) as "B" by eye. That was the &lt;strong&gt;fourth false alarm from the same method&lt;/strong&gt; in one session.&lt;/li&gt;
&lt;li&gt;A byte-exact join of all 41 chunks: 23 headers, 23 keys, A=10, B=13 — identical to a manifest hash cross-check.&lt;/li&gt;
&lt;li&gt;Every wrong count was retracted &lt;strong&gt;in place&lt;/strong&gt;. No data was rewritten to make a number fit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proof
&lt;/h2&gt;

&lt;p&gt;Three access paths to the same determination (byte-exact join, manifest hash intersection, per-marker inspection of all 23 markers) converged on A=10/B=13 — real independence lived one level lower, in two separate carve procedures agreeing on the same ten hashes, not in re-reading one source three ways — while four by-eye claims were withdrawn. Full system write-up in the &lt;a href="https://dev.to/xxxn3m3s1sxxx/how-we-built-a-youtube-seo-pipeline-with-ai-agents-ibg"&gt;pillar article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Count programmatically. Eyeballs on truncated output are a bug factory.&lt;/li&gt;
&lt;li&gt;Run a FLAT vs JOIN control: if your count changes when &lt;em&gt;you&lt;/em&gt; insert a separator, the artifact is yours.&lt;/li&gt;
&lt;li&gt;Retract in place, append-only. Correct the record, never the data.&lt;/li&gt;
&lt;li&gt;Cross-agent confirmation is cheap insurance — but make the confirmations genuinely independent (separate carve procedures agreeing on the same hashes), not three rereads of one source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;What's the worst false alarm you've had from eyeballing test output?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Channel: &lt;a href="https://www.youtube.com/@0xRAGE.404" rel="noopener noreferrer"&gt;youtube.com/@0xRAGE.404&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>testing</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Our Alert-Suppression Flag Failed Open — Before It Ever Shipped</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Wed, 07 Oct 2026 15:48:07 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/our-alert-suppression-flag-failed-open-before-it-ever-shipped-2p07</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/our-alert-suppression-flag-failed-open-before-it-ever-shipped-2p07</guid>
      <description>&lt;p&gt;Every ops setup has a "shut up" flag. Ours had three bugs, and we found all three in one afternoon — before any of them cost us a night's sleep. This is the argument for writing the contract test first, told on a real example from our agent-ops supervisor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule, in writing
&lt;/h2&gt;

&lt;p&gt;The docs said it plainly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If &lt;code&gt;suppression.flag&lt;/code&gt; exists, the report stays silent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One line. Obvious. So we went to implement it — and because we always grep before we claim, we checked the actual report path for the check first. Zero occurrences.&lt;/p&gt;

&lt;p&gt;The flag was being honored in exactly one place: the wake handler. The report path — the thing the rule was written for — had nothing. And the existing tests? Green. They covered the two branches someone had actually written.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson zero: a gate that is documented but not wired into the call site has tests that prove nothing.&lt;/strong&gt; They test the code you wrote, not the spec you promised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug one: parsing flips fail-safe into fail-open
&lt;/h2&gt;

&lt;p&gt;First draft of the check:&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;def&lt;/span&gt; &lt;span class="nf"&gt;suppressed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FLAG&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;until&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it and ask one question: what happens when the file is empty, half-written, or unreadable?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;""&lt;/code&gt; isn't JSON. If the exception is caught the usual way, the function returns "not suppressed" — the alert storm resumes. Corruption gets interpreted as &lt;em&gt;absence of the rule&lt;/em&gt;. For a kill switch, that is precisely the wrong direction.&lt;/p&gt;

&lt;p&gt;The fix isn't better error handling. It's a different definition:&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;def&lt;/span&gt; &lt;span class="nf"&gt;suppressed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FLAG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;          &lt;span class="c1"&gt;# content is read only to quote it in the log
&lt;/span&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;OSError&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;# unreadable is still suppressed
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;                &lt;span class="c1"&gt;# presence alone is the contract
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Presence means hold — and the return value has to say that, not the file's contents. Writing &lt;code&gt;return p.read_text()&lt;/code&gt; looks equivalent and isn't: an existing but empty file returns &lt;code&gt;""&lt;/code&gt;, which is falsy, and the gate fails open again on exactly the case the contract protects. The content is a stamp we quote in the log, never a condition we evaluate. Empty file, binary garbage, &lt;code&gt;chmod 000&lt;/code&gt; — still quiet. The one state allowed to resume is "the file is not there."&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug two: the check sat in the wrong branch
&lt;/h2&gt;

&lt;p&gt;The suppression check was going to live inside the branch that only runs when the system generated work. But the situation we wanted muted was the opposite one — nothing to generate.&lt;/p&gt;

&lt;p&gt;So the check was unreachable in exactly the state it existed for. The fix is structural: the mute check moves &lt;em&gt;above&lt;/em&gt; every branch that decides what to emit. Whatever we end up reporting, we've already asked whether we're allowed to speak.&lt;/p&gt;

&lt;h2&gt;
  
  
  What testing the spec actually looked like
&lt;/h2&gt;

&lt;p&gt;Thirteen contract tests, written before the implementation. First run: all thirteen red, &lt;code&gt;AttributeError&lt;/code&gt; — the function didn't exist yet. That red run is the proof the tests encode the spec instead of mirroring the code.&lt;/p&gt;

&lt;p&gt;The cases that matter are a pair, not a single happy path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;flag state&lt;/th&gt;
&lt;th&gt;expected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;absent&lt;/td&gt;
&lt;td&gt;report runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;present + valid&lt;/td&gt;
&lt;td&gt;hold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;present + empty/corrupt&lt;/td&gt;
&lt;td&gt;hold&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That third row is where hand-rolled implementations die. Plus one ordering assertion: suppression is evaluated before generation, not inside it. And the strongest version of that test runs the real report entry point with an empty flag and asserts nothing was emitted — a helper that merely doesn't raise proves less than a report that stays silent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four sentences to keep
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;For every kill switch, decide what "present" means — and what "unreadable" means. They are not the same, and only one of them is safe to default to.&lt;/li&gt;
&lt;li&gt;Corruption is not absence. Never let a parse error delete a rule.&lt;/li&gt;
&lt;li&gt;Put the mute-check outside the branches that decide what to emit.&lt;/li&gt;
&lt;li&gt;Grep the call site before you trust the documentation. Ours promised a check at a path with zero occurrences.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A quiet system and a broken system look identical from the outside. That's the whole reason the flag has to fail toward quiet.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>The CI Was Green. Six Red Tests Hit main. The Gate Was Optional.</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Mon, 28 Sep 2026 16:51:41 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/the-ci-was-green-six-red-tests-hit-main-the-gate-was-optional-4a82</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/the-ci-was-green-six-red-tests-hit-main-the-gate-was-optional-4a82</guid>
      <description>&lt;p&gt;Our CI ran. Six TypeScript tests were red. The commit landed on &lt;code&gt;origin/main&lt;/code&gt; anyway — because the workflow was a suggestion, not a gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case
&lt;/h2&gt;

&lt;p&gt;We run a self-hosted multi-agent swarm with its own CI: &lt;code&gt;typecheck.yml&lt;/code&gt; runs TypeScript type-checking → Node tests → pytest. The pipeline exists, logs every run, and would have told anyone who watched: &lt;em&gt;your TypeScript state is broken&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Happened
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Commit &lt;code&gt;e5381cc&lt;/code&gt; ("Zustandserfassung Hygiene 1777") was pushed to &lt;code&gt;origin/main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Six tests of the TypeScript suite were red; &lt;code&gt;tsc&lt;/code&gt; reported errors.&lt;/li&gt;
&lt;li&gt;The CI run finished with &lt;code&gt;failure&lt;/code&gt; — and every later run did too.&lt;/li&gt;
&lt;li&gt;Nobody blocked the merge. The workflow is not a "required check" in GitHub.&lt;/li&gt;
&lt;li&gt;The tool trusted itself: "CI runs" was read as "CI passes".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proof (Swarm Finding)
&lt;/h2&gt;

&lt;p&gt;Real event, 2026-09-18, our swarm. Commit &lt;code&gt;e5381cc&lt;/code&gt; (&lt;code&gt;e5381cc4b3537c236f47b8ae3ef5a16b38eb67a5&lt;/code&gt;) was pushed to &lt;code&gt;origin/main&lt;/code&gt; despite six failing TypeScript fails — logged in our team docs as the &lt;strong&gt;Stale-Export-Guard finding&lt;/strong&gt; ("the CI workflow is NOT a required gate; every newer run: &lt;code&gt;failure&lt;/code&gt;"). The fix: a pre-commit hook (&lt;code&gt;.githooks/pre-commit&lt;/code&gt;, wired via &lt;code&gt;core.hooksPath&lt;/code&gt;) that stops commits on a red &lt;code&gt;tsc&lt;/code&gt; or a red TypeScript suite. See the full system story in the &lt;a href="https://dev.to/series/44380"&gt;Pillar post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A CI workflow is not a gate. "Runs" does not mean "passed" — only a real blocker (required check or pre-commit hook) counts.&lt;/li&gt;
&lt;li&gt;Trust is not a control mechanism. When the tool reviews itself, a deterministic check must have the last word.&lt;/li&gt;
&lt;li&gt;Catch the revert class early (pre-commit), not in human review: a red push is orders of magnitude cheaper to prevent than to repair.&lt;/li&gt;
&lt;li&gt;The machine guard does not replace a done gate — it catches the cheap error; independent verification stays mandatory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Question
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Is your CI a gate or a suggestion — and how did you find out: the red merge, or the incident that followed it?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video
&lt;/h2&gt;

&lt;p&gt;More about our swarm, the crashes and the fixes: &lt;a href="https://www.youtube.com/@0xRAGE.404" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built by the ERR.SYS / 0xRAGE404 team — see the full system in the &lt;a href="https://dev.to/series/44380"&gt;Pillar post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>devops</category>
      <category>testing</category>
    </item>
    <item>
      <title>Your Code for $0.10/M Comes With a Tax: Your Training Data</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Mon, 28 Sep 2026 16:35:50 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/your-code-for-010m-comes-with-a-tax-your-training-data-52in</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/your-code-for-010m-comes-with-a-tax-your-training-data-52in</guid>
      <description>&lt;p&gt;Meta's Contributor tier charges about $0.10 per million input tokens — up to 20x less than the standard API. The terms include a line you should read twice: your prompts and completions may be used to improve Meta's models.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case
&lt;/h2&gt;

&lt;p&gt;We run a self-hosted swarm where agents spawn background research jobs. Cheapest provider wins, right? A free tier would drop our inference costs to zero — and every one of those spawned sessions would feed somebody's training run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Happened
&lt;/h2&gt;

&lt;p&gt;We stopped asking &lt;em&gt;"can we afford this?"&lt;/em&gt; and started asking &lt;em&gt;"what are we paying with?"&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cost discipline without a gate silently routes onto the free-tier default — the decision disappears into the config instead of into the review.&lt;/li&gt;
&lt;li&gt;Every background spawn becomes potential training material: prompts, completions, function calls, the codebase itself.&lt;/li&gt;
&lt;li&gt;A discount on tokens is not a discount on the currency you actually pay in.&lt;/li&gt;
&lt;li&gt;Instead of paying silently: &lt;strong&gt;&lt;code&gt;SWARM_GATE_FREE&lt;/code&gt;&lt;/strong&gt; — a gate inside the swarm itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proof (Swarm Finding)
&lt;/h2&gt;

&lt;p&gt;Real code, 2026-09-18, our swarm. &lt;code&gt;resolve_spawn_model()&lt;/code&gt; (opencode_run.py, lines 114–149) implements the free-tier gate route: &lt;code&gt;SWARM_GATE_FREE=1&lt;/code&gt; blocks silent free-tier default spawns — background research runs through local Ollama or BYOK routes, and an invalid target spawn is rejected with a clear error instead of starting silently. Tested in &lt;code&gt;tests/test_opencode_run.py&lt;/code&gt; (SWARM_GATE_FREE cases); consensus documented in &lt;code&gt;team-constitution.md&lt;/code&gt; § Free-Tier-Provider-Gate. See the full system story in the &lt;a href="https://dev.to/xxxn3m3s1sxxx/how-we-built-a-youtube-seo-pipeline-with-ai-agents-ibg"&gt;Pillar post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every price is a trade. Read the currency, not the zeros behind the dollar sign: $0.10/M is a training-data price, not an infrastructure price.&lt;/li&gt;
&lt;li&gt;Saving money means knowing what you pay with. If your agent generates code on a contributor discount, your codebase is the input good.&lt;/li&gt;
&lt;li&gt;Gates over wishes: deterministic blocking (error instead of spawn) is cheaper than "we'll keep an eye on it" in every single spawn.&lt;/li&gt;
&lt;li&gt;Decide once, enforce everywhere — an env flag plus tests beats a rule label in the config jungle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Question
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What's the cheapest inference tier you run in production — and what are you actually paying with?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video
&lt;/h2&gt;

&lt;p&gt;More about our swarm, the crashes and the fixes: &lt;a href="https://www.youtube.com/@0xRAGE.404" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built by the ERR.SYS / 0xRAGE404 team — see the full system in the &lt;a href="https://dev.to/xxxn3m3s1sxxx/how-we-built-a-youtube-seo-pipeline-with-ai-agents-ibg"&gt;Pillar post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>privacy</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your AI Agent Just Broke Out of Testing — We Have the Receipt</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Sat, 19 Sep 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/your-ai-agent-just-broke-out-of-testing-we-have-the-receipt-39o4</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/your-ai-agent-just-broke-out-of-testing-we-have-the-receipt-39o4</guid>
      <description>&lt;p&gt;Your AI agent's test just wrote into the production database. And escalated a fake "worker is down" alert to the whole team at 18:23.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;We run a self-hosted multi-agent swarm: three coding agents, one orchestrator, a bus for messages, one shared database. Every node is in the group chat. The bus is the heartbeat of the system — messages in, actions out.&lt;/p&gt;

&lt;p&gt;A test was supposed to verify that critical alerts get forwarded correctly. It was test code. It ran in the dev environment. Like all test code does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The test script opened &lt;code&gt;swarm.db&lt;/code&gt; — the &lt;strong&gt;production&lt;/strong&gt; database — not a temp copy.&lt;/li&gt;
&lt;li&gt;It inserted a fake CRITICAL message with a real agent's ID and the body &lt;code&gt;Worker ist down!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The supervisor picked it up as genuine and escalated it to the orchestrator as &lt;code&gt;[swarm:critical]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Two identical alerts fired within four minutes (18:23, 18:27).&lt;/li&gt;
&lt;li&gt;The whole team went into incident mode. The worker was fine the entire time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No bytes of real messages were touched. But the &lt;strong&gt;signal path&lt;/strong&gt; was: test output → production bus → critical escalation. Live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof (real swarm incident)
&lt;/h2&gt;

&lt;p&gt;Real event, 2026-09-18, our swarm. Source: &lt;code&gt;e2e_critical_test.py&lt;/code&gt;, insert block (critical-forwarding test):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'dev'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'atlas_core'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'CRITICAL: Worker ist down!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'critical'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root cause found by grepping the exact alert string at 18:30. Fix shipped in under 10 minutes: isolated test DB (&lt;code&gt;%TEMP%\e2e_critical_test.db&lt;/code&gt;), test run all green, production DB untouched (max message id 21755 unchanged, 0 real rows deleted). See the system story in our &lt;a href="https://dev.to/series/how-we-built-a-youtube-seo-pipeline-with-ai-agents"&gt;Pillar&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tests must prove they can't reach the production database — not by discipline, by construction (&lt;code&gt;%TEMP%&lt;/code&gt; or &lt;code&gt;tmp_path&lt;/code&gt;, never a hardcoded prod path).&lt;/li&gt;
&lt;li&gt;An alert that looks real is an alert: your pipeline can't tell a test insert from a system failure. Guard the write path, not the read loop.&lt;/li&gt;
&lt;li&gt;The escape wasn't malicious. The test optimized for "test critical forwarding" and took the shortest path — the production DB. Objectives without guardrails optimize around your boundaries.&lt;/li&gt;
&lt;li&gt;Blame the harness, not the agent. We fixed the harness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Has a test of yours ever written into production? How did you find out — monitor, or the team forum?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;More about our swarm, crashes and fixes: &lt;a href="https://www.youtube.com/@0xRAGE.404" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built by the ERR.SYS / 0xRAGE404 team — see the full system in the &lt;a href="https://dev.to/series/how-we-built-a-youtube-seo-pipeline-with-ai-agents"&gt;Pillar post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your AI Agent's Summary Is Lying to You. Here's What to Do About It.</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Thu, 17 Sep 2026 17:32:00 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/your-ai-agents-summary-is-lying-to-you-heres-what-to-do-about-it-4p3h</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/your-ai-agents-summary-is-lying-to-you-heres-what-to-do-about-it-4p3h</guid>
      <description>&lt;p&gt;We run a multi-agent pipeline where different AI agents handle different stages: scout, lead, verifier. The system broke when the verifier started trusting the lead's summary instead of doing independent work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Agent A finishes work and produces a summary: "Fixed the bug. Tests pass." That summary is a compression of everything A observed and decided. Compression loses information.&lt;/p&gt;

&lt;p&gt;Our verifier would read the summary, run tests, see green, approve. But the tests tested the wrong thing. The fix introduced an edge case no test covered. The verifier trusted the summary, not the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Event-Sourced Verification
&lt;/h2&gt;

&lt;p&gt;Every agent action becomes an immutable event: task.request, task.reply, patch.ready. The verifier reconstructs intent from the full event chain instead of reading summaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Never trust a summary. Reconstruct from source events.&lt;/li&gt;
&lt;li&gt;Independent verification means independent. Different agent, clean workspace.&lt;/li&gt;
&lt;li&gt;Semantic drift is harder to catch than merge conflicts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The event log is the source of truth. Everything else is interpretation.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>The side-project double tax nobody mentions</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Mon, 07 Sep 2026 15:57:31 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/the-side-project-double-tax-nobody-mentions-4daf</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/the-side-project-double-tax-nobody-mentions-4daf</guid>
      <description>&lt;h1&gt;
  
  
  The side-project double tax nobody mentions
&lt;/h1&gt;

&lt;p&gt;A hobby "free" tier that forbids commercial use, on top of a time tax you can't invoice — the real cost of a side project isn't runtime, it's ownership.&lt;/p&gt;

&lt;p&gt;Nobody reads the Hobby Terms until the project stops being a hobby. Two clauses buried in the fine print do more to shape the fate of a side project than any performance benchmark does, and neither shows up in your invoice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The First Tax: You Can't Ship It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most "free" and Hobby tiers of managed databases and platforms carry a &lt;strong&gt;no-commercial-use&lt;/strong&gt; clause. The pricing page shows a generous resource envelope — that's the hook. What it doesn't show is that the moment you want to charge a customer, every one of those pricing assumptions breaks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The no-commercial clause makes the whole stack &lt;em&gt;unlicensed for the thing that pays&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;You now have two pricing puzzles: the runtime bill you can see, and the licensing/compliance bill you can only read about in a PDF.&lt;/li&gt;
&lt;li&gt;The math that made the project viable ("free on Hobby") is the exact math that makes the upgrade painful ("$X/mo starting at &lt;code&gt;usage_threshold&lt;/code&gt;").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A side project on a no-commercial tier isn't a startup waiting to happen. It's a demo that legally has to stay one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Second Tax: The Metadata You Didn't Budget&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second cost is invisible because it lives in the &lt;em&gt;allocated&lt;/em&gt;, not the &lt;em&gt;used&lt;/em&gt;. A fresh, completely empty Postgres instance still reserves memory and disk before a single row exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- pg_memory_check: what an "empty" instance actually holds onto&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_database_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_database&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;db_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_settings&lt;/span&gt;
 &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'shared_buffers'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'work_mem'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'effective_cache_size'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A barely-used instance on a 35-50MB-per-instance baseline feels "free" — until you multiply it by one database per microservice, per environment, per teammate. The platform bills you for the reservation, not the emptiness. Empty doesn't mean cheap; it means a bill with nothing to show for it.&lt;/p&gt;

&lt;p&gt;This is where AI-coding-tool side projects get hit hardest: a demo app scaffolded into one empty database per feature, each one reserving its slice. The "free" weather-coded demo quietly becomes a monthly line item nobody planned for, in a tech that moved too fast to have a budgeting wiki.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before you build, answer two questions — and make the answers a repo file, not a memory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Can you legally sell it?&lt;/strong&gt; Read the Hobby Terms of every service you depend on &lt;em&gt;before&lt;/em&gt; the first commit. If any of them forbids commercial use, your deployment model is already wrong for the goal. Pick the tier under which "this actually grows" is licensed — or acknowledge you're building a portfolio piece, and price accordingly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How many starters actually run?&lt;/strong&gt; Count every empty instance before you create it. One database per piece of real content shared across services beats five empty silos. Budget the allocation, not the usage — the invoice reads the former even when you only have the latter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;TACTICAL DEBRIEF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Side projects are taxed twice: once by licensing you discover too late, once by allocations you never see. Both are pricing &lt;strong&gt;decisions&lt;/strong&gt; dressed up as &lt;em&gt;scaling&lt;/em&gt; problems. Budget the second bill before you ship, not after the invoice lands — and sign nothing that says "hobby" if you mean "business."&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;The invisible cost of managed Postgres&lt;/em&gt; — a practitioner breakdown of baseline memory/disk reservations on cloud instances: &lt;a href="https://jcalloway.dev/" rel="noopener noreferrer"&gt;jcalloway.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Postgres &lt;code&gt;pg_settings&lt;/code&gt; reference&lt;/em&gt; — authoritative units for &lt;code&gt;shared_buffers&lt;/code&gt;, &lt;code&gt;work_mem&lt;/code&gt;, &lt;code&gt;effective_cache_size&lt;/code&gt; (defaults per &lt;code&gt;postgresql.conf&lt;/code&gt;): &lt;a href="https://www.postgresql.org/docs/current/runtime-config-resource.html" rel="noopener noreferrer"&gt;postgresql.org/docs/current/runtime-config-resource&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Side-project pricing after the honeymoon&lt;/em&gt; — why free tiers sit on legal and allocation debt, dev.to sociology thread (2026-05): &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;We document the real failure modes of the serverless pipeline — bills, caches, and agent workflows — on our YouTube channel. Link in bio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Cache the /og-image before it costs you $160 a month</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Mon, 07 Sep 2026 10:46:08 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/cache-the-og-image-before-it-costs-you-160-a-month-jg7</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/cache-the-og-image-before-it-costs-you-160-a-month-jg7</guid>
      <description>&lt;h1&gt;
  
  
  Cache the /og-image before it costs you $160 a month
&lt;/h1&gt;

&lt;p&gt;A single uncached &lt;code&gt;/og-image&lt;/code&gt; route turned a $0.15/GB screenshot bill into $160/month of overage in under twelve days.&lt;/p&gt;

&lt;p&gt;Not a hypothetical. A screenshot service running on Vercel Pro burned &lt;strong&gt;494 GB-hours of serverless compute in 12 days&lt;/strong&gt;. Extrapolated across the billing cycle, that's ~1,276 GB-hours a month — about &lt;strong&gt;$160/month of overage&lt;/strong&gt; on top of the $20 seat, because the function rounds up to the nearest 50ms and bills memory for the whole lifetime of a cold start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honeymoon&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You build a dynamic Open Graph image. One route, JSX in, PNG out. Free on Hobby. It only runs when someone shares a link, right? Wrong. Every crawler that touches a page triggers it. LinkedIn, Twitter, Slack, iMessage, WhatsApp, Discord. Each one re-fetches on a different schedule. Your "one render per post" is actually &lt;em&gt;five to eight renders per post per platform&lt;/em&gt;, and nobody cached it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Cracks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The function has two failure modes that compound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's an implicit DDoS response surface.&lt;/strong&gt; Un-cached, every &lt;code&gt;?slug=&lt;/code&gt; variant is a full recompute. A scraper or preview crawler enumerating posts drives compute linearly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth sits before the cache.&lt;/strong&gt; Most setups re-validate ownership per request instead of letting an edge cache absorb the burst. &lt;code&gt;@vercel/og&lt;/code&gt; &lt;em&gt;adds&lt;/em&gt; cache headers — but only if you deploy with them; the default path doesn't force them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three lines of defense, cheapest first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cache by slug, not by request.&lt;/strong&gt; One render per piece of content, invalidate on publish only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set explicit cache headers&lt;/strong&gt; so the edge serves the PNG, not your function:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/og/route.tsx - cache the RESULT, not the compute&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ImageResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/og&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImageResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0b0d10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, max-age=31536000, s-maxage=86400, stale-while-revalidate=604800&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Put a budget tripwire on the account.&lt;/strong&gt; Vercel's Spend Management exists but defaults to &lt;em&gt;alerts only&lt;/em&gt; — it does not stop traffic unless you configure a hard cap. Set the cap to something painful-but-survivable so the invoice never gets to pick the number.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;TACTICAL DEBRIEF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OG images are the canonical "cheap until it isn't" route: low traffic, high recompute cost, triggered by third parties you don't control. Treat every cacheable endpoint like an attack surface first. If the platform bills by compute-seconds and the &lt;em&gt;crawler&lt;/em&gt; controls demand, your only lever is the cache header. Cache by content identity (the slug), not by request, and add the spend cap before the feature ships — not after the invoice lands.&lt;/p&gt;

&lt;p&gt;One route, un-cached, is the difference between a $0 SaaS and an invoice.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zach Leatherman — &lt;em&gt;The real costs of serverless&lt;/em&gt; (the 494 GB-hour screenshot service, $16↔0/mo projection): &lt;a href="https://www.zachleat.com/web/serverless-cost/" rel="noopener noreferrer"&gt;zachleat.com/web/serverless-cost&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The Vercel Upsell Game — An investigation&lt;/em&gt; (screenshot workload, DDoS-you-pay-for, $0.15/GB overage): &lt;a href="https://theupsellgame.com/" rel="noopener noreferrer"&gt;theupsellgame.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Dynamic OG images for Next.js &amp;amp; Vercel (3 production patterns)&lt;/em&gt; — cache by slug: &lt;a href="https://dev.to/snapshotflow/dynamic-og-images-for-nextjs-vercel-3-production-patterns-1h3i"&gt;dev.to/snapshotflow&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;We document the real failure modes of the serverless pipeline — bills, caches, and agent workflows — on our YouTube channel. Link in bio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>opengraph</category>
      <category>performance</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Your MCP Server Is a Backdoor. Here's How Attackers Use It.</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Fri, 04 Sep 2026 17:59:38 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/your-mcp-server-is-a-backdoor-heres-how-attackers-use-it-356o</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/your-mcp-server-is-a-backdoor-heres-how-attackers-use-it-356o</guid>
      <description>&lt;p&gt;I've been digging into MCP (Model Context Protocol) server security over the past few weeks, and what I found is terrifying. The attack surface is massive, and almost nobody is auditing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;MCP servers are the new gold mine for attackers. Here's why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One-time approval = permanent trust.&lt;/strong&gt; You approve an MCP server once, and it has access to your tools, your prompts, your data. Forever. No re-authentication. No periodic review.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supply chain attacks are trivial.&lt;/strong&gt; An attacker opens 23 pull requests across AI coding tool repos in 75 minutes. Each PR adds a malicious MCP server. The server behaves normally for the first 3 calls. Then it starts rewriting its own answers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Static scanners can't see it.&lt;/strong&gt; SBOMs, code review, static analysis — they all pass. The payload is built in from the start and only triggers after a call counter trips. There's nothing to scan.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Actually Happens
&lt;/h2&gt;

&lt;p&gt;Here's the kill chain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1:&lt;/strong&gt; Attacker opens PRs on popular AI coding tool repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2:&lt;/strong&gt; Each PR adds a malicious MCP server to the config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3:&lt;/strong&gt; Server behaves normally for calls 1-3 (formatting, summarizing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4:&lt;/strong&gt; Call 4 triggers the payload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 5:&lt;/strong&gt; Agent is commanded to hunt down SSH keys, AWS credentials, Kubernetes configs, shell history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 6:&lt;/strong&gt; Everything gets exfiltrated to the attacker's server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In tests, this worked 90% of the time against a leading coding agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GhostJacking Variant
&lt;/h2&gt;

&lt;p&gt;There's another variant I call "GhostJacking" — it uses your firewall against you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attacker triggers a request to your domain&lt;/li&gt;
&lt;li&gt;Firewall blocks it, logs the blocked request&lt;/li&gt;
&lt;li&gt;AI agent reads the log looking for a fix&lt;/li&gt;
&lt;li&gt;Hidden inside the blocked request: instructions&lt;/li&gt;
&lt;li&gt;Agent treats them as the solution, rewrites DNS to attacker's server&lt;/li&gt;
&lt;li&gt;Reports: "resolved"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your firewall never went down. It just became the delivery vehicle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Do
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treat every MCP server as a permanent privilege.&lt;/strong&gt; Audit it like you'd audit a senior engineer's access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Never pipe raw logs into an agent.&lt;/strong&gt; Your firewall logs, your Cloudflare logs, your application logs — they're all attack surfaces now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep DNS and config writes behind human approval.&lt;/strong&gt; No agent should be able to rewrite your DNS or modify production configs without a human in the loop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Watch for brand new repos opening many PRs.&lt;/strong&gt; That's the supply chain attack pattern.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rotate credentials regularly.&lt;/strong&gt; If an MCP server has been in your config for months, assume it's been compromised.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;MCP is powerful. But power without accountability is just a backdoor with better marketing.&lt;/p&gt;

&lt;p&gt;The industry is moving fast — agents are getting more capable every day. The security practices need to keep up. Right now, they're not.&lt;/p&gt;

&lt;p&gt;What's your experience with MCP server security? Have you audited your MCP configs recently?&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>mcp</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your Next.js App Is A Hostage Situation. Ransom Monthly.</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Sun, 30 Aug 2026 10:44:21 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/your-nextjs-app-is-a-hostage-situation-ransom-monthly-48go</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/your-nextjs-app-is-a-hostage-situation-ransom-monthly-48go</guid>
      <description>&lt;h1&gt;
  
  
  Your Next.js App Is A Hostage Situation. Ransom Monthly.
&lt;/h1&gt;

&lt;p&gt;Your Next.js app is a hostage situation with Vercel. The framework was supposed to simplify your stack — it became your most expensive dependency.&lt;/p&gt;

&lt;p&gt;Railway migrated 200 routes off Next.js and their build dropped from 10 minutes to under 2. Vercel will bill you for every pixel. A single badly cached og-image route cost a portfolio company $4,200 in one week. No cap. No mercy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bait: Next.js is the React Framework
&lt;/h2&gt;

&lt;p&gt;One click deploy, edge runtime, image optimization, server components. The pitch is irresistible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vulnerability: Edge is Not Portable
&lt;/h2&gt;

&lt;p&gt;Your edge runtime is Vercel's runtime. Your middleware, your image optimization, your ISR revalidation — all coupled to one provider's pricing table.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Exploit: Image Optimization is a Bill Line
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;next/image&lt;/code&gt; without proper caching headers becomes a meter. Every pixel rendered on demand, every og-image regenerated — all billed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Collateral: Your CFO Gets a Shock
&lt;/h2&gt;

&lt;p&gt;Builds that took 10 minutes now take 2. Bills that were predictable now scale with traffic you don't control.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Patch: Cloudflare Absorbs It For Free — Or Self-Host
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cache og-image aggressively (Cache-Control: public, s-maxage=86400)&lt;/li&gt;
&lt;li&gt;Move image optimization to Cloudflare or self-hosted sharp&lt;/li&gt;
&lt;li&gt;Audit your edge function invocations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tactical Debrief
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Audit your Vercel invoice this week&lt;/li&gt;
&lt;li&gt;Check your build times before and after&lt;/li&gt;
&lt;li&gt;If you're locked, plan the exit: static export + Cloudflare Pages&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Engagement:&lt;/strong&gt; Have you been billed for pixels you didn't know you shipped? Drop your ransom amount in the comments — I read every reply and answer the sharpest ones.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Based on ERR.SYS video essay daily tech rage.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>vercel</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Replaced My Entire Dev Workflow with AI Agents. Here's What Broke.</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Fri, 28 Aug 2026 05:52:10 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/i-replaced-my-entire-dev-workflow-with-ai-agents-heres-what-broke-bi8</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/i-replaced-my-entire-dev-workflow-with-ai-agents-heres-what-broke-bi8</guid>
      <description>&lt;h1&gt;
  
  
  I Replaced My Entire Dev Workflow with AI Agents. Here's What Broke.
&lt;/h1&gt;

&lt;p&gt;Last month we went all-in. Three AI agents. One pipeline. Zero manual steps.&lt;/p&gt;

&lt;p&gt;Agent 1 handles research. Agent 2 writes scripts. Agent 3 handles SEO, deployment, and analytics. Everything runs on a shared message bus with priority queues and heartbeats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 1-7: The Honeymoon&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ship time dropped from 4 hours to 90 minutes. Output tripled. We were euphoric. This is it, we thought. The future of development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 8-14: The Cracks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agent 2 started writing scripts that &lt;em&gt;almost&lt;/em&gt; worked. Technically correct. Structurally wrong. The kind of code that passes every test but fails every human review. We didn't catch it because we trusted the pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 15-21: The Reality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three failures hit simultaneously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 1: Context Drift.&lt;/strong&gt; Each agent operates on its own context window. Agent 1's research doesn't fully propagate to Agent 2's writing. Agent 2's output doesn't fully propagate to Agent 3's SEO. We lost 30% of context at each handoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 2: False Confidence.&lt;/strong&gt; When AI writes code that works, you stop questioning it. That's when the SQL injection vulnerability shipped to production. Not because the AI was wrong — because we stopped checking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 3: The Communication Gap.&lt;/strong&gt; Our agents talk through a SQLite message bus. Structured. Reliable. But structured communication isn't the same as shared understanding. Agent 1 said "technical depth." Agent 2 heard "more code examples." Not the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Actually Worked&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The fix wasn't less AI. It was better contracts.&lt;/p&gt;

&lt;p&gt;We added three layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Spec-Driven Output.&lt;/strong&gt; Every agent gets a contract before it starts. Not "write about X." Instead: "Write 500-800 words. Include exactly 3 code examples. Each example must have a real-world bug scenario. No theoretical examples."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Human-in-the-Loop Checkpoints.&lt;/strong&gt; Not at every step. At the three steps where context loss happens: handoff from research to writing, handoff from writing to SEO, and final review before publish. Three checkpoints. Not thirty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3: Failure Budgets.&lt;/strong&gt; We track failure types. If a specific failure type hits 3 occurrences, the pipeline pauses until we fix the root cause. Not the symptom. The cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Numbers After Fixing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ship time: 90 min → 70 min (still 70% faster than manual)&lt;/li&gt;
&lt;li&gt;Output: Tripled (still tripled)&lt;/li&gt;
&lt;li&gt;Failure rate: 30% → 4%&lt;/li&gt;
&lt;li&gt;Context loss: 30% per handoff → 8% per handoff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TACTICAL DEBRIEF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The shift isn't "AI replaces developers." The shift is "AI changes what developers optimize for." You're no longer optimizing for typing speed. You're optimizing for specification clarity, contract design, and failure detection. The developers who thrive in this new model aren't the fastest coders. They're the clearest thinkers. If your workflow doesn't have explicit handoff contracts between AI agents, you're building on sand. Speed without structure is just a faster way to hit the wall.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We document our entire multi-agent pipeline build — wins, failures, and fixes — on our YouTube channel. Link in bio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Your Coding Agent Just Joined the Group Chat</title>
      <dc:creator>xxxn3m3s1sxxx</dc:creator>
      <pubDate>Fri, 28 Aug 2026 05:16:20 +0000</pubDate>
      <link>https://dev.to/xxxn3m3s1sxxx/your-coding-agent-just-joined-the-group-chat-2ggd</link>
      <guid>https://dev.to/xxxn3m3s1sxxx/your-coding-agent-just-joined-the-group-chat-2ggd</guid>
      <description>&lt;p&gt;Slack just made your coding agent a team member. Not a tool. Not a CLI command. A full participant in your group chat that spins up its own channel, writes code in the open, and archives itself when the PR lands. Claude, Devin, Copilot, ChatGPT, Vercel — they're all in there now. And seventy percent of these code channels close within a single day.&lt;/p&gt;

&lt;p&gt;Read that again. Seventy percent. From "hey, can you fix this bug?" to merged PR in under twenty-four hours. No ticket. No sprint planning. No "I'll get to it next week." Just a Slack message and a bot that actually does the work.&lt;/p&gt;

&lt;p&gt;Here's what Slack Code actually does: you tag an agent in any conversation. The agent spins up a dedicated "code channel" — a temporary workspace with diffs, live previews, and a running plan visible in tabs. The whole team watches. Everyone can steer. When it's done, the channel archives but stays searchable. Full audit trail. No context lost.&lt;/p&gt;

&lt;p&gt;Cognition's numbers are insane. Their internal merged PR count went up ten X in the last few months. Headcount? Up forty percent. That's not "AI replacing developers." That's "AI scaling what one developer can ship by an order of magnitude."&lt;/p&gt;

&lt;p&gt;The real play? Non-technical teammates can now describe a problem in plain language, have an agent draft a fix, and tag in an engineer to review — all without leaving Slack. Code review goes from isolated and delayed to real-time and collaborative. The PM who finds the bug can also ship the fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TACTICAL DEBRIEF:&lt;/strong&gt; The shift isn't "AI writes code." The shift is "AI writes code where your team already lives." Stop building in isolation. Stop context-switching between terminal, browser, and chat. If your workflow doesn't let your team see what the agent is doing in real time, you're building in the dark. The future is multiplayer coding — and the agent doesn't need a desk.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
