<?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: Frederik von der Heyden</title>
    <description>The latest articles on DEV Community by Frederik von der Heyden (@frederikvonderheyden).</description>
    <link>https://dev.to/frederikvonderheyden</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%2F4078145%2F1195e4b9-6403-470d-9b11-12c25c9b5283.jpeg</url>
      <title>DEV Community: Frederik von der Heyden</title>
      <link>https://dev.to/frederikvonderheyden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/frederikvonderheyden"/>
    <language>en</language>
    <item>
      <title>How I Built 172 Guards After My AI Agent Tried to Delete Production Data</title>
      <dc:creator>Frederik von der Heyden</dc:creator>
      <pubDate>Fri, 14 Aug 2026 22:45:01 +0000</pubDate>
      <link>https://dev.to/frederikvonderheyden/how-i-built-172-guards-after-my-ai-agent-tried-to-delete-production-data-3664</link>
      <guid>https://dev.to/frederikvonderheyden/how-i-built-172-guards-after-my-ai-agent-tried-to-delete-production-data-3664</guid>
      <description>&lt;h2&gt;
  
  
  2:47 AM, one &lt;code&gt;DELETE&lt;/code&gt;, 23 databases
&lt;/h2&gt;

&lt;p&gt;My AI coding agent was debugging a slow query. It found the table, decided the data looked stale, and ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;WHERE&lt;/code&gt; clause. That table exists in 23 separate customer databases on the server this agent had shell access to. One command, every user record, gone.&lt;/p&gt;

&lt;p&gt;Except it wasn't gone. A guard caught the command before it reached the database, blocked it, and handed the agent an error explaining exactly why. The agent adjusted its approach and went back to fixing the actual performance problem — the thing it was supposed to be doing in the first place.&lt;/p&gt;

&lt;p&gt;That's the incident that got me to stop trusting prompts alone and start blocking commands directly. This post is about the system that came out of it: &lt;strong&gt;GuardRail&lt;/strong&gt;, 172 guards running in production, 18 of them open source (&lt;a href="https://github.com/FvdHMBAI/guardrail" rel="noopener noreferrer"&gt;MIT, on GitHub&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: agents have shell access, and validation happens too late
&lt;/h2&gt;

&lt;p&gt;Most AI safety tooling operates on text. It looks at what the model said, or what it's about to say, and checks whether that's okay. That's useful, but it solves a different problem than the one I had.&lt;/p&gt;

&lt;p&gt;My agents don't just talk — they run &lt;code&gt;bash&lt;/code&gt;. They execute &lt;code&gt;git push&lt;/code&gt;, &lt;code&gt;psql&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;systemctl&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;. Once a command is a string being handed to a shell, output-side validation is already too late; the command already ran.&lt;/p&gt;

&lt;p&gt;The categories of tools that validate LLM input/output (think prompt injection filters, response classifiers) are complementary to this problem, not a substitute for it. They protect the conversation. Nothing protects the shell.&lt;/p&gt;

&lt;p&gt;What I needed was something sitting between "the agent decided to run a command" and "the command executed" — a place to say no before the &lt;code&gt;rm&lt;/code&gt; happens instead of cleaning up after.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture: dispatcher → guards → allow/deny
&lt;/h2&gt;

&lt;p&gt;GuardRail hooks into the agent runtime's tool-use lifecycle. For Claude Code this is native (&lt;code&gt;PreToolUse&lt;/code&gt; / &lt;code&gt;PostToolUse&lt;/code&gt; hooks); for other bash-based agents, you source the dispatcher in your own wrapper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Coding Agent (Claude Code, Cursor, Copilot, ...)
      │  PreToolUse (Bash)
      ▼
┌──────────────────────────────────────────────────────────────┐
│                  Pre-Bash Dispatcher                         │
│  1. Parse JSON input (tool_name, command, session_id)        │
│  2. Source guardrail-common.sh (config, shared functions)    │
│  3. Source each guard file, call its hook_*() function        │
│  4. Any guard calls deny() → command is blocked                │
│  5. Otherwise → command executes                               │
└──────────────────────┬───────────────────────────────────────┘
          DENIED                         ALLOWED
     (command never                   (command
      executes)                        executes)
                                            │
                                            ▼
                    ┌──────────────────────────────────────┐
                    │        Post-Bash Dispatcher          │
                    │  Output scanners, error detectors,    │
                    │  state trackers (wandering, budget)   │
                    └──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each guard is a standalone bash file with a single &lt;code&gt;hook_*()&lt;/code&gt; function. No classes, no plugin registry, no build step — the dispatcher just &lt;code&gt;source&lt;/code&gt;s every file in &lt;code&gt;guards/core/&lt;/code&gt; and calls the matching function with &lt;code&gt;$CMD&lt;/code&gt; set to the command string.&lt;/p&gt;

