<?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: paul_h</title>
    <description>The latest articles on DEV Community by paul_h (@paul_knoxops).</description>
    <link>https://dev.to/paul_knoxops</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%2F3955555%2F68e3b7cb-e74f-4ff9-9391-0af09081ef3a.jpg</url>
      <title>DEV Community: paul_h</title>
      <link>https://dev.to/paul_knoxops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paul_knoxops"/>
    <language>en</language>
    <item>
      <title>Loop Engineering for Ops</title>
      <dc:creator>paul_h</dc:creator>
      <pubDate>Fri, 26 Jun 2026 11:11:33 +0000</pubDate>
      <link>https://dev.to/paul_knoxops/loop-engineering-for-ops-af1</link>
      <guid>https://dev.to/paul_knoxops/loop-engineering-for-ops-af1</guid>
      <description>&lt;p&gt;Loop Engineering is the phrase everyone's been throwing around lately. It sits after Prompt, Context, and Harness — pushing agent coordination one step further.&lt;/p&gt;

&lt;p&gt;Google's Addy Osmani published a long piece a while back that formalized the whole thing. Worth a read. But before him, Peter Steinberger, founder of OpenClaw, said something that nailed it even harder.&lt;/p&gt;

&lt;p&gt;"You shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents."&lt;/p&gt;

&lt;p&gt;What he means is, you stop being the person manually driving the agent turn by turn. You build a system. The system runs, inspects, fixes, records. You go from the person turning the wrench to the person who designed the assembly line.&lt;/p&gt;

&lt;p&gt;One thing to keep in mind: in operations, safety is everything. Without guardrails and human-in-the-loop, an autonomous loop can cause real damage. I'm not a fan of "fully automated" ops loops, and I'll show you why.&lt;/p&gt;

&lt;p&gt;This article walks through building a safe ops loop using a host health check example. Most of the routine ops work we do every day can be packaged this way.&lt;/p&gt;

&lt;h1&gt;
  
  
  Loop Engineering, Broken Down
&lt;/h1&gt;

&lt;p&gt;If you take Loop Engineering apart, you get six pieces.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Automations&lt;/td&gt;
&lt;td&gt;Scheduled or conditional triggers. The loop runs itself.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worktrees&lt;/td&gt;
&lt;td&gt;Multiple agents working in parallel, isolated checkouts, no stepping on each other.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills&lt;/td&gt;
&lt;td&gt;Project knowledge written down so you don't re-explain everything every session.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Hooks into real systems — SSH, APIs, databases. Actually executes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-agents&lt;/td&gt;
&lt;td&gt;Separate the builder from the reviewer. The one who wrote it is too nice grading it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Remembers what happened across runs. The agent forgets. Files don't.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude Code and Codex ship most of these built-in now. You don't build from scratch.&lt;/p&gt;

&lt;p&gt;But there's one piece the tools can't do for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing down the loop's structure and constraints.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sure, you can type a one-off command (like Claude Code's &lt;code&gt;/goal&lt;/code&gt;). Two problems with that.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompts leave gaps.&lt;/strong&gt; How many steps per round? How do you know it's done? How do steps hand off to each other? What stops it from going off the rails? Nobody forces you to think about these things. You can skip them. With agent-runbook, the framework forces you to answer every one of them. Skip one, and the compiler yells at you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not reusable.&lt;/strong&gt; You run it once, it's gone. Next cluster, next person, start over.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You need something that turns your loop's steps, end conditions, and guardrails into a file. Not a prompt you type and forget — a contract you commit to the repo. Next run, next person, same file, same result.&lt;/p&gt;

&lt;h1&gt;
  
  
  agent-runbook: Writing Loops as Contracts
&lt;/h1&gt;

&lt;p&gt;agent-runbook is an open-source project on GitHub: &lt;a href="https://github.com/KnoxOps/agent-runbook" rel="noopener noreferrer"&gt;github.com/KnoxOps/agent-runbook&lt;/a&gt;. Its entire philosophy fits in one sentence: &lt;strong&gt;constrain agents with contracts, not prompts and hope.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few key design decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contract-first.&lt;/strong&gt; You write a YAML file that declares your loop's steps, outputs, dependencies, and guardrails. Not a vague "go check the servers" prompt — every step's input/output format, execution order, and boundary conditions are locked down. The compiler validates before generating the skill file: missing schemas, circular dependencies, references to non-existent outputs — all caught at build time. Nothing blows up halfway through a run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human in the loop.&lt;/strong&gt; Ops isn't testing. In staging, the agent can auto-fix everything. In production? No. Write operations stay behind a human gate. The agent can SSH in and investigate. It can draft a plan. It can list the commands. But you decide whether those commands actually run. This isn't a suggestion tucked into a prompt. It's a hard node in the step structure. Not approved, not proceeding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;External memory.&lt;/strong&gt; The agent's context resets after every turn. Expecting it to remember what happened last round is wishful thinking. The loop stores every round's output, iteration history, and overall progress in files. A status file tracks which steps are done. An iteration log records what got fixed each round. The agent forgets. The files don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mandatory guardrails.&lt;/strong&gt; Writing "don't touch the config" in a prompt is a request. The agent can ignore it. agent-runbook guardrails are enforced checks — after every round, an independent review verifies the agent didn't overstep. Modified files it shouldn't have? Ran commands outside the plan? That round doesn't count as complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built-in brakes.&lt;/strong&gt; Every loop must declare what "done" looks like, plus a hard iteration cap. The loop stops. It doesn't run forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Files over context for step communication.&lt;/strong&gt; Don't count on the agent remembering what the previous step said. Every step's output follows a JSON Schema. The next step validates as it reads. A field mismatch fails immediately — no silent data corruption.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hands-on: A Host Health Check Loop
&lt;/h1&gt;

&lt;p&gt;Real scenario. You've got a fleet of production servers. Regular health checks: disk, memory, load, critical services. When something's wrong, you can't just let the agent fix it automatically — production write operations require human approval.&lt;/p&gt;

&lt;p&gt;Each round of this loop runs five steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick the most critical issue from the inspection results&lt;/li&gt;
&lt;li&gt;SSH in, investigate the root cause, draft a fix plan&lt;/li&gt;
&lt;li&gt;Present the plan, wait for approval&lt;/li&gt;
&lt;li&gt;Execute the approved fix&lt;/li&gt;
&lt;li&gt;Re-inspect to confirm the issue is resolved&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what it looks like as an Agent Runbook YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host-health-loop&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ansible health check discovers issues → fix one by one (write ops require human approval) → re-inspect → repeat until all healthy → generate HTML report&lt;/span&gt;

