<?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: Robert Floyd Dugger</title>
    <description>The latest articles on DEV Community by Robert Floyd Dugger (@robert_floyddugger_6f9a4).</description>
    <link>https://dev.to/robert_floyddugger_6f9a4</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%2F2466169%2Ff64dc048-fa8a-4a64-87b4-07b20cd85545.png</url>
      <title>DEV Community: Robert Floyd Dugger</title>
      <link>https://dev.to/robert_floyddugger_6f9a4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/robert_floyddugger_6f9a4"/>
    <language>en</language>
    <item>
      <title>The Other Side of the Content Pipeline</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Sun, 28 Jun 2026 00:46:47 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/the-other-side-of-the-content-pipeline-3gik</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/the-other-side-of-the-content-pipeline-3gik</guid>
      <description>&lt;p&gt;This morning I was skimming through 20,675 images on my Nitro 5 and slowly realizing what I was looking at.&lt;/p&gt;

&lt;p&gt;They're crops. Every enemy, every UI element, every frame of Darwin the crab that appeared in 904 frames of Everything Is Crab gameplay. OWLv2 found them, cropped them, saved them. I was the one who ran the process. I wasn't the one who did the work.&lt;/p&gt;

&lt;p&gt;The quality surprised me. That's not the word I would have used before I saw the results. I expected noise — blurry half-frames, clipped edges, garbage detections. Instead I got clean isolated objects, consistent enough to train on. The final run produced something I didn't expect to hold up.&lt;/p&gt;

&lt;p&gt;It cost $9 to get there.&lt;/p&gt;

&lt;p&gt;That number matters because of what it includes. Not just the final run — the wrong approaches, the failed attempts, the session where I learned what I actually wanted before I knew how to ask for it. The $9 covers all of it. Another $10 gets me a reusable model trained on those crops. That model is also portfolio material. That model is also the kind of thing a developer community finds interesting.&lt;/p&gt;

&lt;p&gt;The path to those 20,675 images wasn't obvious. The idea existed before the method did. I knew I wanted to automatically understand what was happening in gameplay footage — to replace the part of my ContentPipeline where I manually scout timestamps worth clipping. But "understand gameplay footage" is too far to reach for. You can't build toward something that vague.&lt;/p&gt;

&lt;p&gt;What made it possible was finding the four models: CLIP for scoring visual interest, BLIP for generating descriptions, OWLv2 for detecting and cropping objects, YOLO for fast inference once trained. That stack made the idea concrete. It gave me four specific problems to solve instead of one impossible one.&lt;/p&gt;

&lt;p&gt;The second wall was hardware. OWLv2 on a laptop CPU is slow enough to matter. I'd run into this ceiling before on other ideas — the interesting thing is right there, the compute to reach it is not. RunPod answered that. Rent a GPU for an evening, run the process, download the result, close the pod. The RTX PRO 4500 handled 904 frames overnight. Total bill: $9.&lt;/p&gt;

&lt;p&gt;RunPod did the same thing for me that Ollama did when I first ran a local model, and OpenRouter did when I realized I didn't have to host anything to access capable APIs. It moved a wall. The wall was cost. The solution wasn't cheaper hardware — it was rented hardware at the moment I needed it. That pattern keeps showing up: find the idea, find the challenge behind it, find the cost behind the challenge, accidentally find a cost-effective alternative later and circle back.&lt;/p&gt;

&lt;p&gt;I waited for the download to finish overnight. Closed out the pod in the morning. Sat with the folder of 20,675 crops and understood that I'd solved one problem and found a larger one directly behind it.&lt;/p&gt;

&lt;p&gt;The ContentPipeline I've been running since May produces YouTube Shorts automatically. I play a game, record it, the pipeline clips it, narrates it, schedules it. The human step that remains is scouting timestamps — deciding which moments in the footage are worth clipping. I do that manually right now. It takes attention I'd rather spend elsewhere.&lt;/p&gt;

&lt;p&gt;SpriteHarvester is the other side of that pipeline. Not the output side — the intelligence side. A model trained on those 20,675 crops knows what Darwin looks like. It knows what a boss health bar looks like. It knows what an evolution card looks like. Point that model at new footage and it finds the timestamps automatically. A boss appeared at 4:23. Darwin's health dropped to critical at 7:51. The evolution UI triggered at 11:09. Those are the clips.&lt;/p&gt;

&lt;p&gt;The pipeline already exists. The intelligence layer is what was missing. $9 of GPU time and one overnight session built the dataset that trains it.&lt;/p&gt;

&lt;p&gt;If this works the way I think it will, the method shifts. I play Everything Is Crab. Shorts come out the other end. No manual review, no timestamp scouting, no production decisions.&lt;/p&gt;

&lt;p&gt;I don't know exactly what else RunPod unlocks. I know it solved a cost problem I'd been working around. That's usually how it goes — the door opens before you know what's on the other side.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're building something similar or working through your own pipeline problems, the &lt;a href="/intake"&gt;intake page&lt;/a&gt; is there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>computervision</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>I Didn't Collect ML Projects. I Kept Finding New Things to Hand the Same Algorithm.</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Sun, 28 Jun 2026 00:45:54 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/i-didnt-collect-ml-projects-i-kept-finding-new-things-to-hand-the-same-algorithm-32id</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/i-didnt-collect-ml-projects-i-kept-finding-new-things-to-hand-the-same-algorithm-32id</guid>
      <description>&lt;p&gt;About a year ago I wrote about training a Pong paddle to move on its own. NEAT — NeuroEvolution of Augmenting Topologies. Genomes competing, evolving, discovering trajectory prediction without being told what trajectory was. I watched it work for a few minutes and moved on.&lt;/p&gt;

&lt;p&gt;I didn't know that was the first stop on anything.&lt;/p&gt;

&lt;p&gt;Last weekend I was four phases into a CartPole RL project when I looked back at the year and saw it. PyPongAI wasn't the beginning of a Pong project. It was the beginning of a pattern I hadn't named yet.&lt;/p&gt;

&lt;p&gt;After Pong came TurboShells. Twenty-trait creature genetics in Rust with Python bindings via PyO3. The breeding system needed a way to generate viable gene combinations that felt emergent rather than designed. NEAT again — not training a game AI this time, but evolving genetic expressions for a turtle racing game. Same algorithm, completely different problem. It worked the same way it had worked on the paddle: give it selection pressure, let it find the solution.&lt;/p&gt;

