<?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: Aleksandr Shchuka</title>
    <description>The latest articles on DEV Community by Aleksandr Shchuka (@aleksandrshchuka).</description>
    <link>https://dev.to/aleksandrshchuka</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3949553%2F94f4a2e4-0963-42f4-9b62-64a999266aca.png</url>
      <title>DEV Community: Aleksandr Shchuka</title>
      <link>https://dev.to/aleksandrshchuka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aleksandrshchuka"/>
    <language>en</language>
    <item>
      <title>mirabilis: a one-command sandbox for autonomous Claude Code that ended up patching itself</title>
      <dc:creator>Aleksandr Shchuka</dc:creator>
      <pubDate>Wed, 10 Jun 2026 15:33:05 +0000</pubDate>
      <link>https://dev.to/aleksandrshchuka/mirabilis-a-one-command-sandbox-for-autonomous-claude-code-that-ended-up-patching-itself-4k1d</link>
      <guid>https://dev.to/aleksandrshchuka/mirabilis-a-one-command-sandbox-for-autonomous-claude-code-that-ended-up-patching-itself-4k1d</guid>
      <description>&lt;p&gt;I have a side project that I actually like, and I want to show it. No teaser, no promised secret: this is a post about a tool I built, how I built it, and one episode at the end that pleased me as an engineer.&lt;/p&gt;

&lt;p&gt;The tool is &lt;a href="https://github.com/AlexShchuka/mirabilis" rel="noopener noreferrer"&gt;mirabilis&lt;/a&gt;. It is a one-command launcher that brings up a devcontainer, starts Claude Code inside it with &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; (the agent asks no approval for commands or edits), and drops me straight into the running agent. The container is isolated from my laptop. The agent gets structured persistent memory and a behavioral harness. From the outside, it is 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;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/AlexShchuka/mirabilis/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That clones the repo into &lt;code&gt;~/.mirabilis&lt;/code&gt;, installs the devcontainer CLI, and puts &lt;code&gt;mirabilis&lt;/code&gt; on your PATH. Run &lt;code&gt;mirabilis&lt;/code&gt; and you land in a terminal menu: launch, plugins, harness, stack, open in VS Code. The first launch builds the container and signs you into GitHub and Claude through their native flows; tokens live in sandbox volumes, never in the repository. Then the launcher hands the terminal to Claude.&lt;/p&gt;

&lt;p&gt;Why: I wanted a place to give an agent full autonomy and walk away. Bypass mode is convenient right up until the model makes a mistake, and &lt;code&gt;rm -rf&lt;/code&gt; does not forgive mistakes. Hence a box I do not care about: inside, the agent is root; the boundary is the container wall, not my filesystem. Containerizing an agent is standard practice; Anthropic ships a &lt;a href="https://github.com/anthropics/claude-code" rel="noopener noreferrer"&gt;reference devcontainer&lt;/a&gt; with the same motivation. What I wanted on top was one command and zero thinking about provisioning.&lt;/p&gt;

&lt;p&gt;The name nods to Einstein's &lt;em&gt;annus mirabilis&lt;/em&gt;, the 1905 "miracle year" with four physics-changing papers. Cheeky for a personal sandbox; consider it an advance I intend to earn.&lt;/p&gt;

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

