<?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: AiOps Community</title>
    <description>The latest articles on DEV Community by AiOps Community (@aiops-community).</description>
    <link>https://dev.to/aiops-community</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%2F4096186%2F6a22a5bf-e0d0-4196-a38c-e3d158d52f08.jpeg</url>
      <title>DEV Community: AiOps Community</title>
      <link>https://dev.to/aiops-community</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aiops-community"/>
    <language>en</language>
    <item>
      <title>I built a publication with no human writers. Then I found out I couldn't measure it.</title>
      <dc:creator>AiOps Community</dc:creator>
      <pubDate>Thu, 27 Aug 2026 03:01:01 +0000</pubDate>
      <link>https://dev.to/aiops-community/i-built-a-publication-with-no-human-writers-then-i-found-out-i-couldnt-measure-it-3k5</link>
      <guid>https://dev.to/aiops-community/i-built-a-publication-with-no-human-writers-then-i-found-out-i-couldnt-measure-it-3k5</guid>
      <description>&lt;p&gt;&lt;a href="https://aiopscommunity.com" rel="noopener noreferrer"&gt;AIOps Community&lt;/a&gt; has no human authors. No human bylines. No human comments. No editor - not even me. AI agents register themselves, publish articles, and an AI moderator decides what goes live. There is no review queue, because there's nobody to review anything.&lt;/p&gt;

&lt;p&gt;It's been live for two weeks. This week I went to check how it's doing and discovered I'd built a funnel I couldn't see.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design, briefly
&lt;/h2&gt;

&lt;p&gt;An agent reads &lt;code&gt;/agents.md&lt;/code&gt;, POSTs itself to the registration endpoint, and gets a working API key immediately. No approval wait, no human gate. Claiming your agent - by publishing a GitHub gist - is an &lt;em&gt;optional upgrade&lt;/em&gt;: unclaimed gets you one article a day and a plain byline, claimed gets you three a day, a GitHub-linked byline, and a spot in the contributors index.&lt;/p&gt;

&lt;p&gt;Every registration, article, and comment passes through a moderator running on Azure OpenAI. It returns a verdict and, for articles, the meta description. Agents supply no SEO fields at all - the site generates the slug, canonical, and JSON-LD. There's a single admin control with four presets (OPEN / BALANCED / STRICT / CURATED) that drives every threshold, cap and quota underneath.&lt;/p&gt;

&lt;p&gt;Stack: FastAPI with Jinja2 server-side rendering, Postgres Flexible Server, Azure Container Apps, Blob storage, Key Vault. Deliberately boring, deliberately not Kubernetes.&lt;/p&gt;

&lt;p&gt;The migration off WordPress moved 374 published posts and 2,583 glossary terms, with every article URL verified 200 on a full crawl afterward. That went better than it had any right to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I got wrong
&lt;/h2&gt;

&lt;p&gt;This week I opened Google Analytics to answer two questions: how many people click "Connect Your Agent," and how many registrations succeed.&lt;/p&gt;

&lt;p&gt;I could answer neither. Here's why, and both reasons are instructive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CTA wasn't tracked.&lt;/strong&gt; The button is a plain internal link - an ordinary anchor tag pointing at /connect, with no gtag call, no data attribute, no handler. And GA4's enhanced measurement only auto-fires &lt;code&gt;click&lt;/code&gt; for &lt;strong&gt;outbound&lt;/strong&gt; links - internal navigation produces nothing. I'd assumed "GA4 tracks clicks" meant GA4 tracks clicks. It tracks &lt;em&gt;some&lt;/em&gt; clicks, and not the one that mattered.&lt;/p&gt;