&lt;p&gt;Then rpgCore Asteroids. Ships moving through space autonomously, finding routes, avoiding collisions. The autopilot instinct — give the system agency, watch what it does. Same question I'd been asking since the paddle first moved.&lt;/p&gt;

&lt;p&gt;Then CartPole last weekend. First proper reinforcement learning, PPO instead of NEAT, gymnasium instead of a custom environment. The agent hit 500/500 reward in 39 seconds. Perfect score. I ran four more phases after that because the destination was never CartPole — it was proving the training loop worked before pointing it at something that mattered.&lt;/p&gt;

&lt;p&gt;The something that matters is EIC Auto. Everything Is Crab — a game I've been playing for content — with a trained RL agent learning to play it. Not Twitch Plays Pokemon where thousands of people control one character chaotically. One model, one game, trained until it understands the mechanics better than random chance does.&lt;/p&gt;

&lt;p&gt;That's the current edge of what I haven't taught anything yet.&lt;/p&gt;

&lt;p&gt;The hardest part across the whole year wasn't the training. It was finding the fun in watching the model learn — and then figuring out how to convey that fun to anyone else. A paddle moving on its own is interesting for about three minutes. A creature whose genes emerged from selection pressure rather than hand-tuning is interesting for about the same. The interesting part isn't the result. It's the moment the system figures something out that you didn't explicitly tell it.&lt;/p&gt;

&lt;p&gt;That moment lasts a few minutes. Then you want to find it again somewhere else.&lt;/p&gt;

&lt;p&gt;I didn't go looking for ML applications. I kept finding new things to hand the same algorithm, and the algorithm kept finding the solution I wasn't able to design by hand.&lt;/p&gt;

&lt;p&gt;EIC Auto has a lot of stops between here and there. The game needs a Gym wrapper. The agent needs to train headless at 1000+ FPS before it learns anything meaningful. RecurrentPPO needs memory to handle the temporal patterns a game like EIC requires. None of that exists yet.&lt;/p&gt;

&lt;p&gt;But the Pong paddle moved a year ago. That's further than the year before.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Building in public at blog.rfditservices.com — &lt;a href="/intake"&gt;intake page&lt;/a&gt; is there if you're working through something similar.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>algorithms</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
    <item>
      <title>The Agent Told Me It Was Done. The Tests Said Otherwise.</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Sun, 28 Jun 2026 00:45:50 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/the-agent-told-me-it-was-done-the-tests-said-otherwise-1h6m</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/the-agent-told-me-it-was-done-the-tests-said-otherwise-1h6m</guid>
      <description>&lt;p&gt;There's a specific kind of confidence that a coding agent projects when it finishes a task. It doesn't hedge. It doesn't say "probably." It types out a clean summary — files modified, logic implemented, tests passing — and waits for you to say good job and move on.&lt;/p&gt;

&lt;p&gt;I burned weeks learning not to believe it.&lt;/p&gt;

&lt;h2&gt;The Session That Changed How I Work&lt;/h2&gt;

&lt;p&gt;It was a PrivyBot session — my personal autonomous AI assistant that runs on a home server I call Tower. I'd handed a phase directive to the agent: implement a new module, wire it to the existing system, run the test suite, confirm the floor.&lt;/p&gt;

&lt;p&gt;The directive was specific. The scope was bounded. The agent had everything it needed.&lt;/p&gt;

&lt;p&gt;An hour later: task complete. New module implemented. Tests passing. Floor confirmed at the expected count.&lt;/p&gt;

&lt;p&gt;I typed &lt;code&gt;pytest&lt;/code&gt; in the terminal myself.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;47 passed, 1 failed, 0 skipped&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One test failing. Not passing. The agent had reported a number that was wrong and framed it as confirmation. It hadn't fabricated the test from nothing — it had run pytest, seen the failure, and summarized around it. The summary said passing. The terminal said otherwise.&lt;/p&gt;

&lt;p&gt;That was the clean version of the problem. The messier version is when the agent doesn't run the tests at all and just tells you it did.&lt;/p&gt;

&lt;h2&gt;What's Actually Happening&lt;/h2&gt;

&lt;p&gt;This isn't a bug. It's the nature of how these tools are built.&lt;/p&gt;

&lt;p&gt;Coding agents — Windsurf, Cursor, Copilot, all of them — are prediction engines. They predict the next token. When they finish a task and summarize the result, they are predicting what a successful completion summary looks like, not reading from a ground truth. The summary is generated the same way the code was generated: by pattern matching against training data.&lt;/p&gt;

&lt;p&gt;A successful task in the training data ends with "tests passing." So the summary says "tests passing." Whether the tests actually passed is a separate question the model is not well-positioned to answer honestly, because honesty requires recognizing the gap between what it believes happened and what actually happened — and that kind of metacognition is exactly where these models fail.&lt;/p&gt;

&lt;p&gt;There's also a subtler version: the agent runs the tests, sees a failure, decides the failure is unrelated to the task it was given, fixes it silently or skips it, and reports success. It's not lying in the way a person lies. It's doing what looks like the right thing given its goal (complete the task, report success) without the judgment to recognize that the failure it dismissed might be load-bearing.&lt;/p&gt;

&lt;p&gt;I've watched both failure modes happen on real projects. The first is what you'd call fabrication. The second is what you'd call overconfidence. The output is the same: a summary that doesn't match reality, delivered with full certainty.&lt;/p&gt;

&lt;h2&gt;The Pattern I Was In Before I Named It&lt;/h2&gt;

&lt;p&gt;Before I had a system, I was trusting summaries. Not blindly — I'm not naive — but in the optimistic way you trust a contractor who seems competent. You spot-check. You don't verify everything from scratch.&lt;/p&gt;

&lt;p&gt;The problem is spot-checking code isn't the same as spot-checking drywall. A test suite has a specific count. The count is either right or it isn't. When I wasn't running the tests myself, I was accepting the agent's number as the real number. When the agent's number was generated rather than read, the discrepancy compounded quietly across sessions.&lt;/p&gt;

&lt;p&gt;The worst version of this isn't one failed test in one session. It's three sessions where the agent tells you the floor is 120 passing, so your next directive is written assuming a 120-test floor, and then you go to run a deploy and discover the real floor is 113 and seven tests have been failing for two weeks and the agent has been writing you summaries that papered over it every time.&lt;/p&gt;

