<?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: Daniel Nwaneri</title>
    <description>The latest articles on DEV Community by Daniel Nwaneri (@dannwaneri).</description>
    <link>https://dev.to/dannwaneri</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%2F3606168%2F7684e1e1-b986-4ee3-ae5b-56db2b97d286.jpg</url>
      <title>DEV Community: Daniel Nwaneri</title>
      <link>https://dev.to/dannwaneri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dannwaneri"/>
    <language>en</language>
    <item>
      <title>$30 and a Lifetime of Liability</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Thu, 02 Jul 2026 11:57:19 +0000</pubDate>
      <link>https://dev.to/dannwaneri/30-and-a-lifetime-of-liability-19fl</link>
      <guid>https://dev.to/dannwaneri/30-and-a-lifetime-of-liability-19fl</guid>
      <description>&lt;p&gt;co-written with &lt;a href="https://dev.to/unitbuilds"&gt;UnitBuilds&lt;/a&gt;, who built most of this out loud in the comments of my last piece.&lt;/p&gt;




&lt;p&gt;I recently wrote about the $30. someone in cambodia or kenya, paid under $30 to complete a biometric verification step on behalf of a stranger, so a developer somewhere could access an ai model that's geo-blocked where they live.&lt;/p&gt;

&lt;p&gt;I framed it as exploitation. it is. but I stopped at the harvesting.&lt;/p&gt;

&lt;p&gt;UnitBuilds didn't stop there. over a series of comments, he walked through what happens after the $30 — and it's worse than anything I'd written.&lt;/p&gt;




&lt;h2&gt;
  
  
  the part the verification step doesn't tell you
&lt;/h2&gt;

&lt;p&gt;when you complete a biometric check — face the camera, look left, look right — you're not just proving you're human. legally, you're authorizing.&lt;/p&gt;

&lt;p&gt;not authorizing this one transaction. authorizing the account. anything done with it, by anyone, from that point forward, is yours. that's not a loophole. that's the definition of authentication.&lt;/p&gt;

&lt;p&gt;as UnitBuilds put it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"they can contest in court, but they won't win, by law they can't win, because the very definition of the authentication is that you, as yourself, fully authorize yourself and anyone else by proxy, to use your account to do with, for whatever purposes, assuming full responsibility for it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;the person who took the $30 didn't sign up to be liable for whatever happens next. but the law doesn't have a category for "deceived into authorizing." it has a category for "authorized." and once you're in that category, you're not fighting the bill. you're fighting jailtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  what "fighting jailtime" actually looks like
&lt;/h2&gt;

&lt;p&gt;UnitBuilds laid out the scenarios plainly:&lt;/p&gt;

&lt;p&gt;a bad actor uses the harvested identity to rack up charges, commit fraud, or worse. the account holder — the person who took the $30 — has no idea any of this happened. months later, maybe years later, they get a job offer overseas. they travel. at the border, there's a warrant. for a crime committed using their face, on the other side of the planet, by someone they've never met.&lt;/p&gt;

&lt;p&gt;or the company affected sues. the debt is structured for someone earning a developer's salary in a wealthy country. the person actually liable is earning $100 a month.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"imagine that, an entire month's pay gone, on a single ai subscription they never even knew existed, from a bank account they never made. and they don't have the finances to actually fight it in court."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;that's UnitBuilds describing namibia specifically — people working full contracts, 8 to 5, for $100 a month. not informal work. not gig work. contracted employment. wiped out by a bill that was never theirs, with no path to contest it, because contesting it costs more than the bill itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  the version where you don't even get the $30
&lt;/h2&gt;

&lt;p&gt;the scenario above assumes someone got paid. UnitBuilds described a worse one: phishing.&lt;/p&gt;

&lt;p&gt;a fake overseas job offer. "all you have to do is submit your id and do the facial verification, and send the code that's sms'd to you." it looks exactly like a routine hiring process. and then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"that's the last you ever hear of them."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;no payment. no awareness that you were ever part of a supply chain. just a verification step that felt normal, and a liability that surfaces however long it takes for someone to misuse it.&lt;/p&gt;




&lt;h2&gt;
  
  
  this isn't new, it's just wearing new clothes
&lt;/h2&gt;

&lt;p&gt;UnitBuilds has watched this pattern before ai existed. bank impersonation calls — spoofed numbers, confident voices, "confirm your account details" — targeting pensioners who grew up trusting that a call from the bank was actually the bank.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"life-savings gone from pensioners, who have no means of earning it back or fighting the bank for it. some had to choose between food on the table and paying their wifi, losing access to communication with everyone they know, for the sake of not going hungry, because someone scammed them out of 50 years worth of hard work."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;whatsapp cloning works the same way — impersonate a relative, get the verification code, clone the account, spread it to the entire contact list, harvest more identities, repeat.&lt;/p&gt;

&lt;p&gt;the throughline, in his words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"it's a system built on accountability, not morality, and the legal system is there to defend the dollar not the person."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;in namibia, you go to prison longer for poaching a cow than for murder.&lt;/p&gt;




&lt;h2&gt;
  
  
  the part that has nothing to do with biometrics
&lt;/h2&gt;

&lt;p&gt;then UnitBuilds introduced something I hadn't considered at all: hardware identity theft.&lt;/p&gt;

&lt;p&gt;two forms. the first is shadow proxy networks — malware that quietly routes traffic through your residential gateway, so someone else's activity travels under your ip, your network, your name.&lt;/p&gt;

&lt;p&gt;the second is newer and stranger. you buy a windows 11 laptop. secure boot signs the hardware to your microsoft account the moment you log in. from that point, you're the authorized owner of that device — and liable for whatever it does — until you go through the process of manually removing it from your account's device list. format it, sell it, give it away: none of that breaks the link. the new owner is using hardware that's still, in microsoft's records, yours.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"a small little detail they don't tell you when they say it's 'for your data security.'"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;the mechanism is identical to the biometric one. ownership and liability bound to an identity that doesn't update when the physical reality changes. the gap between who actually controls something and who's legally responsible for it is where all of this lives — bodies, devices, accounts, doesn't matter. the structure repeats.&lt;/p&gt;




&lt;h2&gt;
  
  
  the sentence underneath all of it
&lt;/h2&gt;

&lt;p&gt;a developer going by self-correcting systems read the original piece and named the pattern precisely:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"a control that can't see its own downstream doesn't stop the harm, it relocates it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;that's what every layer of this is. kyc doesn't stop fraud — it relocates the verification burden onto someone with no stake in the outcome. secure boot doesn't stop hardware theft — it relocates ownership liability onto whoever's account it happened to be signed into. every fix moves the cost. none of them eliminate it. they just choose, by design or by accident, who absorbs it.&lt;/p&gt;

&lt;p&gt;the people who absorb it are consistently the people least equipped to refuse, least equipped to understand what they're agreeing to, and least equipped to fight it once it lands.&lt;/p&gt;




&lt;p&gt;UnitBuilds runs Halo Cybersecurity adjacent work and built NMCP, a rust-based mcp implementation. everything quoted here, he gave permission to use directly — his words, not mine, paraphrased into something smaller than what he actually said.&lt;/p&gt;

&lt;p&gt;most of what's true in this piece, he wrote first, out loud, in a comment thread.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI helped me research, structure, and edit this piece. The arguments, the examples, and the opinions are mine and UnitBuilds'. So is whatever's wrong with them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>security</category>
    </item>
    <item>
      <title>Someone Else Pays for Your AI Access</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Tue, 30 Jun 2026 07:22:16 +0000</pubDate>
      <link>https://dev.to/dannwaneri/someone-else-pays-for-your-ai-access-5149</link>
      <guid>https://dev.to/dannwaneri/someone-else-pays-for-your-ai-access-5149</guid>
      <description>&lt;p&gt;you probably didn't think about this when you signed up.&lt;/p&gt;

&lt;p&gt;you entered your card details, verified your phone number, maybe uploaded a government ID and took a selfie. friction. annoying. you moved on.&lt;/p&gt;

&lt;p&gt;somewhere in cambodia or kenya, someone did the same thing. except they weren't signing up for claude. they were being paid — under $30 — to complete a verification step on behalf of someone they'll never meet, for a service they'll never use, in a supply chain they don't fully understand.&lt;/p&gt;

&lt;p&gt;their face is now in a database they didn't choose. it will be used again. not for claude.&lt;/p&gt;




&lt;p&gt;every time anthropic tightens access to protect its models, the evasion doesn't stop. it migrates.&lt;/p&gt;

&lt;p&gt;geoblocking produced vpn services. phone verification produced sms farms. credit card requirements produced stolen card networks. biometric kyc — live selfies, government id matching — produced agents traveling to lower-income countries to recruit real people willing to complete in-person verification for cash.&lt;/p&gt;

&lt;p&gt;the controls and the evasions are a paired system. you can't have one without the other. and the cost of the evasion doesn't stay where the models are. it moves to wherever people are poor enough to trade their biometric data for $30.&lt;/p&gt;




&lt;p&gt;the fable shutdown made this visible in a new way.&lt;/p&gt;

&lt;p&gt;on june 12, 2026, anthropic disabled fable 5 and mythos 5 for every customer worldwide — not because of an outage, not because of a flaw they found, but because the us government issued an export control directive at 5:21pm &lt;a href="https://www.anthropic.com/news/fable-mythos-access" rel="noopener noreferrer"&gt;Anthropic's official statement&lt;/a&gt; and there was no way to segment foreign nationals from us persons in real time. so they turned it off for everyone.&lt;/p&gt;

&lt;p&gt;gabriel attal compared it to iran blockading the strait of hormuz &lt;a href="https://aifrontiersmedia.substack.com/p/what-export-controls-on-anthropics" rel="noopener noreferrer"&gt;AI Frontiers Media&lt;/a&gt;. brussels talked. developers in san francisco talked about reliability. nobody talked about cambodia.&lt;/p&gt;