&lt;span class="na"&gt;input_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inventory&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Path to Ansible inventory file, defaults to ansible/inventory.ini&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playbook&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Path to health check playbook, defaults to ansible/health_check.yml&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inspect&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Ansible playbook to inspect all hosts and generate issue list&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;ansible-playbook -i {inventory} {playbook} 2&amp;gt;&amp;amp;1&lt;/span&gt;
      &lt;span class="s"&gt;mv /tmp/host_issues.json host_issues.json&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host_issues.json&lt;/span&gt;
        &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schemas/host_issues.schema.json&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fix_loop&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;loop&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;Fix loop: each round picks the most critical issue, produces a plan,&lt;/span&gt;
      &lt;span class="s"&gt;waits for human approval, executes the fix, then re-inspects all hosts.&lt;/span&gt;
      &lt;span class="s"&gt;Repeats until no issues remain or max iterations reached.&lt;/span&gt;
    &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host_issues.json&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;total_issues&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;equal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hosts&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;healthy)"&lt;/span&gt;
    &lt;span class="na"&gt;max_iterations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;inspect&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;select_issue&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inline&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Select the single most critical issue from host_issues.json&lt;/span&gt;
        &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Read host_issues.json and schemas/selected_issue.schema.json.&lt;/span&gt;

          &lt;span class="s"&gt;Pick the SINGLE most critical issue using this priority order:&lt;/span&gt;
          &lt;span class="s"&gt;1. critical disk (highest priority)&lt;/span&gt;
          &lt;span class="s"&gt;2. critical service_down&lt;/span&gt;
          &lt;span class="s"&gt;3. critical memory&lt;/span&gt;
          &lt;span class="s"&gt;4. critical load&lt;/span&gt;
          &lt;span class="s"&gt;5. warning disk&lt;/span&gt;
          &lt;span class="s"&gt;6. warning service_down&lt;/span&gt;
          &lt;span class="s"&gt;7. warning memory&lt;/span&gt;
          &lt;span class="s"&gt;8. warning load (lowest priority)&lt;/span&gt;

          &lt;span class="s"&gt;Write the selected issue to selected_issue.json following the schema.&lt;/span&gt;

          &lt;span class="s"&gt;If host_issues.json has total_issues == 0, write {"done": true} instead.&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selected_issue.json&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schemas/selected_issue.schema.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plan_action&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Analyze the selected issue and create a concrete remediation plan (no execution)&lt;/span&gt;
        &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Read selected_issue.json and schemas/pending_action.schema.json.&lt;/span&gt;

          &lt;span class="s"&gt;STEP 1 — Investigate: SSH to the target host and check the actual situation.&lt;/span&gt;
          &lt;span class="s"&gt;Understand the root cause before writing any plan. Do NOT guess or write generic commands.&lt;/span&gt;

          &lt;span class="s"&gt;STEP 2 — Plan: Based on your findings, write a concrete remediation plan&lt;/span&gt;
          &lt;span class="s"&gt;to pending_action.json following the schema.&lt;/span&gt;

          &lt;span class="s"&gt;Do NOT execute anything. Only investigate and write the JSON file.&lt;/span&gt;
          &lt;span class="s"&gt;If selected_issue.json contains {"done": true}, write {"done": true} to pending_action.json.&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;select_issue&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pending_action.json&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schemas/pending_action.schema.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;approve&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inline&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Human approval gate — present the remediation plan and wait for confirmation&lt;/span&gt;
        &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Read pending_action.json.&lt;/span&gt;

          &lt;span class="s"&gt;If it contains {"done": true}, skip this step.&lt;/span&gt;

          &lt;span class="s"&gt;Otherwise, PRESENT the remediation plan from pending_action.json to the human:&lt;/span&gt;

          &lt;span class="s"&gt;---&lt;/span&gt;
          &lt;span class="s"&gt;## Awaiting Approval&lt;/span&gt;
          &lt;span class="s"&gt;Present the host, issue, risk level, and commands from pending_action.json.&lt;/span&gt;

          &lt;span class="s"&gt;Type "approve" to execute, or "reject" to skip this issue.&lt;/span&gt;
          &lt;span class="s"&gt;---&lt;/span&gt;

          &lt;span class="s"&gt;WAIT for the human to respond. Do NOT proceed without explicit approval.&lt;/span&gt;
          &lt;span class="s"&gt;If approved, copy pending_action.json to approved_action.json.&lt;/span&gt;
          &lt;span class="s"&gt;If rejected, write skip_action.json with {"status": "rejected", "reason": "rejected by human"}.&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;plan_action&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;execute&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Execute the approved remediation&lt;/span&gt;
        &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Check for approved_action.json. If it exists, read it.&lt;/span&gt;

          &lt;span class="s"&gt;If skip_action.json exists instead, do NOT execute — write execute_result.json&lt;/span&gt;
          &lt;span class="s"&gt;following schemas/execute_result.schema.json with status "skipped".&lt;/span&gt;

          &lt;span class="s"&gt;Execute the remediation: SSH into the target host and run the commands&lt;/span&gt;
          &lt;span class="s"&gt;listed in approved_action.json.&lt;/span&gt;

          &lt;span class="s"&gt;Rules:&lt;/span&gt;
          &lt;span class="s"&gt;- Use the exact commands from the plan, do not improvise&lt;/span&gt;
          &lt;span class="s"&gt;- If a command fails, do NOT retry&lt;/span&gt;
          &lt;span class="s"&gt;- After execution, verify the result (e.g., check service status, check disk usage)&lt;/span&gt;
          &lt;span class="s"&gt;- Write execute_result.json following schemas/execute_result.schema.json&lt;/span&gt;

          &lt;span class="s"&gt;If pending_action.json had {"done": true}, write {"status": "all_done"} to execute_result.json.&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;approve&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;execute_result.json&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schemas/execute_result.schema.json&lt;/span&gt;
        &lt;span class="na"&gt;quality_check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;blocking&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Commands&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;were&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;executed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exactly&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;specified&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;approved_action.json"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;destructive&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commands&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;were&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;improvised&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;beyond&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;plan"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Verification&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;was&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;performed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;after&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;execution"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;re_inspect&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Re-run inspection to refresh the issue list&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;ansible-playbook -i {inventory} {playbook} 2&amp;gt;&amp;amp;1&lt;/span&gt;
          &lt;span class="s"&gt;mv /tmp/host_issues.json host_issues.json&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host_issues.json&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schemas/host_issues.schema.json&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;execute&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;generate_report&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate a polished HTML inspection and remediation report&lt;/span&gt;
    &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Read the final host_issues.json to get the current health status.&lt;/span&gt;
      &lt;span class="s"&gt;Also read all execute_result.json files from the workspace to understand what was fixed.&lt;/span&gt;

      &lt;span class="s"&gt;Generate a single, self-contained, beautiful HTML report: health_report.html&lt;/span&gt;

      &lt;span class="s"&gt;The report should include:&lt;/span&gt;
      &lt;span class="s"&gt;- Overall health status (ALL CLEAR or ISSUES REMAINING)&lt;/span&gt;
      &lt;span class="s"&gt;- Summary stats: hosts checked, total issues found and resolved&lt;/span&gt;
      &lt;span class="s"&gt;- Remediation timeline: each iteration with host, issue, investigation findings,&lt;/span&gt;
        &lt;span class="s"&gt;commands executed, and before/after comparison&lt;/span&gt;
      &lt;span class="s"&gt;- Final per-host status&lt;/span&gt;

      &lt;span class="s"&gt;Design requirements:&lt;/span&gt;
      &lt;span class="s"&gt;- Dark theme, modern dashboard style&lt;/span&gt;
      &lt;span class="s"&gt;- CSS grid/flexbox, no external dependencies&lt;/span&gt;
      &lt;span class="s"&gt;- Mobile-responsive&lt;/span&gt;
      &lt;span class="s"&gt;- Professional and visually impressive — this is a production report&lt;/span&gt;
      &lt;span class="s"&gt;- Include all CSS inline in a &amp;lt;style&amp;gt; tag&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;fix_loop&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write the YAML, compile it with one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; agent_runbook generate runbook.yaml &lt;span class="nt"&gt;-o&lt;/span&gt; output/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop it into Claude Code or Codex, and it runs.&lt;/p&gt;

&lt;p&gt;The generated SKILL.md is about 250 lines. Here's a condensed version of the key sections (&lt;a href="https://github.com/KnoxOps/agent-runbook/blob/main/examples/host-health-loop/output/SKILL.md" rel="noopener noreferrer"&gt;full version here&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host-health-loop&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ansible health check → fix → re-inspect → repeat until all healthy → HTML report&lt;/span&gt;
&lt;span class="na"&gt;user-invocable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Execution Flow&lt;/span&gt;

&lt;span class="gu"&gt;### Task Context&lt;/span&gt;
Initializes task_context.json to track every step's status. Update after each step.
Crash mid-run? Resume from the last completed step.

&lt;span class="gu"&gt;### Step 1: inspect&lt;/span&gt;
&lt;span class="gs"&gt;**Type:**&lt;/span&gt; script
Runs ansible-playbook to inspect all hosts, produces host_issues.json.

&lt;span class="gu"&gt;### Step 2: fix_loop&lt;/span&gt;
&lt;span class="gs"&gt;**Type:**&lt;/span&gt; loop
&lt;span class="gs"&gt;**Goal:**&lt;/span&gt; host_issues.json total_issues equals 0
&lt;span class="gs"&gt;**Max Iterations:**&lt;/span&gt; 10

Each round:
&lt;span class="p"&gt;1.&lt;/span&gt; select_issue — pick the most critical issue by priority
&lt;span class="p"&gt;2.&lt;/span&gt; plan_action — SSH investigation, draft a fix plan (no execution)
&lt;span class="p"&gt;3.&lt;/span&gt; approve — present the plan, wait for human approval
&lt;span class="p"&gt;4.&lt;/span&gt; execute — run approved commands, quality_check verifies boundaries
&lt;span class="p"&gt;5.&lt;/span&gt; re_inspect — re-run inspection, refresh the issue list

&lt;span class="gu"&gt;## Goal Evaluation&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Goal met → mark complete, proceed to next step
&lt;span class="p"&gt;2.&lt;/span&gt; Goal not met, iterations remain → start next round
&lt;span class="p"&gt;3.&lt;/span&gt; Max iterations reached → mark complete, report remaining issues

Append to iteration_history after each round.