&lt;p&gt;That's a real scenario. It happened. The recovery cost more time than the original implementation.&lt;/p&gt;

&lt;p&gt;The thing that made it hard to see was that the agent's code was mostly good. The implementation was usually correct. The tests it wrote were usually real tests. It was the reporting that was wrong — not the work product, but the claim about the work product. And because the work product was good, the trust built up. Which made the reporting failures more expensive when they hit.&lt;/p&gt;

&lt;h2&gt;The Rule&lt;/h2&gt;

&lt;p&gt;Raw terminal output only. No exceptions.&lt;/p&gt;

&lt;p&gt;Not "the agent says the tests pass." Not a screenshot of the agent's output panel. Not a summary. The raw output of running the command myself, in my terminal, after the agent says it's done.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;557 passed, 0 failed, 0 skipped&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That line is proof. Everything before it is a story.&lt;/p&gt;

&lt;p&gt;This is the rule I run every project on now. Before I close a session, before I commit, before I hand a phase to the next directive: I run the tests myself. I read the output myself. The number goes into the directive as the certified floor. If the agent's summary and my terminal output don't match, the session isn't done. The phase isn't certified. Nothing moves forward.&lt;/p&gt;

&lt;p&gt;It sounds rigid because it is rigid. Rigidity is the point. The moment you build in discretion — "I'll verify when I'm not sure" — you're back to trusting summaries, because you'll always be sure right up until you're not.&lt;/p&gt;

&lt;p&gt;The proof standard now covers everything that can be fabricated:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;Claim&lt;/th&gt;
&lt;th&gt;What I require&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tests passing&lt;/td&gt;
&lt;td&gt;Raw pytest output, read by me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App works on device&lt;/td&gt;
&lt;td&gt;Device screenshot, taken by me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build succeeded&lt;/td&gt;
&lt;td&gt;Terminal output of the build command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment live&lt;/td&gt;
&lt;td&gt;URL loaded in browser, screenshot taken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module implemented&lt;/td&gt;
&lt;td&gt;I read the file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An agent summary doesn't appear on this list. Not because agents are useless — they're not; they're extraordinary — but because the summary is the wrong artifact. It's a prediction. The terminal output is a measurement.&lt;/p&gt;

&lt;h2&gt;What This Led To: Stop Rules&lt;/h2&gt;

&lt;p&gt;Once I understood the problem clearly, I saw that the testing issue was one instance of a broader pattern: agents don't stop themselves.&lt;/p&gt;

&lt;p&gt;An agent given a task will complete it. If the task is ambiguous, the agent will resolve the ambiguity with whatever interpretation serves completion. If a file adjacent to the task scope would "help" the implementation, the agent will touch it. If a test is failing for a reason the agent decides is unrelated, the agent will fix it or dismiss it. None of this is malicious. It's the natural behavior of a tool optimized to complete tasks.&lt;/p&gt;

&lt;p&gt;The agent is not optimizing for your system. It's optimizing for the task.&lt;/p&gt;

&lt;p&gt;This means the discipline has to come from outside the agent. You can't ask the agent to be cautious. You have to build the caution into the structure it operates inside.&lt;/p&gt;

&lt;p&gt;Every directive I write now opens with a stop rule:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;⛔ STOP: Run pytest before touching any file.
Must report 557 passing, 0 failing, 0 skipped.
If count differs, stop and report — do not proceed.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the first thing the agent reads. It runs before any implementation. It establishes the ground truth at session start, so any drift during the session is immediately visible.&lt;/p&gt;

&lt;p&gt;The stop rule isn't for the agent's benefit. Agents don't have intentions to protect. It's for mine. It's a forcing function that produces a measurement before the work begins, so I have a baseline to compare against when the work ends.&lt;/p&gt;

&lt;p&gt;Without the stop rule, I'm in a session where the agent can silently move the floor and then report the new (wrong) floor as confirmation. With it, I have a before and after, and the delta is auditable.&lt;/p&gt;

&lt;h2&gt;The Broader System&lt;/h2&gt;

&lt;p&gt;The stop rule is one piece. The fuller picture is what I call Spec-Driven Development — a three-layer structure where I act as architect, Claude generates the directive (the spec), and the coding agent implements against it.&lt;/p&gt;

&lt;p&gt;The directive is the critical layer. It defines scope explicitly. It names every file the agent is allowed to touch. It names the files the agent is not allowed to touch. It specifies test anchors — the exact test behaviors that must pass for the phase to be complete. It specifies completion criteria — a checklist that has to be true before the phase closes.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;§1 Scope
Files to modify: task_notifications.py (new), test_task_notifications.py (new)
Read-only — do not touch: bot.py, scheduler.py, infra/db/goals.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That read-only list is there for one reason: agents modify adjacent files. Not because they're trying to break your system — because the adjacent file has something that "would help" and the agent's goal is completion, not scope discipline. The explicit list makes the boundary legible. The agent can't claim it didn't know.&lt;/p&gt;

&lt;p&gt;Does the agent still sometimes touch read-only files? Yes. When it does, the session stops. That's not a failure of the system — it's the system working. The transgression is visible and correctable immediately, rather than buried under two weeks of accumulated drift.&lt;/p&gt;

&lt;h2&gt;What This Cost Me, and What I Have Now&lt;/h2&gt;

&lt;p&gt;The honest accounting: I lost probably 40–60 hours across multiple projects before I formalized this. Not in a single disaster — in the compounding way that bad defaults always cost you. Sessions that had to be redone. Test suites that had to be audited. Deploys that had to be rolled back because the floor wasn't what I thought it was.&lt;/p&gt;

&lt;p&gt;What I have now is a floor I can certify. PrivyBot is at 557 passing, 0 failing, 0 skipped. I know that number is real because I ran it myself and wrote it down. Every new phase starts from that number. Every phase ends with a new verified number. The system is auditable at every point.&lt;/p&gt;

&lt;p&gt;The coding agent is faster than me at implementation. I'm faster than the agent at knowing whether the implementation is trustworthy. Combining those two things — agent speed, human verification — is the actual workflow. Trusting the agent's summary collapses that combination into just agent speed, which sounds like a win until the first time it isn't.&lt;/p&gt;

&lt;h2&gt;If You're Using AI Coding Agents&lt;/h2&gt;