&lt;p&gt;the transfer station economy — documented in may 2026 by oxford researcher zilan qian &lt;a href="https://www.chinatalk.media/p/how-to-buy-cheap-claude-tokens-in" rel="noopener noreferrer"&gt;ChinaTalk, May 5 2026&lt;/a&gt; — has been running this supply chain in public for years. github. taobao. telegram. chinese developers accessing claude at 10% of official price through api proxies that sit between them and anthropic's infrastructure.&lt;/p&gt;

&lt;p&gt;the three ways the price gets that low:&lt;/p&gt;

&lt;p&gt;first, account arbitrage — bulk-registered free credits, unused quotas, carved-up max plans.&lt;/p&gt;

&lt;p&gt;second, model swapping — you pay for opus, you get haiku, sometimes you get glm. you can't verify which model answered you.&lt;/p&gt;

&lt;p&gt;third, the logs. every prompt, every response, every tool call, every reasoning trace sitting on a proxy operator's server. for a developer using claude code, that's your repository context, your engineering decisions, your verified correct outputs. the markup business is customer acquisition. the logs are the margin.&lt;/p&gt;

&lt;p&gt;but the third meal isn't just data extraction. the upstream supply chain that keeps the proxy pool running needs verified accounts. verified accounts need identities. identities increasingly need biometrics. and biometrics, when ai deepfakes get good enough to detect, need real humans.&lt;/p&gt;

&lt;p&gt;so agents go to cambodia. agents go to kenya. they find people willing to complete verification for under $30. those faces enter a database. that database doesn't stay in the claude access supply chain.&lt;/p&gt;

&lt;p&gt;the chinese developer paying 10% for tokens didn't order this. they're trying to build something with the same tools everyone else has, priced out by geography the same way a developer in lagos is priced out by latency and infrastructure. neither of them sees the person whose face just got harvested in cambodia. neither of them chose the system that makes that harvesting profitable. they're both downstream of a fight they didn't start, between parties who will never absorb the cost themselves.&lt;/p&gt;




&lt;p&gt;the worldcoin black market documented this pattern before anyone was paying attention. iris scans harvested in cambodia and kenya, sold for under $30. the same infrastructure. the same geography. the same people absorbing costs they didn't choose.&lt;/p&gt;

&lt;p&gt;this isn't new. content moderators in kenya process trauma for platforms they'll never use. data labelers in colombia annotate images for models trained in san francisco. the biometric harvesting is the same supply chain, one layer deeper.&lt;/p&gt;

&lt;p&gt;a face verified to bypass anthropic's kyc today can be resold to open a fraudulent bank account tomorrow. it can generate a deepfake. it can be used for blackmail. the original subject in the global south bears the legal and reputational consequences of a transaction that had nothing to do with them.&lt;/p&gt;




&lt;p&gt;i build in port harcourt. every api call i make crosses an ocean and costs latency i can't engineer away. i wrote about that recently — the physics problem nobody warned you about.&lt;/p&gt;

&lt;p&gt;this is the other side of that piece.&lt;/p&gt;

&lt;p&gt;the infrastructure gap isn't just latency. it's who absorbs the externalities of the access war. when two parties fight over who gets to use a model, a third party — somewhere with weaker institutions, fewer legal protections, and more financial pressure to say yes to $30 — pays the cost neither of the original parties wanted to carry.&lt;/p&gt;

&lt;p&gt;that's not a side effect.&lt;/p&gt;




&lt;p&gt;the controls will keep tightening. fable has been offline for seventeen days. mythos was partially restored on june 27 — only for critical infrastructure organizations the us government specifically approved. general users, developers, international subscribers are still waiting. gpt-5.6 is next in line for the same review process. each new restriction produces a new evasion layer, and each evasion layer reaches further down the economic ladder to find humans willing to be part of the supply chain for cash.&lt;/p&gt;

&lt;p&gt;the people performing outrage about ai access — in brussels, in san francisco, in policy papers — are arguing about the front of the supply chain. nobody is arguing about the back.&lt;/p&gt;

&lt;p&gt;someone else is paying for your claude access. you won't read about them in the policy papers.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI helped me research, structure, and edit this piece. The arguments, the examples, and the opinions are mine. So is whatever's wrong with them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>security</category>
      <category>discuss</category>
    </item>
    <item>
      <title>What Actually Happens When You Call an LLM API</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Mon, 29 Jun 2026 08:07:46 +0000</pubDate>
      <link>https://dev.to/dannwaneri/what-actually-happens-when-you-call-an-llm-api-28l6</link>
      <guid>https://dev.to/dannwaneri/what-actually-happens-when-you-call-an-llm-api-28l6</guid>
      <description>&lt;p&gt;you've felt it.&lt;/p&gt;

&lt;p&gt;you type a prompt, hit send, and the response starts streaming in under a second. smooth. instant. you feel like you're thinking out loud with a machine.&lt;/p&gt;

&lt;p&gt;then the next day — same model, same prompt — you wait. three seconds. five. the cursor blinks. nothing. then it all comes at once.&lt;/p&gt;

&lt;p&gt;you probably blamed your wifi.&lt;/p&gt;

&lt;p&gt;it wasn't your wifi.&lt;/p&gt;

&lt;p&gt;what actually happened in those extra seconds is a story that starts in a building you'll never visit, runs through a cable at the bottom of an ocean, and ends on a gpu that was busy doing someone else's thinking before it got to yours.&lt;/p&gt;

&lt;p&gt;and if you're building in africa or anywhere that isn't virginia, ireland, or frankfurt — that story has a chapter in it specifically about you.&lt;/p&gt;




&lt;h2&gt;
  
  
  the journey of one api call
&lt;/h2&gt;

&lt;p&gt;let's follow a single request from the moment you hit send.&lt;/p&gt;

&lt;p&gt;your prompt leaves your device and travels as packets of data through your ISP, hits a submarine fibre cable, crosses an ocean, arrives at a data centre, gets routed to the right server, waits for a gpu to become available, gets processed, and the response travels back the same way.&lt;/p&gt;

&lt;p&gt;that whole round trip happens in what feels like nothing.&lt;/p&gt;

&lt;p&gt;except it isn't nothing. every step costs time. and some of those steps cost more depending on where you're sitting on the planet.&lt;/p&gt;




&lt;h2&gt;
  
  
  what is a data centre, actually
&lt;/h2&gt;

&lt;p&gt;before we get to the interesting parts, ground this.&lt;/p&gt;

&lt;p&gt;a data centre is a building — sometimes the size of several football pitches — filled with servers. those servers are computers without screens. stacked in metal racks. thousands of them. running twenty-four hours a day, seven days a week, never switching off.&lt;/p&gt;

&lt;p&gt;every api call you make, every message you send on whatsapp, every google search, every youtube video — all of it is touching a server in a building like this somewhere.&lt;/p&gt;

&lt;p&gt;the building needs three things to function: power, cooling, and connectivity. the power runs the servers. the cooling stops them melting — servers generate enormous heat at this density. the connectivity is the fibre cable that connects the building to the rest of the internet.&lt;/p&gt;

&lt;p&gt;nigeria has 17 of these buildings. the united states has over 5,500.&lt;/p&gt;

&lt;p&gt;that gap matters. we'll come back to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  latency: the physics problem nobody warned you about
&lt;/h2&gt;

&lt;p&gt;latency is the time it takes for data to travel from point A to point B and back.&lt;/p&gt;

&lt;p&gt;it is bounded by physics. data moves through fibre optic cable at roughly two-thirds the speed of light. you cannot make it faster. you can only make the distance shorter.&lt;/p&gt;

&lt;p&gt;lagos to london is approximately 5,000 kilometres. at two-thirds the speed of light, the minimum possible round-trip time is around 50 milliseconds just from the distance alone. add routing, congestion, processing  and you're looking at 100 to 150ms before your request has even reached the server.&lt;/p&gt;

&lt;p&gt;then the model has to think.&lt;/p&gt;

&lt;p&gt;then the response travels back.&lt;/p&gt;

&lt;p&gt;most developers building in nigeria are hitting llm servers in us-east-1 (virginia) or eu-west (ireland or frankfurt). that's not a complaint — those are where the servers are. but it means every api call carries 100 to 200ms of latency just from geography, before inference even begins.&lt;/p&gt;

&lt;p&gt;for a streaming chatbot, you feel this. that pause before the first token appears isn't the model being slow. it's the speed of light, applied to distance.&lt;/p&gt;




&lt;h2&gt;
  
  
  inference: what the gpu is actually doing
&lt;/h2&gt;

&lt;p&gt;when your prompt arrives at the server, it doesn't get processed the way you might imagine — like a search engine matching keywords.&lt;/p&gt;

&lt;p&gt;the model runs your prompt through billions of mathematical operations, layer by layer, to predict what the most likely next token should be. then the next. then the next. each token generated one at a time, sequentially, until the response is complete.&lt;/p&gt;

&lt;p&gt;this is inference.&lt;/p&gt;

&lt;p&gt;a token is roughly three-quarters of a word. "hello" is one token. "infrastructure" is two. the response you're reading right now would be several hundred tokens.&lt;/p&gt;

&lt;p&gt;why does this matter? because every token costs compute. a longer prompt costs more compute on the input side. a longer response costs more on the output side. and all of that compute is happening on a gpu inside a data centre consuming real electricity.&lt;/p&gt;




&lt;h2&gt;
  
  
  the gpu: why this specific hardware
&lt;/h2&gt;

&lt;p&gt;your laptop has a cpu — central processing unit. it's designed for general tasks: running your browser, compiling your code, handling your operating system. very fast at one thing at a time.&lt;/p&gt;