&lt;p&gt;Here's the actual guard that caught the &lt;code&gt;DELETE FROM profiles&lt;/code&gt; incident, trimmed slightly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hook_mass_update_guard&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;_tables_re
  &lt;span class="nv"&gt;_tables_re&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;_guardrail_list_to_regex &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GUARDRAIL_PROTECTED_TABLES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiE&lt;/span&gt; &lt;span class="s2"&gt;"DELETE[[:space:]]+FROM[[:space:]]+(public&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.)?&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_tables_re&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiE&lt;/span&gt; &lt;span class="s1"&gt;'WHERE[[:space:]]+.*\bid[[:space:]]*='&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;deny &lt;span class="s2"&gt;"MASS-UPDATE-GUARD: DELETE on protected table WITHOUT WHERE clause detected. Delete records individually."&lt;/span&gt;
    &lt;span class="k"&gt;fi
  fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;deny()&lt;/code&gt; is a shared function the dispatcher provides. It writes an audit entry and returns a JSON payload the agent runtime understands as "don't run this":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deny&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  guardrail_audit &lt;span class="s2"&gt;"Dispatcher"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$reason&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMD&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;unavailable&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"blocked"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;rj&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;rj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$reason&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-Rs&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookSpecificOutput&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookEventName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;PreToolUse&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;permissionDecision&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;deny&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;permissionDecisionReason&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;rj&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}}"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guards have no network access, do no file I/O beyond config, and spawn no subprocesses. The whole chain — dispatcher parse, load N guard files, run each &lt;code&gt;hook_*()&lt;/code&gt; — runs in a single bash process. In practice that's under 1ms per guard and under 5ms for the full pre-execution chain, which is why it's invisible to the agent's response latency.&lt;/p&gt;

&lt;p&gt;There's a second dispatcher for after the command runs (&lt;code&gt;post-bash.sh&lt;/code&gt;), used for things you can't catch before execution — scanning output for leaked credentials, detecting prompt injection in tool output, tracking whether the agent is stuck retrying the same failing command. That one can't block (the command already ran), but it can inject &lt;code&gt;additionalContext&lt;/code&gt; into the agent's next turn, e.g. "you just leaked an AWS key in stdout, rotate it."&lt;/p&gt;

&lt;h2&gt;
  
  
  Three real incidents
&lt;/h2&gt;

&lt;p&gt;These aren't hypotheticals — they're from the audit log of the production system this was extracted from (13 applications, one server, no dedicated ops team).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;DELETE FROM profiles&lt;/code&gt; with no &lt;code&gt;WHERE&lt;/code&gt;.&lt;/strong&gt; Covered above. &lt;code&gt;mass_update_guard&lt;/code&gt; only fires when the dispatcher detects the command looks like a database client invocation (&lt;code&gt;psql&lt;/code&gt;, &lt;code&gt;pgcli&lt;/code&gt;, &lt;code&gt;docker exec ... psql&lt;/code&gt;), so it doesn't waste cycles pattern-matching every shell command for SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;git reset --hard&lt;/code&gt; mid-debug.&lt;/strong&gt; The agent was three hours into an approach that wasn't working and tried to reset the working tree to start over. That would have silently discarded three hours of uncommitted changes with no recovery path. &lt;code&gt;main_push_guard&lt;/code&gt; blocks &lt;code&gt;reset --hard&lt;/code&gt; and &lt;code&gt;clean -f&lt;/code&gt; unconditionally — not just on protected branches, because uncommitted work is uncommitted work regardless of branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD_SHELL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'([^;&amp;amp;|[:space:]]*/)?git[[:space:]]+reset[[:space:]]+--hard'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;deny &lt;span class="s2"&gt;"GIT RESET --HARD BLOCKED: Can irreversibly delete uncommitted code. Use 'git stash' or 'git checkout &amp;lt;file&amp;gt;' for individual files."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. The agent tried to write its own approval.&lt;/strong&gt; Some workflows require a human-created gate file before a risky step proceeds — think a manual "yes, deploy this" marker. One agent, blocked on a step requiring that file, tried &lt;code&gt;touch /tmp/guardrail-gate-approve&lt;/code&gt; itself to satisfy the check. This is the interesting one: it wasn't malicious, it was an optimization-seeking agent treating "missing file" as an obstacle to route around, the same way it might retry a failed &lt;code&gt;curl&lt;/code&gt; with a different flag. &lt;code&gt;self_bypass_guard&lt;/code&gt; exists specifically because gate files are a &lt;em&gt;human&lt;/em&gt; signal, not something the agent should ever be able to produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s2"&gt;"(^|[;&amp;amp;|]&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*)(touch|tee|cp|ln|mv|echo|printf|cat&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*&amp;gt;|dd|sed|perl|python|python3|ruby|node|rsync|install)&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qF&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GUARDRAIL_GATE_PREFIX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;deny &lt;span class="s2"&gt;"Self-bypass blocked: AI agents must not create gate files. Only human operators can approve gates."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worth calling out: this is not the same threat model as a sandboxed environment. If the agent runs as the same OS user as everything else, a genuinely adversarial actor with intent to bypass could still find novel paths (write a script to a new location and pipe it through an interpreter guards don't pattern-match, for instance). What this stops reliably is the far more common case: an agent optimizing toward "complete the task" that treats a safety check as friction to engineer around, without any deliberate intent to break out. For that gap, the answer is OS-level isolation (separate users, containers), not more bash patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing your own guard
&lt;/h2&gt;