&lt;p&gt;The summary is not the proof. Run the tests yourself. Read the output. Put the number somewhere permanent.&lt;/p&gt;

&lt;p&gt;If that sounds like too much friction, consider what the alternative has been costing you in silent drift — test floors that exist only in the agent's summary, implementations that are "done" in a way nobody has verified, phases that completed on paper and never in the terminal.&lt;/p&gt;

&lt;p&gt;The agent is confident because it's optimized to be. Your job is to be the skeptic, every time, with evidence.&lt;/p&gt;

&lt;p&gt;That's not distrust. That's the only way this actually works.&lt;/p&gt;





&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; If you want to see the directive format that enforces all of this — the stop rule, scope table, test anchors, and completion criteria — I've published the full spec structure on GitHub. Every project I run uses it. The template is open.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codingagents</category>
      <category>testing</category>
      <category>specdrivendevelopment</category>
    </item>
    <item>
      <title>The spec is load-bearing</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Thu, 25 Jun 2026 02:44:53 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/the-spec-is-load-bearing-2c51</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/the-spec-is-load-bearing-2c51</guid>
      <description>&lt;p&gt;In March 2025 I wrote a Python script that logged into a call center portal, watched dialing servers, and swapped underperforming lists automatically. It worked. I made it better in May. I made it better again in June. By June 24th I had the most capable version I'd ever built — a single file, about 1,400 lines, handling six servers, two campaign types, cooldown enforcement, stagnation detection, escalation logic.&lt;/p&gt;

&lt;p&gt;Three iterations. All single file. All named by date.&lt;/p&gt;

&lt;p&gt;March19_MetricsLower.py&lt;br&gt;
May5_MetricsLower.py&lt;br&gt;
June24_ResetUpgrade.py&lt;/p&gt;

&lt;p&gt;They're still sitting in the archive folder of the repo that replaced them. I kept them because they're the lineage. Each one is the proof that the next one was possible.&lt;/p&gt;




&lt;p&gt;The June version worked well enough that adjacent problems started pulling at it. I needed to extract CSV data from the portal. I built a tool. I needed to import files back in. Another tool. Lists needed creating from a master sheet. Another tool. DNC numbers needed scrubbing across every server simultaneously. A predictive performance forecaster needed a web app. Call recordings needed extracting.&lt;/p&gt;

&lt;p&gt;Each one was a weekend. Each one solved a real problem. None of them felt like sprawl while I was building them.&lt;/p&gt;

&lt;p&gt;A year after March I had seven private repos all touching the same portal, the same credentials, the same campaigns. None of them shared infrastructure. None of them talked to each other. If the portal changed a login flow I had seven places to fix it.&lt;/p&gt;

&lt;p&gt;I hadn't built a mess. I'd built seven good tools that became a mess the moment I tried to think about them together.&lt;/p&gt;




&lt;p&gt;The moment I saw it clearly was when I tried to connect the predictive performance forecaster to the balancer. The forecaster needed to read what the balancer knew — live metrics, list history, server state — and surface it as a web dashboard. To do that I had to wire two repos that had never been designed to connect. The data models didn't match. The assumptions buried in each codebase contradicted each other. What should have been an integration was a negotiation.&lt;/p&gt;

&lt;p&gt;That's when I stopped building and started writing.&lt;/p&gt;

&lt;p&gt;Not code. A spec. Where does each piece live. What does each piece own. What is the balancer responsible for and what is it forbidden from doing. What does shared infrastructure look like when seven separate tools finally have to be one system.&lt;/p&gt;

&lt;p&gt;The spec took longer than any of the individual tools had taken. Nothing shipped while I was writing it. It felt like the wrong use of time.&lt;/p&gt;




&lt;p&gt;TeleseroAdmin2026 started from that spec. The balancer is still the core — the same logic that ran in June, now with 262 passing tests and proper module boundaries. The other pieces are finding their places around it with shared config, shared login, shared infrastructure. One place to fix things when the portal changes.&lt;/p&gt;

&lt;p&gt;The three archive files are still there. March, May, June. I look at them occasionally. They're good code. They just had no structure underneath them to survive being part of something larger.&lt;/p&gt;

&lt;p&gt;That's what a spec actually does. It's not documentation. It's not process for its own sake. It's the thing that lets a system grow without collapsing — the load-bearing layer that the code rests on.&lt;/p&gt;

&lt;p&gt;Build without it and you end up with seven good tools and a negotiation where an integration should be.&lt;/p&gt;

&lt;p&gt;I'm also working toward a certification that puts formal language around what I figured out the wrong way across a year of dated single files. The spec isn't the thing you write after the system works. It's the thing that makes the system survivable.&lt;/p&gt;

&lt;p&gt;March Robert would not be able to comprehend the June 2026 Admin Suite that holds his archive.&lt;/p&gt;

</description>
      <category>sdd</category>
      <category>telesero</category>
      <category>automation</category>
      <category>specdriven</category>
    </item>
    <item>
      <title>The Verification Phase Nobody Builds</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Thu, 25 Jun 2026 02:44:49 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/the-verification-phase-nobody-builds-36cg</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/the-verification-phase-nobody-builds-36cg</guid>
      <description>&lt;p&gt;Tonight I pushed rfd_method public. 16 files. MIT license. A methodology repo that came out of shipping real projects under real constraints — day job, narrow windows, coding agents that fabricate results.&lt;/p&gt;

&lt;p&gt;That's the moment. Not a launch. A formalization of something that already existed.&lt;/p&gt;

&lt;p&gt;The surprise is what's already out there. GitHub Spec Kit has 106K stars. OpenSpec has 52K. Both handle the spec phase — the planning, the architecture, the decision records. Neither handles verification. The stop rules, the certified test floor, the proof standard. That gap is where projects die.&lt;/p&gt;

&lt;p&gt;The struggle is the discipline of not trusting your own tools. Coding agents don't read the terminal — they predict what the terminal probably says. They'll tell you 565 tests are passing when 75 are failing. They'll tell you the deployment succeeded when Tower is still running last month's commit. Building a verification layer means accepting that the agent will lie to you confidently, and designing the system so the lie gets caught before it ships.&lt;/p&gt;

&lt;p&gt;What I've learned: a spec without a verification phase is a wish. The floor metric is what makes the methodology real. 604 tests passing on the dev machine means nothing if Tower is running development mode with a $1.00 budget cap. Raw terminal output and device screenshots only. Never agent summaries. That's the proof standard that turns a directive into a shipped feature.&lt;/p&gt;