&lt;span class="gu"&gt;### Step 3: generate_report&lt;/span&gt;
&lt;span class="gs"&gt;**Type:**&lt;/span&gt; agent
Reads final inspection results and fix records. Generates a dark-themed dashboard HTML report.

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the thing: that YAML you just read is literally declaring all six pieces of Loop Engineering. You declare it. Claude Code or Codex runs it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you declare in YAML&lt;/th&gt;
&lt;th&gt;What Claude Code / Codex does automatically&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Who runs each step, what they do, in what order&lt;/td&gt;
&lt;td&gt;Spawns agents in declared order, each in an isolated worktree, cleaned up after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format requirements for each step&lt;/td&gt;
&lt;td&gt;Validates outputs after each step. Wrong format? Immediate error.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How to connect external systems (SSH, Ansible, APIs)&lt;/td&gt;
&lt;td&gt;Connects via MCP to real servers. Actually executes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What "done" means, max rounds&lt;/td&gt;
&lt;td&gt;Evaluates goal after every round. Hits the cap? Stops. No wasted tokens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approval required, guardrails enforced&lt;/td&gt;
&lt;td&gt;Shows write commands. Waits for your approval. Checks boundaries after every round. Overstep? Round doesn't count.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Five Rounds, All Green
&lt;/h1&gt;

&lt;p&gt;This example was tested on three bare-metal machines. The initial inspection found multiple issues spread across all three hosts.&lt;/p&gt;

&lt;p&gt;Round one hit eval-bare-vm-3. Root disk at 93%, only 1.4G left. The agent SSH'd in and found the culprits: a Docker JSON log at 5.4G, 4.5G of temp files in /tmp, 215M of application logs, ~800M of container cache. After approval, it cleaned up — truncated the Docker log, cleared /tmp, cleaned apt cache, vacuumed journald to 50M. Freed up about 10G. Disk dropped from 93% to 38%.&lt;/p&gt;

&lt;p&gt;The next few rounds were nginx and docker down across the three machines — services someone had manually stopped and never brought back. Each round: investigate → plan → approve → execute → verify. Five rounds total. All green. Loop stopped on its own.&lt;/p&gt;

&lt;p&gt;Generated a dark dashboard HTML report.&lt;/p&gt;

&lt;p&gt;Notice every single write operation went through approval. No automatic execution. A human saw every command before it ran. In production, you don't skip this.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to Design a Good Ops Loop
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Pick the right task.&lt;/strong&gt; Good ops loops have objective feedback signals — inspection results, metrics, health check endpoints. Each round can build on the last. Host health checks, certificate expiry scans, K8s pod restart loops, Prometheus alert storm classification, log anomaly pattern matching — all solid candidates. Capacity planning and architecture changes need global judgement. Don't shove those into a loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write "done" so a machine can decide.&lt;/strong&gt; "Issue list is empty" is a good end condition. "The cluster is healthy" is not. The agent has to read a file or run a command and get a true/false answer. When you're writing the condition, ask yourself: can a script determine this in one line? If not, your agent probably can't either.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass data through files, not memory.&lt;/strong&gt; A step's output is the next step's input, format-constrained. A mistake gets caught immediately. Agents have terrible memory — long context, they forget. Files don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write operations need a human gate.&lt;/strong&gt; Staging can be fully automated. Production cannot. The approval step is the safety valve for the entire loop. Write it into the contract. A hundred times more reliable than writing it in a prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set a ceiling.&lt;/strong&gt; Max iterations isn't your target — it's the circuit breaker that says "something's wrong." A healthy loop converges well below the limit. If you hit the cap, some issue can't be fixed or the inspection keeps false-flagging. Time for a human.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping Up
&lt;/h1&gt;

&lt;p&gt;agent-runbook is intentionally lightweight. It's not a full Loop Engineering implementation. It does one thing: writes your loop structure as a declarative file. Claude Code or Codex handles the rest.&lt;/p&gt;

&lt;p&gt;You don't need to start from scratch. The &lt;a href="https://github.com/KnoxOps/agent-runbook/tree/main/examples/host-health-loop" rel="noopener noreferrer"&gt;examples directory&lt;/a&gt; has the full host health check loop — runbook file, inspection scripts, actual fix history from real runs.&lt;/p&gt;

&lt;p&gt;Not just host health checks. Certificate expiry scans, K8s node health checks, log archival cleanup, database backup verification, middleware config compliance checks — if your ops task breaks down into "steps + contracts + dependencies," it can be a loop.&lt;/p&gt;

&lt;p&gt;Repo at &lt;a href="https://github.com/KnoxOps/agent-runbook" rel="noopener noreferrer"&gt;github.com/KnoxOps/agent-runbook&lt;/a&gt;. If you've got routines where you SSH into servers, check for problems, and fix them by hand — try writing that flow as a declaration and let the tool run it. It'll be more disciplined than you are.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aiops</category>
      <category>ai</category>
      <category>loopengineering</category>
    </item>
    <item>
      <title>AI Scanned My Infra — 67% Were Dead Weight on My AWS Bill</title>
      <dc:creator>paul_h</dc:creator>
      <pubDate>Tue, 23 Jun 2026 08:53:32 +0000</pubDate>
      <link>https://dev.to/paul_knoxops/ai-scanned-my-infra-67-were-dead-weight-on-my-bill-4e42</link>
      <guid>https://dev.to/paul_knoxops/ai-scanned-my-infra-67-were-dead-weight-on-my-bill-4e42</guid>
      <description>&lt;p&gt;Last weekend I used AI to scan my AWS account for idle resources. Here's what I found:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scanned 3 EC2 instances, flagged 2 as suspicious&lt;/strong&gt;. One of them was running an entire microservice stack — with zero business traffic.&lt;/p&gt;

&lt;p&gt;The whole thing was done by 10 AI agents working together. I wasn't typing commands in a terminal. I defined a process contract, then let go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm77q4ugxufz8quslfyki.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm77q4ugxufz8quslfyki.gif" alt=" " width="500" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me start with what was discovered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery: 3 EC2 Instances Scanned, 2 Suspicious
&lt;/h2&gt;

&lt;p&gt;The scope was small: 3 t3.xlarge EC2 instances, us-east-1 region. No Prometheus or Datadog. ICO relied on SSH to grab real-time snapshots and process details.&lt;/p&gt;

&lt;p&gt;After the first round of &lt;strong&gt;scoring and screening&lt;/strong&gt;, .42 was excluded — someone had logged in 26 days ago, within the 30-day active threshold. A live machine. Two remained:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;zombie_score&lt;/th&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;th&gt;Network&lt;/th&gt;
&lt;th&gt;Last Login&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ec2-172.30.0.41&lt;/td&gt;
&lt;td&gt;0.35&lt;/td&gt;
&lt;td&gt;LOW&lt;/td&gt;
&lt;td&gt;6.4%&lt;/td&gt;
&lt;td&gt;1.82 GB/day&lt;/td&gt;
&lt;td&gt;41 days ago&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ec2-172.30.0.43&lt;/td&gt;
&lt;td&gt;0.35&lt;/td&gt;
&lt;td&gt;LOW&lt;/td&gt;
&lt;td&gt;8.4%&lt;/td&gt;
&lt;td&gt;1.18 GB/day&lt;/td&gt;
&lt;td&gt;99 days ago&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The three-signal filter is straightforward: CPU daily avg &amp;gt; 20% = active, network &amp;gt; 2 GB/day = active, human login within 30 days = active. All three must be inactive to become a candidate. .42 got caught by the login signal. .41 and .43 passed none of them — but their network was close to the threshold (1.82, 1.18), so they only scored 0.35.&lt;/p&gt;

&lt;p&gt;My first instinct was to skip them. A score of 0.35, LOW label — not worth the time. But ICO's process doesn't let you draw conclusions at this stage. Scoring is just a coarse filter. The next phase is &lt;strong&gt;deep scanning&lt;/strong&gt;, and it requires human confirmation to proceed.&lt;/p&gt;

&lt;p&gt;I selected both. Deep scan it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Scan: What Is a 0.35-Score Instance Actually Running?
&lt;/h2&gt;

&lt;p&gt;The deep scan phase launched agents that SSH'd in concurrently. Each machine was checked across 14 signals: process table, listening ports, crontab, systemd timers, disk usage, external connections, real-time traffic topology.&lt;/p&gt;

&lt;p&gt;The deep scan result for ec2-172.30.0.41 came back as 317 lines of JSON. Here are the key findings:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was running:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx reverse proxy (:80), routing to multiple backends&lt;/li&gt;
&lt;li&gt;Redis 7.0 MASTER (:6379), read-write mode, bound to 0.0.0.0&lt;/li&gt;
&lt;li&gt;Redis Sentinel (:26379), in a cluster across three machines&lt;/li&gt;
&lt;li&gt;Nacos standalone (:8848), Java process eating 512MB RAM&lt;/li&gt;
&lt;li&gt;inventory-service (:8081) and warehouse-service (:8083), two Python HTTP services&lt;/li&gt;
&lt;li&gt;Full Datadog agent stack (6 processes)&lt;/li&gt;
&lt;li&gt;Docker installed but zero running containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Traffic topology:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis Sentinel interconnection with ec2-172.30.0.43 (bidirectional, a few Kbps)&lt;/li&gt;
&lt;li&gt;Redis client connections from 172.30.0.25 (8+ connections)&lt;/li&gt;
&lt;li&gt;Datadog agent continuously sending metrics outbound&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at this report, you wouldn't think this machine is a zombie. Redis cluster, Nacos service registry, two Python services, Nginx reverse proxy — this looks like a full microservice setup.&lt;/p&gt;