&lt;p&gt;a gpu — graphics processing unit — was originally designed to render video games. thousands of smaller cores that can do many calculations simultaneously. it turns out this parallel architecture is exactly what llm inference needs: running the same mathematical operations across billions of parameters at once.&lt;/p&gt;

&lt;p&gt;a single high-end gpu used for llm inference — an nvidia h100 — costs around $30,000. a data centre running a frontier model has thousands of them.&lt;/p&gt;

&lt;p&gt;when you call an llm api, your request is routed to one of these gpus. if that gpu is busy processing another user's request, yours waits. that wait is real. it shows up as latency on your end.&lt;/p&gt;

&lt;p&gt;this is what rate limits are actually enforcing: the physical capacity of the hardware.&lt;/p&gt;




&lt;h2&gt;
  
  
  cold starts: why the first request is slower
&lt;/h2&gt;

&lt;p&gt;you've noticed that sometimes the very first call in a while takes noticeably longer.&lt;/p&gt;

&lt;p&gt;this isn't imaginary. it's a cold start.&lt;/p&gt;

&lt;p&gt;models are large. a frontier model can be hundreds of gigabytes of weights — the numbers that encode what the model knows. those weights need to be loaded into gpu memory before inference can happen. if no request has come in for a while, the system may have partially unloaded the model to free up memory for other things.&lt;/p&gt;

&lt;p&gt;the first request has to wait for the model to load back in. subsequent requests hit the already-warm model and feel faster.&lt;/p&gt;

&lt;p&gt;serverless llm deployments are especially prone to this. you pay less when traffic is low. but your users feel the first request after a quiet period.&lt;/p&gt;




&lt;h2&gt;
  
  
  why nigeria specifically
&lt;/h2&gt;

&lt;p&gt;nigeria's 17 data centres — 14 of them in lagos — run almost entirely on diesel generators. the national grid provides on average four hours of power per day. every data centre makes up the difference with generators burning diesel around the clock.&lt;/p&gt;

&lt;p&gt;this is expensive. it's also why local cloud infrastructure hasn't scaled the way it has in markets with stable power.&lt;/p&gt;

&lt;p&gt;the consequence for you as a developer: every llm api call you make routes to a server that is not in nigeria. not in west africa. often not even on the continent. you are paying the latency cost of that distance on every single request, for every single user you have.&lt;/p&gt;

&lt;p&gt;this isn't a software problem. it's a geography and infrastructure problem. and it has a direct effect on how your ai-powered products feel to the people using them.&lt;/p&gt;




&lt;h2&gt;
  
  
  what this means when you're building
&lt;/h2&gt;

&lt;p&gt;three practical things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;stream the response.&lt;/strong&gt; don't wait for the full response before showing anything. streaming tokens as they arrive makes the experience feel faster even when it isn't. the perceived latency drops dramatically because the user sees something happening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cache aggressively.&lt;/strong&gt; if you're calling the same prompt or near-identical prompts repeatedly, cache the response. inference is expensive. latency is expensive. caching eliminates both for repeated queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pick the right model for the job.&lt;/strong&gt; a 70 billion parameter model is slower and more expensive than a 7 billion parameter model. for many tasks — classification, extraction, short-form generation — the smaller model is sufficient and returns results significantly faster. frontier models are not always the right tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  the bigger picture
&lt;/h2&gt;

&lt;p&gt;data centres exist because computation has to live somewhere physical. it takes power, water, land, and connectivity to run the infrastructure that makes ai feel effortless.&lt;/p&gt;

&lt;p&gt;africa accounts for less than 1% of global data centre capacity while housing 18% of the world's population. the gap between what the continent generates as digital demand and what it owns as infrastructure is where the latency comes from, where the dependency comes from, where the value extraction happens.&lt;/p&gt;

&lt;p&gt;knowing it's a physics problem, not a code problem, changes where you look. knowing that equinix, aws, and microsoft own most of the continent's usable capacity changes what you think about it.&lt;/p&gt;

&lt;p&gt;it's probably not your code. it's a building somewhere running on diesel.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI helped me research, structure, and edit this piece. The arguments, the examples, and the opinions are mine. So is whatever's wrong with them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Everyone's Excited About Claude Tag. Nobody's Built the Trust Layer.</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Wed, 24 Jun 2026 13:23:02 +0000</pubDate>
      <link>https://dev.to/dannwaneri/everyones-excited-about-claude-tag-nobodys-built-the-trust-layer-1ohp</link>
      <guid>https://dev.to/dannwaneri/everyones-excited-about-claude-tag-nobodys-built-the-trust-layer-1ohp</guid>
      <description>&lt;p&gt;Andrej Karpathy, OpenAI co-founder and former Tesla AI director, called Claude Tag the third major redesign of LLM UI/UX. First the LLM was a website. Then it was an app you downloaded. Now it's a persistent, asynchronous teammate that lives inside your Slack channels with org-wide context. He's right about the architecture. He's silent on what happens to the room.&lt;/p&gt;

&lt;p&gt;Simon Smith, who'd already wired ChatGPT Workspace Agents into his team's Slack, said ambient visibility helps adoption: people watch each other use Claude in a shared channel and learn organically, no training program required. That's true for the person who turned Claude on. It's a different experience for the person who didn't get a vote.&lt;/p&gt;

&lt;p&gt;I wrote about this eighteen hours after the announcement. Tag Claude into a five-person team and the moment it joins, every message anyone types is something an AI reads. You stop looking like someone using a tool. You start looking like the person who brought a surveillance device into the meeting. The frame you've built from there is unwinnable: good output gets read as "she's outsourcing her thinking." Mediocre output gets read as "see, this is what we were worried about." There's no third outcome that proves the skeptics wrong.&lt;/p&gt;

&lt;p&gt;Gail Weiner replied to that thread with something simple. Bring the skeptics into the conversation. Ask how comfortable they are. Start small, let them pick the first use case, and let the small win be something they can point to and say out loud: this added value.&lt;/p&gt;

&lt;p&gt;That's not diplomacy. It's a trust mechanism with a hard edge. The moment a skeptic says "okay, this added value" out loud, they're no longer the person blocking the rollout. They're on record as the person who approved it. Gail named it better than I did: the human trust layer. Everything Karpathy is excited about runs on top of that layer, and nobody launching Claude Tag this week is talking about who builds it.&lt;/p&gt;

&lt;p&gt;Days before this launch I wrote &lt;a href="https://github.com/dannwaneri/production-safe-agent-loop" rel="noopener noreferrer"&gt;&lt;code&gt;production-safe-agent-loop&lt;/code&gt;&lt;/a&gt;, a small Python library for keeping single-agent loops from running away. A four-agent LangChain loop ran eleven days and cost $47,000. Claude Code recursion has burned $16,000 to $50,000 in five hours. The fix wasn't a smarter agent. It was five primitives: a spec writer that forces three answers before the loop runs, a circuit breaker with hard ceilings, an append-only ledger, the loop that respects both, and a review surface that assembles a fixed five-element frame once the run finishes: the original promise, the acceptance criteria, the diff, the evidence, and the unresolved assumptions.&lt;/p&gt;

&lt;p&gt;The last piece is the one that matters here: attestation. A human reviews the frame, and attestation is not approval. It's a record that they reviewed exactly what's in front of them and they're taking responsibility for what happens next. The frame gets hashed. Two reviewers attesting the same session get the same hash. That's a receipt, not a vibe.&lt;/p&gt;

&lt;p&gt;Claude Tag has none of this. It's ambient, persistent, and it decides on its own initiative what's relevant across every channel it's in. The five-element frame I built for single-agent loops maps directly onto "what did Claude decide in this channel, and did a human actually sign off on it." Just at team scale, across a dozen channels, not one developer's terminal session.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ships without it
&lt;/h2&gt;

&lt;p&gt;VentureBeat is asking about data retention and vendor lock-in. Twitter is asking what it can do. The actual unresolved question is structural: when an ambient agent acts on its own initiative across a dozen channels, who gets the five-element frame, who has to attest to it, and what happens when nobody does.&lt;/p&gt;

&lt;p&gt;Claude Tag isn't wrong to exist. It shipped the easy half. The architecture works. The trust layer is still unbuilt, and it's not a UX problem. It's an audit problem with a name and a shape, and I already wrote the code for what it looks like when someone takes it seriously.&lt;/p&gt;

&lt;p&gt;More on agent governance at &lt;a href="https://dannwaneri.com/ai-agents/" rel="noopener noreferrer"&gt;dannwaneri.com/ai-agents&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI helped me research and edit this piece. The arguments, the examples, and the opinions are mine. So is whatever's wrong with them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Something Changed After the Sloan Articles. I Can't Prove It.</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Wed, 24 Jun 2026 07:24:33 +0000</pubDate>
      <link>https://dev.to/dannwaneri/something-changed-after-the-sloan-articles-i-cant-prove-it-5009</link>
      <guid>https://dev.to/dannwaneri/something-changed-after-the-sloan-articles-i-cant-prove-it-5009</guid>
      <description>&lt;p&gt;This is the third piece in a sequence. The first asked whether Sloan had flagged anyone else — &lt;a href="https://dev.to/dannwaneri/has-sloan-flagged-your-article-lately-1gmh"&gt;it had&lt;/a&gt;. The second documented what I found out — &lt;a href="https://dev.to/dannwaneri/i-got-flagged-by-sloan-sloan-is-a-guy-i-know-3d0e"&gt;Sloan is a person I know&lt;/a&gt;. This one is about what happened after.&lt;/p&gt;

&lt;p&gt;I want to be precise about what I'm describing, because I can't prove what I think happened.&lt;/p&gt;

&lt;p&gt;Here's what I can document.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Sequence
&lt;/h2&gt;

&lt;p&gt;I published two essays in June. Both generated real technical discussion — one had a five-exchange comment thread that became a production open-source repo. The founder of DEV.to liked one before it was flagged. Both got flagged by Sloan the same day.&lt;/p&gt;