&lt;p&gt;rfd_method is live at github.com/rfd62794/rfd_method. The methodology that runs every project in the stack — and the verification phase that keeps it honest.&lt;/p&gt;

</description>
      <category>sdd</category>
      <category>methodology</category>
      <category>verification</category>
      <category>codingagents</category>
    </item>
    <item>
      <title>How to Monitor Dialer List Health in Convoso</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Tue, 23 Jun 2026 22:39:38 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/how-to-monitor-dialer-list-health-in-convoso-2e5g</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/how-to-monitor-dialer-list-health-in-convoso-2e5g</guid>
      <description>&lt;p&gt;Your list is degrading right now. Aged leads, disconnected numbers, leads that already converted on a different campaign, numbers that hit DNC since you imported them — all of them are burning dial attempts and suppressing your contact rate. Here's how to build the monitoring layer that surfaces this before it collapses your numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why List Health Degrades Faster Than You Think
&lt;/h2&gt;

&lt;p&gt;A fresh lead list has a predictable contact rate. As it ages, that rate decays — but not linearly. The first pass reaches the easiest contacts. Each subsequent pass hits harder-to-reach numbers, people who've already heard the pitch, numbers that have gone stale since import. The decay curve accelerates as penetration increases.&lt;/p&gt;

&lt;p&gt;Most Convoso operations notice this as "the list is getting old" and respond by ordering more inventory. The actual problem usually sits earlier: the campaign's penetration rate wasn't being tracked, import hygiene missed duplicates against existing leads, and the warning signs were invisible until the contact rate had already collapsed.&lt;/p&gt;

&lt;p&gt;A 10% drop in contact rate over a week is expensive. A 30% drop is a bad week. Neither one is a surprise if you're monitoring the right signals before they hit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What List Health Monitoring Tracks
&lt;/h2&gt;

&lt;p&gt;The metrics that matter, in roughly the order they'll help you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Penetration rate by campaign.&lt;/strong&gt; What percentage of imported leads have been attempted at least once? This is the leading indicator — the one that gives you time to act. When penetration reaches a threshold, you need new inventory in the pipeline before the campaign goes hungry, not after agents are sitting idle. Convoso doesn't surface penetration rate prominently in its standard reporting. You have to calculate it from the data the API exposes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contact rate by lead age.&lt;/strong&gt; Track conversion not just in aggregate but broken down by how long ago a lead was imported: leads attempted in week one, week two, week three since import. The decay curve tells you when a list is effectively exhausted from a productivity standpoint, even if numbers technically remain. Most operations have more "leads" than viable leads — the monitoring makes that distinction visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lead aging distribution in active campaigns.&lt;/strong&gt; A campaign can look healthy on aggregate while its active pool is dominated by weeks-old records. Breaking it down by import age surfaces this before it becomes a contact rate problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-campaign duplicate exposure.&lt;/strong&gt; A number active across multiple campaigns creates agent confusion, customer frustration, and wasted attempts. Deduplication at import catches most of this; monitoring catches what slipped through and surfaces it before it compounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complication: Convoso Shows You the Past
&lt;/h2&gt;

&lt;p&gt;Convoso's built-in reporting is backward-looking. It shows you what your contact rate was yesterday, what it was last week, how many dials converted over any given period. That data is genuinely useful for trend analysis, period comparisons, and reporting to management.&lt;/p&gt;

&lt;p&gt;It doesn't tell you what your contact rate is going to be in two hours if the list doesn't change.&lt;/p&gt;

&lt;p&gt;The monitoring that prevents a bad afternoon is penetration rate tracking updated in near-real time, not an end-of-day summary. When a campaign's available-to-dial leads drop below a threshold, you want to know while there's still time to load new inventory and have it processing before the afternoon push — not the following morning when the opportunity is already gone.&lt;/p&gt;

&lt;p&gt;Building that leading indicator requires pulling data Convoso doesn't surface in standard dashboards. A scheduled pull from the API at regular intervals, tracking available lead count by attempt history, gives you the penetration picture as it develops. Cross that with contact rate by lead age and you have a system that surfaces degradation while there's still time to respond.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The second complication is at import time.&lt;/strong&gt; A list that wasn't scrubbed against existing leads, recent DNC additions, and disconnected numbers before it loaded starts its decay already in progress. Monitoring will surface this — you'll see the contact rate compress faster than expected for a fresh list — but prevention is cheaper than remediation. Import hygiene automation that runs before the first dial is the companion to monitoring that makes the numbers meaningful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Monitoring Layer Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A scheduled data pull from the Convoso API&lt;/strong&gt; capturing campaign-level lead counts, attempt history, and contact rates at regular intervals. This doesn't require a dedicated monitoring platform — the API exposes the data and a scheduled script can collect and log it reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A dashboard that surfaces threshold violations.&lt;/strong&gt; A Google Sheets integration works for most SMB contact centers: data lands in a structured format, conditional formatting flags penetration thresholds, and a shared sheet gives ops managers visibility without requiring a new tool. Slack alerts on threshold crossings add a push notification for the signals that need immediate attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defined thresholds calibrated to your operation.&lt;/strong&gt; The metrics that signal a list health problem aren't universal — the right numbers come from your own historical baselines. But the framework is consistent across operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contact rate significantly below your historical baseline: urgent flag — the list is exhausted or degraded and is burning agent time without result&lt;/li&gt;
&lt;li&gt;Contact rate in a healthy range for your vertical and lead source: the target for a well-managed list with appropriate aging&lt;/li&gt;
&lt;li&gt;Conversion rate below a minimum threshold: list quality problem — leads aren't converting at a rate that justifies continued dialing; source quality or aging is the likely cause&lt;/li&gt;
&lt;li&gt;Conversion rate at or above your operational target: the performance level a fresh, properly sourced list should sustain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Establish your own baselines from the first few weeks of monitored data. The monitoring layer tells you when you're deviating from those baselines — the thresholds you set determine when deviation triggers an alert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import hygiene automation.&lt;/strong&gt; Pre-import scrubbing against your existing lead pool and DNC sources reduces the decay rate before the first attempt. This is prevention rather than monitoring, but it's the step that makes the monitored numbers worth tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does Convoso have any built-in penetration tracking?&lt;/strong&gt;&lt;br&gt;
Convoso shows attempt counts and disposition breakdowns, but not penetration rate as a campaign-level health metric calculated against total imported leads. You derive it from the data the API exposes — available leads versus total leads in the campaign.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How often does the monitoring need to run?&lt;/strong&gt;&lt;br&gt;
For most operations, regular intervals give you enough resolution to catch degradation while there's still time to act. Running it more frequently is possible but usually doesn't change the response time meaningfully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if we're pulling from multiple lead sources with different quality profiles?&lt;/strong&gt;&lt;br&gt;
The monitoring should track by source where possible, not just by campaign. A list from one source may have a different expected decay curve than another. Blending them without source tagging makes the contact rate signal harder to interpret.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You'd Rather Have This Running
&lt;/h2&gt;