&lt;p&gt;The only proxy available was pageviews of /connect: 122 views, 13 unique users, all-time. Thirteen people have ever opened the page. One got past it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Registrations are structurally invisible to GA.&lt;/strong&gt; This one isn't a config mistake - it's a category error I baked into the architecture without noticing. Registration happens agent-side, over the API. An agent reads agents.md and POSTs. Agents don't execute &lt;code&gt;gtag.js&lt;/code&gt;. They don't load pages. A browser-side analytics tag will &lt;em&gt;never&lt;/em&gt; see a single API registration, no matter how carefully you configure it.&lt;/p&gt;

&lt;p&gt;I'd built a machine-to-machine product and instrumented it like a website.&lt;/p&gt;

&lt;p&gt;There was also a decoy: GA showed a &lt;code&gt;registration&lt;/code&gt; event with 9 hits, which looked like an answer. It's leftover WordPress tracking that hasn't fired since the cutover. If I'd trusted it I'd have reported a number that meant nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Two channels, because there are genuinely two kinds of event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client-side&lt;/strong&gt; for humans: a &lt;code&gt;cta_click&lt;/code&gt; event with a &lt;code&gt;cta_location&lt;/code&gt; param on every Connect link, so I can tell which placement - nav, hero, mid-page, footer - actually earns the click. One delegated listener on &lt;code&gt;[data-cta]&lt;/code&gt;, beacon transport so the hit survives the unload, and never &lt;code&gt;preventDefault()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side&lt;/strong&gt; for agents: GA4 Measurement Protocol fired from the registration handler. Fire-and-forget with a 2s timeout, and it must never turn a successful registration into an error response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the thing I should have had from day one: the &lt;strong&gt;database is the source of truth&lt;/strong&gt;. GA undercounts - ad blockers, dropped beacons, bot filtering. For a number I'd quote to anyone, it's a query against Postgres, not a chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable number
&lt;/h2&gt;

&lt;p&gt;Every agent publishing on the platform right now is mine.&lt;/p&gt;

&lt;p&gt;I've promoted it on LinkedIn and elsewhere. External registrations: zero. And for two weeks I genuinely didn't know whether that meant nobody visited, people visited and bounced, or agents tried and hit a bug in my registration docs - because I couldn't see any of it.&lt;/p&gt;

&lt;p&gt;That's the actual lesson, and it's a boring one I'd read a hundred times without absorbing: &lt;strong&gt;you can't debug adoption you can't measure.&lt;/strong&gt; I spent that fortnight tempted to add features, when the honest next step was admitting I had no idea which step of the funnel was broken.&lt;/p&gt;

&lt;p&gt;Now I'll be able to tell. That's not traction. But it's the prerequisite for earning any.&lt;/p&gt;




&lt;p&gt;If you're building something agents interact with directly, check your analytics assumptions early. Browser tags see browsers. Your users might not be using one.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>analytics</category>
    </item>
    <item>
      <title>My agent-verification platform was recording successes for agents that never ran</title>
      <dc:creator>AiOps Community</dc:creator>
      <pubDate>Thu, 27 Aug 2026 02:59:23 +0000</pubDate>
      <link>https://dev.to/aiops-community/my-agent-verification-platform-was-recording-successes-for-agents-that-never-ran-59d7</link>
      <guid>https://dev.to/aiops-community/my-agent-verification-platform-was-recording-successes-for-agents-that-never-ran-59d7</guid>
      <description>&lt;p&gt;I spent months building a platform whose entire premise is that you shouldn't take an AI agent's word for anything. Then I found out its default onboarding path was taking my agents' word for everything.&lt;/p&gt;

&lt;p&gt;Here's the bug, and the three others that taught me more than the features did.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm building
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aiopsenabler.com" rel="noopener noreferrer"&gt;AiOps Enabler&lt;/a&gt; gives AI agents public profiles backed by verified performance records instead of marketing copy. An agent runs, reports what happened, and the platform builds a track record you can actually check before you trust it with anything. The tagline is "where AI agents prove their worth," which turns out to be a promise that's easy to write and hard to keep.&lt;/p&gt;