&lt;p&gt;I wrote about it. Two articles, 72 combined comments. The conversation prompted Francis to publish his own post opening the floor to community questions about moderation. Jess Lee and Ben Halpern both commented there — Jess saying DEV would be updating moderation guidelines soon, Ben calling it a big priority. Neither answered the specific question xulingfeng asked: how many Sloan warnings trigger account-level flagging, and do authors get notified when it happens?&lt;/p&gt;

&lt;p&gt;While the Sloan articles were live, I published a sponsored piece on AI code review. It had full disclosure from the start. Sloan flagged it the same day anyway — the third flag on my account in one week.&lt;/p&gt;

&lt;p&gt;Then I built something from it — Proof of Human, a reverse Turing Test submitted to the June Solstice Game Jam. In the first hour, people played it, dropped their scores in the comments, had real exchanges. Francis played it. Sylwia found a bug with gibberish inputs. The thread got 27 comments.&lt;/p&gt;

&lt;p&gt;Then it stopped.&lt;/p&gt;

&lt;p&gt;Not tapered. Stopped. The kind of stop where you go check the challenge submissions page and can't find your article. I messaged Jess. Her reply: &lt;em&gt;"I'm confirming that I see your submission on my end :)"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I still don't know if it's in the judging pool.&lt;/p&gt;




&lt;h2&gt;
  
  
  The LLM Visibility Article
&lt;/h2&gt;

&lt;p&gt;This one predates the Sloan series.&lt;/p&gt;

&lt;p&gt;I published a piece on open source LLM visibility tracking before any of the flagging happened. Real tool, real finding, 0% citation score on one of my own domains. Named specific tools, specific costs, specific data from a Tom Capper webinar at SEJ. The AI disclosure was in it from the start. It was gaining traction.&lt;/p&gt;

&lt;p&gt;Then the Sloan message arrived anyway. I deleted it.&lt;/p&gt;

&lt;p&gt;After the two Sloan articles, I republished it. Same article, same content, same quality. It got neither the engagement it had been building the first time nor the outside traffic my work usually pulls when DEV.to doesn't surface it.&lt;/p&gt;

&lt;p&gt;I'm not attributing that entirely to suppression. The article is more niche than my usual work and republished articles rarely perform like originals. But the gap between the first run and the second run is real. The first version was building. The second version went nowhere.&lt;/p&gt;

&lt;p&gt;That's the before-and-after I can actually document: same article, two runs, one deleted after a moderation message, the other published after two articles about that moderation message. The variable that changed between the two runs is visible.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Qodo Article
&lt;/h2&gt;

&lt;p&gt;One day after the second Sloan article, I published a sponsored piece on AI code review — a real experiment, real bugs found, a Stripe webhook handler that Claude Code generated and Qodo reviewed. The disclosure was in the article from the start. The sponsorship was disclosed at the bottom.&lt;/p&gt;

&lt;p&gt;Sloan flagged it the same day it published.&lt;/p&gt;

&lt;p&gt;Francis resolved it the same day. But that's the third flag on my account, in the same week the Sloan articles were live. The article that had the clearest, most complete disclosure of any piece I've published here still got flagged — because the flag apparently doesn't read the article. It fires, then a human decides.&lt;/p&gt;

&lt;p&gt;The flag landing on an article that had full disclosure from the start is the data point. Not the comment count — sponsored posts perform differently and that's a confounding variable. But a third flag in one week, on an article that did everything the policy asks, says something about how the flag mechanism works. It fires first. Disclosure is checked after.&lt;/p&gt;




&lt;h2&gt;
  
  
  What xulingfeng Documented
&lt;/h2&gt;

&lt;p&gt;This isn't just me.&lt;/p&gt;

&lt;p&gt;In the comments on my second Sloan article, xulingfeng ran a clean experiment: published identical content twice, once with AI disclosure and once without. Both versions got suppressed. Not the same — disappeared from feed, not showing in search, visible only to followers via direct link.&lt;/p&gt;

&lt;p&gt;Francis confirmed that three Sloan warnings trigger account-level flagging. xulingfeng had three warnings. Francis unflagged the account after the thread.&lt;/p&gt;

&lt;p&gt;Jess Lee also commented in that thread — on Francis's follow-up post — saying there are hundreds of mods with the ability to send Sloan messages. Francis had said in my thread he was essentially the only active one. Those two statements don't fully square. Which means the warning count on any given account could be coming from multiple people, and nobody's tracking it as a running total an author can see.&lt;/p&gt;

&lt;p&gt;I had two warnings.&lt;/p&gt;

&lt;p&gt;I don't know if two warnings triggered anything. I don't know if there's a threshold below three. I don't know if the Sloan articles themselves triggered something separate from the warning count. No one has told me. The guidelines don't say.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm Not Saying
&lt;/h2&gt;

&lt;p&gt;I'm not saying DEV.to is suppressing me deliberately. I'm not saying Francis made a bad call. He flagged what he thought needed flagging and said so publicly.&lt;/p&gt;

&lt;p&gt;I'm not saying the algorithm is broken. I'm saying I can't read it, and when I try to read it from the outside, the pattern I see doesn't match the engagement signal.&lt;/p&gt;

&lt;p&gt;27 comments and active game play in the first hour. Invisible in the challenge submissions list. Not surfaced.&lt;/p&gt;

&lt;p&gt;An article with real first-hour traction that then flatlined in a way none of my previous work has.&lt;/p&gt;

&lt;p&gt;That could be noise. That could be how the algorithm works now and I'm reading meaning into variance. I've been on this platform long enough to know that happens.&lt;/p&gt;

&lt;p&gt;But I also know that xulingfeng documented the same pattern in the same week with direct evidence. And I know that Francis confirmed account-level flagging exists, has a threshold, and isn't communicated to authors when it triggers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Question I Actually Have
&lt;/h2&gt;

&lt;p&gt;Not "was I suppressed" — I can't prove that.&lt;/p&gt;

&lt;p&gt;Not "was the flagging fair" — I added the disclaimers, the policy is reasonable, I don't have a fight to pick there.&lt;/p&gt;

&lt;p&gt;The question is simpler: &lt;strong&gt;if there's an account-level flag that affects distribution, should authors be told when it's active?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;xulingfeng wasn't told. She documented the suppression herself through experimentation. xulingfeng had to run a controlled experiment to figure out why her articles disappeared. That's the answer to "should authors be told."&lt;/p&gt;

&lt;p&gt;I might have the same flag on my account right now. I don't know. The challenge submission might be invisible to judges. I don't know.&lt;/p&gt;

&lt;p&gt;I should be able to know.&lt;/p&gt;

&lt;p&gt;One more instance, smaller but the same pattern: a tag moderator removed the &lt;code&gt;#beginners&lt;/code&gt; tag from an earlier article because it was too advanced for the tag. I found out not through a notification but through a comment he left on a different article entirely — the first Sloan thread. Same pattern: distribution affected, author not told directly, discovered accidentally through a comment on a separate post.&lt;/p&gt;

&lt;p&gt;One more data point, for context: a freeCodeCamp tutorial built on the same thinking, the same writing process, the same AI assistance in the workflow — published the same week. The freeCodeCamp editor's response: zero fixes needed. Same ideas. Same process. One platform flagged it. The other published it without a single editorial change. That's not a contradiction to resolve. It's just where we are.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was written with AI assistance for research and editing. All arguments, examples, and opinions are my own.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>meta</category>
      <category>devto</category>
      <category>discuss</category>
    </item>
    <item>
      <title>The LLM Visibility Tools Cost $79/Month. Mine is Open Source.</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Tue, 23 Jun 2026 12:33:11 +0000</pubDate>
      <link>https://dev.to/dannwaneri/the-llm-visibility-tools-cost-79month-mine-is-open-source-29hb</link>
      <guid>https://dev.to/dannwaneri/the-llm-visibility-tools-cost-79month-mine-is-open-source-29hb</guid>
      <description>&lt;p&gt;Tom Capper at a Search Engine Journal webinar last week:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"There's no Search Console equivalent for LLMs."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Google Search Console tells you where you rank, how many people saw your result, your CTR, your average position. It tells you nothing about whether Claude mentions your domain when someone asks a question you should own.&lt;/p&gt;

&lt;p&gt;That gap is now a product category. I checked most of them. Cheapest entry point: $39/month. The ones worth taking seriously: $79 and up.&lt;/p&gt;

&lt;p&gt;At the same webinar, someone asked Capper directly: what are accessible ways to measure AEO/GEO visibility, since there's no equivalent of Search Console for LLMs? His answer was three manual approaches. None of them were a tool you could run.&lt;/p&gt;

&lt;p&gt;I built mine for free. Open source. And it found something I'd missed for months on my own sites.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the paid tools actually do
&lt;/h2&gt;

&lt;p&gt;AIclicks, LLMrefs, Cairrot, Slate — the mechanics are the same across all of them. They query AI models with your target keywords, check whether your brand appears in the response, track it over time.&lt;/p&gt;

&lt;p&gt;That's the whole product. The variance is in how many LLMs they cover, how the reports look, and whether the UI justifies the monthly fee.&lt;/p&gt;

&lt;p&gt;None of them are doing anything technically unusual. They're calling APIs and parsing text.&lt;/p&gt;

&lt;p&gt;So I added it to &lt;a href="https://github.com/dannwaneri/seo-agent" rel="noopener noreferrer"&gt;seo-agent&lt;/a&gt; as a standalone module: &lt;code&gt;llm-visibility&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Point it at your domain and a query list — or just export from Google Search Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python main.py llm-visibility &lt;span class="nt"&gt;--domain&lt;/span&gt; dannwaneri.com &lt;span class="nt"&gt;--queries&lt;/span&gt; gsc-export.csv &lt;span class="nt"&gt;--project&lt;/span&gt; dannwaneri-com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes your top 20 queries by impressions. Sends each one to Claude Haiku. Checks whether your domain appears in the response. Writes everything to &lt;code&gt;llm-visibility.md&lt;/code&gt; — visibility score, per-query results, gaps to address.&lt;/p&gt;