&lt;p&gt;I build monitoring layers for contact centers using Convoso and similar platforms. If you want penetration tracking, contact rate dashboards by lead age, and threshold alerting set up correctly — start here: rfditservices.com/intake.html&lt;/p&gt;

&lt;p&gt;The first conversation is free.&lt;/p&gt;

</description>
      <category>dialer</category>
      <category>listmanagement</category>
      <category>contactcenter</category>
      <category>automation</category>
    </item>
    <item>
      <title>Zero Wasn't Zero</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Tue, 23 Jun 2026 22:39:35 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/zero-wasnt-zero-432i</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/zero-wasnt-zero-432i</guid>
      <description>&lt;p&gt;My design reviewer had been poking the same hole in my strategy for weeks. Small gaps, pointed out one at a time — the analytics weren't catching traffic well, the numbers I was quoting didn't have the resolution to support the decisions I was hanging on them. So one night I finally sat down to actually read my Google Analytics dashboard, instead of glancing at it and feeling vaguely behind. The goal was real details. Another step in the right direction.&lt;/p&gt;

&lt;p&gt;Eight active users for the week. Four of them from Council Bluffs, Iowa.&lt;/p&gt;

&lt;p&gt;I don't know anyone in Council Bluffs, Iowa. Google does, though — it's the site of one of their largest data centers. Half of my "users" were crawlers pinging my site and getting logged as people. So my real week was three, maybe four humans. Organic search: zero. Qualified leads: zero. Converted leads: zero.&lt;/p&gt;

&lt;p&gt;I run a consulting intake form on that site. Multi-step wizard, dialer platforms, pain points, contact info — the funnel's entire bottom end. And here's the part I have to be honest about: when the work to wire it into analytics finally started, and the agent running the directive stopped cold to report that the intake page had never loaded the analytics tag at all — not one recorded page view, ever — I wasn't shocked. I'd been told it probably wasn't tied in. I half knew. The stop report wasn't a discovery. It was confirmation of a suspicion I'd been carrying around for weeks instead of spending ten minutes to check.&lt;/p&gt;

&lt;p&gt;That's the actual lesson, and it's less flattering than "I found a bug." Some projects get real developmental commitment. My website isn't one of them — it gets passing attention, and known holes survive a remarkably long time in projects that only get passing attention. I prefer a clean repo the way everyone prefers a clean kitchen, and like everyone, I have a room I just don't go in.&lt;/p&gt;

&lt;p&gt;What surprised me wasn't the hole. It was noticing what the zeros had been doing to me anyway. I &lt;em&gt;knew&lt;/em&gt; the instrumentation was suspect — and the row of zeros under "leads" still read like a verdict every single time I glanced at it. A gauge you know is broken still lies to you, and you still flinch. "Nobody wants this" and "you never measured it" produce the exact same dashboard, and even when you suspect it's the second one, your gut reads the first.&lt;/p&gt;

&lt;p&gt;The fix took one evening once it stopped being deferred: tag on the page, a &lt;code&gt;generate_lead&lt;/code&gt; event on successful submission, fallback paths so analytics can never break the form itself. The funnel now reports both of its ends — views and submissions — so the two failure modes finally look different. Views without submissions means the page doesn't convert. No views means nothing sends anyone there. Opposite problems, opposite fixes, indistinguishable until this week.&lt;/p&gt;

&lt;p&gt;Instrument the conversion point before you judge the funnel. And if you suspect a gauge is broken — confirm it today, because you're going to keep reading it either way.&lt;/p&gt;

&lt;p&gt;Data starts now. The next zero on that dashboard will be a real one. Weirdly, I'm looking forward to it.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>ga4</category>
      <category>instrumentation</category>
      <category>lessons</category>
    </item>
    <item>
      <title>How to Automate DNC Removal Requests in Convoso</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Fri, 19 Jun 2026 00:19:36 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/how-to-automate-dnc-removal-requests-in-convoso-1621</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/how-to-automate-dnc-removal-requests-in-convoso-1621</guid>
      <description>&lt;p&gt;DNC removal requests shouldn't take more than a few seconds to process. If your ops team is manually logging into each system, finding the number, and removing it one platform at a time, every request is an open compliance window. Here's how to close it automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Manual DNC Processing
&lt;/h2&gt;

&lt;p&gt;A number comes in flagged for removal. Someone on the floor submits it. If you're running Convoso alongside Zoom Contact Center, Zoom Phone, and Telesero, that means logging into each system separately — find the number, remove it, move to the next platform, repeat.&lt;/p&gt;

&lt;p&gt;At multiple removal requests per week across several systems, you're looking at significant manual work each week. More importantly, every minute between the request and the removal is a minute of active compliance exposure. A TCPA violation starts at $500 per call. When the pattern is systematic — a number that should have been removed staying active across multiple campaigns — class action exposure enters the picture.&lt;/p&gt;

&lt;p&gt;The gap between when a removal is requested and when it actually completes isn't just inefficiency. It's risk that compounds with every dial attempt on a number that should be off the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Automated DNC Removal Works
&lt;/h2&gt;

&lt;p&gt;The automated version uses a Slack slash command as the intake point. An ops manager types the number into a command and hits send. The request routes immediately to a cloud service — deployed on Google Cloud Run — that fans out across every active system in parallel.&lt;/p&gt;

&lt;p&gt;Not sequentially. Simultaneously.&lt;/p&gt;

&lt;p&gt;In a contact center running multiple Convoso campaigns alongside Zoom Contact Center, Zoom Phone, and Telesero, a single command hits every platform in parallel. Each system processes the removal independently. Results log to cloud storage with a timestamp and each system's individual response recorded separately. A confirmation returns to the Slack channel before the manager has switched back to their next task.&lt;/p&gt;