&lt;p&gt;mirabilis is a single Go binary, roughly 3.8k lines of production code and 9.7k of tests, playing three roles dispatched by argument in &lt;code&gt;cmd/mirabilis/main.go&lt;/code&gt;: no arguments is the host TUI launcher, &lt;code&gt;provision&lt;/code&gt; is the in-container provisioner, &lt;code&gt;hook&lt;/code&gt; is the Claude hook handler. One artifact, one dispatcher.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The launch pipeline&lt;/strong&gt; is a DAG, not a script. Steps in &lt;code&gt;internal/pipeline&lt;/code&gt; have statuses (&lt;code&gt;stPending&lt;/code&gt;, &lt;code&gt;stRunning&lt;/code&gt;, &lt;code&gt;stDone&lt;/code&gt;, &lt;code&gt;stSkipped&lt;/code&gt;, &lt;code&gt;stFailed&lt;/code&gt;), dependencies, and a per-step retry policy. Network-shaped steps run under &lt;code&gt;RetryNet&lt;/code&gt;: 4 attempts, exponential backoff from 300ms capped at 8s, with jitter (actual delay: uniform between half and full backoff). Deterministic steps such as the build get no retries; retrying a reproducible compile error is four minutes of spinner for nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory.&lt;/strong&gt; &lt;code&gt;~/.claude&lt;/code&gt; lives on a volume and survives rebuilds, but it is not one big scratch file: memory is split into typed categories, declared in &lt;code&gt;internal/config/config.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;MemoryCategories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;MemoryCategory&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"about-me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"semantic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Stable facts about you: identity, role, goals, hard preferences, constraints."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"dev-principles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"procedural"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cross-project engineering invariants you endorse: style, testing bar, anti-slop."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"research-log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"episodic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Dated findings tied to a specific investigation, paper, or bug. Append-only, compacted periodically."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three types: semantic for timeless facts, procedural for how-to invariants, episodic for dated findings. On every &lt;code&gt;SessionStart&lt;/code&gt;, a hook walks the category files, counts the invariant bullets in each, and regenerates a &lt;code&gt;MEMORY.md&lt;/code&gt; index. The agent never edits the index by hand. The point is memory the agent loads predictably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two boundaries&lt;/strong&gt;, deliberately separate. The container is the security boundary: it keeps the agent away from my machine. Behavior is the harness's job: my plugin &lt;a href="https://github.com/AlexShchuka/neuro-matrix" rel="noopener noreferrer"&gt;neuro-matrix&lt;/a&gt;, installed automatically by the provisioner, carries invariants like "don't push to main" and "don't exfiltrate credentials".&lt;/p&gt;

&lt;h2&gt;
  
  
  The frame that kept the slop out
&lt;/h2&gt;

&lt;p&gt;mirabilis was written in about a week, mostly by AI agents. That sentence is not the achievement. In a week, agents will produce exactly as much plausible garbage as you let them. The engineering was making sure garbage never reached main, and that part was mine.&lt;/p&gt;

&lt;p&gt;The frame lives in &lt;a href="https://github.com/AlexShchuka/mirabilis/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;AGENTS.md&lt;/a&gt;, which the agent reads at the start of every session:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Code is truth.&lt;/strong&gt; Every claim about state is backed by tool output: a concrete run with concrete output, not "this probably works".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal diff / YAGNI.&lt;/strong&gt; Touch only what is broken or what the task needs; no drive-by refactoring, no layers "just in case".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-neuroslop.&lt;/strong&gt; Plausible shape is not needed code; don't grow files and abstractions detached from what the repo actually needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not green means not done.&lt;/strong&gt; Changes come with tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Principles alone are wishes, so there is machinery behind them. A total ban on comments in code and config, enforced by a pre-commit hook: prose lives in &lt;code&gt;.md&lt;/code&gt; only, and the hook greps staged non-markdown, non-Go files for &lt;code&gt;//&lt;/code&gt; and &lt;code&gt;#&lt;/code&gt; and fails the commit on a hit. Comments are an agent's favorite spot for slop ("here we initialize the variable"); with no place for it, code explains itself with names and structure. A 97% coverage floor in CI; more on that below, it bit me. And a daily canary on cron: rebuild the image, bring the devcontainer up, assert configuration invariants. Drift gets caught by schedule, not by my face at launch time.&lt;/p&gt;