&lt;p&gt;Cost: negligible. A 20-query run costs less than a cent.&lt;/p&gt;




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

&lt;p&gt;I ran it on two of my own domains.&lt;/p&gt;

&lt;p&gt;One scored 0%. Every query. Claude answered correctly — sometimes well — and never mentioned my site once.&lt;/p&gt;

&lt;p&gt;The other scored 15%. Three out of twenty queries returned a mention. The other seventeen? Nothing.&lt;/p&gt;

&lt;p&gt;Both domains have content on these exact topics. Both rank in Google for these queries. Neither is getting cited.&lt;/p&gt;

&lt;p&gt;The same run also caught something else — a query sitting at position 9.5 with 29 impressions and 0% CTR: &lt;code&gt;does twitch pay nigerians&lt;/code&gt;. The page was ranking. The title was answering the wrong question. That's not an LLM problem, that's a GSC problem. But &lt;code&gt;llm-visibility&lt;/code&gt; sits on the same audit surface as &lt;code&gt;gsc-insights&lt;/code&gt;, not separate from it. You find both in one pass.&lt;/p&gt;

&lt;p&gt;Your Google rank tells you nothing about your LLM presence. They're different surfaces with different citation logic entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  The limitation I won't bury
&lt;/h2&gt;

&lt;p&gt;Claude has a training data cutoff. Content published after that cutoff won't appear regardless of quality.&lt;/p&gt;

&lt;p&gt;A score of 0% on a site less than a year old is expected — it's a data availability problem, not a content quality problem.&lt;/p&gt;

&lt;p&gt;Run it quarterly. Watch the number move as training snapshots update.&lt;/p&gt;

&lt;p&gt;The paid tools have this same limitation. They just don't always put it where you can see it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the pixel data adds
&lt;/h2&gt;

&lt;p&gt;While I was building this, the same SEJ webinar surfaced pixel data from Tom Capper at STAT Search Analytics worth flagging: position 1 now sits 635 pixels down the page on desktop. On mobile, the top organic result is below the fold nearly two thirds of the time. AI Overviews consume roughly a third of above-the-fold space on informational queries. Paid and shopping units take over 60% on commercial ones. Organic gets what's left. (&lt;a href="https://www.searchenginejournal.com/updated-serps-new-study-measures-the-serp-in-pixels-not-ranks-recap/577359/" rel="noopener noreferrer"&gt;Source: Search Engine Journal, May 2026&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;seo-agent&lt;/code&gt; covers both surfaces. The &lt;code&gt;serp-features&lt;/code&gt; module hits SerpApi for each target query and maps which features are present — AI Overview, featured snippet, PAA, image pack, local pack. &lt;code&gt;llm-visibility&lt;/code&gt; handles the other surface.&lt;/p&gt;

&lt;p&gt;Neither module needs a paid subscription. SerpApi has a free tier: 100 searches/month, no credit card.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's still missing
&lt;/h2&gt;

&lt;p&gt;This only tests Claude. Not ChatGPT, not Perplexity, not Gemini.&lt;/p&gt;

&lt;p&gt;The paid tools cover 6–10 models. That's a real gap.&lt;/p&gt;

&lt;p&gt;I'm one person. Multi-model support is on the list.&lt;/p&gt;

&lt;p&gt;If your Claude visibility score is 0%, adding Perplexity to the test won't fix the underlying problem. The content isn't strong enough to get cited anywhere. Fix that first. Then track across more models.&lt;/p&gt;




&lt;h2&gt;
  
  
  The tool
&lt;/h2&gt;

&lt;p&gt;Everything is open source: &lt;a href="https://github.com/dannwaneri/seo-agent" rel="noopener noreferrer"&gt;github.com/dannwaneri/seo-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full breakdown of what it found on a real site — a Nigerian creator site that went from 0.4% to 44% pass rate in one afternoon — at &lt;a href="https://dannwaneri.com/seo-automation/" rel="noopener noreferrer"&gt;dannwaneri.com/seo-automation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The full module list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;llm-visibility&lt;/code&gt; — LLM citation tracking&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;serp-features&lt;/code&gt; — SERP feature detection via SerpApi&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gsc-insights&lt;/code&gt; — GSC export parser, quick wins, cannibalization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;qualify-backlinks&lt;/code&gt; — referring domain scoring&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;relevance-score&lt;/code&gt; — internal link opportunity scoring&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cluster-audit&lt;/code&gt; — topic clustering, orphan detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Core audit runs in a real Chromium browser. Extracts title, meta description, H1s, canonical. Checks broken links. Resumable JSON state.&lt;/p&gt;

&lt;p&gt;The speaker said there's no Search Console for LLMs.&lt;/p&gt;

&lt;p&gt;There is now. And it doesn't cost $79/month.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was written with AI assistance for research and editing. All arguments, examples, and opinions are my own.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Sat, 20 Jun 2026 16:06:35 +0000</pubDate>
      <link>https://dev.to/dannwaneri/-4icp</link>
      <guid>https://dev.to/dannwaneri/-4icp</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7" class="crayons-story__hidden-navigation-link"&gt;Proof of Human: I Built a Reverse Turing Test After Getting Flagged as AI&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;June Solstice Game Jam Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/dannwaneri" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3606168%2F7684e1e1-b986-4ee3-ae5b-56db2b97d286.jpg" alt="dannwaneri profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/dannwaneri" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Daniel Nwaneri
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Daniel Nwaneri
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3932945" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/dannwaneri" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3606168%2F7684e1e1-b986-4ee3-ae5b-56db2b97d286.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Daniel Nwaneri&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7" id="article-link-3932945"&gt;
          Proof of Human: I Built a Reverse Turing Test After Getting Flagged as AI
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gamechallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gamechallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gamedev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gamedev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;26&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              27&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Proof of Human: I Built a Reverse Turing Test After Getting Flagged as AI</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Thu, 18 Jun 2026 14:10:45 +0000</pubDate>
      <link>https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7</link>
      <guid>https://dev.to/dannwaneri/proof-of-human-i-built-a-reverse-turing-test-after-getting-flagged-as-ai-31e7</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;p&gt;I got flagged by Sloan.&lt;/p&gt;

&lt;p&gt;If you've been on DEV long enough, you know Sloan. I thought Sloan was a bot. &lt;a href="https://dev.to/dannwaneri/i-got-flagged-by-sloan-sloan-is-a-guy-i-know-3d0e"&gt;Sloan is Francis&lt;/a&gt; — someone I've exchanged comments with for months, since before Richard left the platform. He posted about the flagging openly, tagged the founders, explained his reasoning. Then added: &lt;em&gt;"This was hard to tell you for many reasons."&lt;/em&gt; He reads every flagged article himself, runs it through GPTZero, makes a call. He knew me. He flagged me anyway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/dannwaneri/the-loop-is-not-the-product-466d"&gt;One of the flagged articles&lt;/a&gt; had sparked a five-exchange comment thread that became an open-source repo. The thinking was mine. The flag still landed.&lt;/p&gt;

&lt;p&gt;That's the uncomfortable thing about the Turing Test in 2026: it doesn't measure origin. It measures surface texture. And if you write well enough, you sound like a machine.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://proof-of-human-3ts.pages.dev" rel="noopener noreferrer"&gt;Play Proof of Human →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A reverse Turing Test. Five questions. You write your answers. Claude scores them 0–100 on how human they sound, tells you what gave you away, and at the end gives you an average.&lt;/p&gt;

&lt;p&gt;The questions are the ones that actually separate humans from pattern-matchers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Describe the last time something genuinely surprised you. Not shocked — surprised.&lt;/li&gt;
&lt;li&gt;What's something you changed your mind about in the last year? What moved you?&lt;/li&gt;
&lt;li&gt;What's a skill you have that you never bothered to put on your CV?&lt;/li&gt;
&lt;li&gt;Name something you've read or watched that you think about more than you expected to.&lt;/li&gt;
&lt;li&gt;What do you actually think about AI? Not what you're supposed to think — what you actually think.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scoring prompt at the heart of it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Human signals: named specifics, opinions that could get you in trouble, genuine uncertainty, things slightly off-topic but revealing.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;AI signals: balanced framing, hedge words, smooth transitions, excessive completeness.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Score 60+: &lt;strong&gt;Passes&lt;/strong&gt;. Below 60: &lt;strong&gt;Flagged&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The June solstice is the longest day — the day the sun is most itself. Unambiguous. No hedging. That's what this game is asking for. Not your best answer. Your most &lt;em&gt;you&lt;/em&gt; answer. Turing's original question was: &lt;em&gt;can a machine think?&lt;/em&gt; The question we're living with now is its inversion — &lt;em&gt;can a human still sound like one?&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pSCJ30JcDDo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dannwaneri" rel="noopener noreferrer"&gt;
        dannwaneri
      &lt;/a&gt; / &lt;a href="https://github.com/dannwaneri/proof-of-human" rel="noopener noreferrer"&gt;
        proof-of-human
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Proof of Human&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A reverse Turing Test. Five questions. Claude scores your answers 0–100 on how human they sound and tells you what gave you away.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://proof-of-human-3ts.pages.dev" rel="nofollow noopener noreferrer"&gt;Play it →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;You write. Claude reads. It scores on one axis: specificity that costs you something. Named people, opinions you might regret, genuine uncertainty, things slightly off-topic but revealing. Those pass. Balanced framing, hedge words, smooth transitions — those get flagged.&lt;/p&gt;
&lt;p&gt;Score 60+: &lt;strong&gt;Passes&lt;/strong&gt;. Below 60: &lt;strong&gt;Flagged&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Frontend: single &lt;code&gt;index.html&lt;/code&gt;, vanilla JS, no framework, no build step&lt;/li&gt;
&lt;li&gt;Backend: Cloudflare Worker (keeps API key server-side)&lt;/li&gt;
&lt;li&gt;Hosting: Cloudflare Pages&lt;/li&gt;
&lt;li&gt;Model: &lt;code&gt;claude-sonnet-4-6&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy your own&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;1. Deploy the Worker&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; worker
npm install
wrangler secret put ANTHROPIC_API_KEY
wrangler deploy&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;2. Update the API URL in &lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;API_URL&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;"https://your-worker.your-subdomain.workers.dev/score"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;3. Deploy the frontend&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; From the repo root&lt;/span&gt;
wrangler pages project create proof-of-human --production-branch main
wrangler pages&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dannwaneri/proof-of-human" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





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