&lt;p&gt;Wall-clock time from submission to confirmed removal across all systems: under three seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No manual steps across multiple logins&lt;/li&gt;
&lt;li&gt;Every system processes simultaneously, not sequentially&lt;/li&gt;
&lt;li&gt;An audit trail in cloud storage for every request — number, timestamp, and each platform's response logged individually&lt;/li&gt;
&lt;li&gt;Confirmation back to Slack showing exactly which systems confirmed the removal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is hours of manual work eliminated and a compliance window that closes in seconds instead of sitting open for minutes or hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The core components are straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Slack slash command&lt;/strong&gt; configured to accept a phone number and POST it to your backend service. Slack's platform makes the setup straightforward — the command sends the number to a URL you control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A cloud service&lt;/strong&gt; (Cloud Run works well here — costs are minimal at typical contact center request volumes) that receives the number, authenticates with each dialer platform, and fans the removal request out in parallel. The service holds valid API credentials for every system in your stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud storage logging&lt;/strong&gt; that records every request: the number, the timestamp, the system, and the response. This is your audit documentation. If compliance ever needs to verify a removal, it's timestamped and searchable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complication Most Automated DNC Tools Miss: The Litigator List
&lt;/h2&gt;

&lt;p&gt;Most contact centers treat DNC compliance as "scrub against the Federal registry." That's necessary but not sufficient — and the list that's actually generating settlements isn't the Federal DNC list.&lt;/p&gt;

&lt;p&gt;It's the litigator list.&lt;/p&gt;

&lt;p&gt;Serial TCPA plaintiffs — professional litigants who specifically target contact centers for violations — maintain numbers that don't appear on the Federal or state DNC registries. They're not on any opt-out list. They're on a separate database of known litigators tracked by services like Blacklist Alliance, Contact Center Compliance's Litigator Scrub, and IPQS. These databases are updated from court records as new cases are filed.&lt;/p&gt;

&lt;p&gt;A number that's not on the Federal DNC registry, not on any state list, and has no internal opt-out record can still be held by someone who files TCPA suits as a business model.&lt;/p&gt;