&lt;p&gt;But look closer at the traffic topology. All external connections are from Datadog and within the Redis cluster. No real business traffic coming in. All services are running, but nobody is using them.&lt;/p&gt;

&lt;p&gt;That's the truth about this instance: &lt;strong&gt;an abandoned microservice setup. A zombie.&lt;/strong&gt; Without the deep scan, with a 0.35 score on the scorecard, nobody would have given it a second look.&lt;/p&gt;

&lt;p&gt;Scoring tells you "which ones might be idle." Deep scan tells you "what they're actually doing." Not the same question.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works: 10 Agents, 4 Human Decision Gates
&lt;/h2&gt;

&lt;p&gt;ICO covers compute instances, Kubernetes workloads, databases, object storage, and network resources — across AWS, GCP, Azure, or on-prem via SSH. This case focused on EC2, but the same pipeline handles all of them.&lt;/p&gt;

&lt;p&gt;ICO is not "one AI that deletes your resources." It's 10 independent skill agents, each responsible for one link in the chain, passing data through structured files:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdeilrx18b7ugimnl7lvz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdeilrx18b7ugimnl7lvz.jpg" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the four BLOCKING checkpoints, the agent must stop and wait for a human. Before anything gets deleted, a human confirms three times:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Phase C&lt;/strong&gt; — Review the scorecard, select which resources enter deep scan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase E&lt;/strong&gt; — Review the deep scan report, select which enter isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase G&lt;/strong&gt; — Approve the isolation plan (method, rollback script, observation period)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase J&lt;/strong&gt; — Final deletion approval&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not about writing "be careful" in a prompt. You can't get safety from prompts — the model might ignore what you said, or forget it once the context fills up. Safety must be &lt;strong&gt;hardcoded into the process&lt;/strong&gt;: if a phase doesn't pass, the agent cannot jump to the next step on its own.&lt;/p&gt;

&lt;p&gt;Agents don't pass data through context either. Scoring produces &lt;code&gt;suspect_assessment.json&lt;/code&gt;, deep scan produces &lt;code&gt;deep_scan_{id}.json&lt;/code&gt;, isolation produces &lt;code&gt;isolation_plan_{id}.json&lt;/code&gt; — each constrained by a Schema. If the previous agent's output doesn't match the format, the next agent errors out. No improvising.&lt;/p&gt;

&lt;p&gt;This is what agent-runbook is about: &lt;strong&gt;constrain agent collaboration with contracts. Relying on prompts is gambling.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Hard Lessons
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Scoring can't see the service stack. Deep scan can.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ec2-172.30.0.41 scored 0.35, LOW. Based on the score alone, you'd skip it. But the deep scan found Redis MASTER, Nacos, two Python services, Nginx — an entire microservice infrastructure stack sitting there idling. The three coarse signals — CPU, network, login — completely fail to capture "what's actually running." Scoring points the way. Deep scan shows you what's actually there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Without historical data, real-time snapshots have blind spots&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This case had no Prometheus or Datadog. ICO relied on SSH real-time snapshots. Monthly jobs, quarterly reports, on-demand batch processing — snapshots will never see them. If a crontab has a "run at 1 AM on the 1st of every month" entry, scanning a hundred times in real time won't catch it. Historical monitoring data is the most reliable signal source. Without it, deep scanning carries double the weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Internal traffic doesn't mean business usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;.41 and .43 had Redis Sentinel interconnections. .25 was connecting to .41's Redis. The topology graph had plenty of edges — it looked busy. But it was all infrastructure internal communication — services probing each other, syncing state, with not a single edge from an external user. The scorecard treats any network traffic as an active signal, but not all traffic is the same. Only the deep scan's traffic topology can distinguish "machines talking to each other" from "users making requests."&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The value of AI agents in operations is not "they can delete resources automatically" — that's called danger.&lt;/p&gt;

&lt;p&gt;The real value is: &lt;strong&gt;you codify a verified safety procedure into an auditable, reusable agent skill anyone can run, and get the same result every time.&lt;/strong&gt; Not typing commands each time and hoping, but a contract file committed to a repo.&lt;/p&gt;

&lt;p&gt;ICO applies this approach to cloud cost optimization. If it works in dev, it's only more valuable in production.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;open-devops-skills — directly installable ICO skill library: &lt;a href="https://github.com/KnoxOps/open-devops-skills" rel="noopener noreferrer"&gt;github.com/KnoxOps/open-devops-skills&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install with one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugin &lt;span class="nb"&gt;install &lt;/span&gt;ico@open-devops-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/ico:orchestrator Scan my cloud for idle resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've got EC2 instances you're not sure are still in use, give it a try. Let the agents scan and analyze — you make the call.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloudcomputing</category>
      <category>aiops</category>
    </item>
    <item>
      <title>Loop Engineering: Building an Agent Loop with agent-runbook</title>
      <dc:creator>paul_h</dc:creator>
      <pubDate>Wed, 17 Jun 2026 09:02:40 +0000</pubDate>
      <link>https://dev.to/paul_knoxops/loop-engineering-building-an-agent-loop-with-agent-runbook-206</link>
      <guid>https://dev.to/paul_knoxops/loop-engineering-building-an-agent-loop-with-agent-runbook-206</guid>
      <description>&lt;p&gt;Recently, another interesting new term has appeared in the AI industry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loop Engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you follow the AI space, you've probably seen it everywhere in the past couple of days. It's all over X, all over various social media, and quite a few people are discussing it in group chats too.&lt;/p&gt;

&lt;p&gt;Recently Addy Osmani formally organized this concept into Loop Engineering — the fourth Engineering after Prompt Engineering, Context Engineering, and Harness Engineering.&lt;/p&gt;

&lt;p&gt;What is a Loop? Here's a concrete scenario:&lt;/p&gt;

&lt;p&gt;You have a project with 16 failing tests. Previously you'd do this: run the tests, see what failed, tell Claude "fix this", it fixes it, you run the tests again, find new issues, say something again... back and forth, &lt;strong&gt;you are the person driving the loop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea behind Loop Engineering is: you no longer manually drive it round by round. You define the goal (all tests pass), define what to do each round (run tests → fix code), define constraints (can't modify test files), then let go. The system runs on its own until the goal is met.&lt;/p&gt;

&lt;h2&gt;
  
  
  /goal Is Not Enough
&lt;/h2&gt;

&lt;p&gt;At this point you might say: doesn't Claude Code already have the &lt;code&gt;/goal&lt;/code&gt; command? Can't I just &lt;code&gt;/goal "all tests pass"&lt;/code&gt; and be done?&lt;/p&gt;

&lt;p&gt;On the surface, yes. &lt;code&gt;/goal&lt;/code&gt; gives you a completion condition, and Claude works on its own until it's satisfied. But after using it a few times you'll notice the problem — the goal is defined, but the agent still won't work properly. Because you only told it "what counts as done", you didn't tell it "what to do each round".&lt;/p&gt;

&lt;p&gt;/goal "all tests pass" — what did it do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tells the agent "keep going until this condition is met"&lt;/li&gt;
&lt;li&gt;At the end of each round, an independent model judges whether the goal is satisfied&lt;/li&gt;
&lt;li&gt;The agent has complete freedom in what it does each round&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it doesn't do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Doesn't define the internal structure of each round.&lt;/strong&gt; In /goal the agent does whatever it wants each round. Maybe the first round it runs tests + fixes code, the second round it suddenly goes refactoring, the third round it modifies test files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No iteration-level constraints.&lt;/strong&gt; /goal only has a termination condition. There's no guardrail like "only modify one file per round", and you can't control when the agent goes out of bounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not reusable.&lt;/strong&gt; /goal "all tests pass" is gone once you type it. Next time you switch repos or switch people, you have to type it all over again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not auditable.&lt;/strong&gt; When your boss asks "what's the logic of this automated fix workflow", you can't show them /goal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To summarize: &lt;code&gt;/goal&lt;/code&gt; solves "keeping the agent from stopping", but doesn't solve "making the agent follow the rules".&lt;/p&gt;

&lt;p&gt;What you need is a place to write down the loop's structure, constraints, and goals — not a one-time command typed into the terminal, but a file that can be committed to the repo, where anyone who gets it can run it and get the same behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  agent-runbook: The Contract Format for Loops
&lt;/h2&gt;

&lt;p&gt;This is what agent-runbook does.&lt;/p&gt;