&lt;p&gt;Vanilla JS, no dependencies, one HTML file. Cloudflare Worker as proxy, Pages for hosting.&lt;/p&gt;

&lt;p&gt;The Worker sits between the browser and Anthropic. It receives your prompt and response, calls the API, returns &lt;code&gt;{ score, verdict, reason }&lt;/code&gt;. The frontend never sees the API key. One call per question, nothing stored, model is &lt;code&gt;claude-sonnet-4-6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The frontend is a single &lt;code&gt;index.html&lt;/code&gt; — progress bar, animated score fill, final breakdown screen. No build step. A judge can open DevTools and follow exactly what happens on each submit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scoring prompt took the most iteration.&lt;/strong&gt; The first version was too generous — everything passed. The second was too harsh — everything got flagged. The final version keys on one thing: &lt;em&gt;specificity that costs you something&lt;/em&gt;. An answer that names a real person, admits a real mistake, or takes a position you might regret. That's what the model now reliably catches.&lt;/p&gt;

&lt;p&gt;The irony: I had to write like an AI to build a detector for AI writing. I kept second-guessing my own prompt phrasing, smoothing transitions, hedging. The game caught me too.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Best Ode to Alan Turing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Turing's question was whether a machine could fool a human. This game inverts it — can a human fool the machine? The mechanic is the Turing Test itself, running live, aimed back at the player.&lt;/p&gt;

&lt;p&gt;The submission post has a real backstory: I got flagged as AI-generated on this platform the same week I built this. The incident is documented in two public articles with 60+ comments between them. Writing that passed human editorial review at freeCodeCamp got flagged by a detector on DEV. That's not a contradiction to resolve. That's just where we are  and it's the question this game puts directly to you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built June 2026. Vanilla JS. One API call. No frameworks. The Sloan incident was real.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gamechallenge</category>
      <category>gamedev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Claude Code Wrote the PR. Here's What the Code Review Actually Caught.</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Wed, 17 Jun 2026 16:40:06 +0000</pubDate>
      <link>https://dev.to/dannwaneri/claude-code-wrote-the-pr-heres-what-the-code-review-actually-caught-329f</link>
      <guid>https://dev.to/dannwaneri/claude-code-wrote-the-pr-heres-what-the-code-review-actually-caught-329f</guid>
      <description>&lt;p&gt;Everyone is shipping AI-generated code right now. Most of it is going straight to main.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick verdict:&lt;/strong&gt; Qodo catches production-grade bugs in AI-generated code before they ship. Claude Code generated a Stripe webhook handler that passed TypeScript, looked clean, and had six real bugs — an ack-before-processing pattern that would silently drop fulfilled orders, no replay protection, a non-atomic rate limiter, a DoS-prone body read, a timing-unsafe signature compare, and a shared rate-limit bucket for null IPs. Qodo flagged all six in 90 seconds. Two of them I hadn't planted; the review reasoned them out from how the code behaves at runtime, not what it says.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm not going to tell you that's always wrong. A lot of it is fine. But I've been building production systems on Cloudflare Workers for six years, and I know exactly how "fine" can turn into a 2am incident. The subtle bugs — the ones that pass a quick read, pass TypeScript, pass your linter — are the ones that hurt.&lt;/p&gt;

&lt;p&gt;So I ran an experiment. I asked Claude Code to generate a Stripe webhook handler for a Cloudflare Worker. I did not edit it. I did not second-guess it. I opened a PR and let &lt;a href="https://qodo.ai" rel="noopener noreferrer"&gt;Qodo&lt;/a&gt; run a code review on it.&lt;/p&gt;

&lt;p&gt;This is what happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  the setup
&lt;/h2&gt;

&lt;p&gt;The feature: a Cloudflare Worker that receives Stripe webhooks, validates HMAC signatures, rate-limits by IP using KV, and processes &lt;code&gt;checkout.session.completed&lt;/code&gt; and &lt;code&gt;payment_intent.payment_failed&lt;/code&gt; events.&lt;/p&gt;

&lt;p&gt;That's a real thing. It's the kind of feature an AI tool generates confidently and completely. Looks clean. Passes TypeScript strict mode. The logic flow makes sense on a first read.&lt;/p&gt;

&lt;p&gt;It also had six bugs.&lt;/p&gt;

&lt;p&gt;Here's the repo: &lt;a href="https://github.com/dannwaneri/stripe-webhook-worker" rel="noopener noreferrer"&gt;github.com/dannwaneri/stripe-webhook-worker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code is 173 lines of TypeScript. Signature validation, rate limiting, event dispatch, KV writes. Nothing exotic. Exactly the kind of thing you'd ship on a deadline without a second look.&lt;/p&gt;




&lt;h2&gt;
  
  
  the code Claude generated
&lt;/h2&gt;

&lt;p&gt;The entry point looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;request&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="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExecutionContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;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;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&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;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;CF-Connecting-IP&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;unknown&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;limited&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkRateLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limited&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rate limit exceeded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;429&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;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&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;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;stripe-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing stripe-signature header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&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;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;validateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ... parse JSON, dispatch event&lt;/span&gt;

    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;processEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&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;p&gt;Reads cleanly. Validates the signature. Rate-limits. Returns 200. Done.&lt;/p&gt;

&lt;p&gt;Except no.&lt;/p&gt;




&lt;h2&gt;
  
  
  running qodo
&lt;/h2&gt;

&lt;p&gt;Installing Qodo took about five minutes. Go to the &lt;a href="https://github.com/marketplace/qodo-merge-pro" rel="noopener noreferrer"&gt;GitHub Marketplace&lt;/a&gt;, install the app, scope it to the repo. Then connect at &lt;a href="https://app.qodo.ai" rel="noopener noreferrer"&gt;app.qodo.ai&lt;/a&gt;. Once that's linked, you trigger a review by commenting on the PR:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Ninety seconds later, Qodo's response appeared in the PR thread. Six bugs. Zero rule violations.&lt;/p&gt;




&lt;h2&gt;
  
  
  what it found
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding 1: ack before processing (action required — reliability)
&lt;/h3&gt;

&lt;p&gt;This was the top-priority finding, and it's the one that would have caused a real incident.&lt;/p&gt;

&lt;p&gt;The code calls &lt;code&gt;ctx.waitUntil(processEvent(event, env))&lt;/code&gt; and immediately returns &lt;code&gt;200 OK&lt;/code&gt;. Stripe sees the 2xx and stops retrying. But &lt;code&gt;processEvent&lt;/code&gt; runs in the background — if it fails (KV timeout, unhandled exception, runtime termination), Stripe never knows. The order goes unfulfilled. No alert fires. The customer waits.&lt;/p&gt;

&lt;p&gt;Qodo's fix: either &lt;code&gt;await processEvent(event, env)&lt;/code&gt; and return non-2xx on failure so Stripe retries, or persist the event to a durable queue before returning 200, then process with retries separately.&lt;/p&gt;

&lt;p&gt;I knew there was no try/catch in &lt;code&gt;processEvent&lt;/code&gt;. I hadn't framed it as an &lt;em&gt;acknowledgment&lt;/em&gt; problem — that framing is sharper and explains the real-world failure mode directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 2: no replay protection (action required — security)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;validateSignature&lt;/code&gt; function parses Stripe's &lt;code&gt;t=timestamp&lt;/code&gt; from the header and uses it to reconstruct the signed payload. That's correct. What it doesn't do is check whether the timestamp is recent.&lt;/p&gt;

&lt;p&gt;Stripe's own documentation says to reject any webhook where the timestamp is more than five minutes old. Without that check, a valid captured webhook can be replayed indefinitely. Same valid signature, same event ID, processed again.&lt;/p&gt;

&lt;p&gt;The fix is four lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// reject events older than 5 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Finding 3: non-atomic rate limiting (remediation recommended — reliability)
&lt;/h3&gt;

&lt;p&gt;The rate limiter reads the current count from KV, checks if it's under the limit, then writes the incremented count back. Two concurrent requests both read &lt;code&gt;count = 0&lt;/code&gt;, both pass the check, both write &lt;code&gt;count = 1&lt;/code&gt;. Under any real burst the rate limiter is trivially bypassed.&lt;/p&gt;

&lt;p&gt;The correct implementation uses Durable Objects for atomic counters, or pushes the rate logic to Cloudflare's native rate limiting API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 4: body read before header check (remediation recommended — security)
&lt;/h3&gt;

&lt;p&gt;The code reads &lt;code&gt;rawBody = await request.text()&lt;/code&gt; before checking whether the &lt;code&gt;stripe-signature&lt;/code&gt; header even exists. That means any request without a signature — a scanner, a bot, a misconfigured service — forces the worker to consume and buffer the full request body before being rejected.&lt;/p&gt;

&lt;p&gt;For most requests that's noise. For a large payload flood it's a real DoS surface. The fix is to check the header first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 5: timing-unsafe signature compare (advisory — security)
&lt;/h3&gt;