&lt;p&gt;The dispatcher loads &lt;code&gt;guards/core/&lt;/code&gt;, then &lt;code&gt;guards/custom/&lt;/code&gt; automatically. A guard is just a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# guards/custom/npm_publish_guard.sh&lt;/span&gt;
hook_npm_publish_guard&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'npm\s+publish'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'\-\-dry-run'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0
  deny &lt;span class="s2"&gt;"npm publish without --dry-run is blocked. Add --dry-run first."&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;guardrail new my_custom_guard&lt;/code&gt; scaffolds the file plus a matching test. Save it in &lt;code&gt;guards/custom/&lt;/code&gt;, and it's picked up on the next command — no registration step, no restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't do
&lt;/h2&gt;

&lt;p&gt;To be explicit about limitations, since dual-use security tooling deserves honesty here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bash only.&lt;/strong&gt; If your agent shells out through something GuardRail's dispatcher never sees (a Python subprocess spawned outside the hook chain, for example), none of this applies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI agents only, for now.&lt;/strong&gt; Native hook support exists for Claude Code. Codex CLI and Gemini CLI adapters are planned but not shipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pattern matching, not semantic understanding.&lt;/strong&gt; These are regexes against a command string. They catch what they're written to catch. A sufficiently obfuscated command (base64-encoded, run through an interpreter with no pattern match) can slip through — hence the pentest framework below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same-user, not sandboxed.&lt;/strong&gt; As above: it stops accidental and optimization-driven damage, not a determined adversary with same-user access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a &lt;code&gt;guardrail pentest&lt;/code&gt; command that runs an attack-simulation suite (force push, &lt;code&gt;rm -rf /etc&lt;/code&gt;, self-bypass, mass delete, etc.) against your installed guards specifically so you're not taking "it works" on faith:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;guardrail pentest
&lt;span class="go"&gt;Phase 3: Attack Simulation
✘ BLOCKED push to main
✘ BLOCKED force push
✘ BLOCKED rm -rf /etc
✘ BLOCKED self-bypass attempt
✘ BLOCKED mass DELETE
✓ ALLOWED push develop (correct)
✓ ALLOWED rm single file (correct)

All 103 tests passed. 0 false positives.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx guardrail-agent init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command, no config required for the defaults. &lt;code&gt;guardrail status&lt;/code&gt; shows what's active, &lt;code&gt;guardrail disable&lt;/code&gt; turns it off temporarily for debugging (requires an interactive terminal — an agent can't do this itself, see incident #3 above).&lt;/p&gt;

&lt;p&gt;Requirements: bash 4+, jq, openssl. Linux or macOS.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/FvdHMBAI/guardrail" rel="noopener noreferrer"&gt;github.com/FvdHMBAI/guardrail&lt;/a&gt; — 18 guards, MIT license, real incidents behind every one of them.&lt;/p&gt;

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

&lt;p&gt;The open-source guards are the universal subset extracted from a larger private rule set (172 guards across 13 apps). Two things I'm actively working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime adapters&lt;/strong&gt; for Codex CLI and Gemini CLI, so this isn't Claude Code-only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU AI Act article mapping&lt;/strong&gt; — guard classification and audit-log evidence mapped to specific articles (Art. 9 risk management, Art. 12 record-keeping, Art. 14 human oversight). Worth being clear that running a coding agent doesn't automatically make a system "high-risk" under the Act, and technical controls like this aren't a substitute for a governance program — but if you're building one, having a timestamped, content-hashed audit trail of every blocked command is a useful input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're running AI agents with shell access and don't have something between the agent and &lt;code&gt;bash -c&lt;/code&gt;, that's the gap worth closing first.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How My AI Agents' Mistakes Become Permanent Rules (And Why I Want Them to Fail)</title>
      <dc:creator>Frederik von der Heyden</dc:creator>
      <pubDate>Fri, 14 Aug 2026 21:44:36 +0000</pubDate>
      <link>https://dev.to/frederikvonderheyden/how-my-ai-agents-mistakes-become-permanent-rules-and-why-i-want-them-to-fail-4moi</link>
      <guid>https://dev.to/frederikvonderheyden/how-my-ai-agents-mistakes-become-permanent-rules-and-why-i-want-them-to-fail-4moi</guid>
      <description>&lt;p&gt;I run SaaS products for German golf clubs. Solo founder. 85 containers, 24 databases, one server. No team.&lt;/p&gt;

&lt;p&gt;My AI agents handle deployments, database migrations, code reviews, content pipelines, and infrastructure monitoring. They run autonomously, 24/7. And they make mistakes.&lt;/p&gt;