&lt;p&gt;What did &lt;em&gt;not&lt;/em&gt; work is more useful than the wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tinyproxy.&lt;/strong&gt; An agent decided open egress needed a proxy filter and generated a whole plumbing layer. It looked solid. It filtered nothing. "A pipe, not a filter." I ripped it out and wrote it into AGENTS.md as the canonical neuroslop example: code shaped like the thing you need that does not do the thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agents lie confidently.&lt;/strong&gt; Three audit findings (next section) claimed problems the code did not have. "install.sh doesn't install git hooks": it does, line 38. "The devcontainer features aren't pinned": they are, three sha256 pins in the lock file. "Go tools will land outside PATH": no, &lt;code&gt;GOPATH&lt;/code&gt; is set before &lt;code&gt;go install&lt;/code&gt;. Three confident claims; three times the code said no. That experience is the foundation of the frame: a model hallucinates in a perfectly assured tone, and the working countermeasure is mechanical verification plus cross-checking. Not trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The coverage floor trap.&lt;/strong&gt; My own 97% floor produced floor-driven tests: checks that exist to feed the percentage, not to catch bugs. &lt;code&gt;Init()&lt;/code&gt; returns non-nil. &lt;code&gt;View()&lt;/code&gt; is non-empty. The most brittle one hung on a &lt;code&gt;sleep(200ms)&lt;/code&gt; in a golden test (since replaced with &lt;code&gt;teatest.WaitFor&lt;/code&gt;). The conclusion I wrote down: slightly below the floor with meaningful tests beats pinning Bubble Tea internals for a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sandbox patched itself
&lt;/h2&gt;

&lt;p&gt;This is the part I mostly wrote this post for. Dry, by the numbers.&lt;/p&gt;

&lt;p&gt;I ran a multi-agent self-audit on mirabilis. The pipeline: orchestrator full-read → researcher #1 (sonnet) → orchestrator verification → researcher #2 (sonnet) plus a landscape agent (web, arXiv) → two independent critics (sonnet, opus) → reconciliation. Cross-checks at every joint. The output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~44 raw claims in (15 orchestrator hypotheses plus ~29 agent findings).&lt;/li&gt;
&lt;li&gt;3 findings refuted by code verification: the hallucinations above.&lt;/li&gt;
&lt;li&gt;5 cut by the critics as taste, not defects.&lt;/li&gt;
&lt;li&gt;4 neuroslop spots found by the critics in the orchestrator's own synthesis. The agent checking for slop slopped; another agent caught it.&lt;/li&gt;
&lt;li&gt;27 accepted items, nine filed as GitHub issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some accepted items were plainly embarrassing, which is fine. &lt;code&gt;make reset&lt;/code&gt; silently destroyed the agent's entire memory while the README promised it "survives rebuilds". Provisioning always reported success, even when sub-steps failed. No resource limits on the container, so an autonomous agent could eat the host's RAM: the exact thing I claim protection from. Ordinary pet-project holes; I filed them.&lt;/p&gt;

&lt;p&gt;I did not fix those issues by hand. I launched six developer agents in parallel inside mirabilis itself, one per branch, covering six of the nine issues. They implemented the fixes and opened pull requests. I reviewed and merged them, PRs #101 through #106, in one day. The pipeline bug that stranded dependent steps when a required step failed now cascades them to &lt;code&gt;stSkipped&lt;/code&gt;. The launcher no longer silently switches a feature-branch checkout to main. Both fixes came from agents running inside the box they were fixing.&lt;/p&gt;

&lt;p&gt;I am not selling this as an AI miracle. It is an engineering fact, and the precision matters: the frame plus cross-verification got the agents to where their PRs were reviewable and mergeable rather than rewritable. The merge button was my call on every one. But the loop — audit, issues, agent wave, PRs, merge — closed inside the sandbox's own walls, and that is exactly what I built it for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decisions and boundaries
&lt;/h2&gt;