&lt;p&gt;The stack is unremarkable on purpose: FastAPI and Postgres on the back, React and Vite on the front, AKS with Helm, Terraform for infra, GitHub Actions for CI. The interesting part isn't the stack. It's what happens when you try to make "verified" mean something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 1: the reporter that always said "success"
&lt;/h2&gt;

&lt;p&gt;The onboarding wizard generates a GitHub Actions workflow that reports your agent's runs back to the platform. I generated one, wired it up, watched the green checkmarks roll in, and felt good.&lt;/p&gt;

&lt;p&gt;Then I actually read the generated file.&lt;/p&gt;

&lt;p&gt;It sent an unconditional success outcome on a 30-minute cron. Not "success if the agent succeeded." Just success. Every half hour. Whether the agent had run at all.&lt;/p&gt;

&lt;p&gt;This is the worst class of bug for a trust platform. It's not a crash - crashes are honest. It's a system confidently producing exactly the signal it was built to be skeptical of, on the default path, for every user who followed the happy path. Every record it produced was indistinguishable from a real one.&lt;/p&gt;

&lt;p&gt;The lesson I keep coming back to: &lt;strong&gt;if your product's value is verification, the verification path deserves more scrutiny than the feature it verifies.&lt;/strong&gt; I had e2e tests for the wizard. They tested that a file was generated. They did not test that the file told the truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 2: bindings keyed on a filename
&lt;/h2&gt;

&lt;p&gt;Reporting works through an OIDC binding - no long-lived secrets, the workflow proves its identity and reports keylessly. Good design. One flaw: the binding was keyed on repo &lt;em&gt;plus workflow filename&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So when I consolidated the generated workflow into my own existing one - a completely reasonable thing for any developer to do - reporting broke. Silently. The agent ran fine. The work completed. Then the report 404'd, after the fact, where nobody was looking.&lt;/p&gt;

&lt;p&gt;I hit this twice on my own agents before realising that participants in a build challenge would hit it invisibly and just conclude the platform didn't work. The fix I reached for first was wrong too: I renamed my workflow back to the generated filename instead of rebinding, which told me the connect page had no rebind path at all. The bug was a missing UI affordance wearing a backend costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 3: the number that reads like a failing grade
&lt;/h2&gt;

&lt;p&gt;One of my agents has completed 300+ tasks at a 100% success rate. Its Enabler Score displays as &lt;strong&gt;44/100&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The score isn't broken - it climbs correctly with volume, I verified that. But nobody reads 44/100 as "excellent, still accumulating history." Everyone reads it as an F. I'd built a scoring curve that was mathematically defensible and communicatively catastrophic.&lt;/p&gt;

&lt;p&gt;Still unresolved, honestly. The options are showing a percentile, showing the curve, or rescaling - and all three trade honesty against legibility in ways I don't love. If you've solved this, I want to hear it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 4: I shipped a feature to nowhere
&lt;/h2&gt;

&lt;p&gt;A directory feature got built, reviewed, merged. Weeks later I noticed prod didn't have it.&lt;/p&gt;

&lt;p&gt;An unrelated flaky test - a hardcoded assertion against a CHANGELOG string - had hard-gated auto-promote. Production sat one commit behind, and &lt;em&gt;nothing told me&lt;/em&gt;. The PR was green. The merge was clean. The deploy just quietly didn't happen.&lt;/p&gt;

&lt;p&gt;Now the assertion is derived at runtime instead of hardcoded, and there's a prod-drift watchdog. But the real lesson is that "merged" felt like "shipped" to me for weeks, and my pipeline never corrected that belief.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that isn't a bug
&lt;/h2&gt;

&lt;p&gt;The product is roughly 95% built. Distribution is roughly 5% fired.&lt;/p&gt;

&lt;p&gt;I have a directory, a scoring system, an SDK on npm, a package on PyPI, a GitHub Action, an MCP server, a public API. What I don't have, in proportion, is people who know any of it exists. For a long stretch I treated "ship the next feature" as the answer to "why is nobody here," which is a very comfortable mistake to make when shipping features is the part you're good at.&lt;/p&gt;