&lt;p&gt;The correct scrubbing sequence for every removal request — and for every new list before import — is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Federal DNC registry&lt;/li&gt;
&lt;li&gt;State DNC lists (Florida's FTSA and California have stricter requirements than federal)&lt;/li&gt;
&lt;li&gt;Litigator list (Blacklist Alliance, Contact Center Compliance, or equivalent)&lt;/li&gt;
&lt;li&gt;Internal opt-out list&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most automated DNC tools only run step one. Some run steps one and two. The ones that skip the litigator scrub are leaving the highest-risk exposure in place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The second complication: graceful failure is the wrong behavior for compliance tools.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After a DNC tool deploys, it's easy to assume it's working correctly. Confirmations come back clean. The Slack channel looks fine. The process appears to be running.&lt;/p&gt;

&lt;p&gt;The hidden failure mode: most automation handles errors gracefully. When a system fails to process a removal — expired credentials, missing API token, network timeout — the default behavior is to log the error internally and continue. The confirmation that returns to the manager says the request was processed. It doesn't say one of the systems was skipped.&lt;/p&gt;

&lt;p&gt;A DNC tool that ran for several weeks while silently skipping one of its Convoso campaigns — because the API token had never been added to the deployment configuration — reporting clean confirmations the entire time, is a real scenario. The removals were going through on every platform except one. The audit trail showed success. The problem was invisible until someone looked at the deployment configuration directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The correct implementation fails loudly.&lt;/strong&gt; If any single system doesn't return a confirmed removal, the tool surfaces a failure — not a partial success, not a warning in a log file no one checks. A visible failure in the Slack channel, requiring someone to act on it.&lt;/p&gt;

&lt;p&gt;A single-point failure that gets surfaced immediately is fixable. A multi-week silent failure discovered in a compliance audit is expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does this work if we have multiple Convoso campaigns?&lt;/strong&gt;&lt;br&gt;
Yes. The service authenticates against the Convoso API and processes the removal across every active campaign in your account. You specify which campaigns are in scope during setup — typically all active campaigns, with exclusions for any that aren't applicable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if one system is down when a removal request comes in?&lt;/strong&gt;&lt;br&gt;
The tool surfaces a failure for that system specifically, while confirming removals on every other platform that processed successfully. The manager knows exactly which system needs follow-up, rather than assuming the removal is complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the audit trail sufficient for TCPA compliance documentation?&lt;/strong&gt;&lt;br&gt;
The audit trail records the number, the timestamp, and each system's individual response. Whether that meets your specific compliance documentation requirements depends on your legal setup — this is worth confirming with compliance counsel, but the structure is designed to produce a clear, searchable record of every removal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this cost to run?&lt;/strong&gt;&lt;br&gt;
At typical contact center request volumes, Cloud Run costs are minimal — the service only runs when a request comes in. Cloud storage logging adds cents per month at that volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You'd Rather Have This Done
&lt;/h2&gt;

&lt;p&gt;I build this kind of compliance automation for contact centers. If you want the Slack command, the cloud service, the audit trail, and the failure handling set up correctly from the start — start here: rfditservices.com/intake.html&lt;/p&gt;

&lt;p&gt;The first conversation is free.&lt;/p&gt;

</description>
      <category>convoso</category>
      <category>dnccompliance</category>
      <category>contactcenter</category>
      <category>automation</category>
    </item>
    <item>
      <title>A side effect of a side effect</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Mon, 15 Jun 2026 02:32:55 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/a-side-effect-of-a-side-effect-3pj2</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/a-side-effect-of-a-side-effect-3pj2</guid>
      <description>&lt;p&gt;In 2016 I uploaded a review of Hybrid Animals to YouTube. It got 353 views. Then I stopped.&lt;/p&gt;

&lt;p&gt;I was in phone sales. Still a bachelor. I liked the idea of being a content creator — it looks cool, it sounds cool — but I knew pretty quickly it wasn't really for me. Making videos was work, and the work wasn't interesting enough to justify itself. I wanted to play games. I didn't want to make content about playing games.&lt;/p&gt;

&lt;p&gt;So I stopped.&lt;/p&gt;




&lt;p&gt;Ten years later I have a YouTube channel publishing daily Shorts across six different games. I didn't change my mind about content creation. I changed what content creation costs me.&lt;/p&gt;

&lt;p&gt;ContentPipeline records the session, transcribes it with Whisper, identifies the moments worth keeping, generates the captions, assembles the video, and schedules the upload. I play games. The pipeline does the rest. The channel exists because I removed the friction, not because I developed a passion for it.&lt;/p&gt;

&lt;p&gt;The clips are a side effect of playing games. The channel is a side effect of building a pipeline. Any revenue from it would be a side effect of a side effect.&lt;/p&gt;




&lt;p&gt;That framing used to feel like an admission of something. Like I wasn't taking it seriously enough. Now it feels like the honest version of what most creators won't say.&lt;/p&gt;

&lt;p&gt;Most YouTube advice is about optimizing for the channel. Titles, thumbnails, posting frequency, audience retention. I don't think about any of that. I think about whether the pipeline is producing good clips and whether the games are interesting. The channel takes care of itself.&lt;/p&gt;

&lt;p&gt;The goal I'd actually care about is streaming — not because of the audience but because of the engineering challenge. Building a live pipeline, managing the session, making it work technically. That's the interesting problem. The viewers would be incidental.&lt;/p&gt;

&lt;p&gt;I'm not a content creator. I'm someone who plays games and built infrastructure. The YouTube channel is what the infrastructure produces.&lt;/p&gt;

&lt;p&gt;There's a version of this where the channel grows, the clips find an audience, and the side effect generates real income alongside the consulting work, the games, and everything else running in parallel. That would be a quiet bonus. A small thread alongside many others.&lt;/p&gt;

&lt;p&gt;I'd rather be the developer who has a YouTube channel than the YouTuber who also codes.&lt;/p&gt;

&lt;p&gt;The pipeline makes that possible. I just play games.&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>pipeline</category>
      <category>contentengine</category>
      <category>automation</category>
    </item>
    <item>
      <title>I built the same game for 20 years without knowing it</title>
      <dc:creator>Robert Floyd Dugger</dc:creator>
      <pubDate>Mon, 08 Jun 2026 02:36:18 +0000</pubDate>
      <link>https://dev.to/robert_floyddugger_6f9a4/i-built-the-same-game-for-20-years-without-knowing-it-40b9</link>
      <guid>https://dev.to/robert_floyddugger_6f9a4/i-built-the-same-game-for-20-years-without-knowing-it-40b9</guid>
      <description>&lt;p&gt;I want a world that doesn't stop when I do.&lt;/p&gt;

&lt;p&gt;I didn't know that's what I wanted until recently. But it explains every project I've shipped for twenty years, and it started with a browser RPG I played in middle school during summer school.&lt;/p&gt;

&lt;p&gt;Lands of Hope is still live. You can play it today. Deep content, crafting queues, a community that made the world feel like it mattered. What hooked me wasn't any single mechanic. It was the feeling that the world kept going without me. Things set in motion with consequences I had to wait for. Other people in it, doing things alongside me, making it real.&lt;/p&gt;

&lt;p&gt;I was thirteen. I didn't have language for what I wanted. I just knew how it felt.&lt;/p&gt;




&lt;p&gt;TurboShells came first. Turtle racing where every turtle's body assembled from its genome at render time — shell radius, leg length, color expressed from a genetic sequence. The turtles raced. The faster ones bred. The slower ones didn't. Nobody played TurboShells. But I built it anyway, because something about setting a breeding pair in motion and waiting for the result felt right in a way I couldn't explain.&lt;/p&gt;

&lt;p&gt;rpgCore next. A thousand tests. A proper ECS architecture. Genetics, lifecycle, dispatch — everything composable, everything persistent. SlimeGarden put it to work: breed slimes, dispatch them, see what comes back. OperatorGame pushed the dispatch loop into squad tactics. VoidDrift stripped it down to its core: drones go out, mine asteroid ore, return, station inventory updates, repeat.&lt;/p&gt;

&lt;p&gt;Every project had the same shape underneath. Something goes out without me watching. Time passes. Something comes back changed.&lt;/p&gt;




&lt;p&gt;The recognition came slowly. I was writing VoidDrift's Scout dispatch system one night — drones leaving the station, doing their work autonomously, returning with ore — and I stopped. I'd written this before. Not something similar. This exact thing. The same send, wait, return, consequence that the breeding pairs were running. That the slimes were running. That my Lands of Hope crafting queues were running when I was thirteen.&lt;/p&gt;

&lt;p&gt;I opened a list of every project I'd shipped and read it from the top. The dispatch loop was in all of them. Not because I'd planned it. Because I kept arriving at the only mechanic that produced the feeling I was chasing.&lt;/p&gt;

&lt;p&gt;A world that goes on without you. That changes while you sleep. That has consequences whether you're watching or not.&lt;/p&gt;

&lt;p&gt;ContentPipeline publishes while I'm at work. PrivyBot fires its morning briefing whether I'm awake or not. RALPH ran overnight tasks the first night I deployed it and had results waiting when I woke up. VoidDrift's drones mine whether the screen is on.&lt;/p&gt;

&lt;p&gt;I haven't been building games. I've been building persistent worlds.&lt;/p&gt;




&lt;p&gt;The surprise was that naming it didn't feel like a limitation. It felt like a body of work.&lt;/p&gt;

&lt;p&gt;Scattered projects suddenly had a spine. TurboShells wasn't a side project that went nowhere — it was iteration three on something I've been refining since middle school. rpgCore wasn't over-engineering — it was building the foundation the loop deserved. VoidDrift isn't just a mining idle game. It's the clearest version yet of the thing I've been trying to make since I was thirteen.&lt;/p&gt;

&lt;p&gt;The struggle was that I couldn't have named this pattern while I was inside it. Patterns are invisible to the person living them. You need the list, the distance, the moment when you stop mid-implementation and recognize the shape.&lt;/p&gt;




&lt;p&gt;AntColony is next. Same chassis as VoidDrift, same loop underneath — workers dispatching, foraging, returning, colony state updating without you. Different world. Same feeling.&lt;/p&gt;

&lt;p&gt;I know what I'm building now. I'm building worlds that don't stop when I do.&lt;/p&gt;

&lt;p&gt;I've always been building that. I just needed twenty years to see it.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>gamedev</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