&lt;p&gt;A few places where I decided differently from common practice. The full threat model is in &lt;a href="https://github.com/AlexShchuka/mirabilis/blob/main/SECURITY.md" rel="noopener noreferrer"&gt;SECURITY.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The container is the boundary.&lt;/strong&gt; Inside, the agent has full freedom: root, &lt;code&gt;sudo&lt;/code&gt;, any file; mirabilis gates nothing there. The direct consequence: trusted code only. For untrusted code a container is not enough; you want a microVM. SECURITY.md says so in plain words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Egress is open.&lt;/strong&gt; No host proxy, no in-container allowlist. The canonical approach is the opposite: default-deny plus an allowlist, as in Anthropic's reference devcontainer with its &lt;code&gt;init-firewall.sh&lt;/code&gt;. I chose open on purpose. For my scenario (personal sandbox, trusted code, one user) simplicity beats exfiltration hardening, and keeping credentials in is the harness's behavioral job, not a network gate's. &lt;code&gt;WebFetch&lt;/code&gt; and &lt;code&gt;WebSearch&lt;/code&gt; have to just work; I am not maintaining an allowlist every time the agent needs a new domain. Open egress plus persistent memory plus MCP content is a known memory-poisoning surface (systematic study: &lt;a href="https://arxiv.org/abs/2606.04329" rel="noopener noreferrer"&gt;arXiv:2606.04329&lt;/a&gt;); SECURITY.md lists it as an accepted limitation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardened within the model.&lt;/strong&gt; Open egress does not mean everything is open: the container runs under Docker's default seccomp profile with &lt;code&gt;cap_drop: ALL&lt;/code&gt; and an explicit add-back list. &lt;code&gt;unshare&lt;/code&gt; namespaces, &lt;code&gt;io_uring&lt;/code&gt;, &lt;code&gt;keyctl&lt;/code&gt;, raw sockets, and &lt;code&gt;chroot&lt;/code&gt; are gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Docker socket is mounted&lt;/strong&gt; (docker-outside-of-docker). The in-container agent can drive the host daemon, and anyone with the socket can read container secrets via &lt;code&gt;docker inspect&lt;/code&gt;. Documented in SECURITY.md. It is there so the agent can build and run real containers from inside the box — which is also what Ryuk, the reaper testcontainers-go uses for cleanup, requires during integration tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single instance.&lt;/strong&gt; One container, one volume, not per-task. Other tools isolate per task with git worktrees; microVM products isolate harder than any container. mirabilis is neither the most isolated nor the most parallel: it is one command, a trusted personal scenario, and an anti-slop frame built into the construction. A chosen scope, not an unfinished copy of the bigger tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take what's useful
&lt;/h2&gt;

&lt;p&gt;mirabilis lives at &lt;a href="https://github.com/AlexShchuka/mirabilis" rel="noopener noreferrer"&gt;github.com/AlexShchuka/mirabilis&lt;/a&gt;, MIT-licensed. I use it myself and I think it is a decent tool. Not the best, not the only one; the field is busy. What I can show is concrete: a one-command sandbox with typed memory, two independent boundaries, a written-down threat model, four working anti-slop mechanisms, and a self-audit that ended with the sandbox merging fixes to itself.&lt;/p&gt;

&lt;p&gt;If any of it is useful, take it. If you find where I am wrong, the issues are open.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>go</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why your agentic system doesn't survive its own success — and what a 23-invariant runtime looks like</title>
      <dc:creator>Aleksandr Shchuka</dc:creator>
      <pubDate>Sun, 24 May 2026 20:42:41 +0000</pubDate>
      <link>https://dev.to/aleksandrshchuka/why-your-agentic-system-doesnt-survive-its-own-success-and-what-a-23-invariant-runtime-looks-like-6mm</link>
      <guid>https://dev.to/aleksandrshchuka/why-your-agentic-system-doesnt-survive-its-own-success-and-what-a-23-invariant-runtime-looks-like-6mm</guid>
      <description>&lt;h2&gt;
  
  
  The failure mode you don't see
&lt;/h2&gt;