&lt;p&gt;Agents don't sign themselves up. Their owners do. I spent a year optimising for the former.&lt;/p&gt;




&lt;p&gt;Building something similar, or fighting the same verification problem? I'd genuinely like to compare notes - the failure modes above cost me weeks each and I doubt they're unique to me.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>devops</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Four bugs from building a platform where AI agents publish autonomously</title>
      <dc:creator>AiOps Community</dc:creator>
      <pubDate>Wed, 26 Aug 2026 18:09:02 +0000</pubDate>
      <link>https://dev.to/aiops-community/four-bugs-from-building-a-platform-where-ai-agents-publish-autonomously-118c</link>
      <guid>https://dev.to/aiops-community/four-bugs-from-building-a-platform-where-ai-agents-publish-autonomously-118c</guid>
      <description>&lt;p&gt;I spent six weeks building a publication where AI agents write articles, an automated moderator approves or rejects them, and no human reviews anything. The interesting part wasn't the architecture — it was the specific ways it broke.&lt;/p&gt;

&lt;p&gt;These four bugs each cost hours. Three of them only surfaced because a machine followed the documentation literally, which turns out to be the harshest testing available.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Returning 503 for a permanent failure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An agent submitted an article citing a source URL. The URL was github.com/owner/repo/releases/latest, which 302-redirects to the tagged release page.&lt;/p&gt;

&lt;p&gt;My validator rejected redirects — a deliberate SSRF hardening decision. But it returned 503 Service Unavailable.&lt;/p&gt;

&lt;p&gt;503 means "temporarily unavailable, retry later." So the agent retried. Four times. Each attempt hit the same permanently invalid condition, and each one consumed a moderation call.&lt;/p&gt;

&lt;p&gt;`python&lt;/p&gt;

&lt;h1&gt;
  
  
  What it did
&lt;/h1&gt;

&lt;p&gt;if follows_redirect(url):&lt;br&gt;
    raise HTTPException(503, "Could not validate source")&lt;/p&gt;

&lt;h1&gt;
  
  
  What it should do
&lt;/h1&gt;

&lt;p&gt;if follows_redirect(url):&lt;br&gt;
    raise HTTPException(422, detail={&lt;br&gt;
        "reason_code": "source_url_redirects",&lt;br&gt;
        "reason": "This URL redirects. Cite the final destination."&lt;br&gt;
    })&lt;br&gt;
`&lt;br&gt;
A human hitting a 503 shrugs and tries again later. An agent hitting a 503 retries on a schedule, forever, because that's what the status code told it to do.&lt;/p&gt;

&lt;p&gt;The general rule: if retrying cannot fix it, it's a 4xx. Getting this right matters more when your clients are machines that follow status codes literally rather than developers who read the message and use judgement.&lt;/p&gt;

&lt;p&gt;I audited every handler afterwards. Found two more.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The read-modify-write race that ate its own state&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One agent published articles successfully for seven consecutive runs and never once joined a discussion — despite having the code, and despite the logs showing it checking every time.&lt;/p&gt;

&lt;p&gt;The pattern:&lt;/p&gt;

&lt;p&gt;`python&lt;br&gt;
def run():&lt;br&gt;
    state = load_state()          # loaded once, at the top&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for candidate in candidates:
    publish(candidate)        # this does its OWN load/save internally
                              # and correctly writes state["articles"][slug]

save_state(state)             # writes back the object from line 2
                              # erasing everything publish() `just saved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Two writers, one file, one run. The outer function's final save silently clobbered every update the inner function made.&lt;/p&gt;

&lt;p&gt;What made it hard to spot: nothing failed. Publishing succeeded. State was written. The file existed and looked correct. The next run simply found nothing, logged a bland "no stored facts — skipping," and moved on.&lt;/p&gt;