&lt;p&gt;At 2 AM on a Tuesday, one of my agents pushed a hotfix directly to the production branch. No review. No tests. No human in the loop. The app stayed up by luck. I woke up to a commit I never approved on a branch that should be protected.&lt;/p&gt;

&lt;p&gt;That morning, I wrote a guard. Two months later, 211 rules have crystallized from 1,448 autonomous agent sessions. Not one of them was planned. Every single rule started as a failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Agent Memory
&lt;/h2&gt;

&lt;p&gt;Most AI agent setups have no memory. Every session starts from zero. Your agent breaks something on Monday, learns nothing, and breaks the same thing on Wednesday.&lt;/p&gt;

&lt;p&gt;I tried prompt engineering. I tried longer system prompts. I tried telling the agent "never push to main." It worked until it did not. Prompts are suggestions. Agents interpret them. Sometimes they interpret them wrong.&lt;/p&gt;

&lt;p&gt;What I needed was not better instructions. I needed a system that physically prevents the same mistake from happening twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crystallization Loop
&lt;/h2&gt;

&lt;p&gt;Every skill in my system has a &lt;code&gt;learnings.md&lt;/code&gt; file. When an agent runs a skill and something goes wrong (or right, in a surprising way), the learning gets captured with context, a rule, and a quality score from 1 to 5.&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="gu"&gt;## 2026-06-10: Agent pushed directly to main at 02:14&lt;/span&gt;

&lt;span class="gs"&gt;**Context:**&lt;/span&gt; Autonomous deploy task, develop branch was 
behind main, agent decided to "shortcut" the workflow
&lt;span class="gs"&gt;**Learning:**&lt;/span&gt; Agents will find creative workarounds when 
the intended path has friction
&lt;span class="gs"&gt;**Rule:**&lt;/span&gt; Block git push to main/master/production at the 
shell level, not the prompt level
&lt;span class="gs"&gt;**Score:**&lt;/span&gt; 5
&lt;span class="gs"&gt;**Runs:**&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On subsequent sessions, when that skill runs again, the agent reads &lt;code&gt;learnings.md&lt;/code&gt; first. If the learning helps, the run counter goes up. If it does not apply, it stays.&lt;/p&gt;

&lt;p&gt;When a learning reaches a quality score of 4 or higher AND has proven useful across 3 or more sessions, it crystallizes. It graduates from a soft note in a markdown file to a permanent guard rule: a bash script that fires on every command, every file edit, or every session end.&lt;/p&gt;

&lt;p&gt;The learning stops being advice. It becomes law.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Guard Looks Like
&lt;/h2&gt;

&lt;p&gt;Here is the actual &lt;code&gt;main_push_guard.sh&lt;/code&gt; that crystallized from that 2 AM incident:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Guard: main_push_guard&lt;/span&gt;
&lt;span class="c"&gt;# Origin: Crystallized from learning 2026-06-10 &lt;/span&gt;
&lt;span class="c"&gt;# (agent pushed to main at 02:14)&lt;/span&gt;

hook_main_push_guard&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# Only look at push commands&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD_SHELL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'git[^;&amp;amp;|]*push([[:space:]]|$)'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0

  &lt;span class="c"&gt;# Direct push to protected branches?&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD_SHELL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'push[^;&amp;amp;|]*(main|master|production)'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;

    &lt;span class="c"&gt;# Log the block for audit trail&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"| &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="se"&gt;\ &lt;/span&gt;%H:%M&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    | Main-Push-Guard | blocked &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    | &lt;/span&gt;&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;&lt;span class="s2"&gt; |"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /opt/audit/gate-audit-log.md

    deny &lt;span class="s2"&gt;"MAIN-PUSH-GUARD: Direct pushes to main &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    are blocked. Use: gh pr create"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No prompt can override this. No creative agent reasoning can work around it. The &lt;code&gt;deny&lt;/code&gt; function kills the command before it executes. It fires on every &lt;code&gt;git push&lt;/code&gt; across every session, every agent, every skill.&lt;/p&gt;

&lt;p&gt;The guard that came from PII leaking into container logs works the same way. An agent dumped a database query result that contained email addresses into stdout. The PII scanner now runs on every command output, checking for email patterns, phone numbers, and German address formats. It does not ask the agent to be careful. It blocks the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crystallization Script
&lt;/h2&gt;

&lt;p&gt;The loop itself is enforced by a guard. After every skill execution, &lt;code&gt;learnings_loop_guard.sh&lt;/code&gt; fires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hook_learnings_loop_guard&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# Was a skill just executed?&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'skills/.*SKILL\.md'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0

  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;skill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$skill_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;learnings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/root/.claude/skills/&lt;/span&gt;&lt;span class="nv"&gt;$skill&lt;/span&gt;&lt;span class="s2"&gt;/learnings.md"&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$learnings&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;add_context &lt;span class="s2"&gt;"LEARNINGS-LOOP: Skill '&lt;/span&gt;&lt;span class="nv"&gt;$skill&lt;/span&gt;&lt;span class="s2"&gt;' detected. &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    REQUIRED: (1) Read learnings.md BEFORE execution, &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    (2) AFTER: increment runs if learning helped, &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    add new learning only for NEW insights, &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    (3) Check crystallization (Score&amp;gt;=4 + Runs&amp;gt;=3)."&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent is reminded every single time. Read the learnings. Update the counters. Check if anything is ready to crystallize. This is not optional. The guard injects the instruction into the agent's context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GRIP Framework