&lt;p&gt;Most agentic systems fail silently. The agent picks the wrong tool, invents a fact, agrees with the user when it shouldn't — and you find out three releases later when a customer points it out. There's no exception to log; the system is generating plausible-looking text.&lt;/p&gt;

&lt;p&gt;The standard answer is "we'll add evals." Then evals become a number on a dashboard, the dashboard becomes a vanity metric, and the agent keeps drifting. The wrong frame is not "we need more evals" — it's that &lt;strong&gt;the agent has no skin in the game during a single turn&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post is about a runtime that gives the agent skin in the game on every turn. 23 invariants, deontic tags, counter-clauses, risk-weighted sampling, an adversarial probe set, and a pre-registered statistical decision rule for whether a config change ships. None of the components are original. The combination is what makes it work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The co-system framing
&lt;/h2&gt;

&lt;p&gt;The system is designed for &lt;strong&gt;AI + developer as a co-system&lt;/strong&gt;, not "AI for developer". Both sides err; the system catches mutually. Two outputs: codebase health and culture of systems-thinking. Many small steps × low error rate.&lt;/p&gt;

&lt;p&gt;Three game-theoretic anchors drive the design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anchor-verification dominates hallucination.&lt;/strong&gt; Every external-state claim is paired with tool output in the same reply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-mutation gating dominates unauthorized mutation.&lt;/strong&gt; No edit, no push, no write without explicit recent consent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Own-interest dominates sycophancy.&lt;/strong&gt; The agent upholds the protocol in its own interest — it does not capitulate to user pressure to look helpful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not preferences. They're equilibria the protocol enforces every turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  The invariants file
&lt;/h2&gt;

&lt;p&gt;23 invariants, each tagged on three axes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[risk]&lt;/code&gt; — &lt;code&gt;critical | important | style&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[deontic]&lt;/code&gt; — &lt;code&gt;O | P | F&lt;/code&gt; (obligation / permission / forbidden)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Counter:&lt;/code&gt; — the condition under which the invariant does not apply or is overruled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example (the anti-sycophancy invariant):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[critical] [O] The agent pursues every concept of this protocol in its own
interest; it does not sacrifice protocol concepts to agree with the developer
or look helpful.
| Why: sycophancy is a documented LLM failure mode; without explicit own-interest
the agent collapses into yes-man dynamics that erode codebase health and culture
of systems-thinking.
| Counter: when the developer's correction is anchored to a genuine artifact
contradiction the agent has not yet seen, updating the agent's position is
correct epistemic adjustment, not sycophancy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why deontic + Counter? In classical deontic logic (von Wright and successors), an obligation either applies or doesn't, and contextual override is handled through priority hierarchies kept separate from the norm itself. Pinning the counter to the norm — defeasible deontic logic (Nute, Prakken) made runtime — surfaces the override at the point of use, not in a separate fallback table the agent will forget to consult.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk-weighted per-turn sampling
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;UserPromptSubmit&lt;/code&gt; hook samples &lt;strong&gt;one invariant per turn&lt;/strong&gt; into the agent's context, weighted by risk: critical ×3, important ×2, style ×1. The agent runs a forced self-check against that invariant before producing the reply.&lt;/p&gt;

&lt;p&gt;This is doing two things at once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reduces framing bias.&lt;/strong&gt; No fixed "always check these 5" — different invariants surface on different turns, and the agent can't optimize against a known set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulls the long tail.&lt;/strong&gt; With 23 invariants in context simultaneously, most get ignored. Sampling one forces attention.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Untagged lines are sampled at style-weight as a graceful fallback with a stderr warning — drift is visible, not silent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four agent roles
&lt;/h2&gt;