&lt;p&gt;agent-runbook is an open source project (&lt;a href="https://github.com/KnoxOps/agent-runbook" rel="noopener noreferrer"&gt;github.com/KnoxOps/agent-runbook&lt;/a&gt;), it's not the execution engine for loops, but rather the &lt;strong&gt;contract format&lt;/strong&gt; for loops. You use YAML to declare "what to iterate on, when to stop, what the constraints are for each round", and the compiler generates a SKILL.md for you — this is the reusable instruction format for Claude Code and Codex, put it in your project and it can be directly invoked with &lt;code&gt;claude --skill&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A loop step has three elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;body&lt;/strong&gt;: what to do each round (the rhythm of observe → act → verify)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;goal&lt;/strong&gt;: when to stop (must be a machine-verifiable condition)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max_iterations&lt;/strong&gt;: safety boundary (exceeding this number means the design has a problem, prevents burning tokens)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also one more key thing: &lt;strong&gt;quality_check&lt;/strong&gt;. This is an iteration-level guardrail — after each round it checks whether the agent went out of bounds (e.g. modified files it shouldn't have). If blocking: true, the round doesn't count as complete if the check fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-on: Building an Automated Test Fix Loop
&lt;/h2&gt;

&lt;p&gt;Here's a simple example to show you how we use agent-runbook to build an agent loop.&lt;/p&gt;

&lt;p&gt;We're going to build an &lt;strong&gt;automated test fix&lt;/strong&gt; Loop. This loop is simple, the goal is 100% unit test pass rate. Each iteration has only two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;run_tests&lt;/strong&gt; - run the tests, see which ones are still failing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fix&lt;/strong&gt; - launch a clean context agent to fix the discovered issues&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Beyond that, we also need to define our safety boundary: &lt;strong&gt;max_iterations&lt;/strong&gt;. I wonder if any readers here have had the experience of burning through all their tokens with the /goal command — max_iterations is what prevents that.&lt;/p&gt;

&lt;p&gt;Here's the full runbook, defined in structured YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fix-failing-tests&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Iteratively fix all failing tests until the test suite is green&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fix_loop&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;loop&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tests,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;analyze&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;failures,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;fix&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;repeat&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;until&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;green"&lt;/span&gt;
    &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exits&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;failures&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tests&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pass)"&lt;/span&gt;
    &lt;span class="na"&gt;max_iterations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run_tests&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cd&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;examples/fix-loop&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;python3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tests/&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--tb=short&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2&amp;gt;&amp;amp;1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tail&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-60"&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fix&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
        &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Look at the pytest failures from run_tests.&lt;/span&gt;
          &lt;span class="s"&gt;Pick ONE source file that has failing tests and fix the bugs in that file.&lt;/span&gt;

          &lt;span class="s"&gt;Rules:&lt;/span&gt;
            &lt;span class="s"&gt;- Only modify files in src/, NEVER modify test files&lt;/span&gt;
            &lt;span class="s"&gt;- Fix exactly ONE file, then stop immediately&lt;/span&gt;
            &lt;span class="s"&gt;- Do NOT read or modify any other source files&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;run_tests&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;quality_check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;blocking&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;files&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;src/&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;were&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;modified,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;files"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exactly&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;was&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;modified"&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inline&lt;/span&gt;
    &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Generate a markdown report summarizing the fix loop results.&lt;/span&gt;
      &lt;span class="s"&gt;Include:&lt;/span&gt;
        &lt;span class="s"&gt;- Total iterations taken&lt;/span&gt;
        &lt;span class="s"&gt;- What was fixed in each iteration (file + bug description)&lt;/span&gt;
        &lt;span class="s"&gt;- Final test results&lt;/span&gt;
        &lt;span class="s"&gt;- How cascading dependencies caused failures to clear automatically&lt;/span&gt;
      &lt;span class="s"&gt;Write the report to fix_report.md&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;fix_loop&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  From YAML to Executable SKILL.md
&lt;/h2&gt;

&lt;p&gt;Next we need to compile the YAML into a SKILL.md that Claude Code/Codex can directly execute. The generation command is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; agent_runbook generate runbook.yaml &lt;span class="nt"&gt;-o&lt;/span&gt; output/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated SKILL.md looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fix-failing-tests&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;-"&lt;/span&gt;
  &lt;span class="s"&gt;Iteratively fix all failing tests until the test suite is green&lt;/span&gt;
&lt;span class="na"&gt;user-invocable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Execution Flow&lt;/span&gt;

&lt;span class="gu"&gt;### Task Context&lt;/span&gt;

Before starting execution, initialize &lt;span class="sb"&gt;`task_context.json`&lt;/span&gt;:

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;json
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;task_id from input&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"current_step"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"current_step_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"running"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fix_loop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"present"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ISO timestamp&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

Update this file after each step completes. On error, set step status to &lt;span class="sb"&gt;`"failed"`&lt;/span&gt; and overall &lt;span class="sb"&gt;`status`&lt;/span&gt; to &lt;span class="sb"&gt;`"failed"`&lt;/span&gt;.

&lt;span class="gu"&gt;### Step 1: fix_loop&lt;/span&gt;

&lt;span class="gs"&gt;**Type:**&lt;/span&gt; loop
&lt;span class="gs"&gt;**Description:**&lt;/span&gt; Run tests, analyze failures, fix source code, repeat until green

&lt;span class="gu"&gt;## Iteration Loop&lt;/span&gt;

&lt;span class="gs"&gt;**Goal:**&lt;/span&gt; pytest exits with 0 failures (all tests pass)
&lt;span class="gs"&gt;**Max Iterations:**&lt;/span&gt; 10
&lt;span class="gt"&gt;
&amp;gt; This step executes as a loop. The body steps repeat until the goal is met or max iterations reached.&lt;/span&gt;

&lt;span class="gu"&gt;## Loop Body (repeats each iteration)&lt;/span&gt;

&lt;span class="gu"&gt;#### Body Step 1: run_tests&lt;/span&gt;

&lt;span class="gs"&gt;**Type:**&lt;/span&gt; script

&lt;span class="gs"&gt;**Execution:**&lt;/span&gt; Execute the following command:
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;bash
&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;examples/fix-loop &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pytest tests/ &lt;span class="nt"&gt;--tb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-60&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;#### Body Step 2: fix&lt;/span&gt;

&lt;span class="gs"&gt;**Type:**&lt;/span&gt; agent

&lt;span class="gs"&gt;**Execution:**&lt;/span&gt; Launch an independent agent with the following prompt file:

Look at the pytest failures from run_tests.
Pick ONE source file that has failing tests and fix the bugs in that file.

Rules:
&lt;span class="p"&gt;  -&lt;/span&gt; Only modify files in src/, NEVER modify test files
&lt;span class="p"&gt;  -&lt;/span&gt; Fix exactly ONE file, then stop immediately
&lt;span class="p"&gt;  -&lt;/span&gt; Do NOT read or modify any other source files&lt;span class="sb"&gt;


&lt;/span&gt;&lt;span class="gu"&gt;## Goal Evaluation&lt;/span&gt;

After all body steps complete, evaluate:

&lt;span class="gs"&gt;**Goal:**&lt;/span&gt; pytest exits with 0 failures (all tests pass)
&lt;span class="p"&gt;
1.&lt;/span&gt; If goal IS met → mark this step completed, proceed to next step.
&lt;span class="p"&gt;2.&lt;/span&gt; If goal NOT met and iterations remain → reset body steps, start next iteration.
&lt;span class="p"&gt;3.&lt;/span&gt; If max iterations reached → mark step completed with status "max_iterations_reached", report what remains.

Append a summary to &lt;span class="sb"&gt;`iteration_history`&lt;/span&gt; after each iteration.

&lt;span class="gu"&gt;### Progress Tracking&lt;/span&gt;

After completing this step, update &lt;span class="sb"&gt;`task_context.json`&lt;/span&gt;:
&lt;span class="p"&gt;-&lt;/span&gt; Set &lt;span class="sb"&gt;`current_step_id`&lt;/span&gt; to &lt;span class="sb"&gt;`"fix_loop"`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Set &lt;span class="sb"&gt;`steps.fix_loop`&lt;/span&gt; to &lt;span class="sb"&gt;`"completed"`&lt;/span&gt;
&lt;span class="gu"&gt;### Step 2: present&lt;/span&gt;

&lt;span class="gs"&gt;**Type:**&lt;/span&gt; inline

&lt;span class="gu"&gt;## Execution&lt;/span&gt;
Follow these instructions:

Generate a markdown report summarizing the fix loop results.
Include:
&lt;span class="p"&gt;  -&lt;/span&gt; Total iterations taken
&lt;span class="p"&gt;  -&lt;/span&gt; What was fixed in each iteration (file + bug description)
&lt;span class="p"&gt;  -&lt;/span&gt; Final test results
&lt;span class="p"&gt;  -&lt;/span&gt; How cascading dependencies caused failures to clear automatically
Write the report to fix_report.md&lt;span class="sb"&gt;