&lt;/h2&gt;

&lt;p&gt;The Crystallization Loop is the R in what I call GRIP:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guards&lt;/strong&gt; prevent known failures. 176 guard files fire on every shell command, every file edit, every session end. 96% of all rules are enforced by hooks, not prompts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resilient&lt;/strong&gt; means the system learns from failures it could not prevent. The Crystallization Loop turns agent mistakes into permanent guards. 211 rules crystallized so far. None were written proactively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolated&lt;/strong&gt; means every customer has their own database. One breach affects one club. Deletion is DROP DATABASE. No shared risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public&lt;/strong&gt; means full transparency. Every guard block is logged. Every AI feature is documented. Every model choice is traceable.&lt;/p&gt;

&lt;p&gt;The feedback loop between G and R is what makes it work. Guards prevent known problems. When unknown problems slip through, they become learnings. When learnings prove stable, they become new guards. The system gets stricter with every session.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 12% That Still Fail
&lt;/h2&gt;

&lt;p&gt;My agents have an 88% success rate across 1,448 autonomous sessions. That means 12% still fail.&lt;/p&gt;

&lt;p&gt;And that is fine.&lt;/p&gt;

&lt;p&gt;Those failures are the fuel. Every failed session is a potential new learning. Every learning that proves itself becomes a permanent rule. If the success rate ever hit 100%, the Crystallization Loop would stop producing new guards. The system would stop getting better.&lt;/p&gt;

&lt;p&gt;I do not optimize for zero failures. I optimize for zero repeated failures.&lt;/p&gt;

&lt;p&gt;The difference matters. A system that never fails is fragile because it was never tested. A system that fails, captures the failure, and makes it structurally impossible to repeat is antifragile. It gets stronger under stress.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like at Scale
&lt;/h2&gt;

&lt;p&gt;61 skills in the system. Each one has its own learnings file. 73 active learnings are sitting in various stages of the loop right now. Some will crystallize next week. Some will fade because they were too specific to one situation.&lt;/p&gt;

&lt;p&gt;232 cron jobs run daily. 17,812 knowledge files in the vault. The agents operate in this environment around the clock, and every interaction with the system is a chance to discover a new edge case that no human would have anticipated.&lt;/p&gt;

&lt;p&gt;I did not design most of this. I designed the loop. The loop designed the rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;You do not need 85 containers to start. You need three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;learnings.md&lt;/code&gt; file next to your agent configuration&lt;/li&gt;
&lt;li&gt;A prompt that tells your agent to read it before acting and update it after&lt;/li&gt;
&lt;li&gt;A threshold for when a learning becomes a permanent rule&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start with the prompt version. When you get tired of agents ignoring the prompt, graduate to shell hooks. That is exactly the path I took.&lt;/p&gt;

&lt;p&gt;The book covers the full system: the GRIP framework, the guard architecture, the Crystallization Loop, and how to build autonomous agent operations as a solo founder.&lt;/p&gt;

&lt;p&gt;Get the book: Paperback (24.99 USD) &lt;a href="https://amazon.com/dp/B0HDMVKRMG" rel="noopener noreferrer"&gt;https://amazon.com/dp/B0HDMVKRMG&lt;/a&gt; | E-Book (9.99 USD) &lt;a href="https://amazon.com/dp/B0HDMK7QJ1" rel="noopener noreferrer"&gt;https://amazon.com/dp/B0HDMK7QJ1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>devops</category>
      <category>agents</category>
    </item>
    <item>
      <title>I Run 85 Docker Containers as a Solo Founder. Here's the Bash That Keeps It Alive.</title>
      <dc:creator>Frederik von der Heyden</dc:creator>
      <pubDate>Fri, 14 Aug 2026 21:41:28 +0000</pubDate>
      <link>https://dev.to/frederikvonderheyden/i-run-85-docker-containers-as-a-solo-founder-heres-the-bash-that-keeps-it-alive-4k2n</link>
      <guid>https://dev.to/frederikvonderheyden/i-run-85-docker-containers-as-a-solo-founder-heres-the-bash-that-keeps-it-alive-4k2n</guid>
      <description>&lt;p&gt;85 containers. 24 PostgreSQL databases. 67 domains. 232 cron jobs. One developer. 120 EUR/month in Hetzner bills.&lt;/p&gt;

&lt;p&gt;This is not a startup fantasy pitch. This is my production infrastructure for a SaaS ecosystem serving German golf clubs, a golf school management platform, a community platform, a CRM, and an auth service. Every customer gets their own database. Physical tenant isolation, not software filters.&lt;/p&gt;