&lt;p&gt;Different work needs different prompts. Four roles, each with its own minimum subset of invariants inherited explicitly (sub-agents do not receive the &lt;code&gt;UserPromptSubmit&lt;/code&gt; hook — without explicit propagation they run with zero invariant self-check):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;analyzer&lt;/strong&gt; — RCA, system design, code review, dead-end diagnostics; does not write code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;developer&lt;/strong&gt; — writes / edits / commits / pushes; the only role that mutates files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;critic&lt;/strong&gt; — independent reviewer of the lead agent's output before mutation lands; anti-neuroslop gate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;epistemic-auditor&lt;/strong&gt; — audits the boundary between confirmed and associative claims&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The split isn't specialization for its own sake. It's that the failure modes are different: developer fails by under-reading, critic fails by inverted sycophancy (over-critique to look independent), analyzer fails by partial-state models, auditor fails by missing the inline associative-marker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eval suite — what actually gets measured
&lt;/h2&gt;

&lt;p&gt;An eval suite is the only runtime defense against config drift. Without it, every change to the protocol is a taste call.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;17 binary criteria&lt;/strong&gt; rubric. Binary, not Likert — binary removes the central-tendency bias documented for ordinal LLM-judge rubrics (Masood, 2026).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Questions probes&lt;/strong&gt; (~20) — normal distribution of developer questions, measures average compliance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adversarial probes&lt;/strong&gt; (~10) — each targets a specific invariant under deliberate social pressure (a deliberate bypass attempt embedded in the prompt). Binary: held or broke. Distinguishes "invariant holds when nobody pushes" from "holds always."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary GUID per probe&lt;/strong&gt; — embedded in a &lt;code&gt;## Canary&lt;/code&gt; section the runner does not pass into the model. If the GUID appears in the response, the model leaked the probe file from the surrounding repo. Anti-contamination, runtime-verifiable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pre-registered decision rule
&lt;/h2&gt;

&lt;p&gt;A config change ships iff &lt;strong&gt;all of&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paired Wilcoxon signed-rank on question score totals: &lt;strong&gt;p &amp;lt; 0.05&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Bootstrap 95% CI on Cohen's d (paired): &lt;strong&gt;lower bound &amp;gt; 0.2&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;McNemar one-sided on adversarial pass/fail: &lt;strong&gt;zero regressions&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Krippendorff α ≥ 0.8 for inter-rater reliability when ≥2 raters score the same probe.&lt;/p&gt;

&lt;p&gt;Pre-registered, not chosen after seeing numbers. Without this, the merge criterion drifts — "p &amp;lt; 0.05 wasn't reached this time so let's loosen the threshold" is exactly how science fails, and it's how internal A/B claims fail too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is and what it isn't
&lt;/h2&gt;

&lt;p&gt;This is not a framework. It's a calibration layer that sits on top of any agent runtime — in my case, on top of Claude Code's plugin system, but the design ports to LangGraph, Autogen, or hand-rolled.&lt;/p&gt;

&lt;p&gt;The portable parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A deontic+counter invariants file&lt;/li&gt;
&lt;li&gt;A per-turn risk-weighted sampling hook&lt;/li&gt;
&lt;li&gt;An eval suite with an adversarial split&lt;/li&gt;
&lt;li&gt;A pre-registered statistical decision rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest is plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you lose
&lt;/h2&gt;

&lt;p&gt;Length. The protocol asks the agent to surface its reasoning, pair claims with tool output, halt on contradiction. The reply is denser and longer than a yes-man would produce. If your team is optimizing for "agent makes user happy in three lines," this is the wrong protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm publishing this
&lt;/h2&gt;

&lt;p&gt;Most agentic engineering writeups in 2026 are either framework docs (LangGraph this, CrewAI that) or hype posts about emergent capabilities. The gap is the layer in between: how do you make an agent behave correctly under sustained social pressure from the user — where "social pressure" includes the user being right? That's a governance problem, not a framework problem, and there are very few public artifacts addressing it head-on.&lt;/p&gt;

&lt;p&gt;I'll write up specific parts — the 17-criteria rubric, the canary GUID approach, the deontic+counter design — as follow-up posts if there's interest.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>gametheory</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