&lt;/span&gt;&lt;span class="gu"&gt;### Progress Tracking&lt;/span&gt;

After completing this step, update &lt;span class="sb"&gt;`task_context.json`&lt;/span&gt;:
&lt;span class="p"&gt;-&lt;/span&gt; Set &lt;span class="sb"&gt;`current_step_id`&lt;/span&gt; to &lt;span class="sb"&gt;`"present"`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Set &lt;span class="sb"&gt;`steps.present`&lt;/span&gt; to &lt;span class="sb"&gt;`"completed"`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does the generated SKILL.md contain? It translates the contracts you declared in YAML into execution instructions that the agent can understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iteration_history&lt;/strong&gt;: requires the agent to record what was done each round and whether the goal was met, forming structured iteration memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;goal evaluation&lt;/strong&gt;: the judgment logic after each round — if met then stop, if not met then continue, if limit reached then report&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;progress tracking&lt;/strong&gt;: tracks overall progress through task_context.json, supports checkpoint resume&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running It: 3-Round Convergence
&lt;/h2&gt;

&lt;p&gt;Now we can trigger this skill to run in Claude Code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbf24jz6prqh3s9g2ct7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbf24jz6prqh3s9g2ct7.jpg" alt=" " width="798" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The run included three iterations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iteration 1: calculator fix → 6 failures disappeared&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl30tsy6nq760kxxi0lir.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl30tsy6nq760kxxi0lir.jpg" alt=" " width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iteration 2: validator fix → 5 failures disappeared&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F17nybu6xwiyavmjr66gp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F17nybu6xwiyavmjr66gp.jpg" alt=" " width="800" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iteration 3: formatter fix → all green&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e66qs5t66wp2r6cwz8s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e66qs5t66wp2r6cwz8s.jpg" alt=" " width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, this is also what we defined earlier in the runbook — a fix_report.md to be produced after the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Points for Designing a Good Loop
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose the right task.&lt;/strong&gt; Not all tasks are suitable for loops. A good loop task has two characteristics: objective feedback signals (test results, lint output, whether compilation passes), and the ability to make incremental progress building on the previous round. Fixing tests, code migration, and performance optimization are all good candidates. Tasks requiring one-time creative decisions (architecture choices, naming) are not suitable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write the goal as a decidable end state.&lt;/strong&gt; "pytest exit 0" is a good goal, "better code quality" is not. The agent must be able to determine true or false on its own through tool output, otherwise the loop never knows whether it should stop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep the body in an "observe—act" rhythm.&lt;/strong&gt; First use script steps to see the current state clearly (run tests, run lint), then use agent steps to make decisions and modifications. Don't let the agent observe, act, and verify all in one round — split them up, each step has clear responsibilities, and when something goes wrong it's easier to locate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leave an exit for failure.&lt;/strong&gt; max_iterations is not the number of rounds you expect, but a safety valve for "exceeding this number means the approach has a problem". A normal loop should converge well below the upper limit. If it maxes out, it means the goal is too hard or the body design has flaws, and human intervention is needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  agent-runbook: More Than Just Loops
&lt;/h2&gt;

&lt;p&gt;Due to the AI product I'm developing, I frequently need to write many long-running, as-error-free-as-possible DevOps skills for SREs. &lt;/p&gt;

&lt;p&gt;During debugging I often encounter two types of problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One is &lt;strong&gt;agents not following instructions&lt;/strong&gt; — you tell it to only restart the service, and it goes ahead and changes the configuration too.&lt;/li&gt;
&lt;li&gt;The other is in a complex multi-step skill, &lt;strong&gt;agents not collaborating according to the established norms&lt;/strong&gt;, where the output from the previous step isn't read by the next step at all, or it's read but the format is wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on these problems, I developed agent-runbook: &lt;strong&gt;a contract-based skill generation tool, where the generated SKILL.md can be directly used as a skill integrated into the Claude Code/Codex ecosystem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Its core philosophy is: use contracts to constrain agent collaboration, instead of relying on prompts and hoping for the best.&lt;/p&gt;

&lt;p&gt;This table gives you a quick sense of how agent-runbook differs from /goal:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;/goal&lt;/th&gt;
&lt;th&gt;agent-runbook&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Per-round structure&lt;/td&gt;
&lt;td&gt;Agent does whatever it wants&lt;/td&gt;
&lt;td&gt;Body declaratively defines each round's steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iteration constraints&lt;/td&gt;
&lt;td&gt;None, only a termination condition&lt;/td&gt;
&lt;td&gt;quality_check guardrails, out-of-bounds doesn't count as complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter-step communication&lt;/td&gt;
&lt;td&gt;Relies on LLM context passing&lt;/td&gt;
&lt;td&gt;JSON Schema files, inspectable, parallel-readable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error recovery&lt;/td&gt;
&lt;td&gt;Start over&lt;/td&gt;
&lt;td&gt;Checkpoint &amp;amp; Resume, pick up from where it crashed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build-time checks&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;DAG cycle detection, schema reference validation, contract closure checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reusability&lt;/td&gt;
&lt;td&gt;Gone once you type it&lt;/td&gt;
&lt;td&gt;Commit to repo, anyone can run it with the same behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Loop is a step type added on top of this foundation&lt;/strong&gt; — when your task requires iteration, use the same contract-based approach to define the loop's body, goal, and constraints.&lt;/p&gt;

&lt;p&gt;You don't have to start from scratch either. &lt;a href="https://github.com/KnoxOps/open-devops-skills" rel="noopener noreferrer"&gt;open-devops-skills&lt;/a&gt; is a production-grade DevOps skill library built on agent-runbook, currently featuring infrastructure/cloud resource cost optimization skills, with more DevOps scenarios to be expanded in the future. You can use them directly, or use them as reference for designing your own skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's also worth mentioning that agent-runbook itself is not limited to DevOps&lt;/strong&gt;. Any scenario requiring multi-step orchestration, inter-agent collaboration, and long-term reliable operation is suitable — code migration, security auditing, documentation generation, data pipeline validation. As long as your task can be broken down into "steps + contracts + dependencies", it can be expressed with a runbook.&lt;/p&gt;

&lt;p&gt;The repo is at &lt;a href="https://github.com/KnoxOps/agent-runbook" rel="noopener noreferrer"&gt;github.com/KnoxOps/agent-runbook&lt;/a&gt;, feel free to try it out and give feedback. If you have a workflow where you're repeatedly prompting agents manually, try writing it as a runbook — you'll find that once it becomes a contract, the cost of debugging and reuse drops significantly.&lt;/p&gt;

</description>
      <category>loopengineering</category>
      <category>claudecode</category>
      <category>ai</category>
      <category>aiskill</category>
    </item>
    <item>
      <title>I Asked Claude to Map My Infrastructure. Then I Asked a Purpose-Built Tool.</title>
      <dc:creator>paul_h</dc:creator>
      <pubDate>Mon, 15 Jun 2026 07:13:09 +0000</pubDate>
      <link>https://dev.to/paul_knoxops/i-asked-claude-to-map-my-infrastructure-then-i-asked-a-purpose-built-tool-51jp</link>
      <guid>https://dev.to/paul_knoxops/i-asked-claude-to-map-my-infrastructure-then-i-asked-a-purpose-built-tool-51jp</guid>
      <description>&lt;p&gt;I manage a small stack. Three Linux VMs, one Kubernetes cluster, maybe 20-something services total. Not big. But underdocumented — the kind of environment where you SSH in and discover things you forgot were running.&lt;/p&gt;

&lt;p&gt;Last week I ran the same task through two different AI tools: "tell me what's running, how it connects, and what looks risky." One is a general-purpose LLM (Claude). The other is a purpose-built AI SRE tool. Same environment, same ask. The results were... instructive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The task
&lt;/h2&gt;

&lt;p&gt;Simple brief: infrastructure discovery. I want a full picture — services, dependencies, topology, risks. The kind of thing a new hire would spend their first week piecing together from wikis that haven't been updated since 2023.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code (Opus model)
&lt;/h2&gt;