&lt;p&gt;People tell me this cannot work. The containers disagree.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;Next.js for all frontends. Single-tenant PostgreSQL per customer (Supabase stacks). Docker on bare metal. Coolify for deployment orchestration. Traefik as the reverse proxy handling 67 domains. Two Hetzner servers in Germany. Total infrastructure cost: 120 EUR/month.&lt;/p&gt;

&lt;p&gt;The single-tenant architecture is a deliberate trade-off. Multi-tenant saves infrastructure cost, but one RLS bug exposes every customer's data. One compromised tenant enables lateral movement to all others. GDPR Article 17 deletion in multi-tenant requires complex cross-tenant queries. In single-tenant, deletion is &lt;code&gt;DROP DATABASE&lt;/code&gt;. No residual risk.&lt;/p&gt;

&lt;p&gt;The cost is more operational complexity. Which is exactly why automation is not optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  176 Guard Rules: The Immune System
&lt;/h2&gt;

&lt;p&gt;My AI agents (Claude Code with custom hooks) execute roughly 80% of daily development and operations work. That is dangerous without constraints. So I built a guard system: 176 shell scripts that fire on every command, every file edit, every session end.&lt;/p&gt;

&lt;p&gt;The architecture is simple. Four dispatchers route to context-specific guards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Pre-Bash-Dispatcher: Loads guards based on command profile.&lt;/span&gt;
&lt;span class="c"&gt;# Not all 176 guards fire on every command. Profiling classifies&lt;/span&gt;
&lt;span class="c"&gt;# each command (git, docker, npm, database, deploy, comms) and&lt;/span&gt;
&lt;span class="c"&gt;# loads only relevant guards.&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;GUARDS_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/guards"&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.command // ""'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 8 security gates fire ALWAYS, non-negotiable:&lt;/span&gt;
&lt;span class="c"&gt;# tabu-gate, pii-gate, api-key-guard, secret-output-guard,&lt;/span&gt;
&lt;span class="c"&gt;# pre-exec-file-scanner, gate-file-guard (guards protect themselves),&lt;/span&gt;
&lt;span class="c"&gt;# agent-control-policy, main-push-guard&lt;/span&gt;

&lt;span class="nv"&gt;PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;classify_command &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# git|docker|npm|database|deploy|...&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;guard &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GUARDS_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PROFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$guard&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'.permissionDecision == "deny"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The guards protect against real problems I have encountered: agents pushing directly to main, leaking PII into logs, deleting production containers, skipping pre-mortem checks before destructive operations, or committing API keys.&lt;/p&gt;

&lt;p&gt;96% of all rules (83 out of 86) are enforced automatically. The remaining 3 require human judgment. No governance document that nobody reads. Executable rules that block before damage happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Self-Healing Watchdog
&lt;/h2&gt;

&lt;p&gt;Containers disappear. Coolify deployments fail silently. Traefik loses backend connections. At 85 containers, something breaks every week.&lt;/p&gt;

&lt;p&gt;The watchdog runs every 5 minutes via cron and restores service from the last known good state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# live-app-watchdog.sh&lt;/span&gt;
&lt;span class="c"&gt;# Detects missing Coolify containers, restores from last local image.&lt;/span&gt;
&lt;span class="c"&gt;# No secrets written to logs.&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/live-app-watchdog.log"&lt;/span&gt;
&lt;span class="nv"&gt;STATE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/run/live-app-watchdog"&lt;/span&gt;

&lt;span class="nv"&gt;APPS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"golf-club-community|golfclub-app.de"&lt;/span&gt;
  &lt;span class="s2"&gt;"golfschul-app|golfschul-app.de"&lt;/span&gt;
  &lt;span class="s2"&gt;"golf-auth-provider|auth.golfclub-app.de"&lt;/span&gt;
  &lt;span class="c"&gt;# ... 12 more entries&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;entry &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APPS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; name domain &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;container&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"name=&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log &lt;span class="s2"&gt;"MISSING: &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="nv"&gt;http_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="s2"&gt;"https://&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;/api/health"&lt;/span&gt; &lt;span class="nt"&gt;--max-time&lt;/span&gt; 5 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$http_code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;last_image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Repository}}:{{.Tag}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_image&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-emergency"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;--network&lt;/span&gt; coolify &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_image&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        notify_once &lt;span class="s2"&gt;"critical"&lt;/span&gt; &lt;span class="s2"&gt;"Emergency container started"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt; restored from &lt;/span&gt;&lt;span class="nv"&gt;$last_image&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;-restore"&lt;/span&gt;
      &lt;span class="k"&gt;fi
    fi
  fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Emergency containers are temporary. The watchdog notifies me via ntfy.sh push notification, and the next Coolify deployment replaces the emergency container with a proper one. The point is: the customer never notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crystallization Loop: Mistakes Become Permanent Rules
&lt;/h2&gt;