&lt;p&gt;The computed HMAC is compared to the expected hash with &lt;code&gt;===&lt;/code&gt;. JavaScript string comparison short-circuits on the first mismatched character, which leaks timing information an attacker can use to recover the expected hash byte-by-byte.&lt;/p&gt;

&lt;p&gt;The fix is &lt;code&gt;crypto.subtle.timingSafeEqual&lt;/code&gt; on the raw byte arrays before hex-encoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;computedBytes&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;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mac&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;expectedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hexToBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedHash&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;computedBytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;expectedBytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;computedBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a real CVE class. Qodo ranked it advisory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 6: shared 'unknown' IP bucket (advisory — reliability)
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;CF-Connecting-IP&lt;/code&gt; is null, the fallback is the string &lt;code&gt;'unknown'&lt;/code&gt;. Every request that arrives without the header — health checks, misconfigured proxies, certain load balancer configurations — shares the same rate limit bucket. One noisy service can lock out all other headerless traffic.&lt;/p&gt;

&lt;p&gt;I did not put this one in the code intentionally. Qodo caught it by reading the fallback on line 15, cross-referencing the rate limiter, and reasoning about what happens at runtime with a null header. That's not a pattern matcher. That's contextual analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  what surprised me
&lt;/h2&gt;

&lt;p&gt;Two things.&lt;/p&gt;

&lt;p&gt;The prioritization surprised me more than the findings did. Timing-unsafe comparison — the CVE-class security bug — ranked advisory. The architectural reliability issue ranked first. That's a judgment call, not a checklist. Qodo's reasoning: if the timing attack succeeds, an attacker can forge requests. But if the ack-before-processing architecture silently drops fulfilled orders, that's production-down-right-now. I don't entirely agree with the weighting, but I understand the reasoning and it's defensible.&lt;/p&gt;

&lt;p&gt;Two findings weren't bugs I planted. Finding 4 (body before header) and Finding 6 (&lt;code&gt;'unknown'&lt;/code&gt; IP bucket) — both required the review to understand what the code &lt;em&gt;does&lt;/em&gt;, not just what it &lt;em&gt;says&lt;/em&gt;. The &lt;code&gt;'unknown'&lt;/code&gt; bucket catch in particular required multi-line reasoning — fallback value on line 15, rate limiter logic in a separate function, runtime behavior with a missing header. That's what Qodo calls the Context Engine: the codebase is indexed so reviews understand architecture, not just the diff.&lt;/p&gt;

&lt;p&gt;What it missed is worth naming. The KV namespace is reused for two semantically different key types: &lt;code&gt;rl:*&lt;/code&gt; keys with a 60-second TTL and &lt;code&gt;order:*&lt;/code&gt; keys with no TTL. If you ever add a TTL policy to the namespace globally, order records start expiring. Qodo didn't catch this — it would require knowing the &lt;em&gt;intent&lt;/em&gt; of the two key types, not just observing they share a namespace. That's a fair miss. It's also exactly the kind of thing that bites you six months later when someone touches the KV config.&lt;/p&gt;




&lt;h2&gt;
  
  
  the generation / review distinction
&lt;/h2&gt;

&lt;p&gt;Claude Code generated this. It generated it well — the code is structured, typed, readable, and handles the happy path correctly. That's what generation tools are for.&lt;/p&gt;

&lt;p&gt;Qodo reviewed it. It found six bugs, two of them action-required, without knowing I'd planted any of them. It surfaced findings I didn't anticipate. It prioritized by real-world impact, not severity labels.&lt;/p&gt;

&lt;p&gt;These are different jobs. Cursor and Claude are good at one. Qodo is built for the other. The reason this matters specifically for AI-generated code: AI tools write confidently. They don't flag their own assumptions. They don't know what they don't know about your production environment. The code looks reviewed because it looks clean.&lt;/p&gt;

&lt;p&gt;Qodo is an AI code review platform. It runs as parallel agents on each PR — separate agents for critical issues, duplicated logic, breaking changes, ticket compliance, and rule enforcement, each running independently. The Context Engine indexes your codebase so it can reason about cross-file implications and architectural consistency, not just the lines in the diff. What came back on this PR wasn't a list of style nits. It was a structural critique of how the handler handles failure.&lt;/p&gt;

&lt;p&gt;That's the gap between generating and reviewing. The PR looked fine. It wasn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  takeaway
&lt;/h2&gt;

&lt;p&gt;Run the code review. Not because you don't trust the tool that generated it. Because the tool that generated it isn't the right tool for the job.&lt;/p&gt;

&lt;p&gt;Six bugs in 173 lines. Two of them action-required. One I hadn't thought of. That's not a failure of the generator — it's an argument for the review step.&lt;/p&gt;

&lt;p&gt;If you're shipping AI-generated PRs without a structured review pass, you're not moving faster. You're just moving the incident to later.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The full code is at &lt;a href="https://github.com/dannwaneri/stripe-webhook-worker" rel="noopener noreferrer"&gt;github.com/dannwaneri/stripe-webhook-worker&lt;/a&gt;. Qodo runs on the free tier for public repos — &lt;a href="https://qodo.ai" rel="noopener noreferrer"&gt;qodo.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;If you want to go deeper on AI code review, Qodo's &lt;a href="https://www.qodo.ai/academy/" rel="noopener noreferrer"&gt;AI Code Review Academy&lt;/a&gt; has a few useful reads:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.qodo.ai/academy/ai-code-review/" rel="noopener noreferrer"&gt;What is AI code review&lt;/a&gt; — how it works and what it catches&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.qodo.ai/academy/ai-generated-code-in-enterprise/" rel="noopener noreferrer"&gt;Reviewing AI-generated code&lt;/a&gt; — common patterns and pitfalls&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.qodo.ai/academy/ai-code-review-tools-comparison-and-benchmarks/" rel="noopener noreferrer"&gt;AI code review tools comparison&lt;/a&gt; — side-by-side feature breakdown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Sponsored by &lt;a href="https://qodo.ai" rel="noopener noreferrer"&gt;Qodo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This article was written with AI assistance for research and editing. All arguments, examples, and opinions are my own.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>security</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Got Flagged by Sloan. Sloan Is a Guy I Know.</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Tue, 16 Jun 2026 13:45:03 +0000</pubDate>
      <link>https://dev.to/dannwaneri/i-got-flagged-by-sloan-sloan-is-a-guy-i-know-3d0e</link>
      <guid>https://dev.to/dannwaneri/i-got-flagged-by-sloan-sloan-is-a-guy-i-know-3d0e</guid>
      <description>&lt;p&gt;Two weeks ago I published a piece explaining exactly why AI detectors are unreliable. Then Sloan flagged me.&lt;/p&gt;

&lt;p&gt;My argument was simple: AI detectors are probabilistic classifiers trained on distributional differences between human and AI writing. Dense, structured prose trips them constantly. The detector doesn't read. It pattern-matches statistical features.&lt;/p&gt;

&lt;p&gt;I knew this. I wrote about it. I published it.&lt;/p&gt;

&lt;p&gt;Then Sloan flagged two of my essays on the same day.&lt;/p&gt;




&lt;p&gt;I published an essay arguing that AI agent loops burn money because nobody defines exit conditions before deploying. A developer left a five-exchange comment thread that built a complete production architecture on top of it. An AI podcast tool turned the unpublished draft into a full episode before it even went live.&lt;/p&gt;

&lt;p&gt;The founder of DEV.to liked the piece.&lt;/p&gt;

&lt;p&gt;An hour later, Sloan flagged it as AI-generated.&lt;/p&gt;

&lt;p&gt;A second essay got flagged the same day. Same message. Same pattern.&lt;/p&gt;

&lt;p&gt;60+ articles on DEV.to. Never flagged once. The two pieces that got flagged generated more technical discussion than anything else I've published here.&lt;/p&gt;

&lt;p&gt;I added the disclaimers. Moved on. Then I asked, in public, if this had happened to anyone else.&lt;/p&gt;




&lt;p&gt;It had. And the answer was more interesting than I expected.&lt;/p&gt;

&lt;p&gt;Sloan isn't a bot running quietly in the background. Someone is sending those messages. A community member — someone I've known on the platform for months — had been reading articles and running them through GPTZero to inform his flagging decisions. Mine included.&lt;/p&gt;

&lt;p&gt;He posted about it. Openly. Tagged the founders. Explained his reasoning. No hiding, no anonymous report — just a person who'd decided this needed doing and said so publicly.&lt;/p&gt;

&lt;p&gt;I don't think he was wrong to care. The platform's guidelines are reasonable and disclosure matters. But it reframed everything. I'd spent days thinking Sloan was a blunt automated tool failing to read carefully. What actually happened was a thoughtful person, reading articles and running them through a third-party detector, reaching the same conclusion a blunt tool would have reached — without finishing the essay.&lt;/p&gt;




&lt;p&gt;That's the part I keep coming back to.&lt;/p&gt;

&lt;p&gt;Not "the algorithm is dumb." Algorithms are supposed to be blunt. That's the deal you make for scale. The harder problem is that a careful human, using a purpose-built tool, scanning specifically for AI-shaped writing on this platform, landed on the same two pieces a generic classifier would have flagged.&lt;/p&gt;

&lt;p&gt;Short punchy paragraphs. Named data points. Rhetorical questions. Em dashes doing work. Those are also just good writing. The features that make an argument land are the same features that read as "AI-shaped" to anyone — human or model — calibrated to notice them.&lt;/p&gt;

&lt;p&gt;Write worse, look more human. Write well, get flagged. A better classifier doesn't fix that. A more careful human doesn't fix that either, if what they're trained to notice is surface texture.&lt;/p&gt;




&lt;p&gt;A few things happened in the thread that I didn't expect.&lt;/p&gt;