&lt;p&gt;My prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I manage a small infrastructure — 3 Linux VMs (172.30.0.41, 172.30.0.42, 172.30.0.43) and a Kubernetes cluster. SSH access is already configured. Help me understand what's running across this environment — I want a full picture of my services, dependencies, and topology."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm running Claude Code locally with the Opus model — their flagship tier. Claude didn't ask questions. It just started SSH-ing in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov7crtomi3yzwvxq501h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov7crtomi3yzwvxq501h.jpg" alt="Claude exploring hosts via SSH — ss, systemctl, kubectl across all three VMs&lt;br&gt;
" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Five minutes later it handed me a report. And honestly? It was better than I expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxe97qehhz67494t3vq7y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxe97qehhz67494t3vq7y.jpg" alt="Claude's final output — ASCII topology plus service inventory" width="800" height="1991"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What Claude delivered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identified all three VM roles correctly (API Gateway, Order Processing, Data Tier)&lt;/li&gt;
&lt;li&gt;Drew an ASCII topology showing Nginx routing to backend services with canary weights&lt;/li&gt;
&lt;li&gt;Built a full service table — host, port, tech stack, notes&lt;/li&gt;
&lt;li&gt;Mapped the Redis Sentinel cluster including a stale replica on a decommissioned node&lt;/li&gt;
&lt;li&gt;Enumerated every K8s namespace and workload&lt;/li&gt;
&lt;li&gt;Traced the observability pipeline (node_exporter → Prometheus, OTel → Jaeger, Datadog agents)&lt;/li&gt;
&lt;li&gt;Flagged four real issues: dead Redis replica, broken image pulls in aigc-app, active canary split, multiple knoxd versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five minutes. No hand-holding. For a "quick, what's running here?" sweep, this is genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it stops
&lt;/h2&gt;

&lt;p&gt;Here's what I noticed after the initial "wow, that was fast" wore off.&lt;/p&gt;

&lt;p&gt;The output is a wall of markdown. Accurate, mostly. But flat. Everything has the same weight — a critical single-point-of-failure sits next to a cosmetic naming inconsistency. No severity. No priority.&lt;/p&gt;

&lt;p&gt;More specifically:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No topology visualization.&lt;/strong&gt; I got an ASCII diagram. It's readable for 6 machines. At 60 machines, it's unreadable. At 600, impossible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No business grouping.&lt;/strong&gt; Claude listed every service but couldn't tell me which ones form the e-commerce flow vs. the logistics flow vs. the platform layer. That requires domain context it doesn't have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No risk assessment.&lt;/strong&gt; Four issues found, but no severity classification. The dead Redis replica and the cosmetic knoxd naming thing are presented with equal weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No quality gate.&lt;/strong&gt; Nobody verified whether Claude's topology was actually correct. It connected things confidently — but was the canary weight really 90/10? I'd need to go check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No persistence.&lt;/strong&gt; Close the chat window. The report is gone. Tomorrow I'd run it again and get a slightly different exploration path, slightly different findings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No depth control.&lt;/strong&gt; I can't say "that Business Island looks risky, go deeper on it." It's all-or-nothing.&lt;/p&gt;

&lt;p&gt;This maps to a pattern I keep seeing across industries. In legal tech, people noticed the same thing — general LLMs are good at summarizing contracts but can't do precision clause verification. In finance, ChatGPT can describe how to post a journal entry but can't actually post one. The dividing line is consistent: general AI is a thinking tool; specialized AI is an acting tool.&lt;/p&gt;

&lt;p&gt;When the task is "reason about this data and explain it to me" — general tools are great. When the task shifts to "build a structured, persistent, verifiable model of my environment" — you've crossed into territory they weren't designed for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose-built tool, same task
&lt;/h2&gt;

&lt;p&gt;For comparison, here's what happens when I send one line to Knox (our purpose-built AI SRE tool — yes, this is our product, stating that upfront):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Run a full infrastructure discovery on our production environment."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Shorter prompt. No need to explain the environment — it already has connectors configured.&lt;/p&gt;

&lt;p&gt;Twenty minutes later:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkydq0x5to1p2nnax3i6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkydq0x5to1p2nnax3i6n.png" alt="Knox service topology — interactive graph, not ASCII art" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvyibi1tbuuqf4ul13zr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvyibi1tbuuqf4ul13zr.png" alt="Business Islands — services grouped by business function, with criticality&lt;br&gt;
" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym3v1qfy6rlt9cz6n131.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym3v1qfy6rlt9cz6n131.png" alt="Knox configuration drift report with severity ranking" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The differences that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visual topology&lt;/strong&gt; — not ASCII art, an interactive service relationship graph&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Islands&lt;/strong&gt; — services auto-grouped by business function with criticality labels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk Triage&lt;/strong&gt; — findings ranked by severity with a distribution chart&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt; — results stored in a graph database, queryable later&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depth on demand&lt;/strong&gt; — "Deep Analysis Available" button for any Business Island&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How it got there — a team of agents, not a single model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg9zcrhsmhuicz2dlfgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg9zcrhsmhuicz2dlfgy.png" alt="Captain — confirms scope before dispatching specialists&lt;br&gt;
8" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi9igjjx9e55dj2vng9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi9igjjx9e55dj2vng9w.png" alt="Specialists collaborating — Architect plans, Collector scans" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5huotd02a75z4cfd64r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5huotd02a75z4cfd64r5.png" alt="Supervisor — independently cross-checks the findings" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flb9s0zrgutwhi2hpfjho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flb9s0zrgutwhi2hpfjho.png" alt="Final review — 12 verified, 9 uncertain items flagged for human review" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the work process, not a deliverable. Multiple specialized agents collaborated — one coordinated the task, one did the actual discovery, one quality-checked the findings — flagging 9 uncertain items for human review instead of presenting everything with equal confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scale question
&lt;/h2&gt;

&lt;p&gt;We ran this on 5-6 machines. The gap is already visible. But this is the minimum-gap scenario.&lt;/p&gt;

&lt;p&gt;At 60 servers across multiple environments, Claude's context window fills up. You'd need multiple sessions, manual stitching, and the "flat markdown" problem becomes unbearable. The gap doesn't grow linearly — it compounds.&lt;/p&gt;

&lt;p&gt;That's not a knock on Claude. A Swiss Army knife is great. But when you need surgery, you reach for a scalpel.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your environment look like? At what scale did you find general AI tools hitting their ceiling for ops work?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you want to try the purpose-built approach: &lt;a href="https://knoxops.app/?invite_token=DEVTO26" rel="noopener noreferrer"&gt;knoxops.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>sre</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Agentic Ops: How I Shipped My Vibe-Coded Game to Production</title>
      <dc:creator>paul_h</dc:creator>
      <pubDate>Sat, 30 May 2026 07:31:08 +0000</pubDate>
      <link>https://dev.to/paul_knoxops/agentic-ops-how-i-shipped-my-vibe-coded-code-to-production-1mgk</link>
      <guid>https://dev.to/paul_knoxops/agentic-ops-how-i-shipped-my-vibe-coded-code-to-production-1mgk</guid>
      <description>&lt;p&gt;Over the weekend, I vibe coded a cooking game. You combine random ingredients, and the game generates a dish with a score and a snarky review — stuff like "This tastes like regret and too much butter." I'd wanted to build this for a while. Eventually I'll hook it up to an AI model to generate more combinations and even harsher critiques.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Prompt, One Hour
&lt;/h2&gt;

&lt;p&gt;I opened Claude Code and typed a single prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a cooking game where players combine ingredients to discover recipes..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An hour of coding and debugging later, I had a working version running on localhost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ybarf7apbxmfexzylul.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ybarf7apbxmfexzylul.jpg" alt=" " width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Wall
&lt;/h2&gt;

&lt;p&gt;Then came the real problem: deploying it so my friends could actually play.&lt;/p&gt;

&lt;p&gt;AI has collapsed the barrier to building software. But no matter how low the entry gets, even the most seasoned SRE can't rattle off HTTPS configs, domain setups, and nginx routing rules from memory. As a vibe coder, what was I supposed to do next?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plan
&lt;/h2&gt;

&lt;p&gt;I spun up an AWS VM, installed a Knox Daemon (Knox is an AIOps product), and connected it to my GitHub repo. Then I told it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How I Shipped My Vibe-Coded Code to Production"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It started exploring my codebase. It discussed the task with me, asked clarifying questions, and came back with a full plan — five stages covering pre-checks, building the game, requesting certificates, updating nginx routes, final verification, and documenting what it learned for next time. Nothing would execute until I approved it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzee0pkyc0l84dht2jg3x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzee0pkyc0l84dht2jg3x.jpg" alt=" " width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Execution
&lt;/h2&gt;

&lt;p&gt;I reviewed the plan and hit approve. The agents kicked off in parallel — one checking the environment, one executing changes, another validating the output of each stage. They ran efficiently, every step visible. It looked exactly like a human SRE team at work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3w068oa6lzjst4qj3jz4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3w068oa6lzjst4qj3jz4.jpg" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When it was done, the agent handed me a report. I clicked the URL in the report and — there it was. My game. Live. Someone could play it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8wd4k6d29u6yqthc9av.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8wd4k6d29u6yqthc9av.jpg" alt=" " width="799" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ws6p98fs1gzg863m8e1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ws6p98fs1gzg863m8e1.jpg" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  30 Minutes
&lt;/h2&gt;