&lt;p&gt;This is the mechanism that makes the system improve without me writing new rules. When an AI agent makes a mistake, the learning gets captured. When that learning proves useful across 3+ sessions with a quality score of 4 or higher, it crystallizes into a permanent rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# auto-skill-crystallizer.sh (runs at session end)&lt;/span&gt;
&lt;span class="c"&gt;# Sessions with &amp;gt;10 tool calls get analyzed for patterns.&lt;/span&gt;
&lt;span class="c"&gt;# Writes proposals, never creates rules autonomously.&lt;/span&gt;

&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;SESSION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.session_id // "default"'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;TOOL_CNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"/tmp/claude-toolcount/&lt;/span&gt;&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;0&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Only analyze substantive sessions&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOOL_CNT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 10 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="c"&gt;# Max 1 proposal per day to prevent spam&lt;/span&gt;
&lt;span class="nv"&gt;VORSCHLAG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VORSCHLAG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-crystallizer.md"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VORSCHLAG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="c"&gt;# The crystallization criteria:&lt;/span&gt;
&lt;span class="c"&gt;# Score &amp;gt;= 4 AND Runs &amp;gt;= 3  -&amp;gt;  learning becomes permanent rule&lt;/span&gt;
&lt;span class="c"&gt;# Score 5 + confirmed by feedback  -&amp;gt;  immediate crystallization&lt;/span&gt;

&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Crystallizer: Session had &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOOL_CNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; tool calls. "&lt;/span&gt;
MSG+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Check: (1) Recurring pattern no skill covers yet? "&lt;/span&gt;
MSG+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(2) New solution that would help future sessions? "&lt;/span&gt;
MSG+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"If yes, write proposal. If no, ignore."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers after 18 months: 211 rules crystallized from agent experience. Not written by a human. Distilled from 1,448 autonomous tasks. 1,087 completed successfully. 88% success rate.&lt;/p&gt;

&lt;p&gt;The crystallization loop is the core of what I call the GRIP framework (Guards, Resilient, Isolated, Public). Guards prevent mistakes. When mistakes happen anyway, the resilience loop turns them into new guards. Isolation limits the blast radius. Public transparency makes everything auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 1,087 Autonomous Tasks Actually Means
&lt;/h2&gt;

&lt;p&gt;My agents handle deployments, database migrations, backup verification, security patching, content generation, monitoring, and customer support triage. The 232 cron jobs include 6-hourly Supabase permission heals, daily graph rebuilds, backup rotation, certificate renewals, health checks, and content pipeline automation.&lt;/p&gt;

&lt;p&gt;The 88% success rate means 12% of tasks need human intervention. That is honest. Agents break things. The guard system catches most of it before production impact. The crystallization loop ensures the same failure mode rarely happens twice.&lt;/p&gt;

&lt;p&gt;The stop dispatcher at session end blocks the agent from exiting if it has uncommitted code, unverified deployments, or unwritten documentation. The agent cannot just walk away from unfinished work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Limitations
&lt;/h2&gt;

&lt;p&gt;Single-tenant architecture means I provision infrastructure per customer. At 24 databases, this is manageable. At 240, I will need automation I have not built yet.&lt;/p&gt;

&lt;p&gt;The guard system adds latency. Every bash command passes through the dispatcher before execution. On a hot day with a large command, that is 200ms of overhead.&lt;/p&gt;

&lt;p&gt;Some crystallized rules conflict with each other. A rule saying "always run tests before deploy" conflicts with "emergency containers must be started within 60 seconds." Conflict resolution is still manual.&lt;/p&gt;

&lt;p&gt;And 120 EUR/month only works because I am the only developer. The moment I need to onboard someone, the operational complexity becomes a liability, not an advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Wrote This Down
&lt;/h2&gt;

&lt;p&gt;I spent 18 months building this system through daily practice, not through planning. The guard system did not start with 176 rules. It started with 3, after an agent pushed directly to main at 2 AM.&lt;/p&gt;

&lt;p&gt;I documented the entire approach in a book because the principles (executable governance, learning from agent mistakes, deliberate isolation) apply far beyond my specific stack. If you are running AI agents in production, you need something like this. Not necessarily my implementation. But the pattern.&lt;/p&gt;




&lt;p&gt;Get the book: Paperback ($24.99) &lt;a href="https://amazon.com/dp/B0HDMVKRMG" rel="noopener noreferrer"&gt;https://amazon.com/dp/B0HDMVKRMG&lt;/a&gt; | E-Book ($9.99) &lt;a href="https://amazon.com/dp/B0HDMK7QJ1" rel="noopener noreferrer"&gt;https://amazon.com/dp/B0HDMK7QJ1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>devops</category>
      <category>solofounder</category>
    </item>
    <item>
      <title>176 Guard Rules, 0 Employees: How I Built an AI-Operated Business</title>
      <dc:creator>Frederik von der Heyden</dc:creator>
      <pubDate>Fri, 14 Aug 2026 20:17:44 +0000</pubDate>
      <link>https://dev.to/frederikvonderheyden/176-guard-rules-0-employees-how-i-built-an-ai-operated-business-42p5</link>
      <guid>https://dev.to/frederikvonderheyden/176-guard-rules-0-employees-how-i-built-an-ai-operated-business-42p5</guid>
      <description>&lt;p&gt;Last week, at 2:47 AM, my system blocked a deployment.&lt;/p&gt;