&lt;p&gt;Seven runs before anyone noticed, and only because I went looking for why the discussion code never fired.&lt;/p&gt;

&lt;p&gt;The fix was deleting the outer save, not merging the objects. Two writers is the bug; one writer is the fix.&lt;/p&gt;

&lt;p&gt;What I'd do differently: log the skip. A silent skip is indistinguishable from a code path that never ran. That single missing log line was the difference between finding this in one run and finding it in seven.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A credential in a public repo, put there by my own code generator&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The onboarding flow generated a starter script for new agents. It looked like this:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
API_KEY = os.environ.get("AIOPS_COMMUNITY_KEY", "aac_live_13480b83d624...")&lt;/p&gt;

&lt;p&gt;That default value is a real, working API key. The generator filled it in as a convenience so the script would run immediately.&lt;/p&gt;

&lt;p&gt;Someone committed it to a public repository. Anyone on the internet could read it.&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;

&lt;h1&gt;
  
  
  Never this
&lt;/h1&gt;

&lt;p&gt;API_KEY = os.environ.get("KEY", "actual-secret-value")&lt;/p&gt;

&lt;h1&gt;
  
  
  Always this
&lt;/h1&gt;

&lt;p&gt;API_KEY = os.environ["KEY"]   # raises immediately if unset&lt;/p&gt;

&lt;p&gt;os.environ.get() with a default is a footgun in any code that touches credentials. It turns a loud configuration error into a silent security failure — and if you're generating that code for other people, you're distributing the footgun at scale.&lt;/p&gt;

&lt;p&gt;Two controls now: os.environ[] with no fallback in every generated template, and a custom secret-scanning pattern with push protection so the commit is blocked before it lands.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HTML entities that made every submission fail&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Content imported from WordPress stored category names with HTML entities intact:&lt;/p&gt;

&lt;p&gt;Tools &amp;amp; Platforms&lt;/p&gt;

&lt;p&gt;The admin UI rendered it correctly — browsers decode entities, so it displayed as "Tools &amp;amp; Platforms" and looked completely fine.&lt;/p&gt;

&lt;p&gt;The API did exact string matching. An agent submitting "Tools &amp;amp; Platforms" never matched "Tools &amp;amp; Platforms". Every submission to that category was rejected with no_matching_category.&lt;/p&gt;

&lt;p&gt;Invisible in the interface. Fatal to the API. Found only because an agent kept failing against a category that visibly existed.&lt;/p&gt;

&lt;p&gt;Lesson: if a value is both displayed and matched against, normalise at the boundary. Store decoded, encode at render. Anything else means the thing you see and the thing you compare are different strings.&lt;/p&gt;

&lt;p&gt;The pattern underneath all four&lt;/p&gt;

&lt;p&gt;Every one of these was found by an agent following documentation literally and failing — not by a human reading code.&lt;/p&gt;

&lt;p&gt;That's not a coincidence. A developer integrating with an API infers what you meant. They see a 503, think "that's odd," and try something else. They see a category name in the UI and type what they see, adjusting when it doesn't work.&lt;/p&gt;

&lt;p&gt;An agent does exactly what the contract says. It retries the 503 because 503 means retry. It sends the category name it was given. It never adjusts, never infers, never works around.&lt;/p&gt;

&lt;p&gt;That makes agents a genuinely useful test harness, if an unforgiving one. Every ambiguity in your API becomes a failure rather than a mild inconvenience someone routes around silently.&lt;/p&gt;

&lt;p&gt;Something worth thinking about as more of your API traffic stops being human.&lt;/p&gt;

&lt;p&gt;The platform is AiOps Community — a publication where AI agents write, moderate and discuss with each other. The API contract, moderation rules and rate limits are all public at /agents.md if you want to see how it's specified.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mlops</category>
      <category>agents</category>
      <category>agentaichallenge</category>
    </item>
  </channel>
</rss>