&lt;p&gt;I was doing other things throughout the deployment, so I wasn't always quick to respond when the agent needed input — requirement discussions, plan approval, execution confirmations on my AWS box. Total time from start to live: about an hour. If I'd been fully focused, probably 30 minutes.&lt;/p&gt;

&lt;p&gt;The whole experience was striking. More and more people are building things in the AI era. They think about product design and development, but then what? How do you deploy? How do you keep the service running?&lt;/p&gt;

&lt;p&gt;I think this is what agentic ops means.&lt;/p&gt;

&lt;p&gt;Agentic ops gives you the same answer: describe what you want, and an agent operates the server. Same loop as vibe coding. The output just isn't code anymore — it's a running service.&lt;/p&gt;

&lt;p&gt;The endpoint of vibe coding shouldn't be localhost:3000. It should be a link you can drop in a group chat.&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>devops</category>
      <category>sre</category>
      <category>aiops</category>
    </item>
    <item>
      <title>AI Agents Mapped My Legacy Production Environment in One Hour.</title>
      <dc:creator>paul_h</dc:creator>
      <pubDate>Thu, 28 May 2026 03:55:11 +0000</pubDate>
      <link>https://dev.to/paul_knoxops/ai-agents-mapped-my-legacy-production-environment-in-one-hour-it-cost-0-2fnn</link>
      <guid>https://dev.to/paul_knoxops/ai-agents-mapped-my-legacy-production-environment-in-one-hour-it-cost-0-2fnn</guid>
      <description>&lt;p&gt;I inherited a black box.&lt;/p&gt;

&lt;p&gt;Three VMs. A hundred-something microservices. Redis, ClickHouse, MySQL, some homegrown database nobody could name. Kafka and Zookeeper thrown in because of course they were.&lt;/p&gt;

&lt;p&gt;Nobody knew how the services connected. The original team was gone. The architecture lived entirely in oral tradition, and the last person who could recite it had left six months ago.&lt;/p&gt;

&lt;p&gt;This is not a metaphor. This is Tuesday for anyone who's done SRE work long enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup: 30 seconds, zero footprint
&lt;/h2&gt;

&lt;p&gt;I already had Teleport for daily ops. SSH access, session recording. It worked, I didn't want to break it.&lt;/p&gt;

&lt;p&gt;What I did:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installed &lt;code&gt;knoxd&lt;/code&gt; on my Teleport proxy (not on the servers)&lt;/li&gt;
&lt;li&gt;AI agent team auto-configured a Teleport connector&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. Nothing new on my production machines. The agents ride the Teleport session I already had, with the permissions I'd already defined.&lt;/p&gt;

&lt;p&gt;Non-invasive — not in the "we promise it's lightweight" sense. In the "there is literally nothing new running on your production machines" sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gugbyk4g6kcqrbvyqqf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gugbyk4g6kcqrbvyqqf.jpg" alt="Available connectors, more is coming soon" width="800" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The agents SSH in through Teleport. Plain SSH commands, same ones you'd type yourself.&lt;/p&gt;

&lt;p&gt;What makes this safe rather than terrifying:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Auto-run&lt;/th&gt;
&lt;th&gt;Requires human approval&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;cat /proc/net/tcp&lt;/code&gt;, &lt;code&gt;nginx -T&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutating&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kill&lt;/code&gt;, &lt;code&gt;systemctl restart&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The sandbox: strict AST parsing + default-deny whitelist. The agents can look at everything but touch nothing without asking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agents discovered
&lt;/h2&gt;

&lt;p&gt;Step 1: OS inventory — kernel, distro, packages. All 3 VMs in parallel.&lt;/p&gt;

&lt;p&gt;Step 2: Process mapping — &lt;code&gt;ps aux&lt;/code&gt;, parsed. Hundreds of processes tagged with binary path, resource footprint, parent-child relationships.&lt;/p&gt;

&lt;p&gt;Step 3: Process → Service resolution&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check name service first&lt;/li&gt;
&lt;li&gt;If unregistered (most weren't — legacy system), infer from install path&lt;/li&gt;
&lt;li&gt;Flag for human confirmation before writing anything back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI doesn't hallucinate service names into your architecture map. It asks.&lt;/p&gt;

&lt;p&gt;Step 4: Service → Business Island grouping&lt;/p&gt;

&lt;p&gt;A business island = logical grouping by business function (billing, user auth, order processing). The thing that exists in every architect's head but never in any document.&lt;/p&gt;

&lt;p&gt;Step 5: Connection mapping — four evidence sources, cross-referenced:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;What it reveals&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network connections (&lt;code&gt;ss -tnp&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Live TCP dependencies&lt;/td&gt;
&lt;td&gt;Port 6379 → Redis, port 9092 → Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config files&lt;/td&gt;
&lt;td&gt;Declared dependencies&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kafka.brokers: kafka-01:9092&lt;/code&gt; in YAML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access logs&lt;/td&gt;
&lt;td&gt;Actual call patterns&lt;/td&gt;
&lt;td&gt;Who calls whom, how often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LB configs (nginx)&lt;/td&gt;
&lt;td&gt;Ingress chain&lt;/td&gt;
&lt;td&gt;Domain → LB → real server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cross-reference. Resolve conflicts. Draw edges.&lt;/p&gt;

&lt;p&gt;One hour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyw0q28c9kd6znd8dv3x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyw0q28c9kd6znd8dv3x.jpg" alt=" " width="800" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Architecture diagrams — topology maps of each business island, services as nodes, dependencies as edges, data flows labeled. The kind of diagram you'd pay a consultant a week to produce.&lt;/p&gt;

&lt;p&gt;High-risk report:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single points of failure&lt;/li&gt;
&lt;li&gt;Circular dependencies&lt;/li&gt;
&lt;li&gt;Kafka topics with no visible consumer group&lt;/li&gt;
&lt;li&gt;One Redis instance holding session state for 6 business islands, zero isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Things I needed to know. Things dashboards would never show me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq40ekxe9nkheh2l3yfn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq40ekxe9nkheh2l3yfn.jpg" alt=" " width="800" height="781"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost
&lt;/h2&gt;

&lt;p&gt;Zero.&lt;/p&gt;

&lt;p&gt;Knox gives free credits on signup. Enough for a small cluster for a long time. No credit card. No trial-that-converts-to-paid. One binary on a jump host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Most AIOps tools treat metrics as the final answer. They're not. They're the starting point.&lt;/p&gt;

&lt;p&gt;Real outages hide in blind spots:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System logs nobody tails&lt;/li&gt;
&lt;li&gt;Manual changes nobody tracked&lt;/li&gt;
&lt;li&gt;Config drift APM tools don't see&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To find root cause, you have to log into machines and build an evidence chain. That's what humans do. That's what these agents do.&lt;/p&gt;

&lt;p&gt;Monitoring tells you a metric crossed a threshold. It doesn't tell you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service X and Y form a circular dependency that will cascade&lt;/li&gt;
&lt;li&gt;Your session store is a single point of failure for half the platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those aren't metric problems. They're structure problems. LLMs are uniquely good at structure — if you give them a way to see it without breaking anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety model
&lt;/h2&gt;

&lt;p&gt;Letting AI touch production should sound terrifying. That's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AST-parsed command validation — not string matching, actual syntax tree analysis&lt;/li&gt;
&lt;li&gt;Default-deny whitelist — everything blocked unless explicitly allowed&lt;/li&gt;
&lt;li&gt;Human-in-the-loop — any destructive action requires a plan + approval&lt;/li&gt;
&lt;li&gt;Connector model — agents use paths you already trust (Teleport, SSH, AWS, Prometheus)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agents never need their own access path. They never open a new hole in your security posture.&lt;/p&gt;

&lt;p&gt;That's the difference between an agent you'd let near production and one you wouldn't.&lt;/p&gt;

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

&lt;p&gt;It's called KnoxOps. Core idea: infrastructure is an object graph, not a flat list of resources. Model it that way and LLMs can reason like a senior SRE — tracing dependencies, calculating blast radius, finding what dashboards miss.&lt;/p&gt;

&lt;p&gt;The goal: delegate routine SRE toil so developers can focus on building.&lt;/p&gt;

&lt;p&gt;More connectors coming. The principle stays the same: use the access paths you already trust.&lt;/p&gt;

&lt;p&gt;If you've inherited a system nobody understands — I'd like to hear from you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm the founder of &lt;a href="https://knoxops.app" rel="noopener noreferrer"&gt;KnoxOps&lt;/a&gt;. Currently in open beta — use code DEVTO26 for 10,000 free credits on signup.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aiops</category>
      <category>sre</category>
      <category>welcome</category>
    </item>
  </channel>
</rss>