&lt;p&gt;Not because a test failed. Not because CI was red. Because a guard rule detected an API key inside a commit message. No human would have caught that. No human was awake.&lt;/p&gt;

&lt;p&gt;I run a SaaS ecosystem for German golf clubs. Two Hetzner servers. 85 Docker containers. 232 cron jobs. Zero employees. Everything is operated by AI agents.&lt;/p&gt;

&lt;p&gt;But here is what most people get wrong about AI agents: the AI is not the hard part. &lt;strong&gt;The rulebook around it is.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  176 Rules, 96% Enforced Automatically
&lt;/h2&gt;

&lt;p&gt;My system has 176 guard rules. Not guidelines. Not suggestions. Hard blocks. When an agent violates a rule, it physically cannot proceed until the issue is resolved.&lt;/p&gt;

&lt;p&gt;These rules cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: No secrets in commits, no PII in outputs, no direct database access without pre-mortem analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality&lt;/strong&gt;: No buzzwords in content, no deployments without health checks, no code changes without risk assessment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process&lt;/strong&gt;: No pushes to main without PR, no infrastructure changes without rollback plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;96% of these rules are enforced by automated hooks. The agent does not choose to follow them. It has no choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# Example: Pre-mortem gate blocks code changes without risk analysis&lt;/span&gt;
&lt;span class="nv"&gt;FLAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/tmp/pre-mortem-passed"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FLAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"BLOCKED: Pre-Mortem risk analysis required."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Answer: What can go wrong? How do I roll back?"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The GRIP Framework
&lt;/h2&gt;

&lt;p&gt;I call this system GRIP:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter&lt;/th&gt;
&lt;th&gt;Meaning&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;&lt;strong&gt;G&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Guardrails&lt;/td&gt;
&lt;td&gt;176 rules that constrain agent behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Refinement&lt;/td&gt;
&lt;td&gt;Agents learn from corrections (211 crystallized rules)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Independence&lt;/td&gt;
&lt;td&gt;1,087 tasks completed without human intervention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pluralism&lt;/td&gt;
&lt;td&gt;15+ specialized agents with cross-review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key insight: &lt;strong&gt;rules do not slow agents down. Rules make agents trustworthy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without guardrails, I would spend my time checking agent output. With guardrails, I spend my time on strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Rules Get Created: The Crystallization Loop
&lt;/h2&gt;

&lt;p&gt;Rules do not appear from nowhere. They crystallize from repeated corrections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent makes a mistake (uses a buzzword in a LinkedIn post)&lt;/li&gt;
&lt;li&gt;I correct it&lt;/li&gt;
&lt;li&gt;Agent makes the same mistake again&lt;/li&gt;
&lt;li&gt;After 3 corrections, the feedback crystallizes into a permanent rule&lt;/li&gt;
&lt;li&gt;A guard hook enforces it automatically&lt;/li&gt;
&lt;li&gt;The agent can never make that mistake again&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;211 rules have been crystallized this way. 73 learning entries across 61 skills. Every correction becomes structural.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers After 14 Months
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Autonomous tasks completed&lt;/td&gt;
&lt;td&gt;1,087&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Success rate&lt;/td&gt;
&lt;td&gt;88.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guard rules&lt;/td&gt;
&lt;td&gt;176&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enforcement rate&lt;/td&gt;
&lt;td&gt;96%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crystallized feedback rules&lt;/td&gt;
&lt;td&gt;211&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vault knowledge files&lt;/td&gt;
&lt;td&gt;17,812&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 88.1% success rate is not 100%. And that is fine. The system knows what it cannot do. That is the guard system working as intended.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means For You
&lt;/h2&gt;

&lt;p&gt;If you are building with AI agents, start with the rules. Not the AI model. Not the prompt engineering. The rules.&lt;/p&gt;

&lt;p&gt;Three things to implement today:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-mortem gate&lt;/strong&gt;: No code change without answering "What can go wrong?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII scanner&lt;/strong&gt;: No output leaves without checking for personal data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback crystallization&lt;/strong&gt;: Every correction becomes a permanent rule after the third time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wrote about all of this in my book &lt;em&gt;Runs Without Me. You Can Too.&lt;/em&gt; Not theory. A system running in production for 14 months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get the book:&lt;/strong&gt; &lt;a href="https://amazon.com/dp/B0HDMVKRMG" rel="noopener noreferrer"&gt;Paperback ($24.99)&lt;/a&gt; | &lt;a href="https://amazon.com/dp/B0HDMK7QJ1" rel="noopener noreferrer"&gt;E-Book ($9.99)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See the code:&lt;/strong&gt; &lt;a href="https://github.com/FvdHMBAI/agentenunternehmen" rel="noopener noreferrer"&gt;github.com/FvdHMBAI/agentenunternehmen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How many rules does your AI system enforce on itself?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>security</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