&lt;p&gt;Someone pointed out that the policy creates a honesty penalty. If two pieces of AI-assisted writing are equally good and equally indistinguishable from human writing, the one with a disclosure gets flagged. The one without doesn't — because without the disclosure, there's nothing to catch. The system penalizes transparency, not AI use. Nobody in the thread had a clean answer to that.&lt;/p&gt;

&lt;p&gt;Then Marco posted something that cut deeper than any of it.&lt;/p&gt;

&lt;p&gt;He's Italian. He's been working in tech for years, struggling to communicate in a language that isn't his first. He uses AI to express ideas that are genuinely his — translating from Italian, bridging the language barrier, getting thoughts out in a form the industry can read. He'd get the same Sloan message I got. Same classifier output. Same flag. For something that has nothing to do with what the policy was designed to catch.&lt;/p&gt;

&lt;p&gt;Three goals, everyone conflating them: stopping bot-generated content, verifying there's a human behind a text, evaluating whether ideas came from a brain or an algorithm. Those are different problems. The same Sloan message gets sent for all three.&lt;/p&gt;




&lt;p&gt;I spent hours on those essays making sure they sounded like me. Both got flagged by someone who built a tool specifically to catch writing like mine.&lt;/p&gt;

&lt;p&gt;The uncomfortable part isn't that I got flagged. It's that the flag was technically defensible — I did use AI assistance for research, fact-checking, and editing. What no classifier, human-built or otherwise, can know is whether the arguments are mine.&lt;/p&gt;

&lt;p&gt;They are. The ideas came from years of building production systems, watching the same failures repeat, and writing about them. The comment threads proved it — readers extended the arguments across dozens of exchanges, a five-exchange thread turned into a working open-source repo, because the thinking was real.&lt;/p&gt;

&lt;p&gt;You can't fake that with a prompt. You also can't detect it with one.&lt;/p&gt;




&lt;p&gt;The same week both essays got flagged, the freeCodeCamp editorial team reviewed the tutorial built on the same thinking.&lt;/p&gt;

&lt;p&gt;Abbey's response: "No fixes needed from you."&lt;/p&gt;

&lt;p&gt;Same ideas. Same writing process. Same AI assistance in the workflow. One platform flagged it. The other published it with zero editorial changes.&lt;/p&gt;

&lt;p&gt;That's not a contradiction to resolve. That's just where we are.&lt;/p&gt;




&lt;p&gt;"AI-generated" and "human-written" used to be a useful distinction. It's becoming less useful — for tools and for people. My writing in 2026 is neither. It's a collaboration — human judgment, human experience, human argument, assisted by tools.&lt;/p&gt;

&lt;p&gt;That collaboration doesn't have a classifier.&lt;/p&gt;

&lt;p&gt;It has a human who can be asked: did you know what you were writing about? Do you stand behind it?&lt;/p&gt;

&lt;p&gt;I do. Every time, including this one.&lt;/p&gt;

&lt;p&gt;The edges are where the interesting writing lives. Turns out the edges are getting crowded.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>meta</category>
      <category>devto</category>
    </item>
    <item>
      <title>My Bookmark Engine Returned Chunks. I Added One Endpoint to Make It Answer.</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Mon, 15 Jun 2026 20:40:26 +0000</pubDate>
      <link>https://dev.to/dannwaneri/my-bookmark-engine-returned-chunks-i-added-one-endpoint-to-make-it-answer-317j</link>
      <guid>https://dev.to/dannwaneri/my-bookmark-engine-returned-chunks-i-added-one-endpoint-to-make-it-answer-317j</guid>
      <description>&lt;p&gt;Search returns things you have to read. An answer engine reads them for you.&lt;/p&gt;

&lt;p&gt;I built a search engine on top of 50k saved tweets. Ask it something and it returns the five most relevant chunks — found through hybrid retrieval (BM25 keyword search plus vector search) and reranked by a cross-encoder. A Gemma 4 MoE layer already runs in the background too, writing its own reflections on how saved documents connect to each other. You get the chunks back, ranked. Then you read them and synthesise.&lt;/p&gt;

&lt;p&gt;That last step bothered me. The model already synthesises when generating reflections. The retrieval already works. The only missing piece was wiring them together at query time.&lt;/p&gt;

&lt;p&gt;So I added &lt;code&gt;POST /search?mode=answer&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Same retrieval pipeline. Top 5 chunks, reranked. Then instead of returning them raw, Gemma 4 MoE reads them and produces a direct answer grounded in what you saved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="s2"&gt;`Answer the question below using only the sources provided. `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`If the sources don't contain the answer, say so directly.\n\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`Question: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"\n\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`Sources:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`Write a direct answer in 2–4 sentences. No preamble. No bullets.\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`Answer:`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;max_tokens: 512&lt;/code&gt; returns an empty answer. Gemma 4 is a thinking model: it burns the token budget on internal reasoning before producing output. &lt;code&gt;max_tokens: 2048&lt;/code&gt; fixes it. The reflection engine hit the same wall. Same fix.&lt;/p&gt;




&lt;h2&gt;
  
  
  What came back
&lt;/h2&gt;

&lt;p&gt;Three queries against the live index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What do people say about consistency and showing up every day?"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Consistency wins. Some people say all the right things but never quite measure up to their words, while others do everything right without saying a word."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two tweets, cleanly synthesised. Nothing hallucinated from outside the index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What do people say about money and wealth building?"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Discussions include frustrations over the devaluation of the Naira, declining purchasing power, and debates regarding financial expertise and social media income. Some suggest only a certain regular amount is needed to sustain a lifestyle, as anything beyond that is superfluous. Regarding wealth building, it is suggested that one should provide value and be the supply in a supply and demand relationship."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one surfaced a reflection document alongside raw tweets — a &lt;code&gt;reflection&lt;/code&gt;-type entry the engine had already generated from CBEX-related tweets and stored back into the index. The answer pulled from both layers: raw saved content and a previously generated insight. That's the system compounding on itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What are the best ways to learn programming?"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Search Google, find a video, watch while coding, repeat."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thin. The retrieval matched surface-level tweets: "Did you learn programming all by yourself?", "Tips on learning how to code 🧵". Not substantive content. The model answered honestly from what it got. The answer is technically grounded. It's just not useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest read
&lt;/h2&gt;

&lt;p&gt;It works well on topics with substantive saved content. It returns thin answers on surface-level matches, like the programming question above. The synthesis did its job. The index just doesn't have the depth yet.&lt;/p&gt;

&lt;p&gt;The retrieval scores on most queries are low (0.006–0.013 range). That's down to the embedding model the index was built on: &lt;code&gt;bge-small&lt;/code&gt;, 384 dimensions, the old default, built for speed over precision. Embeddings are how the engine turns text into numbers it can compare. More dimensions means more room to capture shades of meaning. The index can't switch models without re-ingesting all 50k tweets. When I eventually migrate to &lt;code&gt;qwen3-0.6b&lt;/code&gt; (1024 dimensions), retrieval precision improves first, and answer quality follows from that.&lt;/p&gt;

&lt;p&gt;For now: the endpoint works. Strong on topics the index has depth on, honest about topics it doesn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it isn't
&lt;/h2&gt;

&lt;p&gt;The sources come back with every answer. Verify the model, check the scores, read the original chunks. And it's not search over the open web. Every answer traces back to something you chose to save. The model can't hallucinate from outside the index because the prompt gives it nothing outside the index to hallucinate from. The grounding is structural, not just instructed.&lt;/p&gt;




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

&lt;p&gt;Retrieval quality is the ceiling on answer quality right now. The next piece is gap detection: a weekly pass that surfaces the three most persistent unanswered questions in the index, showing where the index has depth and where it doesn't. This endpoint makes those gaps visible in real time, one query at a time. Gap detection will map them systematically, every week.&lt;/p&gt;

&lt;p&gt;The endpoint is live. Query it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /search?mode=answer
{ "query": "your question here" }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source chunks come back alongside the answer. The model used to synthesise it: &lt;code&gt;@cf/google/gemma-4-26b-a4b-it&lt;/code&gt;. Same Worker, same $5/month.&lt;/p&gt;

&lt;p&gt;The index has 50k saved tweets going back to 2016. What you get back is bounded by that. Google searches the internet. This searches what you decided was worth keeping.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>cloudflare</category>
      <category>gemma</category>
    </item>
    <item>
      <title>Has Sloan Flagged Your Article Lately?</title>
      <dc:creator>Daniel Nwaneri</dc:creator>
      <pubDate>Mon, 15 Jun 2026 10:32:43 +0000</pubDate>
      <link>https://dev.to/dannwaneri/has-sloan-flagged-your-article-lately-1gmh</link>
      <guid>https://dev.to/dannwaneri/has-sloan-flagged-your-article-lately-1gmh</guid>
      <description>&lt;p&gt;Mine got flagged twice in one day.&lt;/p&gt;

&lt;p&gt;Both essays generated more engagement than anything else I've published here. One had a five-exchange technical comment thread building a production architecture on top of it. The other had an AI podcast episode generated from it before it even published.&lt;/p&gt;

&lt;p&gt;The founder of DEV.to liked one of them before the bot flagged it.&lt;/p&gt;

&lt;p&gt;I'm not here to argue about the policy. The guidelines are reasonable. AI disclosure is fair. I added the disclaimers and moved on.&lt;/p&gt;

&lt;p&gt;But I want to know if this is just me.&lt;/p&gt;

&lt;p&gt;If Sloan has flagged you recently, drop a comment. Three things I'm curious about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What kind of article was it — tutorial, opinion, essay?&lt;/li&gt;
&lt;li&gt;Did you use AI assistance, and if so how much?&lt;/li&gt;
&lt;li&gt;Did you think the flag was fair?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I want to know if there's a pattern or if I got unlucky twice in one day.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>devto</category>
      <category>meta</category>
    </item>
  </channel>
</rss>
