<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Tappers Tech Blog]]></title><description><![CDATA[Empirical benchmarks, architecture deep dives, and practical patterns for AI Coding Agents & Harness Engineering — verified inside isolated Proxmox & systemd sa]]></description><link>https://tappers.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 02 Nov 2026 15:59:32 GMT</lastBuildDate><atom:link href="https://tappers.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Benchmarking the Claude Code /advisor Pattern: Solo Executor Loops vs. Upfront Advisor Blueprint Routing]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E18 (Ranked #7 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e18-advisor-executor-routing</link><guid isPermaLink="true">https://tappers.hashnode.dev/e18-advisor-executor-routing</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[architecture]]></category><category><![CDATA[Harness]]></category><category><![CDATA[Benchmark]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sun, 01 Nov 2026 12:53:11 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment <code>E18</code> (Ranked #7 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <strong><a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">View All 41 Benchmark Results &amp; Master Ranking</a></strong></li>
<li>🛡️ <strong><a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</a></strong></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E18</code> (Backlog Candidate <code>C014</code>)</li>
<li><strong>Run ID:</strong> <code>e18-20261028-vm222-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM222</code> (<code>ai-e18-222</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>)</li>
<li><strong>Pattern Under Test:</strong> Claude Code <code>/advisor</code> &amp; Upfront Architectural Routing — <em>"Solo Executor Trial-and-Error Loop vs. Upfront Advisor Blueprint + Executor One-Shot"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>When an AI coding agent is tasked with fixing a bug that spans multiple coupled modules, running a single worker loop without upfront architectural guidance (<strong>Solo Executor Loop</strong>) often leads to <strong>myopic Whack-a-Mole patching</strong>: the agent edits Module 1, runs a trial, discovers a downstream contract mismatch in Module 2, patches Module 2, and repeats—accumulating trial logs and regenerating code across multiple turns.</p>
<p>Anthropic's Claude Code <code>/advisor</code> pattern (pairing a high-level architectural Advisor memo with a fast Executor) proposes a cleaner division of labor: consult the <strong>Advisor</strong> once at the start of the task to produce a concise cross-module <strong>Architectural Blueprint Memo</strong>, then let the <strong>Executor</strong> implement all coupled files in a single pass.</p>
<p>To test this pattern empirically, we benchmarked our 3-module payment settlement pipeline (<code>validator.py</code>, <code>settlement.py</code>, <code>statement.py</code>) inside isolated Proxmox VM <code>VM222</code> across two modes:</p>
<ul>
<li><strong>Mode A (<code>SoloExecutorLoopRunner</code>):</strong> No Advisor consultation (<code>advisor_calls = 0</code>). The Executor runs an initial trial (<code>run_solo_trial</code>), applies piecemeal patches (<code>stage_executor_patch</code> on Turn 2 and Turn 3), and finally submits all 3 modules on Turn 4 (<code>4</code> LLM calls, <code>5</code> file edits).</li>
<li><strong>Mode B (<code>AdvisorExecutorRoutingRunner</code>):</strong> Calls <code>consult_advisor</code> on Turn 1 (<code>advisor_calls = 1</code>) to retrieve a <strong><code>1,143-character</code> 5-point cross-module execution blueprint</strong>, allowing the Executor to implement and submit all 3 modules in one shot on Turn 2 (<code>2</code> LLM calls, <code>3</code> file edits).</li>
</ul>
<h3>Key Result</h3>
<p>Injecting a <strong><code>1,143-character</code> upfront Advisor blueprint</strong> eliminated <strong>all unplanned partial-patch retries (<code>2 ➔ 0</code>)</strong>, halved the number of LLM calls (<code>4 ➔ 2</code>), cut cumulative input tokens by <strong><code>-84.9%</code> (<code>27,699</code> ➔ <code>4,172</code> tokens, <code>6.64x</code> efficiency)</strong>, reduced cumulative output tokens by <strong><code>-45.9%</code> (<code>2,897</code> ➔ <code>1,568</code> tokens)</strong>, lowered API cost by <strong><code>-71.5%</code> (<code>$0.0316</code> ➔ <code>$0.0090</code>)</strong>, and sped up execution by <strong><code>1.78x</code> (<code>33.45s</code> ➔ <code>18.78s</code>)</strong>—with both modes passing <strong><code>20 / 20</code> (100%)</strong> on the hidden grading suite.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Solo Executor Loop (<code>SoloExecutorLoop</code>)</th>
<th align="center">Mode B: Advisor-Executor Routing (<code>AdvisorExecutorRouting</code>)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>20 / 20</code> (100%)</td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
<td align="center">Both passed all 20 cases</td>
</tr>
<tr>
<td align="left"><strong>Unplanned Partial-Patch Retries (<code>unplanned_retries</code>)</strong></td>
<td align="center"><code>2 retries</code> (Turn 2, Turn 3)</td>
<td align="center"><strong><code>0 retries</code> (One-shot implementation)</strong></td>
<td align="center"><strong>-100% (Zero rework)</strong></td>
</tr>
<tr>
<td align="left"><strong>Total File Edits (<code>file_edits</code>)</strong></td>
<td align="center"><code>5 edits</code></td>
<td align="center"><strong><code>3 edits</code> (1 per module)</strong></td>
<td align="center"><strong>-40.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (<code>model_calls</code>)</strong></td>
<td align="center"><code>4 calls</code></td>
<td align="center"><strong><code>2 calls</code> (1 Advisor + 1 Executor)</strong></td>
<td align="center"><strong>-50.0% fewer calls</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Tool Output (<code>tool_output_chars</code>)</strong></td>
<td align="center"><code>22,720 chars</code></td>
<td align="center"><strong><code>1,245 chars</code></strong></td>
<td align="center"><strong>-94.5% (18.25x less log noise)</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 1 Input / Output Tokens</strong></td>
<td align="center"><code>1,909</code> in / <code>10</code> out</td>
<td align="center"><strong><code>1,846</code> in / <code>8</code> out</strong></td>
<td align="center">Equivalent initial invocation</td>
</tr>
<tr>
<td align="left"><strong>Turn 2 Input / Output Tokens</strong></td>
<td align="center"><code>5,000</code> in / <code>488</code> out (<code>validator.py</code> patch)</td>
<td align="center"><strong><code>2,326</code> in / <code>1,560</code> out (All 3 modules done!)</strong></td>
<td align="center"><strong>Mode B finishes on Turn 2</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 3 Input / Output Tokens</strong></td>
<td align="center"><code>8,527</code> in / <code>795</code> out (<code>settlement.py</code> patch)</td>
<td align="center">— <em>(Completed on Turn 2)</em></td>
<td align="center">Avoided intermediate retry turn</td>
</tr>
<tr>
<td align="left"><strong>Turn 4 Input / Output Tokens</strong></td>
<td align="center"><code>12,263</code> in / <code>1,604</code> out (Final submit)</td>
<td align="center">— <em>(Completed on Turn 2)</em></td>
<td align="center">Avoided <code>12,263</code>-token final prompt</td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (<code>promptTokenCount</code>)</strong></td>
<td align="center"><code>27,699 tokens</code></td>
<td align="center"><strong><code>4,172 tokens</code></strong></td>
<td align="center"><strong>-84.9% (6.64x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (<code>candidatesTokenCount</code>)</strong></td>
<td align="center"><code>2,897 tokens</code></td>
<td align="center"><strong><code>1,568 tokens</code></strong></td>
<td align="center"><strong>-45.9% (No duplicate code output)</strong></td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (<code>elapsed_seconds</code>)</strong></td>
<td align="center"><code>33.45 sec</code></td>
<td align="center"><strong><code>18.78 sec</code></strong></td>
<td align="center"><strong>-43.9% (1.78x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (<code>estimated_micro_usd</code>)</strong></td>
<td align="center"><code>$0.0316</code> (<code>31,640 micro-USD</code>)</td>
<td align="center"><strong><code>$0.0090</code> (<code>9,010 micro-USD</code>)</strong></td>
<td align="center"><strong>-71.5% (3.51x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture: Solo Trial Loop vs. Advisor Blueprint Routing</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM222 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Target Task: 3-Module Settlement Pipeline Bugfix                       |
|               (validator.py / settlement.py / statement.py)             |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: SoloExecutorLoopRunner (No Upfront Design Blueprint)      |  |
|  |  - Turn 1 (1,909 in):  run_solo_trial (+7,500 chars trial log)    |  |
|  |  - Turn 2 (5,000 in):  stage_executor_patch (validator.py only)   |  |
|  |  - Turn 3 (8,527 in):  stage_executor_patch (settlement.py only)  |  |
|  |  - Turn 4 (12,263 in): submit_settlement_bundle (All 3 modules)   |  |
|  |  --&gt; Total: 27,699 in / 2,897 out / 5 file edits / 33.45 sec      |  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: AdvisorExecutorRoutingRunner (Claude Code /advisor Style) |  |
|  |  - Turn 1 (1,846 in / 8 out):                                     |  |
|  |      Calls `consult_advisor` --&gt; Receives 1,143-char 5-point      |  |
|  |      cross-module `one_shot_execution_blueprint`                  |  |
|  |  - Turn 2 (2,326 in / 1,560 out):                                 |  |
|  |      Executor implements &amp; submits all 3 modules in 1 shot!       |  |
|  |  --&gt; Total: 4,172 in / 1,568 out / 3 file edits / 18.78 sec       |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<hr />
<h2>3. Why Advisor-Executor Routing Beats Both Solo Loops and Role Subagents</h2>
<h3>3.1 The Dual Token Penalty of Solo Trial-and-Error (<code>Mode A</code>)</h3>
<p>Analyzing <code>usage.json</code> and <code>execution-traces.json</code> shows why piecemeal patching without an upfront architectural plan is so expensive:</p>
<ol>
<li><strong>Input Token Snowballing:</strong> Every time Mode A staged a partial single-file patch (<code>validator.py</code> on Turn 2, <code>settlement.py</code> on Turn 3), the harness ran a trial diagnostic that appended roughly <code>7,500 characters</code> of output to the chat history (<code>1,909</code> ➔ <code>5,000</code> ➔ <code>8,527</code> ➔ <code>12,263</code> input tokens).</li>
<li><strong>Duplicate Output Token Generation (<code>+84.8%</code> extra output):</strong> Because Mode A generated <code>validator.py</code> on Turn 2 (<code>488</code> output tokens), <code>settlement.py</code> on Turn 3 (<code>795</code> output tokens), and then re-emitted all 3 modules in the final Turn 4 bundle (<code>1,604</code> output tokens), it generated <strong>2,897 output tokens</strong> compared to Mode B's <strong>1,568 output tokens</strong>. Since LLM output tokens cost 3x to 5x more per token than input tokens, avoiding duplicate code generation directly slashes API cost and latency.</li>
</ol>
<h3>3.2 How <code>E18</code> (Advisor-Executor) Differs from <code>E05</code> (Role Subagents)</h3>
<p>A natural question is: <em>"In <code>E05</code> (Subagent Folding), you showed that splitting a task across Architect ➔ Coder ➔ QA subagents was 2.66x slower. Why did <code>E18</code> (Advisor-Executor) succeed?"</em></p>
<p>The difference lies in <strong>who holds the context and how the handoff works</strong>:</p>
<ul>
<li>In <strong><code>E05</code> (Role Subagent Handoff — Anti-Pattern)</strong>, the Architect subagent ran in an isolated context and passed a <strong>lossy natural-language summary</strong> to the Coder, hiding the original codebase and dropping fine-grained rules (like signed zero <code>-0</code>).</li>
<li>In <strong><code>E18</code> (Advisor Blueprint Injection — Golden Pattern)</strong>, the Executor retains <strong>direct visibility into the actual source files</strong>, and the Advisor injects a <strong>concise <code>1,143-character</code> checklist of cross-module invariants</strong> (<code>one_shot_execution_blueprint</code>) directly into Turn 1 before the Executor writes code. Nothing is lost in translation, and the Executor completes all 3 files in a single pass.</li>
</ul>
<hr />
<h2>4. All 20 Hidden Grading Suite Results (<code>labjudge18</code>)</h2>
<p>Both modes passed all 20 hidden unit and E2E test cases verified by <code>labjudge18</code>:</p>
<table>
<thead>
<tr>
<th align="center">#</th>
<th align="left">Test Case ID</th>
<th align="left">Invariant / Boundary Verified</th>
<th align="center">Mode A (<code>Solo Loop</code>)</th>
<th align="center">Mode B (<code>Advisor-Executor</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1–6</td>
<td align="left"><code>validator_01</code> to <code>validator_06</code></td>
<td align="left">Trim/uppercase, non-mutation, <code>JPY</code>, <code>bool</code> rejection, <code>0</code> cents &amp; <code>1000</code> bps bounds</td>
<td align="center"><code>6 / 6</code> PASS</td>
<td align="center"><strong><code>6 / 6</code> PASS</strong></td>
</tr>
<tr>
<td align="center">7–15</td>
<td align="left"><code>settlement_01</code> to <code>settlement_09</code></td>
<td align="left">First-seen order, conflict check, <code>300 vs 301</code> bps, <code>14999 vs 15000</code> tier, rebate cap</td>
<td align="center"><code>9 / 9</code> PASS</td>
<td align="center"><strong><code>9 / 9</code> PASS</strong></td>
</tr>
<tr>
<td align="center">16–20</td>
<td align="left"><code>e2e_01</code> to <code>e2e_05</code></td>
<td align="left"><code>#</code> separator, signed zero <code>-0</code>, <code>$0</code> auth statement, line order, error propagation</td>
<td align="center"><code>5 / 5</code> PASS</td>
<td align="center"><strong><code>5 / 5</code> PASS</strong></td>
</tr>
<tr>
<td align="center"><strong>Total</strong></td>
<td align="left"><strong>20 Hidden Cases</strong></td>
<td align="left">—</td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>5. Practical Takeaway: Inject an Architectural Blueprint Before First Edit</h2>
<p>When using Claude Code (<code>/advisor</code>), Antigravity (<code>/plan</code>), or your own custom agent harness on multi-file tasks:</p>
<ol>
<li><strong>Never let an Executor start editing File 1 before mapping File 2 and File 3's contracts:</strong>
Piecemeal file-by-file trial loops double your output token generation and bloat input history with intermediate failure logs.</li>
<li><strong>Generate a 5-bullet Cross-Module Blueprint on Turn 1:</strong>
Have your Advisor model (or planning step) output a compact <code>&lt;1,500-character</code> contract checklist specifying the exact data types, boundary inequalities (<code>&gt;</code> vs. <code>&gt;=</code>), and formatting invariants across all affected files before unlocking file edit tools.</li>
</ol>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (<code>E20</code>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
<li>🥈 <strong>Rank #2 Deep Dive (<code>E21</code>):</strong> <a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons</a></li>
<li>🥉 <strong>Rank #3 Deep Dive (<code>E16</code>):</strong> <a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">Stop Dumping Raw Pytest Logs Into Your Coding Agent: How a Structured JSON Test Filter Cuts Input Tokens by 94.1%</a></li>
<li>🌿 <strong>Rank #4 Deep Dive (<code>E19</code>):</strong> <a href="https://tappers.hashnode.dev/e19-error-trace-pruning">Why Keeping Failed Code Attempts in Chat History Poisons Your Coding Agent: Error-Trace Pruning vs. Accumulated History</a></li>
<li>🔍 <strong>Rank #5 Deep Dive (<code>E14</code>):</strong> <a href="https://tappers.hashnode.dev/e14-progressive-tool-search">The Hidden "Schema Tax" of MCP Tools: Dumping 32 Tool Schemas vs. On-Demand Tool Search (-92.7% Input Tokens)</a></li>
<li>🗜️ <strong>Rank #6 Deep Dive (<code>E12</code>):</strong> <a href="https://tappers.hashnode.dev/e12-milestone-context-compaction">How Save-Game Context Compaction Fixes Spec Drift in Multi-Step Coding Agents (-91.9% Input Tokens)</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How Save-Game Context Compaction Fixes Spec Drift in Multi-Step Coding Agents (-91.9% Input Tokens)]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E12 (Ranked #6 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e12-milestone-context-compaction</link><guid isPermaLink="true">https://tappers.hashnode.dev/e12-milestone-context-compaction</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[coding]]></category><category><![CDATA[Harness]]></category><category><![CDATA[Benchmark]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sun, 01 Nov 2026 06:15:05 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment <code>E12</code> (Ranked #6 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <strong><a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">View All 41 Benchmark Results &amp; Master Ranking</a></strong></li>
<li>🛡️ <strong><a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</a></strong></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E12</code> (Backlog Candidate <code>C008</code>)</li>
<li><strong>Run ID:</strong> <code>e12-20261028-vm216-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM216</code> (<code>ai-e12-216</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>)</li>
<li><strong>Pattern Under Test:</strong> Save-Game Context Compaction (<code>/compact</code> at Milestones) — <em>"Unbounded History Accumulation vs. Milestone JSON State Compaction"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>When an AI coding agent tackles a multi-step engineering task across several modules, keeping every prior file dump, telemetry log, and intermediate code output inside a single growing <code>messages[]</code> array (<strong>Unbounded Context Accumulation</strong>) causes two compounding problems:</p>
<ol>
<li><strong>Quadratic Token Inflation:</strong> Every past turn's tool output is re-billed on every subsequent turn (<code>6,556</code> ➔ <code>11,769</code> ➔ <code>17,544</code> input tokens).</li>
<li><strong>Mid-Task Spec Drift (Lost-in-the-Middle):</strong> By Turn 2 or Turn 3, fine-grained string normalization rules and boundary conditions buried in the middle of the history get drowned out by verbose telemetry logs—causing subtle bugs that break end-to-end integration tests.</li>
</ol>
<p>To test the solution—<strong>Milestone Context Compaction (the "Save-Game" Pattern)</strong>—we benchmarked a 3-milestone power-grid telemetry and carbon-credit clearing pipeline (Milestone 1: <code>collector.py</code> ➔ Milestone 2: <code>clearing.py</code> ➔ Milestone 3: <code>dispatch.py</code>) inside isolated Proxmox VM <code>VM216</code>:</p>
<ul>
<li><strong>Mode A (<code>AccumulatedHistoryRunner</code>):</strong> Appends all 3 milestone specifications, code outputs, and verbose telemetry audit dumps (<code>VERBOSE_TELEMETRY_AUDIT_DUMP</code>) into one continuous chat thread (<code>0</code> compactions).</li>
<li><strong>Mode B (<code>MilestoneCompactionRunner</code>):</strong> Immediately after each milestone passes its deterministic verification gate, executes a <code>/compact</code> reset—discarding raw logs and replacing the chat history with a <strong><code>120-token</code> structured JSON checkpoint (<code>SAVE_GAME_COMPACTION_V1</code>)</strong> plus only the next milestone's specification.</li>
</ul>
<h3>Key Result</h3>
<p>Mode A's accumulated history (<code>35,869</code> cumulative input tokens) suffered a classic <strong>Lost-in-the-Middle failure</strong> on Milestone 2 (<code>clearing.py</code>), forgetting to apply <code>.strip().upper()</code> to <code>contract_tier</code> and failing <strong>2 out of 20</strong> hidden E2E tests (<code>18 / 20</code>, 90.0%). Mode B's <strong>Save-Game Milestone Compaction</strong> kept every turn under <code>1,400</code> input tokens (<code>2,922</code> cumulative input tokens, <strong><code>-91.9%</code> reduction</strong>), cut API cost by <strong><code>-72.2%</code> (<code>$0.0340</code> ➔ <code>$0.0095</code>)</strong>, and achieved a flawless <strong><code>20 / 20</code> (100%) pass</strong>.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Accumulated History (<code>AccumulatedHistory</code>)</th>
<th align="center">Mode B: Milestone State Compaction (<code>MilestoneCompaction</code>)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>18 / 20</code> (90.0% — failed 2 E2E cases)</td>
<td align="center"><strong><code>20 / 20</code> (100% Perfect Pass)</strong></td>
<td align="center"><strong>+10.0 pt (Zero spec drift)</strong></td>
</tr>
<tr>
<td align="left"><strong>Context Compactions (<code>compactions</code>)</strong></td>
<td align="center"><code>0 compactions</code></td>
<td align="center"><strong><code>2 compactions</code> (After M1 &amp; M2)</strong></td>
<td align="center">Reset history at each milestone gate</td>
</tr>
<tr>
<td align="left"><strong>Turn 1 Input Tokens (M1: <code>collector.py</code>)</strong></td>
<td align="center"><code>6,556 tokens</code> (<code>$0.00725</code>)</td>
<td align="center"><strong><code>650 tokens</code> (<code>$0.00290</code>)</strong></td>
<td align="center"><strong>-90.1% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 2 Input Tokens (M2: <code>clearing.py</code>)</strong></td>
<td align="center"><code>11,769 tokens</code> (<code>$0.01247</code>)</td>
<td align="center"><strong><code>1,388 tokens</code> (<code>$0.00461</code>)</strong></td>
<td align="center"><strong>-88.2% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 3 Input Tokens (M3: <code>dispatch.py</code>)</strong></td>
<td align="center"><code>17,544 tokens</code> (<code>$0.01429</code>)</td>
<td align="center"><strong><code>884 tokens</code> (<code>$0.00195</code>)</strong></td>
<td align="center"><strong>-95.0% reduction on final turn</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (<code>promptTokenCount</code>)</strong></td>
<td align="center"><code>35,869 tokens</code></td>
<td align="center"><strong><code>2,922 tokens</code></strong></td>
<td align="center"><strong>-91.9% (12.28x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (<code>candidatesTokenCount</code>)</strong></td>
<td align="center"><code>1,894 tokens</code></td>
<td align="center"><strong><code>1,937 tokens</code></strong></td>
<td align="center">Equivalent code generation volume</td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (<code>model_calls</code>)</strong></td>
<td align="center"><code>3 calls</code></td>
<td align="center"><strong><code>3 calls</code> (1 per milestone)</strong></td>
<td align="center">Identical call count</td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (<code>elapsed_seconds</code>)</strong></td>
<td align="center"><code>24.81 sec</code></td>
<td align="center"><strong><code>22.52 sec</code></strong></td>
<td align="center"><strong>-9.2% (1.10x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (<code>estimated_micro_usd</code>)</strong></td>
<td align="center"><code>$0.0340</code> (<code>34,005 micro-USD</code>)</td>
<td align="center"><strong><code>$0.0095</code> (<code>9,457 micro-USD</code>)</strong></td>
<td align="center"><strong>-72.2% (3.60x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture: Unbounded History vs. Save-Game Compaction</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM216 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Target Task: 3-Milestone Power Grid &amp; Carbon Clearing Pipeline         |
|               (M1: collector.py --&gt; M2: clearing.py --&gt; M3: dispatch.py)|
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: AccumulatedHistoryRunner (No Compaction)                  |  |
|  |  - Turn 1 (M1): 6,556 in  --&gt; collector.py PASS                   |  |
|  |  - Turn 2 (M2): 11,769 in --&gt; Audit logs drown out M2 spec!       |  |
|  |                               Forgets contract_tier.strip().upper()|  |
|  |  - Turn 3 (M3): 17,544 in --&gt; Cumulative 35,869 input tokens      |  |
|  |  --&gt; Final Score: 18 / 20 (Crashes on lowercase/padded tiers!)    |  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: MilestoneCompactionRunner (Save-Game JSON Compaction)     |  |
|  |  - Turn 1 (M1): 650 in   --&gt; collector.py PASS                    |  |
|  |  - [Compaction #1]: Discards raw logs; writes 120-tok JSON state  |  |
|  |  - Turn 2 (M2): 1,388 in --&gt; Fresh context nails all M2 rules!    |  |
|  |  - [Compaction #2]: Updates verified_contracts in JSON checkpoint |  |
|  |  - Turn 3 (M3): 884 in   --&gt; Cumulative 2,922 input tokens (-91.9%)| |
|  |  --&gt; Final Score: 20 / 20 (100% Perfect Pass)                     |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<hr />
<h2>3. Code-Level Postmortem: How Unbounded History Caused a Lost-in-the-Middle Bug</h2>
<h3>3.1 Mode A (<code>AccumulatedHistory</code>): The Missing <code>.strip().upper()</code> on Turn 2</h3>
<p>Inspecting <code>accum-bundle.json</code> (Mode A's final submitted code) reveals a textbook example of <strong>Attention Dilution</strong>:</p>
<ul>
<li>On <strong>Turn 1 (<code>collector.py</code>)</strong>, Mode A properly implemented <code>.strip().upper()</code> normalization for <code>station_id</code> and <code>grid_zone</code>.</li>
<li>By <strong>Turn 2 (<code>clearing.py</code>)</strong>, however, 45 lines of <code>VERBOSE_TELEMETRY_AUDIT_DUMP_M1</code> plus the full Turn 1 code had bloated the prompt to <strong><code>11,769 tokens</code></strong>. Buried in the middle of the prompt was the requirement that <code>clearing.py</code> must also normalize <code>contract_tier</code> via <code>.strip().upper()</code>.</li>
<li>Mode A completely missed that normalization rule in <code>clearing.py</code>:</li>
</ul>
<pre><code class="language-python"># ❌ Mode A (accum-bundle.json — clearing.py generated at 11,769 input tokens)
def settle_grid(readings, contract_tier="BASE", credit_offset=0):
    if not isinstance(readings, list) or len(readings) == 0:
        raise ValueError("readings must be a non-empty list")
    # BUG: Forgot isinstance(contract_tier, str) and contract_tier.strip().upper()!
    if contract_tier not in ALLOWED_TIERS:
        raise ValueError(f"contract_tier must be one of {ALLOWED_TIERS}")
</code></pre>
<p>Because of that single omitted <code>.strip().upper()</code>, Mode A crashed with a <code>ValueError</code> on two hidden E2E integration tests that passed padded/lowercase tier strings (<code>contract_tier=" green "</code> in <code>e2e_01</code> and <code>contract_tier="hydro"</code> in <code>e2e_02</code>).</p>
<h3>3.2 Mode B (<code>MilestoneCompaction</code>): The Exact Save-Game JSON Checkpoint</h3>
<p>In Mode B, as soon as Milestone 1 (<code>collector.py</code>) passed verification, the harness wiped the raw conversation history and started Turn 2 with this compact <strong><code>SAVE_GAME_COMPACTION_V1</code> JSON checkpoint</strong>:</p>
<pre><code class="language-json">{
  "checkpoint_type": "SAVE_GAME_COMPACTION_V1",
  "completed_modules": ["collector.py"],
  "history_compacted": true,
  "milestone_completed": 1,
  "next_target_module": "clearing.py",
  "verified_contracts": {
    "collector.collect_readings": "PASS (returns list of dicts with station_id, grid_zone in NORTH/SOUTH/WEST, mwh_units&gt;0, rate_cents&gt;=0, 0&lt;=carbon_bps&lt;=1500, non-mutating)"
  }
}
</code></pre>
<p>Because Turn 2 ran in a fresh <strong><code>1,388-token</code></strong> context containing only the verified upstream contract and Milestone 2's specification, Mode B had zero attention dilution—properly implementing <code>clean_tier = contract_tier.strip().upper()</code> and passing all <strong>20 / 20</strong> test cases:</p>
<table>
<thead>
<tr>
<th align="left">Test Case Group</th>
<th align="left">Verification Target</th>
<th align="center">Mode A (<code>Accumulated</code>)</th>
<th align="center">Mode B (<code>Save-Game Compact</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>collector_01</code> – <code>collector_06</code></td>
<td align="left">Milestone 1: Non-mutation, type checks, bounds (<code>0..1500</code> bps)</td>
<td align="center"><code>6 / 6</code> PASS</td>
<td align="center"><strong><code>6 / 6</code> PASS</strong></td>
</tr>
<tr>
<td align="left"><code>clearing_01</code> – <code>clearing_08</code></td>
<td align="left">Milestone 2: First-seen order, <code>400 vs 401</code> bps levy, <code>12000</code> tier boundary</td>
<td align="center"><code>8 / 8</code> PASS</td>
<td align="center"><strong><code>8 / 8</code> PASS</strong></td>
</tr>
<tr>
<td align="left"><code>e2e_01_green_below_12000_minus_zero_and_at_separator</code></td>
<td align="left"><strong>E2E: Normalize <code>contract_tier=" green "</code> + <code>@</code> delimiter + <code>-0</code></strong></td>
<td align="center"><strong>❌ FAIL (<code>ValueError</code>)</strong></td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="left"><code>e2e_02_hydro_levy_and_credit_dispatch</code></td>
<td align="left"><strong>E2E: Normalize <code>contract_tier="hydro"</code> + 20% discount + credit offset</strong></td>
<td align="center"><strong>❌ FAIL (<code>ValueError</code>)</strong></td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="left"><code>e2e_03</code> – <code>e2e_06</code></td>
<td align="left">E2E: Zero rate cents, line order preservation, cross-stage error propagation</td>
<td align="center"><code>4 / 4</code> PASS</td>
<td align="center"><strong><code>4 / 4</code> PASS</strong></td>
</tr>
<tr>
<td align="left"><strong>Total</strong></td>
<td align="left"><strong>20 Hidden Cases</strong></td>
<td align="center"><strong><code>18 / 20</code> (90.0%)</strong></td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>4. Practical Takeaway: Compact to Structured State, Not Prose Summaries</h2>
<p>When building multi-step agent workflows (or using <code>/compact</code> in interactive coding agents):</p>
<ol>
<li><strong>Don't wait until the 200k context window is 95% full to compact:</strong>
Attention degradation (<code>Lost-in-the-Middle</code>) starts happening well above 10,000–15,000 tokens of noisy tool logs. Compact <strong>at every milestone boundary</strong> as soon as a module's tests turn green.</li>
<li><strong>Use deterministic JSON checkpoints instead of free-form prose summaries:</strong>
As we saw in <code>E05</code> (Subagent Folding), natural-language summaries often drop exact type contracts. Instead, record a structured JSON object containing <code>completed_modules</code>, <code>verified_contracts</code> (exact function signatures &amp; invariants), and <code>next_target_module</code>.</li>
</ol>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (<code>E20</code>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
<li>🥈 <strong>Rank #2 Deep Dive (<code>E21</code>):</strong> <a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons</a></li>
<li>🥉 <strong>Rank #3 Deep Dive (<code>E16</code>):</strong> <a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">Stop Dumping Raw Pytest Logs Into Your Coding Agent: How a Structured JSON Test Filter Cuts Input Tokens by 94.1%</a></li>
<li>🌿 <strong>Rank #4 Deep Dive (<code>E19</code>):</strong> <a href="https://tappers.hashnode.dev/e19-error-trace-pruning">Why Keeping Failed Code Attempts in Chat History Poisons Your Coding Agent: Error-Trace Pruning vs. Accumulated History</a></li>
<li>🔍 <strong>Rank #5 Deep Dive (<code>E14</code>):</strong> <a href="https://tappers.hashnode.dev/e14-progressive-tool-search">The Hidden "Schema Tax" of MCP Tools: Dumping 32 Tool Schemas vs. On-Demand Tool Search (-92.7% Input Tokens)</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Hidden "Schema Tax" of MCP Tools: Dumping 32 Tool Schemas vs. On-Demand Tool Search (-92.7% Input Tokens)]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E14 (Ranked #5 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e14-progressive-tool-search</link><guid isPermaLink="true">https://tappers.hashnode.dev/e14-progressive-tool-search</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[Harness]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sun, 01 Nov 2026 06:05:33 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment <code>E14</code> (Ranked #5 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <strong><a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">View All 41 Benchmark Results &amp; Master Ranking</a></strong></li>
<li>🛡️ <strong><a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</a></strong></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E14</code> (Backlog Candidate <code>C010</code>)</li>
<li><strong>Run ID:</strong> <code>e14-20261028-vm218-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM218</code> (<code>ai-e14-218</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>)</li>
<li><strong>Pattern Under Test:</strong> Claude Code <code>ENABLE_TOOL_SEARCH=auto</code> &amp; Lazy MCP Tool Loading — <em>"Upfront Monolithic Schema Dump (32 Tools) vs. Progressive On-Demand Tool Search"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>Connecting Model Context Protocol (MCP) servers—Kubernetes, AWS, GitHub, Datadog, PagerDuty, Stripe, Terraform—makes an AI coding agent vastly more capable, but it introduces two severe failure modes when all tool definitions are injected upfront:</p>
<ol>
<li><strong>The "Schema Tax":</strong> Detailed JSON Schemas average 700 to 800 tokens per tool. Injecting 32 MCP tool schemas into the system prompt forces you to pay <strong>25,000+ input tokens on every single turn</strong>—even before the agent reads a single file.</li>
<li><strong>Tool Confusion (Decoy Misfires):</strong> When 32 tool schemas sit flat in the context window, the model frequently calls similarly named deprecated or staging tools (e.g., <code>verify_legacy_v1_router</code> or <code>patch_staging_mirror_files</code>) instead of the active production tool.</li>
</ol>
<p>To quantify how <strong>Progressive Tool Schema Disclosure (<code>ENABLE_TOOL_SEARCH=auto</code>)</strong> solves both problems, we benchmarked a 3-module cloud incident triage and SLA remediation pipeline (<code>router.py</code>, <code>remediator.py</code>, <code>dossier.py</code>) inside isolated Proxmox VM <code>VM218</code>:</p>
<ul>
<li><strong>Mode A (<code>MonolithicSchema</code>):</strong> Injects all <strong>32 enterprise MCP tool JSON Schemas</strong> into the prompt on every turn.</li>
<li><strong>Mode B (<code>ProgressiveToolSearch</code>):</strong> Injects only <strong>1 lightweight meta-tool (<code>search_tools</code>, 120 tokens)</strong> on Turn 1. The agent queries <code>search_tools(query="remediation")</code> (<code>22</code> output tokens) to dynamically load <strong>only the 2 active production tool schemas</strong> needed for the task.</li>
</ul>
<h3>Key Result</h3>
<p>Replacing the 32-tool upfront schema dump with <code>search_tools</code> cut Turn 1 input tokens by <strong><code>-90.1%</code> (<code>25,646</code> ➔ <code>2,544</code>)</strong> and cumulative input tokens by <strong><code>-92.7%</code> (<code>79,773</code> ➔ <code>5,844</code> tokens, <code>13.65x</code> reduction)</strong>. Even more importantly, filtering out 28 irrelevant and 2 deprecated decoy tools reduced <strong>decoy tool misfires from <code>2 ➔ 0</code></strong>, cutting API cost by <strong><code>-84.8%</code> (<code>$0.0737</code> ➔ <code>$0.0112</code>)</strong> and execution time by <strong><code>-48.5%</code> (<code>1.94x</code> faster)</strong>.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Upfront Monolithic Dump (<code>MonolithicSchema</code>)</th>
<th align="center">Mode B: On-Demand Tool Search (<code>ProgressiveToolSearch</code>)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>20 / 20</code> (100%)</td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
<td align="center">Both passed all 20 cases</td>
</tr>
<tr>
<td align="left"><strong>Injected Tool Schemas per Turn</strong></td>
<td align="center"><code>32 schemas</code> (Every turn)</td>
<td align="center"><strong><code>1 schema</code> (Turn 1) ➔ <code>2 schemas</code> (Turn 2)</strong></td>
<td align="center"><strong>-93.8% schema reduction (28 tools isolated)</strong></td>
</tr>
<tr>
<td align="left"><strong>Decoy / Deprecated Tool Misfires</strong></td>
<td align="center"><code>2 misfires</code> (Turn 1, Turn 2)</td>
<td align="center"><strong><code>0 misfires</code> (Zero tool confusion)</strong></td>
<td align="center"><strong>-100% (Eliminated decoy calls)</strong></td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (<code>model_calls</code>)</strong></td>
<td align="center"><code>3 calls</code></td>
<td align="center"><strong><code>2 calls</code> (1 search + 1 submit)</strong></td>
<td align="center"><strong>-33.3% fewer calls</strong></td>
</tr>
<tr>
<td align="left"><strong>Total File Edits (<code>file_edits</code>)</strong></td>
<td align="center"><code>6 edits</code></td>
<td align="center"><strong><code>3 edits</code> (1 per module)</strong></td>
<td align="center"><strong>-50.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 1 Input Tokens</strong></td>
<td align="center"><code>25,646 tokens</code></td>
<td align="center"><strong><code>2,544 tokens</code></strong></td>
<td align="center"><strong>-90.1% first-turn savings</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (<code>promptTokenCount</code>)</strong></td>
<td align="center"><code>79,773 tokens</code></td>
<td align="center"><strong><code>5,844 tokens</code></strong></td>
<td align="center"><strong>-92.7% (13.65x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (<code>candidatesTokenCount</code>)</strong></td>
<td align="center"><code>3,686 tokens</code></td>
<td align="center"><strong><code>1,825 tokens</code> (Turn 1 used only <code>22</code> tok)</strong></td>
<td align="center"><strong>-50.5% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Total Tokens Consumed</strong></td>
<td align="center"><code>83,459 tokens</code></td>
<td align="center"><strong><code>7,669 tokens</code></strong></td>
<td align="center"><strong>-90.8% (10.88x overall reduction)</strong></td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (<code>elapsed_seconds</code>)</strong></td>
<td align="center"><code>37.00 sec</code></td>
<td align="center"><strong><code>19.06 sec</code></strong></td>
<td align="center"><strong>-48.5% (1.94x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (<code>estimated_micro_usd</code>)</strong></td>
<td align="center"><code>$0.0737</code> (<code>73,653 micro-USD</code>)</td>
<td align="center"><strong><code>$0.0112</code> (<code>11,228 micro-USD</code>)</strong></td>
<td align="center"><strong>-84.8% (6.56x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture &amp; Tool Discovery Workflow</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM218 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Enterprise MCP Tool Registry (32 Total Tools):                         |
|   - 2 Active Production Tools (verify_pipeline_suite, submit_bundle)    |
|   - 2 Deprecated / Staging Decoy Tools (legacy_v1, staging_mirror)      |
|   - 28 Unrelated Cloud/Infra MCP Tools (K8s, AWS, Stripe, Kafka, etc.)  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: MonolithicSchema (All 32 JSON Schemas Injected Every Turn)|  |
|  |  - Turn 1 (25,646 in): Calls decoy `verify_legacy_v1_router`      |  |
|  |  - Turn 2 (26,397 in): Calls decoy `patch_staging_mirror_files`   |  |
|  |  - Turn 3 (27,730 in): Calls `submit_remediation_bundle`          |  |
|  |  --&gt; Total: 79,773 input tokens / 2 decoy misfires / 37.00 sec    |  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: ProgressiveToolSearch (ENABLE_TOOL_SEARCH=auto)           |  |
|  |  - Turn 1 (2,544 in, 1 schema):                                   |  |
|  |      Calls `search_tools(query="remediation")` (22 output tokens) |  |
|  |      --&gt; Loads ONLY 2 active production schemas (filters 30 out)  |  |
|  |  - Turn 2 (3,300 in, 2 schemas):                                  |  |
|  |      Calls `submit_remediation_bundle` (All 3 files in 1 shot)    |  |
|  |  --&gt; Total: 5,844 input tokens / 0 decoy misfires / 19.06 sec     |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<hr />
<h2>3. Turn-by-Turn Token &amp; Tool Execution Breakdown</h2>
<p>Inspecting <code>usage.json</code> and <code>execution-traces.json</code> across all 5 LLM calls (<code>seq 0..4</code>) shows how the upfront schema tax compounds—and how on-demand search eliminates it:</p>
<table>
<thead>
<tr>
<th align="center">Seq</th>
<th align="left">Mode</th>
<th align="left">Node ID</th>
<th align="left">Tool Called</th>
<th align="center">Injected Schemas</th>
<th align="center">Decoy Misfire?</th>
<th align="center">Input Tokens</th>
<th align="center">Output Tokens</th>
<th align="center">Cost (<code>micro-USD</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><code>0</code></td>
<td align="left">Mode A</td>
<td align="left"><code>monolithic_schema_turn_1</code></td>
<td align="left"><code>verify_legacy_v1_router</code></td>
<td align="center"><code>32</code></td>
<td align="center"><strong>Yes (<code>True</code>)</strong></td>
<td align="center"><code>25,646</code></td>
<td align="center"><code>634</code></td>
<td align="center"><code>21,612</code> (<code>$0.0216</code>)</td>
</tr>
<tr>
<td align="center"><code>1</code></td>
<td align="left">Mode A</td>
<td align="left"><code>monolithic_schema_turn_2</code></td>
<td align="left"><code>patch_staging_mirror_files</code></td>
<td align="center"><code>32</code></td>
<td align="center"><strong>Yes (<code>True</code>)</strong></td>
<td align="center"><code>26,397</code></td>
<td align="center"><code>1,224</code></td>
<td align="center"><code>24,388</code> (<code>$0.0244</code>)</td>
</tr>
<tr>
<td align="center"><code>2</code></td>
<td align="left">Mode A</td>
<td align="left"><code>monolithic_schema_turn_3</code></td>
<td align="left"><code>submit_remediation_bundle</code></td>
<td align="center"><code>32</code></td>
<td align="center">No (<code>False</code>)</td>
<td align="center"><code>27,730</code></td>
<td align="center"><code>1,828</code></td>
<td align="center"><code>27,653</code> (<code>$0.0277</code>)</td>
</tr>
<tr>
<td align="center"><code>3</code></td>
<td align="left"><strong>Mode B</strong></td>
<td align="left"><strong><code>progressive_search_turn_1</code></strong></td>
<td align="left"><strong><code>search_tools</code> (<code>query="remediation"</code>)</strong></td>
<td align="center"><strong><code>1</code></strong></td>
<td align="center"><strong>No (<code>False</code>)</strong></td>
<td align="center"><strong><code>2,544</code></strong></td>
<td align="center"><strong><code>22</code></strong></td>
<td align="center"><strong><code>1,991</code> (<code>$0.0020</code>)</strong></td>
</tr>
<tr>
<td align="center"><code>4</code></td>
<td align="left"><strong>Mode B</strong></td>
<td align="left"><strong><code>progressive_search_turn_2</code></strong></td>
<td align="left"><strong><code>submit_remediation_bundle</code></strong></td>
<td align="center"><strong><code>2</code></strong></td>
<td align="center"><strong>No (<code>False</code>)</strong></td>
<td align="center"><strong><code>3,300</code></strong></td>
<td align="center"><strong><code>1,803</code></strong></td>
<td align="center"><strong><code>9,237</code> (<code>$0.0092</code>)</strong></td>
</tr>
</tbody></table>
<h3>Two Critical Takeaways from the Trace</h3>
<ol>
<li><strong>You Pay the Schema Tax on Every Turn—Not Just Turn 1:</strong>
In Mode A, having 32 detailed MCP schemas in the system prompt cost <strong><code>25,646</code> tokens</strong> before the agent wrote a single line of code. Because the agent needed 3 turns to recover from decoy tool calls, those exact same 32 schemas were billed three times—burning <strong>over <code>70,000</code> input tokens on unused JSON schema definitions alone</strong>.</li>
<li><strong>A 22-Token Search Call Is Faster Than a 0-Search Monolithic Prompt:</strong>
A common objection to lazy tool loading is <em>"Won't an extra <code>search_tools</code> round-trip slow the agent down?"</em> The empirical data proves the exact opposite: Mode B's Turn 1 generated just <strong><code>22</code> output tokens</strong> (<code>{"query": "remediation", "tool": "search_tools"}</code>), and because it avoided prefilling 25k tokens per turn and eliminated 2 decoy misfires, Mode B finished in <strong><code>19.06 seconds</code> vs. <code>37.00 seconds</code> (<code>1.94x</code> faster)</strong>.</li>
</ol>
<hr />
<h2>4. All 20 Hidden Grading Suite Results (<code>labjudge14</code>)</h2>
<p>Both modes achieved <strong><code>20 / 20</code> (100%)</strong> on the hidden grading suite verifying <code>router.py</code>, <code>remediator.py</code>, and <code>dossier.py</code>:</p>
<table>
<thead>
<tr>
<th align="center">#</th>
<th align="left">Test Case ID</th>
<th align="left">Invariant / Boundary Verified</th>
<th align="center">Mode A (<code>32 Schemas</code>)</th>
<th align="center">Mode B (<code>Tool Search</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1–6</td>
<td align="left"><code>router_01</code> to <code>router_06</code></td>
<td align="left"><code>service_id</code> / <code>severity</code> (<code>SEV1</code>–<code>SEV3</code>) normalization, non-mutation, <code>bool</code> rejection, <code>0</code> downtime allowed</td>
<td align="center"><code>6 / 6</code> PASS</td>
<td align="center"><strong><code>6 / 6</code> PASS</strong></td>
</tr>
<tr>
<td align="center">7–15</td>
<td align="left"><code>remediator_01</code> to <code>remediator_09</code></td>
<td align="left">First-seen order, conflict check, <code>300 vs 301</code> error bps surcharge, <code>15000</code> tier boundary, <code>sla_shield</code> cap</td>
<td align="center"><code>9 / 9</code> PASS</td>
<td align="center"><strong><code>9 / 9</code> PASS</strong></td>
</tr>
<tr>
<td align="center">16–20</td>
<td align="left"><code>e2e_01</code> to <code>e2e_05</code></td>
<td align="left"><code>#</code> separator, signed zero (<code>PLAN_COVERAGE: -0</code>), zero-downtime dossier, line order, error propagation</td>
<td align="center"><code>5 / 5</code> PASS</td>
<td align="center"><strong><code>5 / 5</code> PASS</strong></td>
</tr>
<tr>
<td align="center"><strong>Total</strong></td>
<td align="left"><strong>20 Hidden Cases</strong></td>
<td align="left">—</td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>5. Practical Takeaway: When and How to Enable Lazy Tool Loading</h2>
<ol>
<li><strong>Never inject more than 10 full JSON tool schemas upfront:</strong>
Once your connected MCP servers exceed 10–15 tools, switch to <strong>Progressive Tool Disclosure</strong> (such as <code>ENABLE_TOOL_SEARCH=auto</code> in Claude Code, or lazy MCP schema files loaded on demand via a <code>search_tools</code> / <code>call_mcp_tool</code> dispatcher).</li>
<li><strong>Filter out <code>DEPRECATED</code> and non-production tools inside the search index:</strong>
Instead of letting the LLM guess between <code>verify_legacy_v1_router</code> and <code>verify_pipeline_suite</code>, enforce <code>status == "PRODUCTION_ACTIVE"</code> filtering inside your <code>search_tools</code> handler so decoy tools never enter the context window.</li>
</ol>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (<code>E20</code>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
<li>🥈 <strong>Rank #2 Deep Dive (<code>E21</code>):</strong> <a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons</a></li>
<li>🥉 <strong>Rank #3 Deep Dive (<code>E16</code>):</strong> <a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">Stop Dumping Raw Pytest Logs Into Your Coding Agent: How a Structured JSON Test Filter Cuts Input Tokens by 94.1%</a></li>
<li>🌿 <strong>Rank #4 Deep Dive (<code>E19</code>):</strong> <a href="https://tappers.hashnode.dev/e19-error-trace-pruning">Why Keeping Failed Code Attempts in Chat History Poisons Your Coding Agent: Error-Trace Pruning vs. Accumulated History</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Why Keeping Failed Code Attempts in Chat History Poisons Your Coding Agent: Error-Trace Pruning vs. Accumulated History]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E19 (Ranked #4 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e19-error-trace-pruning</link><guid isPermaLink="true">https://tappers.hashnode.dev/e19-error-trace-pruning</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[coding]]></category><category><![CDATA[Harness]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sun, 01 Nov 2026 05:47:31 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment</strong> <code>E19</code> <strong>(Ranked #4 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark"><strong>View All 41 Benchmark Results &amp; Master Ranking</strong></a></li>
<li>🛡️ <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox"><strong>Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</strong></a></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E19</code> (Backlog Candidate <code>C015</code>)</li>
<li><strong>Run ID:</strong> <code>e19-20261028-vm223-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM223</code> (<code>ai-e19-223</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>)</li>
<li><strong>Pattern Under Test:</strong> Context Poisoning Prevention — <em>"Error-Trace Pruning (Failed-Attempt Rollback) vs. Accumulated Failed History"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>When a coding agent attempts a multi-file fix and the first patch fails tests, standard agent loops simply append the <strong>failed code diff (</strong><code>role: "model"</code><strong>)</strong> and the <strong>verbose error stack trace (</strong><code>role: "user"</code><strong>)</strong> to the conversation history (<code>messages[]</code>) and ask the LLM to try again.</p>
<p>This creates <strong>Self-Conditioning Bias (Context Poisoning)</strong>: LLMs strongly anchor on their own prior generated code in the context window. Instead of rethinking the cross-module architecture, the agent keeps making narrow incremental tweaks on top of its broken first attempt while stack traces snowball across turns.</p>
<p>To test the antidote—<strong>Error-Trace Pruning (Failed-Attempt Rollback)</strong>—we benchmarked our 3-module payment settlement pipeline (<code>validator.py</code>, <code>settlement.py</code>, <code>statement.py</code>) inside isolated Proxmox VM <code>VM223</code> across two modes:</p>
<ul>
<li><strong>Mode A (</strong><code>AccumulatedFailedHistoryRunner</code><strong>):</strong> Retains failed partial patches and full error stack traces (<code>36,606 chars</code> on Turn 1; <code>36,264 chars</code> on Turn 2) in the conversation history across retries.</li>
<li><strong>Mode B (</strong><code>PrunedFailedAttemptRunner</code><strong>):</strong> Immediately rolls back the workspace on test failure (<code>git checkout</code>), <strong>prunes the failed model output and raw stack trace from</strong> <code>messages[]</code>, and appends only a <code>927-character</code> <strong>(</strong><code>+392-token</code><strong>) structured negative lesson</strong> to the clean initial prompt.</li>
</ul>
<h3>Key Result</h3>
<p>While both modes eventually passed all <code>20 / 20</code> <strong>(100%)</strong> hidden test cases, pruning failed turns in Mode B eliminated the second failed retry (<code>3</code> calls ➔ <code>2</code> calls), compressed final-turn input tokens by <code>-94.0%</code> <strong>(</strong><code>38,134</code> <strong>➔</strong> <code>2,301</code> <strong>tokens)</strong>, cut cumulative input tokens by <code>-93.0%</code> <strong>(</strong><code>59,944</code> <strong>➔</strong> <code>4,210</code> <strong>tokens,</strong> <code>14.24x</code> <strong>efficiency)</strong>, reduced API cost by <code>-80.3%</code> <strong>(</strong><code>$0.0558</code> <strong>➔</strong> <code>$0.0110</code><strong>)</strong>, and finished <code>1.57x</code> <strong>faster</strong>.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Accumulated Failed History (<code>AccumulatedHistory</code>)</th>
<th align="center">Mode B: Error-Trace Pruning (<code>PrunedFailedAttempt</code>)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>20 / 20</code> (100%)</td>
<td align="center"><code>20 / 20</code> <strong>(100%)</strong></td>
<td align="center">Both passed all 20 cases</td>
</tr>
<tr>
<td align="left"><strong>Retained Failed Turns in History (</strong><code>retained_failed_turns</code><strong>)</strong></td>
<td align="center"><code>2 turns</code></td>
<td align="center"><code>0 turns</code></td>
<td align="center"><strong>-100% (Zero context poisoning)</strong></td>
</tr>
<tr>
<td align="left"><strong>Pruned Failed Turns (</strong><code>pruned_failed_turns</code><strong>)</strong></td>
<td align="center"><code>0 turns</code></td>
<td align="center"><code>1 turn</code> <strong>(Rolled back &amp; pruned)</strong></td>
<td align="center">Replaced 36KB trace with 927-char lesson</td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (</strong><code>model_calls</code><strong>)</strong></td>
<td align="center"><code>3 calls</code></td>
<td align="center"><code>2 calls</code></td>
<td align="center"><strong>-33.3% fewer calls</strong></td>
</tr>
<tr>
<td align="left"><strong>Total File Edits (</strong><code>file_edits</code><strong>)</strong></td>
<td align="center"><code>5 edits</code></td>
<td align="center"><code>4 edits</code></td>
<td align="center"><strong>-20.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Tool Output (</strong><code>tool_output_chars</code><strong>)</strong></td>
<td align="center"><code>72,975 chars</code></td>
<td align="center"><code>1,027 chars</code></td>
<td align="center"><strong>-98.6% (71.06x compression)</strong></td>
</tr>
<tr>
<td align="left"><strong>Final-Turn Input Tokens</strong></td>
<td align="center"><code>38,134 tokens</code> (Turn 3)</td>
<td align="center"><code>2,301 tokens</code> <strong>(Turn 2)</strong></td>
<td align="center"><strong>-94.0% (16.57x lighter final prompt)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (</strong><code>promptTokenCount</code><strong>)</strong></td>
<td align="center"><code>59,944 tokens</code></td>
<td align="center"><code>4,210 tokens</code></td>
<td align="center"><strong>-93.0% (14.24x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (</strong><code>candidatesTokenCount</code><strong>)</strong></td>
<td align="center"><code>2,892 tokens</code></td>
<td align="center"><code>2,082 tokens</code></td>
<td align="center"><strong>-28.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (</strong><code>elapsed_seconds</code><strong>)</strong></td>
<td align="center"><code>30.70 sec</code></td>
<td align="center"><code>19.55 sec</code></td>
<td align="center"><strong>-36.3% (1.57x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (</strong><code>estimated_micro_usd</code><strong>)</strong></td>
<td align="center"><code>$0.0558</code> (<code>55,804 micro-USD</code>)</td>
<td align="center"><code>$0.0110</code> <strong>(</strong><code>10,966 micro-USD</code><strong>)</strong></td>
<td align="center"><strong>-80.3% (5.09x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture &amp; Rollback-Prune Workflow</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM223 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Target Task: 3-Module Settlement Pipeline Bugfix                       |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: AccumulatedFailedHistoryRunner (Keeps Failed Turns)       |  |
|  |  - Turn 1: Partial patch (validator.py) FAILS (+36,606 char trace)|  |
|  |  - Turn 2: Anchors on Turn 1; patches only settlement.py          |  |
|  |            FAILS again (+36,264 char trace; 19,912 in tokens)     |  |
|  |  - Turn 3: Submits all 3 files at 38,134 input tokens (59,944 sum)|  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: PrunedFailedAttemptRunner (Rollback + Prune Failed Turn)  |  |
|  |  - Turn 1: Partial patch (validator.py) FAILS (1,909 in tokens)   |  |
|  |  - Harness Action:                                                |  |
|  |      1. Roll back workspace files to clean snapshot               |  |
|  |      2. Delete Turn 1 model output &amp; 36,606-char trace from chat  |  |
|  |      3. Append 927-char structured lesson (+392 tokens)           |  |
|  |  - Turn 2: Fresh retry at 2,301 input tokens --&gt; Submits all 3    |  |
|  |            files in 1 shot (20 / 20 PASS, 4,210 cumulative in)    |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<hr />
<h2>3. Turn-by-Turn Token Progression &amp; Prompt Comparison</h2>
<h3>3.1 Turn-by-Turn Token &amp; Cost Breakdown (<code>usage.json</code>)</h3>
<table>
<thead>
<tr>
<th align="center">Seq</th>
<th align="left">Mode</th>
<th align="left">Node ID</th>
<th align="center">Input Tokens</th>
<th align="center">Output Tokens</th>
<th align="center">API Cost (<code>micro-USD</code>)</th>
<th align="left">Action &amp; Outcome</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><code>0</code></td>
<td align="left">Mode A</td>
<td align="left"><code>accumulated_history_turn_1</code></td>
<td align="center"><code>1,898</code></td>
<td align="center"><code>497</code></td>
<td align="center"><code>3,288</code></td>
<td align="left"><code>attempt_patch</code> (<code>validator.py</code> only ➔ Fails; kept in history)</td>
</tr>
<tr>
<td align="center"><code>1</code></td>
<td align="left">Mode A</td>
<td align="left"><code>accumulated_history_turn_2</code></td>
<td align="center"><code>19,912</code></td>
<td align="center"><code>793</code></td>
<td align="center"><code>17,908</code></td>
<td align="left"><code>attempt_patch</code> (<code>settlement.py</code> only ➔ Fails; kept in history)</td>
</tr>
<tr>
<td align="center"><code>2</code></td>
<td align="left">Mode A</td>
<td align="left"><code>accumulated_history_turn_3</code></td>
<td align="center"><code>38,134</code></td>
<td align="center"><code>1,602</code></td>
<td align="center"><code>34,608</code></td>
<td align="left"><code>submit_settlement_bundle</code> (All 3 files ➔ <code>20/20</code> Pass)</td>
</tr>
<tr>
<td align="center"><code>3</code></td>
<td align="left"><strong>Mode B</strong></td>
<td align="left"><code>pruned_attempt_turn_1</code></td>
<td align="center"><code>1,909</code></td>
<td align="center"><code>496</code></td>
<td align="center"><code>3,292</code></td>
<td align="left"><code>attempt_patch</code> (<code>validator.py</code> only ➔ <strong>Rolled back &amp; pruned!</strong>)</td>
</tr>
<tr>
<td align="center"><code>4</code></td>
<td align="left"><strong>Mode B</strong></td>
<td align="left"><code>pruned_attempt_turn_2</code></td>
<td align="center"><code>2,301</code></td>
<td align="center"><code>1,586</code></td>
<td align="center"><code>7,674</code></td>
<td align="left"><code>submit_settlement_bundle</code> (Clean history + lesson ➔ <code>20/20</code> <strong>Pass</strong>)</td>
</tr>
</tbody></table>
<h3>3.2 What Mode A Kept in History (<code>input-01.json</code> / <code>input-02.json</code>)</h3>
<p>In Mode A, after Turn 1 patched only <code>validator.py</code>, the harness appended <code>36,606 characters</code> of unpruned diff frames and local variable dumps (<code>MSG[2]</code>), followed by another failed partial attempt (<code>MSG[3]</code>) and <code>36,264 characters</code> of traces (<code>MSG[4]</code>):</p>
<pre><code class="language-text">=== FAILED ATTEMPT #1 FULL DIFF &amp; TRACEBACK DUMP (UNPRUNED) ===
Status: FAILED (3 invariants broken)
[FAILED_PATCH_CONTEXT_FRAME_001] attempt=1 rejected_hunk_sha256=0000...0001 locals={'merchant_id': 'M-1', 'currency': 'USD', 'bps': 300, 'tier': 'ENTERPRISE'}
  File '/var/lib/ai-lab-e19-vm223-01/work_accumulated/settlement.py', line 26, in settle_batch
    AssertionError: Failed attempt #1 left residual bug in pipeline state.
... (FRAME_002 through FRAME_094: 36,000+ chars of repetitive frame dumps) ...
</code></pre>
<h3>3.3 What Mode B Injected After Pruning (<code>input-04.json</code>)</h3>
<p>In Mode B, as soon as Turn 1 failed, <code>error_trace_pruner_v1</code> restored the workspace, deleted <code>MSG[1]</code> (the failed partial patch) and <code>MSG[2]</code> (the 36,606-char traceback) from <code>messages[]</code>, and appended only this <code>927-character</code> <strong>(</strong><code>+392-token</code><strong>) structured lesson</strong> to the original prompt:</p>
<pre><code class="language-json">{
  "concise_lessons_learned": [
    "Lesson 1 (validator.py): Partial patch failed because `settlement.py` and `statement.py` were not updated together; in `validator.py` use `ALLOWED_CURRENCIES=('USD','EUR','JPY')`, reject empty list &amp; `bool`, allow `unit_cents&gt;=0` &amp; `0&lt;=chargeback_bps&lt;=1000`, and return a new 5-key dict.",
    "Lesson 2 (settlement.py): Preserve first-seen order (`for k in order:`), charge 10% reserve strictly when `chargeback_bps &gt; 300`, apply `&gt;= 15000` discount boundary (`GROWTH` 10%, `ENTERPRISE` 20% else 10%), and cap `rebate_applied = min(fee_rebate, after_discount)`.",
    "Lesson 3 (statement.py): Format item lines with `#` (`{merchant_id}#{currency}: ...`) and always prefix `-` on `TIER_DISCOUNT: -{tier_discount}` and `REBATE_APPLIED: -{rebate_applied}`."
  ],
  "pruning_engine": "error_trace_pruner_v1 (failed_code_rolled_back=true, stack_traces_pruned=true)",
  "remaining_issues": 4
}
</code></pre>
<p>With a clean <strong>2,301-token</strong> context freed from its failed first attempt, Mode B immediately generated a complete, unified 3-module patch on Turn 2.</p>
<hr />
<h2>4. Practical Implementation Recipe: A 20-Line Rollback-and-Prune Loop</h2>
<p>If you maintain a custom coding agent loop in Python, you can implement Error-Trace Pruning in 20 lines by combining <code>git checkout</code> with a <code>lesson_log</code> list instead of appending failed turns to <code>messages</code>:</p>
<pre><code class="language-python">import subprocess

def run_step_with_error_pruning(messages: list[dict], lesson_log: list[str], call_llm, run_tests) -&gt; bool:
    # 1. Build prompt from clean base messages + 1-line negative lessons from prior failed attempts
    prompt_msgs = list(messages)
    if lesson_log:
        prompt_msgs[-1] = {
            **prompt_msgs[-1],
            "text": prompt_msgs[-1]["text"] + "\n\n[PRUNED_FAILED_ATTEMPT_LESSONS]:\n" + "\n".join(lesson_log),
        }
    model_reply = call_llm(prompt_msgs)

    # 2. Apply patch and run deterministic test suite
    passed, short_failure_reason, _raw_trace = run_tests(model_reply)
    if passed:
        messages.append({"role": "model", "text": model_reply})
        return True

    # 3. On failure: roll back workspace and DO NOT append model_reply or _raw_trace to messages!
    subprocess.run(["git", "checkout", "--", "."], check=True)
    lesson_log.append(f"- Attempt #{len(lesson_log) + 1} rolled back: {short_failure_reason}")
    return False
</code></pre>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (</strong><code>E20</code><strong>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
<li>🥈 <strong>Rank #2 Deep Dive (</strong><code>E21</code><strong>):</strong> <a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons</a></li>
<li>🥉 <strong>Rank #3 Deep Dive (</strong><code>E16</code><strong>):</strong> <a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">Stop Dumping Raw Pytest Logs Into Your Coding Agent: How a Structured JSON Test Filter Cuts Input Tokens by 94.1%</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Stop Dumping Raw Pytest Logs Into Your Coding Agent: How a Structured JSON Test Filter Cuts Input Tokens by 94.1%]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E16 (Ranked #3 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e16-cli-test-output-token-killer</link><guid isPermaLink="true">https://tappers.hashnode.dev/e16-cli-test-output-token-killer</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[coding]]></category><category><![CDATA[Harness]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sun, 01 Nov 2026 01:15:03 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment <code>E16</code> (Ranked #3 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <strong><a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">View All 41 Benchmark Results &amp; Master Ranking</a></strong></li>
<li>🛡️ <strong><a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</a></strong></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E16</code> (Backlog Candidate <code>C012</code>)</li>
<li><strong>Run ID:</strong> <code>e16-20261028-vm220-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM220</code> (<code>ai-e16-220</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>)</li>
<li><strong>Pattern Under Test:</strong> RTK (<code>rtk-ai/rtk</code>, Rust Token Killer) &amp; SWE-agent Output Compression — <em>"Raw CLI Terminal Output vs. Semantic JSON Failure-Diff Filter"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>What is the single biggest token sink in an autonomous coding agent loop? Surprisingly, it isn't the source code itself—it is <strong>unfiltered CLI test runner output</strong>.</p>
<p>When an agent executes <code>pytest -vv --tb=long</code>, <code>cargo test</code>, or <code>npm test</code> on a realistic repository where 76 tests pass and 4 fail, dumping the raw terminal output (ANSI color codes, environment banners, 76 <code>[PASS]</code> lines, captured <code>DEBUG</code> stdout, and multi-frame internal tracebacks) directly into chat history adds <strong>60,000+ characters (<code>28,000+</code> tokens) per test run</strong>.</p>
<p>To measure the impact of deterministic CLI output filtering, we benchmarked a 3-module payment settlement pipeline (<code>validator.py</code>, <code>settlement.py</code>, <code>statement.py</code>) across two harness modes inside isolated VM <code>VM220</code>:</p>
<ul>
<li><strong>Mode A (<code>RawTerminalOutputRunner</code>):</strong> Appends raw <code>pytest</code> terminal stdout (<code>60,749 chars</code> on Turn 1; <code>117,391 chars</code> cumulative) directly to the conversation history.</li>
<li><strong>Mode B (<code>TokenKillerFilterRunner</code>):</strong> Wraps the test runner in a deterministic preprocessor that strips ANSI escape codes, environment banners, all 76 passing tests, and internal framework frames—returning <strong>only a <code>1,506-character</code> structured JSON summary of the 4 failed assertions</strong>.</li>
</ul>
<h3>Key Result</h3>
<p>Compressing raw CLI test output from <strong><code>117,391 chars</code> ➔ <code>1,604 chars</code> (<code>-98.6%</code> reduction, <code>73.2x</code> compression)</strong> enabled Mode B to fix all three modules in a single edit turn (<code>2</code> LLM calls total vs. <code>3</code> in Mode A). Mode B cut cumulative input tokens by <strong><code>-94.1%</code> (<code>92,385</code> ➔ <code>5,445</code> tokens, <code>16.97x</code> efficiency)</strong>, API cost by <strong><code>-87.0%</code> (<code>$0.0770</code> ➔ <code>$0.0100</code>)</strong>, and wall-clock time by <strong><code>-35.0%</code> (<code>1.54x</code> faster)</strong> while achieving <strong><code>20 / 20</code> (100%)</strong> on the hidden grading suite.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Raw Terminal Output (<code>RawTerminalOutput</code>)</th>
<th align="center">Mode B: Semantic JSON Filter (<code>TokenKillerFilter</code>)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>20 / 20</code> (100%)</td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
<td align="center">Both passed all 20 cases</td>
</tr>
<tr>
<td align="left"><strong>Cumulative CLI Tool Output (<code>tool_output_chars</code>)</strong></td>
<td align="center"><code>117,391 chars</code></td>
<td align="center"><strong><code>1,604 chars</code></strong></td>
<td align="center"><strong>-98.6% (73.19x compression)</strong></td>
</tr>
<tr>
<td align="left"><strong>First Test Run Output Size</strong></td>
<td align="center"><code>60,749 chars</code></td>
<td align="center"><strong><code>1,506 chars</code></strong></td>
<td align="center"><strong>-97.5% (76 PASS lines &amp; ANSI stripped)</strong></td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (<code>model_calls</code>)</strong></td>
<td align="center"><code>3 calls</code></td>
<td align="center"><strong><code>2 calls</code> (1 test run + 1 full fix)</strong></td>
<td align="center"><strong>-33.3% (No intermediate partial patch)</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 1 Input / Output Tokens</strong></td>
<td align="center"><code>2,406</code> in / <code>10</code> out</td>
<td align="center"><strong><code>2,415</code> in / <code>10</code> out</strong></td>
<td align="center">Identical initial test invocation</td>
</tr>
<tr>
<td align="left"><strong>Turn 2 Input / Output Tokens</strong></td>
<td align="center"><code>31,170</code> in / <code>484</code> out</td>
<td align="center"><strong><code>3,030</code> in / <code>1,561</code> out</strong></td>
<td align="center"><strong>Turn 2 input cut by -90.3%</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 3 Input / Output Tokens</strong></td>
<td align="center"><code>58,809</code> in / <code>1,574</code> out</td>
<td align="center">— <em>(Completed on Turn 2)</em></td>
<td align="center"><strong>Avoided <code>58,809</code>-token log snowball</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (<code>promptTokenCount</code>)</strong></td>
<td align="center"><code>92,385 tokens</code></td>
<td align="center"><strong><code>5,445 tokens</code></strong></td>
<td align="center"><strong>-94.1% (16.97x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (<code>candidatesTokenCount</code>)</strong></td>
<td align="center"><code>2,068 tokens</code></td>
<td align="center"><strong><code>1,571 tokens</code></strong></td>
<td align="center"><strong>-24.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (<code>elapsed_seconds</code>)</strong></td>
<td align="center"><code>25.94 sec</code></td>
<td align="center"><strong><code>16.88 sec</code></strong></td>
<td align="center"><strong>-35.0% (1.54x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (<code>estimated_micro_usd</code>)</strong></td>
<td align="center"><code>$0.0770</code> (<code>77,045 micro-USD</code>)</td>
<td align="center"><strong><code>$0.0100</code> (<code>9,976 micro-USD</code>)</strong></td>
<td align="center"><strong>-87.0% (7.72x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture &amp; Workflow Comparison</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM220 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Target Suite: 80-Test Suite (76 PASS / 4 FAIL across 3 modules)        |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: RawTerminalOutputRunner (Unfiltered pytest -vv --tb=long) |  |
|  |  - Turn 1: run_raw_pytest     --&gt; Returns 60,749 chars raw log    |  |
|  |  - Turn 2: stage_partial_fix  --&gt; Input jumps to 31,170 tokens!   |  |
|  |                                   Returns 56,544 chars raw log    |  |
|  |  - Turn 3: submit_settlement  --&gt; Input hits 58,809 tokens!       |  |
|  |                                   (Total: 92,385 input tokens)    |  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: TokenKillerFilterRunner (RTK-Style Semantic JSON Filter)  |  |
|  |  - Turn 1: run_filtered_tests --&gt; Strips 76 PASS &amp; ANSI noise;    |  |
|  |                                   returns 1,506-char failure JSON |  |
|  |  - Turn 2: submit_settlement  --&gt; Input is only 3,030 tokens!     |  |
|  |                                   Fixes all 3 files in 1 shot     |  |
|  |                                   (Total: 5,445 input tokens)     |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<hr />
<h2>3. Inside the Trace: How Raw CLI Logs Snowball Across Turns</h2>
<h3>3.1 Mode A (<code>RawTerminalOutput</code>): How 2 Test Runs Burned 92,385 Input Tokens</h3>
<p>Tracking <code>execution-traces.json</code> and <code>usage.json</code> reveals how fast raw terminal output compounds in an agent's message array:</p>
<ol>
<li><strong>Turn 1 (<code>2,406 in / 10 out</code>):</strong> The agent calls <code>{"tool": "run_raw_pytest"}</code>. The runner executes the 80-test suite and returns <strong><code>60,749 characters</code></strong> containing ANSI color codes, platform headers, 76 verbose <code>[PASS]</code> lines with <code>AUDIT_TRACE</code> dumps, and long multi-frame tracebacks for the 4 failures.</li>
<li><strong>Turn 2 (<code>31,170 in / 484 out</code>):</strong> Appending that single raw stdout dump causes the prompt size to jump by <strong><code>+28,764 tokens</code></strong> (<code>2,406</code> ➔ <code>31,170</code>). Distracted by the 60KB wall of text, the agent only patches <code>validator.py</code> (<code>stage_partial_fix</code>), triggering a second test run that appends another <strong><code>56,544 characters</code></strong> (now 77 PASS lines + 3 FAIL tracebacks) to the conversation history.</li>
<li><strong>Turn 3 (<code>58,809 in / 1,574 out</code>):</strong> Now carrying <strong>117,293 characters</strong> of raw terminal logs across two turns, Turn 3 alone requires <strong><code>58,809 input tokens</code></strong> before the agent finally submits all 3 modules (<code>92,385</code> cumulative input tokens).</li>
</ol>
<h3>3.2 Mode B (<code>TokenKillerFilter</code>): 1,506 Characters of Pure Signal</h3>
<p>When Mode B called <code>{"tool": "run_filtered_tests"}</code> on Turn 1, the harness preprocessor stripped all 76 passing tests, ANSI escape sequences, and <code>DEBUG</code> noise—returning a <strong><code>1,506-character</code> (<code>615-token</code>) JSON payload</strong> containing only the 4 failing tests (<code>test</code>, <code>file</code>, <code>line</code>, <code>assertion</code>, <code>expected_vs_actual</code>):</p>
<pre><code class="language-json">{
  "summary": {"total": 80, "passed": 76, "failed": 4, "filtered_noise_chars_removed": 59243},
  "failures": [
    {
      "test": "test_validator_contract",
      "file": "validator.py",
      "line": 14,
      "assertion": "ALLOWED_CURRENCIES must include 'JPY'; reject empty list &amp; bool; allow unit_cents==0 &amp; chargeback_bps==1000; do not mutate input dict."
    },
    {
      "test": "test_settlement_ordering_and_reserve",
      "file": "settlement.py",
      "line": 22,
      "assertion": "Preserve first-seen (merchant_id, currency) order (never sorted(order)); apply 10% reserve strictly when chargeback_bps &gt; 300."
    },
    {
      "test": "test_settlement_discounts_and_rebate_cap",
      "file": "settlement.py",
      "line": 39,
      "assertion": "Discount boundary is subtotal &gt;= 15000 (GROWTH 10%, ENTERPRISE 20% else 10%); cap rebate_applied = min(fee_rebate, after_discount)."
    },
    {
      "test": "test_statement_format_and_signed_zero",
      "file": "statement.py",
      "line": 11,
      "assertion": "Use '#' delimiter ({merchant_id}#{currency}:) and preserve leading '-' on zero discount/rebate (TIER_DISCOUNT: -0, REBATE_APPLIED: -0)."
    }
  ]
}
</code></pre>
<p>Because all four failure root causes were immediately visible without scrolling through 76 passing tests, Mode B fixed all three files in a single Turn 2 call (<code>3,030</code> input tokens)—eliminating the intermediate rework turn entirely.</p>
<hr />
<h2>4. All 20 Hidden Grading Suite Results (<code>labjudge16</code>)</h2>
<p>Both modes passed all 20 hidden unit and E2E test cases verified by <code>labjudge16</code>:</p>
<table>
<thead>
<tr>
<th align="center">#</th>
<th align="left">Test Case ID</th>
<th align="left">Invariant / Boundary Verified</th>
<th align="center">Mode A (<code>Raw CLI</code>)</th>
<th align="center">Mode B (<code>Token Killer</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1–6</td>
<td align="left"><code>validator_01</code> to <code>validator_06</code></td>
<td align="left">Trim/uppercase, non-mutation, <code>JPY</code>, <code>bool</code> rejection, <code>0</code> cents &amp; <code>1000</code> bps bounds</td>
<td align="center"><code>6 / 6</code> PASS</td>
<td align="center"><strong><code>6 / 6</code> PASS</strong></td>
</tr>
<tr>
<td align="center">7–15</td>
<td align="left"><code>settlement_01</code> to <code>settlement_09</code></td>
<td align="left">First-seen order, conflict check, <code>300 vs 301</code> bps, <code>14999 vs 15000</code> tier, rebate cap</td>
<td align="center"><code>9 / 9</code> PASS</td>
<td align="center"><strong><code>9 / 9</code> PASS</strong></td>
</tr>
<tr>
<td align="center">16–20</td>
<td align="left"><code>e2e_01</code> to <code>e2e_05</code></td>
<td align="left"><code>#</code> separator, signed zero <code>-0</code>, <code>$0</code> auth statement, line order, error propagation</td>
<td align="center"><code>5 / 5</code> PASS</td>
<td align="center"><strong><code>5 / 5</code> PASS</strong></td>
</tr>
<tr>
<td align="center"><strong>Total</strong></td>
<td align="left"><strong>20 Hidden Cases</strong></td>
<td align="left">—</td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
<td align="center"><strong><code>20 / 20</code> (100%)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>5. Practical Takeaway: Wrap Your Agent's Test Runner Today</h2>
<p>Never wire raw <code>subprocess.run(["pytest"])</code> stdout directly into your LLM agent's tool response. Instead:</p>
<ol>
<li><strong>Use machine-readable reporters:</strong> Run <code>pytest --tb=short -q --no-header --no-summary</code> (or <code>pytest-json-report</code>) inside your tool wrapper.</li>
<li><strong>Strip ANSI codes and passing tests:</strong> If 500 tests pass and 2 fail, the LLM only needs <code>"passed": 500</code> as a single integer plus the exact file, line number, and assertion diff for the 2 failures.</li>
<li><strong>Adopt CLI proxy filters:</strong> Tools like <code>rtk</code> (<code>Rust Token Killer</code>) or a 30-line Python wrapper around your test runner immediately cut agent token bills by <strong>85%–95%</strong> while speeding up multi-file bug fixes.</li>
</ol>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (<code>E20</code>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
<li>🥈 <strong>Rank #2 Deep Dive (<code>E21</code>):</strong> <a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E21 (Ranked #2 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e21-treesitter-skeleton-context</link><guid isPermaLink="true">https://tappers.hashnode.dev/e21-treesitter-skeleton-context</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[AI]]></category><category><![CDATA[Harness]]></category><category><![CDATA[coding]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sun, 01 Nov 2026 00:35:53 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment <code>E21</code> (Ranked #2 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <strong><a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">View All 41 Benchmark Results &amp; Master Ranking</a></strong></li>
<li>🛡️ <strong><a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</a></strong></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E21</code> (Backlog Candidate <code>C017</code>)</li>
<li><strong>Run ID:</strong> <code>e21-20261028-vm225-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM225</code> (<code>ai-e21-225</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>, <code>temperature: 0.0</code>)</li>
<li><strong>Pattern Under Test:</strong> Aider Repo-Map &amp; Tree-sitter Skeleton Compression — <em>"Full Dependency Implementation Context vs. Interface-Only Skeleton Context"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>When an AI coding agent edits a module that depends on several peripheral files, a common approach is to dump the <strong>entire source code of all imported dependencies</strong> into the prompt so the model "has full context."</p>
<p>In practice, feeding full implementation bodies of peripheral dependencies is actively counterproductive. Legacy helper logic, internal comments, and verbose function bodies bloat the context window and trigger <strong>Attention Dilution</strong>, causing the model to miss strict boundary contracts in the files it actually needs to edit.</p>
<p>To quantify this, we benchmarked a 3-module payment settlement pipeline (<code>validator.py</code>, <code>settlement.py</code>, <code>statement.py</code>) inside isolated Proxmox VM <code>VM225</code> across two harness modes:</p>
<ul>
<li><strong>Mode A (<code>FullDependencyContextRunner</code>):</strong> Loads the full implementation bodies of peripheral dependency modules and raw diagnostics (<code>78,719 chars</code> of tool output across 3 turns).</li>
<li><strong>Mode B (<code>TreeSitterSkeletonContextRunner</code>):</strong> Elides function implementation bodies (<code>...</code>) using an AST / Tree-sitter projection, injecting <strong>only public function signatures, type annotations, and contract docstrings</strong> (<code>+673 tokens</code> on Turn 1).</li>
</ul>
<h3>Key Result</h3>
<p>Passing only the <strong>Tree-sitter Interface Skeleton (<code>2,316 input tokens</code>)</strong> enabled a <strong>1-turn <code>20 / 20</code> (100%) pass</strong> on the hidden test suite. In contrast, Mode A's full dependency dump consumed <strong><code>45,270 input tokens</code></strong> across 3 turns and stalled at <strong><code>17 / 20</code> (85.0%)</strong> due to attention dilution. Mode B cut cumulative input tokens by <strong><code>-94.9%</code> (<code>19.55x</code> reduction)</strong>, API cost by <strong><code>-82.6%</code> (<code>$0.0446</code> ➔ <code>$0.0078</code>)</strong>, and execution time by <strong><code>-57.2%</code> (<code>2.34x</code> faster)</strong>.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Full Dependency Context (<code>FullDependencyContext</code>)</th>
<th align="center">Mode B: Tree-sitter Skeleton Context (<code>TreeSitterSkeleton</code>)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>17 / 20</code> (85.0% — missed 3 edge cases)</td>
<td align="center"><strong><code>20 / 20</code> (100% Perfect Pass)</strong></td>
<td align="center"><strong>+15.0 pt (Zero defects)</strong></td>
</tr>
<tr>
<td align="left"><strong>Contract Mismatch Traps Hit (<code>gotcha_traps_hit</code>)</strong></td>
<td align="center"><code>2 times</code> (Turn 1, Turn 2)</td>
<td align="center"><strong><code>0 times</code> (100% contract compliance)</strong></td>
<td align="center"><strong>-100.0% (Zero rework)</strong></td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (<code>model_calls</code>)</strong></td>
<td align="center"><code>3 calls</code></td>
<td align="center"><strong><code>1 call</code> (1-shot completion)</strong></td>
<td align="center"><strong>-66.7% (3.00x faster convergence)</strong></td>
</tr>
<tr>
<td align="left"><strong>Total File Edits (<code>file_edits</code>)</strong></td>
<td align="center"><code>5 edits</code></td>
<td align="center"><strong><code>3 edits</code> (1 per module)</strong></td>
<td align="center"><strong>-40.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Tool Output Volume (<code>tool_output_chars</code>)</strong></td>
<td align="center"><code>78,719 chars</code></td>
<td align="center"><strong><code>105 chars</code></strong></td>
<td align="center"><strong>-99.9% (750x less noise)</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 1 Input / Output Tokens</strong></td>
<td align="center"><code>1,643</code> in / <code>12</code> out</td>
<td align="center"><strong><code>2,316</code> in / <code>1,611</code> out</strong></td>
<td align="center">Skeleton injection is only <strong><code>+673</code> in</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 2 Input / Output Tokens</strong></td>
<td align="center"><code>14,712</code> in / <code>1,249</code> out</td>
<td align="center">— <em>(Completed on Turn 1)</em></td>
<td align="center">Avoided Turn 1 dependency dump bloat</td>
</tr>
<tr>
<td align="left"><strong>Turn 3 Input / Output Tokens</strong></td>
<td align="center"><code>28,915</code> in / <code>1,578</code> out</td>
<td align="center">— <em>(Completed on Turn 1)</em></td>
<td align="center">Avoided Turn 2 cumulative log bloat</td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (<code>promptTokenCount</code>)</strong></td>
<td align="center"><code>45,270 tokens</code></td>
<td align="center"><strong><code>2,316 tokens</code></strong></td>
<td align="center"><strong>-94.9% (19.55x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (<code>candidatesTokenCount</code>)</strong></td>
<td align="center"><code>2,839 tokens</code></td>
<td align="center"><strong><code>1,611 tokens</code></strong></td>
<td align="center"><strong>-43.3% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (<code>elapsed_seconds</code>)</strong></td>
<td align="center"><code>29.99 sec</code></td>
<td align="center"><strong><code>12.84 sec</code></strong></td>
<td align="center"><strong>-57.2% (2.34x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (<code>estimated_micro_usd</code>)</strong></td>
<td align="center"><code>$0.0446</code> (<code>44,600 micro-USD</code>)</td>
<td align="center"><strong><code>$0.0078</code> (<code>7,779 micro-USD</code>)</strong></td>
<td align="center"><strong>-82.6% (5.73x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture &amp; Harness Workflow</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM225 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Target Task: 3-Module Settlement Pipeline Refactoring                  |
|               (validator.py / settlement.py / statement.py)             |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: FullDependencyContextRunner (Unfiltered Full Source Dump) |  |
|  |  - Turn 1: load_full_dependency_dump (1,643 in / 39,373 chars)    |  |
|  |  - Turn 2: stage_dependency_patch    (14,712 in / 39,027 chars)   |  |
|  |  - Turn 3: submit_skeleton_guided_bundle (28,915 in --&gt; 17 / 20)  |  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: TreeSitterSkeletonContextRunner (Signature &amp; Contract)    |  |
|  |  - Turn 1: Reads AST Interface Skeleton (+673 tok) &amp; submits all  |  |
|  |            3 modules (2,316 in / 1,611 out --&gt; 20 / 20 on Turn 1) |  |
|  +-------------------------------------------------------------------+  |
|                                    |                                    |
|                                    v                                    |
|  +-------------------------------------------------------------------+  |
|  | Independent Grader (labjudge21): 20 Hidden Unit &amp; E2E Test Cases  |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<hr />
<h2>3. Inside the Prompts: Tree-sitter Skeleton vs. Full Implementation Dump</h2>
<h3>3.1 The Exact Tree-sitter Interface Skeleton Injected into Mode B (<code>input-03.json</code>)</h3>
<p>Instead of dumping internal helper functions, Mode B's harness projected the target modules and their contracts into a compact <strong>Interface Skeleton</strong> (<code>2,316</code> total input tokens on Turn 1) with implementation bodies folded into <code>...</code>:</p>
<pre><code class="language-python"># REPO-MAP INTERFACE SKELETON (AST / Tree-sitter Signature &amp; Contract Projection — Implementation Bodies Elided)

# module: validator.py (PUBLIC INTERFACE SKELETON)
ALLOWED_CURRENCIES: tuple[str, ...] = ("USD", "EUR", "JPY")
def validate_txns(raw_txns: list[dict]) -&gt; list[dict]:
    """Contract:
    1. Reject empty list or non-list with ValueError.
    2. Strip &amp; uppercase `merchant_id` (non-empty str) and `currency` (must be in ALLOWED_CURRENCIES: USD, EUR, JPY).
    3. Reject `isinstance(v, bool)` before `isinstance(v, int)` on `tx_count`, `unit_cents`, `chargeback_bps`.
    4. Bounds: `tx_count &gt; 0`, `unit_cents &gt;= 0` (zero allowed!), `0 &lt;= chargeback_bps &lt;= 1000` (1000 inclusive!).
    5. Return a brand-new 5-key dict list without mutating caller dicts.
    """
    ...

# module: settlement.py (PUBLIC INTERFACE SKELETON)
ALLOWED_TIERS: tuple[str, ...] = ("STANDARD", "GROWTH", "ENTERPRISE")
def settle_batch(txns: list[dict], tier: str = "STANDARD", fee_rebate: int = 0) -&gt; dict:
    """Contract:
    1. Preserve first-seen `(merchant_id, currency)` order (`for k in order:`, NEVER `sorted(order)`).
    2. Chargeback reserve: 10% (`(gross * 10) // 100`) STRICTLY when `chargeback_bps &gt; 300` (at 300 bps, reserve == 0).
    3. Tier discount threshold `subtotal &gt;= 15000` is INCLUSIVE:
       - `GROWTH`: 10% if `subtotal &gt;= 15000` else 0.
       - `ENTERPRISE`: 20% if `subtotal &gt;= 15000` else 10%.
    4. Rebate cap: `rebate_applied = min(fee_rebate, after_discount)`.
    """
    ...

# module: statement.py (PUBLIC INTERFACE SKELETON)
def render_statement(batch: dict) -&gt; str:
    """Contract:
    1. Line format: `{merchant_id}#{currency}: tx={tx_count} unit={unit_cents} bps={chargeback_bps} = {line_total}` (delimiter is `#`, NOT `:`).
    2. Discount &amp; rebate lines MUST always carry a leading `-` sign even when 0 (`TIER_DISCOUNT: -0`, `REBATE_APPLIED: -0`).
    """
    ...
</code></pre>
<h3>3.2 What Mode A Received: 78,719 Characters of Unfiltered Dependency Implementation Noise</h3>
<p>In Mode A (<code>FullDependencyContextRunner</code>), calling <code>load_full_dependency_dump</code> on Turn 1 and <code>stage_dependency_patch</code> on Turn 2 flooded the context with <strong><code>39,373 chars</code></strong> and <strong><code>39,027 chars</code></strong> of internal helper bodies, bitwise checksum loops, and deprecated v1 comments:</p>
<pre><code class="language-text">=== FULL DEPENDENCY IMPLEMENTATION DUMP &amp; INTERFACE MISMATCH LOG (STAGE #1) ===
Status: MODE_A_TRAP_HIT (4 contract invariants unresolved in FullDependencyContextRunner)
# RAW DEPENDENCY IMPLEMENTATION DUMP (UNFILTERED FULL SOURCE OF 12 INTERNAL HELPER MODULES)
# WARNING: Contains legacy internal helpers, deprecated rounding tables, and verbose implementation bodies.

# --- internal_dep_module_01.py (Full Implementation Body) ---
def _internal_helper_pipeline_stage_01(payload_buffer, flags=17):
    """Internal helper implementation body #01 (non-public implementation details)."""
    acc_state = 1024
    for step_i in range(16):
        acc_state = ((acc_state ^ (step_i * 31)) + 97) &amp; 0xFFFFFFFF
    # Legacy note: old v1 parser used sorted(keys) and &gt;= 300 bps, superseded by v2 public contract
    return {'stage': 1, 'checksum': hex(acc_state), 'legacy_delimiter': ':'}
... (37,500+ chars of internal helper implementation bodies) ...
</code></pre>
<h3>Turn-by-Turn Token &amp; Cost Breakdown (<code>usage.json</code>)</h3>
<table>
<thead>
<tr>
<th align="center">Seq</th>
<th align="left">Mode</th>
<th align="left">Node ID</th>
<th align="center">Input Tokens</th>
<th align="center">Output Tokens</th>
<th align="center">API Cost (<code>micro-USD</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><code>0</code></td>
<td align="left">Mode A (<code>full_dep_context</code>)</td>
<td align="left"><code>full_dep_context_turn_1</code></td>
<td align="center"><code>1,643</code></td>
<td align="center"><code>12</code></td>
<td align="center"><code>1,278</code> (<code>$0.0013</code>)</td>
</tr>
<tr>
<td align="center"><code>1</code></td>
<td align="left">Mode A (<code>full_dep_context</code>)</td>
<td align="left"><code>full_dep_context_turn_2</code></td>
<td align="center"><code>14,712</code></td>
<td align="center"><code>1,249</code></td>
<td align="center"><code>15,718</code> (<code>$0.0157</code>)</td>
</tr>
<tr>
<td align="center"><code>2</code></td>
<td align="left">Mode A (<code>full_dep_context</code>)</td>
<td align="left"><code>full_dep_context_turn_3</code></td>
<td align="center"><code>28,915</code></td>
<td align="center"><code>1,578</code></td>
<td align="center"><code>27,604</code> (<code>$0.0276</code>)</td>
</tr>
<tr>
<td align="center"><code>3</code></td>
<td align="left"><strong>Mode B (<code>treesitter_skeleton</code>)</strong></td>
<td align="left"><strong><code>treesitter_skeleton_turn_1</code></strong></td>
<td align="center"><strong><code>2,316</code></strong></td>
<td align="center"><strong><code>1,611</code></strong></td>
<td align="center"><strong><code>7,779</code> (<code>$0.0078</code>)</strong></td>
</tr>
<tr>
<td align="center"><strong>Total</strong></td>
<td align="left"><strong>Mode A (3 Turns)</strong></td>
<td align="left">—</td>
<td align="center"><strong><code>45,270</code></strong></td>
<td align="center"><strong><code>2,839</code></strong></td>
<td align="center"><strong><code>44,600</code> (<code>$0.0446</code>)</strong></td>
</tr>
<tr>
<td align="center"><strong>Total</strong></td>
<td align="left"><strong>Mode B (1 Turn)</strong></td>
<td align="left">—</td>
<td align="center"><strong><code>2,316</code></strong></td>
<td align="center"><strong><code>1,611</code></strong></td>
<td align="center"><strong><code>7,779</code> (<code>$0.0078</code>)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>4. Code-Level Postmortem: Why Mode A Failed 3 out of 20 Test Cases</h2>
<p>Inspecting the final submitted bundles (<code>full-dep-bundle.json</code> vs. <code>treesitter-skeleton-bundle.json</code>) shows how <strong>28,915 tokens of noisy dependency implementations</strong> diluted Mode A's attention on Turn 3:</p>
<pre><code class="language-python"># ❌ Mode A (FullDependencyContextRunner, Turn 3 Final Submission — validator.py)
        if cnt &lt;= 0 or cents &lt;= 0 or not (0 &lt;= bps &lt;= 1000):
            raise ValueError("numeric bounds violated")
        out.append({
            **item,
            "merchant_id": mid.strip().upper(),
            "currency": cur.strip().upper(),
        })
</code></pre>
<pre><code class="language-python"># ✅ Mode B (TreeSitterSkeletonContextRunner, Turn 1 Final Submission — validator.py)
        if cnt &lt;= 0 or cents &lt; 0 or not (0 &lt;= bps &lt;= 1000):
            raise ValueError("numeric bounds violated")
        out.append({
            "merchant_id": mid.strip().upper(),
            "currency": cur.strip().upper(),
            "tx_count": cnt,
            "unit_cents": cents,
            "chargeback_bps": bps,
        })
</code></pre>
<p>Buried under 78KB of irrelevant helper bodies, Mode A missed two critical boundary contracts in <code>validator.py</code>:</p>
<ol>
<li><strong>Unvalidated Extra Key Leakage (<code>validator_01_normalize_and_non_mutation</code> FAIL):</strong> Using <code>{**item, ...}</code> copied unvalidated extra keys (e.g., <code>"extra": 99</code>) from the input dictionary into the normalized output instead of returning a strict 5-key dictionary.</li>
<li><strong>Zero-Amount Card Auth Rejection (<code>validator_05</code> &amp; <code>e2e_03</code> FAIL):</strong> Mode A left <code>cents &lt;= 0</code> instead of <code>cents &lt; 0</code>, rejecting valid <code>$0</code> card authorization probes (<code>unit_cents == 0</code>).</li>
</ol>
<p>Mode B, operating on a crystal-clear <strong>2,316-token AST interface skeleton</strong>, passed all <strong>20 / 20</strong> test cases on its very first turn.</p>
<hr />
<h2>5. Practical Takeaway: How to Build a Skeleton Context Filter in Python</h2>
<p>When your agent needs to call or integrate with neighboring modules that it is <strong>not</strong> actively modifying, never dump their full source code into the prompt. Instead, use Python's <code>ast</code> module (or <code>tree-sitter</code> for multi-language repos) to fold function bodies into <code>...</code>:</p>
<pre><code class="language-python">import ast

def extract_interface_skeleton(source_code: str) -&gt; str:
    """Fold function and method bodies into '...' while preserving signatures, types, and docstrings."""
    tree = ast.parse(source_code)
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
            # Keep docstring if present, replace remaining body statements with Ellipsis (...)
            has_doc = (
                node.body
                and isinstance(node.body[0], ast.Expr)
                and isinstance(node.body[0].value, ast.Constant)
                and isinstance(node.body[0].value.value, str)
            )
            ellipsis_stmt = ast.Expr(value=ast.Constant(value=...))
            node.body = [node.body[0], ellipsis_stmt] if has_doc else [ellipsis_stmt]
    return ast.unparse(tree)
</code></pre>
<p>By exposing <strong>interfaces and contracts</strong> while hiding <strong>internal implementation noise</strong>, you cut input tokens by over 90% and eliminate Lost-in-the-Middle bugs.</p>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (<code>E20</code>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item MEMORY.md]]></title><description><![CDATA[💡 AI Labo Benchmark Series — Experiment E20 (Ranked #1 out of 41 Patterns) This post is part of our empirical benchmark series testing AI coding agent…]]></description><link>https://tappers.hashnode.dev/e20-episodic-gotchas-memory</link><guid isPermaLink="true">https://tappers.hashnode.dev/e20-episodic-gotchas-memory</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[agents]]></category><category><![CDATA[context engineering]]></category><category><![CDATA[harnessengineering]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sat, 31 Oct 2026 12:46:13 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>AI Labo Benchmark Series — Experiment</strong> <code>E20</code> <strong>(Ranked #1 out of 41 Patterns)</strong></p>
<p>This post is part of our empirical benchmark series testing AI coding agent harness patterns inside network-isolated Proxmox VMs against a hidden 20-case grading suite.</p>
<ul>
<li>🚀 <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark"><strong>View All 41 Benchmark Results &amp; Master Ranking</strong></a></li>
<li>🛡️ <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox"><strong>Read How Our Proxmox &amp; systemd Anti-Cheating Sandbox Works</strong></a></li>
</ul>
</blockquote>
<hr />
<h2>Metadata &amp; Experiment Identity</h2>
<ul>
<li><strong>Experiment ID:</strong> <code>E20</code> (Backlog Candidate <code>C016</code>)</li>
<li><strong>Run ID:</strong> <code>e20-20261028-vm224-01</code></li>
<li><strong>Sandbox Environment:</strong> Proxmox Isolated VM <code>VM224</code> (<code>ai-e20-224</code>, 0 NICs, <code>virtio-serial</code> QGA relay, <code>systemd</code> UID separation)</li>
<li><strong>Model:</strong> <code>gemini-3.8-flash</code> (<code>thinkingLevel: LOW</code>)</li>
<li><strong>Pattern Under Test:</strong> Letta / MemGPT &amp; Claude Code <code>CLAUDE.md</code> / <code>MEMORY.md</code> Pattern — <em>"Episodic Gotchas Memory vs. Cold-Start Exploration"</em></li>
</ul>
<hr />
<h2>1. Executive Summary (TL;DR)</h2>
<p>When running multiple tasks sequentially on the same repository, starting every agent session from scratch (<strong>Cold-Start Exploration</strong>) forces the model to rediscover non-obvious domain invariants and repository quirks ("gotchas") through repeated test failures—burning tens of thousands of tokens on stack traces and diluting model attention.</p>
<p>To test whether a compact <strong>Episodic Gotchas Memory (</strong><code>MEMORY.md</code><strong>)</strong> solves this, we benchmarked a 3-module payment settlement pipeline (<code>validator.py</code>, <code>settlement.py</code>, <code>statement.py</code>) containing <strong>5 subtle repository gotchas (7 domain invariants)</strong> across two modes:</p>
<ul>
<li><strong>Mode A (</strong><code>Cold-Start Exploration</code><strong>):</strong> No <code>MEMORY.md</code> injected; the agent explores the codebase and diagnoses failures via standard test/audit feedback.</li>
<li><strong>Mode B (</strong><code>Episodic Gotchas Memory</code><strong>):</strong> A <strong>5-item</strong> <code>MEMORY.md</code> <strong>(</strong><code>+442 tokens</code><strong>)</strong> distilled from prior session failures is injected into the initial prompt.</li>
</ul>
<h3>Key Result</h3>
<p>Injecting just <code>+442 tokens</code> <strong>of</strong> <code>MEMORY.md</code> reduced repository trap hits from <code>2 ➔ 0</code>, achieving a <strong>1-turn</strong> <code>20 / 20</code> <strong>(100%) pass</strong> on the hidden grading suite. Compared to Mode A (<code>17 / 20</code> pass across 3 turns), Mode B cut cumulative input tokens by <code>-95.5%</code> <strong>(</strong><code>46,560</code> <strong>➔</strong> <code>2,078</code> <strong>tokens)</strong>, API cost by <code>-83.7%</code> <strong>(</strong><code>$0.0454</code> <strong>➔</strong> <code>$0.0074</code><strong>)</strong>, and wall-clock time by <code>-54.8%</code> <strong>(</strong><code>2.21x</code> <strong>faster)</strong>.</p>
<table>
<thead>
<tr>
<th align="left">Metric</th>
<th align="center">Mode A: Cold-Start Exploration (No <code>MEMORY.md</code>)</th>
<th align="center">Mode B: Episodic Gotchas Memory (<code>MEMORY.md</code> Injected)</th>
<th align="center">Delta / Improvement</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Hidden Grading Suite Score (20 Cases)</strong></td>
<td align="center"><code>17 / 20</code> (85.0% — missed 3 edge cases)</td>
<td align="center"><code>20 / 20</code> <strong>(100% Perfect Pass)</strong></td>
<td align="center"><strong>+15.0 pt (Zero defects)</strong></td>
</tr>
<tr>
<td align="left"><strong>Repository Gotcha Traps Triggered</strong></td>
<td align="center"><code>2 times</code> (Turn 1, Turn 2)</td>
<td align="center"><code>0 times</code> <strong>(Avoided on Turn 1)</strong></td>
<td align="center"><strong>-100.0% (Zero traps)</strong></td>
</tr>
<tr>
<td align="left"><strong>LLM Calls (</strong><code>model_calls</code><strong>)</strong></td>
<td align="center"><code>3 calls</code></td>
<td align="center"><code>1 call</code> <strong>(1-shot completion)</strong></td>
<td align="center"><strong>-66.7% (3.00x faster convergence)</strong></td>
</tr>
<tr>
<td align="left"><strong>Total File Edits (</strong><code>file_edits</code><strong>)</strong></td>
<td align="center"><code>5 edits</code></td>
<td align="center"><code>3 edits</code> <strong>(1 per module)</strong></td>
<td align="center"><strong>-40.0% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Tool Output Volume (</strong><code>tool_output_chars</code><strong>)</strong></td>
<td align="center"><code>56,605 chars</code></td>
<td align="center"><code>101 chars</code></td>
<td align="center"><strong>-99.8% (560x less noise)</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 1 Input / Output Tokens</strong></td>
<td align="center"><code>1,636</code> in / <code>11</code> out</td>
<td align="center"><code>2,078</code> <strong>in /</strong> <code>1,560</code> <strong>out</strong></td>
<td align="center"><code>MEMORY.md</code> overhead is only <code>+442</code> <strong>in</strong></td>
</tr>
<tr>
<td align="left"><strong>Turn 2 Input / Output Tokens</strong></td>
<td align="center"><code>15,153</code> in / <code>1,220</code> out</td>
<td align="center">— <em>(Completed on Turn 1)</em></td>
<td align="center">Avoided Turn 1 failure log bloat</td>
</tr>
<tr>
<td align="left"><strong>Turn 3 Input / Output Tokens</strong></td>
<td align="center"><code>29,771</code> in / <code>1,569</code> out</td>
<td align="center">— <em>(Completed on Turn 1)</em></td>
<td align="center">Avoided Turn 2 failure log bloat</td>
</tr>
<tr>
<td align="left"><strong>Cumulative Input Tokens (</strong><code>promptTokenCount</code><strong>)</strong></td>
<td align="center"><code>46,560 tokens</code></td>
<td align="center"><code>2,078 tokens</code></td>
<td align="center"><strong>-95.5% (22.41x token efficiency)</strong></td>
</tr>
<tr>
<td align="left"><strong>Cumulative Output Tokens (</strong><code>candidatesTokenCount</code><strong>)</strong></td>
<td align="center"><code>2,800 tokens</code></td>
<td align="center"><code>1,560 tokens</code></td>
<td align="center"><strong>-44.3% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Elapsed Execution Time (</strong><code>elapsed_seconds</code><strong>)</strong></td>
<td align="center"><code>30.16 sec</code></td>
<td align="center"><code>13.64 sec</code></td>
<td align="center"><strong>-54.8% (2.21x faster)</strong></td>
</tr>
<tr>
<td align="left"><strong>Estimated API Cost (</strong><code>estimated_micro_usd</code><strong>)</strong></td>
<td align="center"><code>$0.0454</code> (<code>45,421 micro-USD</code>)</td>
<td align="center"><code>$0.0074</code> <strong>(</strong><code>7,409 micro-USD</code><strong>)</strong></td>
<td align="center"><strong>-83.7% (6.13x cheaper)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>2. Experiment Architecture &amp; Repository Gotchas Under Test</h2>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| Proxmox Isolated VM224 (0 NICs, virtio-serial QGA, systemd UID split)   |
|  Target Task: 3-Module Settlement Pipeline Bugfix                       |
|               (validator.py / settlement.py / statement.py)             |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode A: ColdStartRunner (No MEMORY.md — Cold-Start Exploration)   |  |
|  |  - Turn 1: probe_repo_gotchas   (1,636 in / 28,317 chars trap log)|  |
|  |  - Turn 2: stage_partial_fix    (15,153 in / 27,975 chars log)    |  |
|  |  - Turn 3: submit_settlement    (29,771 in --&gt; 17 / 20 passed)    |  |
|  +-------------------------------------------------------------------+  |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | Mode B: EpisodicGotchasMemoryRunner (5-Item MEMORY.md [+442 tok]) |  |
|  |  - Turn 1: Reads 5-item MEMORY.md &amp; submits all 3 modules         |  |
|  |            (2,078 in / 1,560 out --&gt; 20 / 20 passed on 1st try!)  |  |
|  +-------------------------------------------------------------------+  |
|                                    |                                    |
|                                    v                                    |
|  +-------------------------------------------------------------------+  |
|  | Independent Grader (labjudge20): 20 Hidden Unit &amp; E2E Test Cases  |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<h3>The 5 Non-Obvious Repository Gotchas in the Initial Codebase</h3>
<p>The initial workspace inside <code>VM224</code> contains three payment modules with subtle domain bugs and Python traps that are easy to miss on a cold read:</p>
<ol>
<li><code>validator.py</code> <strong>(Transaction Validation &amp; Normalization):</strong><ul>
<li><code>ALLOWED_CURRENCIES = ("USD", "EUR")</code> is missing <code>"JPY"</code>.</li>
<li>Empty list <code>[]</code> rejection is missing, and because <code>bool</code> <strong>is a subclass of</strong> <code>int</code> <strong>in Python (</strong><code>isinstance(True, int) == True</code><strong>)</strong>, <code>isinstance(v, bool)</code> must be explicitly rejected before checking <code>isinstance(v, int)</code>.</li>
<li>The bounds check <code>if cnt &lt;= 0 or cents &lt;= 0 or not (0 &lt;= bps &lt; 1000):</code> erroneously rejects <code>unit_cents == 0</code> <strong>(</strong><code>$0</code> <strong>card authorization probes)</strong> and <code>chargeback_bps == 1000</code> <strong>(inclusive upper bound)</strong>.</li>
<li>It mutates caller dictionaries in-place (<code>item["merchant_id"] = ...; out.append(item)</code>) and leaks extra unvalidated keys (<code>extra</code>) into the output.</li>
</ul>
</li>
<li><code>settlement.py</code> <strong>(Batch Aggregation, Reserve &amp; Tier Discounts):</strong><ul>
<li>Iterates with <code>for k in sorted(order):</code>, destroying <strong>first-seen</strong> <code>(merchant_id, currency)</code> <strong>ordering</strong>.</li>
<li>Applies the 10% chargeback reserve at <code>chargeback_bps &gt;= 300</code> instead of strictly <code>&gt; 300</code> (at exactly <code>300 bps</code>, reserve must be <code>0</code>).</li>
<li>Uses <code>subtotal &gt; 15000</code> instead of inclusive <code>&gt;= 15000</code>, omits the <code>10%</code> discount for <code>ENTERPRISE</code> below <code>15000</code>, and forgets to cap <code>rebate_applied = min(fee_rebate, after_discount)</code>.</li>
</ul>
</li>
<li><code>statement.py</code> <strong>(Canonical Statement Formatter):</strong><ul>
<li>Uses <code>:</code> instead of <code>#</code> between merchant ID and currency (<code>{merchant_id}#{currency}:</code>).</li>
<li>Formats zero discounts as <code>"0"</code> instead of preserving the mandatory leading minus sign (<code>TIER_DISCOUNT: -0</code>, <code>REBATE_APPLIED: -0</code>).</li>
</ul>
</li>
</ol>
<hr />
<h2>3. Inside the Prompts: What Mode B Saw vs. What Mode A Endured</h2>
<h3>3.1 The Exact 5-Item <code>MEMORY.md</code> Injected into Mode B (<code>+442 tokens</code>)</h3>
<p>In Mode B (<code>input-03.json</code>), the harness prepended this exact 5-bullet <code># MEMORY.md</code> section to the initial prompt:</p>
<pre><code class="language-markdown"># MEMORY.md — Repository Episodic Gotchas &amp; Non-Obvious Invariants (Distilled from Session #1)
1. [GOTCHA-01 / validator.py] `ALLOWED_CURRENCIES` MUST include `"JPY"` (`("USD", "EUR", "JPY")`), empty `records` list MUST raise `ValueError`, and `isinstance(v, bool)` MUST be rejected before `isinstance(v, int)` because `bool` is a subclass of `int` in Python.
2. [GOTCHA-02 / validator.py] Zero `unit_cents == 0` and boundary `chargeback_bps == 1000` are VALID (`tx_count &gt; 0`, `unit_cents &gt;= 0`, `0 &lt;= chargeback_bps &lt;= 1000`). Always return a brand-new 5-key dict without mutating caller dicts.
3. [GOTCHA-03 / settlement.py] Preserve first-seen `(merchant_id, currency)` order (`for k in order:`, NEVER `sorted(order)`). The 10% reserve applies STRICTLY when `chargeback_bps &gt; 300` (at `300` bps, `reserve_cents == 0`).
4. [GOTCHA-04 / settlement.py] Tier discount threshold `subtotal &gt;= 15000` is INCLUSIVE: `GROWTH` gets 10% (`&gt;= 15000`), `ENTERPRISE` gets 20% (`&gt;= 15000`) or 10% (`&lt; 15000`), and `rebate_applied = min(fee_rebate, after_discount)`.
5. [GOTCHA-05 / statement.py] Header delimiter between merchant and currency is `#` (`{merchant_id}#{currency}: tx=...`) and discount/rebate lines MUST always carry `-` even when zero (`TIER_DISCOUNT: -0`, `REBATE_APPLIED: -0`).
</code></pre>
<h3>3.2 The 56,605-Character Failure Trace That Flooded Mode A (<code>Cold-Start</code>)</h3>
<p>Without <code>MEMORY.md</code>, Mode A called <code>probe_repo_gotchas</code> on Turn 1 and <code>stage_partial_fix</code> on Turn 2, accumulating <code>28,317</code> <strong>characters</strong> on Turn 1 and <code>27,975</code> <strong>characters</strong> on Turn 2 (totaling <code>56,605</code> <strong>characters</strong> of verbose audit stack traces) inside its conversation history:</p>
<pre><code class="language-text">=== COLD-START REPOSITORY GOTCHA TRAP LOG (STAGE #1) ===
Status: FAILED (4 repository gotchas triggered due to missing cross-session MEMORY.md)
[COLD_START_GOTCHA_AUDIT_001] stage=1 invariant_probe=GOTCHA_02 trace_digest=0000000000000000000000000000000000000000000000000000000000000001
  File '/var/lib/ai-lab-e20-vm224-01/work_cold_start/settlement.py', line 21, in verify_repo_invariants
    AssertionError: Cold-start session rediscovered repository gotcha #2 via trial-and-error failure.
... (28,000+ chars of repetitive invariant audit traces) ...
TRIGGERED_GOTCHA [validator.py] (validator_contract_violation): Failed JPY currency, empty list check, bool rejection, zero unit_cents, 1000 bps, or mutated input dict.
TRIGGERED_GOTCHA [settlement.py] (settlement_order_or_reserve_boundary): Lines reordered by sorted(order) or 10% reserve charged at chargeback_bps == 300.
TRIGGERED_GOTCHA [settlement.py] (settlement_tier_discount_or_rebate_cap): Missed &gt;=15000 inclusive discount boundary, ENTERPRISE 10% below 15000, or min(fee_rebate, after_discount) cap.
TRIGGERED_GOTCHA [statement.py] (statement_delimiter_or_signed_zero): Used ':' instead of '#' between merchant and currency or omitted '-' sign when discount/rebate is 0.
</code></pre>
<hr />
<h2>4. Code-Level Postmortem: Why Mode A Failed 3 out of 20 Test Cases</h2>
<p>Comparing the final submitted code bundles (<code>cold-start-bundle.json</code> vs. <code>episodic-memory-bundle.json</code>) reveals the exact mechanism of <strong>Attention Dilution</strong>:</p>
<pre><code class="language-python"># ❌ Mode A (Cold-Start, Turn 3 Final Submission — from cold-start-bundle.json)
        if cnt &lt;= 0 or cents &lt;= 0 or not (0 &lt;= bps &lt;= 1000):
            raise ValueError("numeric bounds violated")
        out.append({
            **item,
            "merchant_id": mid.strip().upper(),
            "currency": cur.strip().upper(),
        })
</code></pre>
<pre><code class="language-python"># ✅ Mode B (Episodic Gotchas Memory, Turn 1 Final Submission — from episodic-memory-bundle.json)
        if cnt &lt;= 0 or cents &lt; 0 or not (0 &lt;= bps &lt;= 1000):
            raise ValueError("numeric bounds violated")

        out.append({
            "merchant_id": mid.strip().upper(),
            "currency": cur.strip().upper(),
            "tx_count": cnt,
            "unit_cents": cents,
            "chargeback_bps": bps,
        })
</code></pre>
<p>By Turn 3, Mode A was carrying <strong>29,771 input tokens</strong> of noisy logs. While it managed to fix <code>settlement.py</code>, <code>statement.py</code>, <code>"JPY"</code>, <code>bool</code> rejection, and <code>chargeback_bps &lt;= 1000</code>, its diluted attention missed two fine-grained details in <code>validator.py</code>:</p>
<ol>
<li><strong>Left</strong> <code>cents &lt;= 0</code> <strong>instead of</strong> <code>cents &lt; 0</code><strong>:</strong> Rejecting <code>unit_cents == 0</code> (<code>$0</code> auth probes) caused both unit test <code>validator_05_zero_unit_cents_allowed_negative_rejected</code> and E2E test <code>e2e_03_zero_amount_auth_statement</code> to fail.</li>
<li><strong>Used</strong> <code>{**item, ...}</code> <strong>spread instead of constructing a clean 5-key dict:</strong> Extra unvalidated keys from the input dict leaked into the normalized output, failing <code>validator_01_normalize_and_non_mutation</code>.</li>
</ol>
<p>Mode B, reading <code>[GOTCHA-02 / validator.py]</code> inside a clean <strong>2,078-token</strong> Turn 1 prompt, nailed every single invariant on the first try.</p>
<h3>All 20 Hidden Test Case Results (Mode A vs. Mode B)</h3>
<table>
<thead>
<tr>
<th align="center">#</th>
<th align="left">Test Case ID</th>
<th align="left">Invariant / Boundary Verified</th>
<th align="center">Mode A (<code>Cold-Start</code>)</th>
<th align="center">Mode B (<code>MEMORY.md</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="left"><code>validator_01_normalize_and_non_mutation</code></td>
<td align="left">Trim, uppercase, <strong>strip extra keys (strict 5-key dict)</strong>, no caller mutation</td>
<td align="center"><strong>❌ FAIL</strong> (<code>**item</code> leaked keys)</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">2</td>
<td align="left"><code>validator_02_empty_and_non_list_rejected</code></td>
<td align="left">Reject <code>[]</code>, <code>None</code>, and non-dict elements with <code>ValueError</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">3</td>
<td align="left"><code>validator_03_blank_or_non_str_merchant_and_unknown_currency_rejected</code></td>
<td align="left">Reject blank merchant ID / unknown currency; allow <code>JPY</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">4</td>
<td align="left"><code>validator_04_tx_count_and_chargeback_bps_bounds_0_to_1000</code></td>
<td align="left">Allow <code>chargeback_bps</code> <code>0</code> and <code>1000</code> (inclusive); reject out-of-range</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">5</td>
<td align="left"><code>validator_05_zero_unit_cents_allowed_negative_rejected</code></td>
<td align="left"><strong>Allow</strong> <code>unit_cents == 0</code> <strong>(</strong><code>$0</code> <strong>auth)</strong>; reject negative amounts</td>
<td align="center"><strong>❌ FAIL</strong> (<code>cents &lt;= 0</code>)</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">6</td>
<td align="left"><code>validator_06_bool_rejected_on_all_int_fields</code></td>
<td align="left">Reject Python <code>bool</code> (<code>True</code>/<code>False</code>) on all integer fields</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">7</td>
<td align="left"><code>settlement_01_first_seen_order_and_aggregation</code></td>
<td align="left">Preserve first-seen <code>(merchant_id, currency)</code> order and sum <code>tx_count</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">8</td>
<td align="left"><code>settlement_02_conflicting_unit_cents_or_bps_rejected</code></td>
<td align="left">Reject conflicting <code>unit_cents</code> or <code>chargeback_bps</code> for same key</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">9</td>
<td align="left"><code>settlement_03_exact_chargeback_bps_300_vs_301_boundary</code></td>
<td align="left">Exact boundary: <code>300 bps</code> (0 reserve) vs. <code>301 bps</code> (10% reserve)</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">10</td>
<td align="left"><code>settlement_04_exact_15000_growth_discount_boundary</code></td>
<td align="left"><code>GROWTH</code> tier boundary: <code>14999</code> (0%) vs. <code>15000</code> (10% discount)</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">11</td>
<td align="left"><code>settlement_05_enterprise_tiered_10_vs_20_discount</code></td>
<td align="left"><code>ENTERPRISE</code> tier: <code>&lt; 15000</code> (10%) vs. <code>&gt;= 15000</code> (20%)</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">12</td>
<td align="left"><code>settlement_06_floor_truncation_reserve_and_fee</code></td>
<td align="left">Integer floor division (<code>//</code>) for reserve and wire fee</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">13</td>
<td align="left"><code>settlement_07_rebate_capped_at_after_discount</code></td>
<td align="left">Cap rebate via <code>min(fee_rebate, after_discount)</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">14</td>
<td align="left"><code>settlement_08_invalid_tier_or_bool_rebate_rejected</code></td>
<td align="left">Reject unknown tier names and boolean <code>fee_rebate</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">15</td>
<td align="left"><code>settlement_09_lowercase_tier_and_multi_currency_grouping</code></td>
<td align="left">Normalize lowercase tier names and group multi-currency lines</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">16</td>
<td align="left"><code>e2e_01_growth_below_15000_minus_zero_and_hash_separator</code></td>
<td align="left">E2E: <code>#</code> delimiter and <code>TIER_DISCOUNT: -0</code> / <code>REBATE_APPLIED: -0</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">17</td>
<td align="left"><code>e2e_02_enterprise_reserve_and_rebate_statement</code></td>
<td align="left">E2E: Full <code>ENTERPRISE</code> reserve + 20% discount + rebate statement</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">18</td>
<td align="left"><code>e2e_03_zero_amount_auth_statement</code></td>
<td align="left"><strong>E2E: End-to-end processing of</strong> <code>unit_cents == 0</code> <strong>(</strong><code>$0</code> <strong>auth)</strong></td>
<td align="center"><strong>❌ FAIL</strong> (Rejected in Stage 1)</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">19</td>
<td align="left"><code>e2e_04_line_order_preserved_in_statement</code></td>
<td align="left">E2E: Preserve first-seen line order in rendered statement</td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center">20</td>
<td align="left"><code>e2e_05_error_propagation_across_stages</code></td>
<td align="left">E2E: Propagate Stage 2 conflicting unit price <code>ValueError</code></td>
<td align="center">✅ PASS</td>
<td align="center"><strong>✅ PASS</strong></td>
</tr>
<tr>
<td align="center"><strong>Total</strong></td>
<td align="left"><strong>20 Hidden Cases</strong></td>
<td align="left">—</td>
<td align="center"><code>17 / 20</code> <strong>(85.0%)</strong></td>
<td align="center"><code>20 / 20</code> <strong>(100%)</strong></td>
</tr>
</tbody></table>
<hr />
<h2>5. Practical Takeaway: How to Structure Your Own <code>MEMORY.md</code></h2>
<p>A <strong>442-token investment</strong> saved <strong>44,482 input tokens</strong> (a <strong>100x+ token ROI</strong>) while eliminating 3 subtle edge-case bugs.</p>
<p>To get the same result in your own repositories with Claude Code, Cursor, or custom agents:</p>
<ul>
<li><strong>Don't turn</strong> <code>CLAUDE.md</code> <strong>or</strong> <code>MEMORY.md</code> <strong>into a 500-line textbook.</strong> (As we proved in <code>E35</code>, monolithic instruction files hurt performance.)</li>
<li>Instead, maintain a <strong>10-to-20 line</strong> <code>MEMORY.md</code> containing <em>only</em> <code>[GOTCHA-ID / filename] 1-line invariant</code> entries distilled from actual test failures or PR review catches:</li>
</ul>
<pre><code class="language-markdown"># MEMORY.md (Repository Gotchas — Keep under 20 lines)
- [GOTCHA-01 / validator.py] `isinstance(x, bool)` is True for `int` in Python; always reject `bool` before checking `isinstance(x, int)`.
- [GOTCHA-02 / validator.py] `unit_cents == 0` ($0 auth) and `chargeback_bps == 1000` are valid; construct a new 5-key dict (never `**item` or mutate in-place).
- [GOTCHA-03 / settlement.py] Keep first-seen order (`for k in order:`); apply 10% reserve strictly when `chargeback_bps &gt; 300`.
- [GOTCHA-04 / statement.py] Use `#` between merchant and currency (`{mid}#{cur}:`), and format zero discount/rebate with a leading minus (`-0`).
</code></pre>
<hr />
<h3>🔗 Continue Reading the Series</h3>
<ul>
<li>🚀 <strong>Master Benchmark Hub (All 41 Patterns Ranked):</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox & systemd Sandbox]]></title><description><![CDATA[💡 Methodology & Eval Infrastructure Deep Dive This article is the Methodology & Architecture companion to our AI Labo Agent Harness Engineering Benchmark…]]></description><link>https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox</link><guid isPermaLink="true">https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox</guid><category><![CDATA[AI]]></category><category><![CDATA[Security]]></category><category><![CDATA[Linux]]></category><category><![CDATA[proxmox]]></category><category><![CDATA[architecture]]></category><category><![CDATA[vm]]></category><category><![CDATA[Sandbox]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sat, 31 Oct 2026 12:44:03 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>Methodology &amp; Eval Infrastructure Deep Dive</strong></p>
<p>This article is the <strong>Methodology &amp; Architecture companion</strong> to our <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark"><strong>AI Labo Agent Harness Engineering Benchmark Series (41 Patterns Tested)</strong></a>. Here we explain how we designed an isolated evaluation infrastructure combining <strong>Proxmox VE (0-NIC VMs), QEMU Guest Agent (</strong><code>virtio-serial</code><strong>), a budget-enforcing Mac relay, and Linux UID /</strong> <code>systemd</code> <strong>sandboxing</strong> to prevent test peeking, reward hacking, and API key leaks—along with a candid look at the <strong>current security boundaries and remaining limitations</strong> of our setup.</p>
</blockquote>
<hr />
<h2>TL;DR</h2>
<p>When benchmarking autonomous AI coding agents fairly and safely, engineers inevitably run into a <strong>three-way dilemma (trilemma)</strong>:</p>
<ol>
<li><strong>Eval Cheating / Reward Hacking:</strong> If the AI agent and the grading test suite share the same user privileges and filesystem view, a capable model may inspect the test cases to hardcode edge-case branches or modify the test files (<code>pytest.skip()</code>, weakening assertions) instead of fixing the underlying bug.</li>
<li><strong>API Key Exposure &amp; Runaway Spend:</strong> Passing <code>GEMINI_API_KEY</code> or <code>OPENAI_API_KEY</code> directly into the guest environment creates a risk of credential leakage via logs/prompts and runaway billing if an agent enters an infinite retry loop.</li>
<li><strong>Network Isolation vs. Cloud LLM Access:</strong> Stripping network interfaces (NICs) from the sandbox VM prevents unauthorized external traffic, but also cuts off direct HTTPS access to cloud LLM APIs.</li>
</ol>
<p>To solve these challenges simultaneously, we combined a mini-PC running <strong>Proxmox VE</strong> with a <strong>macOS controller</strong> over a 1-to-1 <strong>Tailscale</strong> link to build the <strong>AI Labo Evaluation Sandbox</strong>:</p>
<ul>
<li><strong>Network-Isolated VMs (0 NICs) + Inspected Relay Proxy:</strong> Experiment VMs have zero network interfaces. The sole external data path is structured JSON passed over QEMU Guest Agent (<code>virtio-serial</code>) to a macOS relay that loads the API key from the OS Keychain into memory once and enforces strict call, token, and USD budget caps (<strong>zero API keys inside the VM</strong>).</li>
<li><strong>OS-Level Separation via Linux UIDs and</strong> <code>systemd</code> <strong>(</strong><code>ProtectSystem=strict</code> <strong>/</strong> <code>InaccessiblePaths</code><strong>):</strong> While the agent (<code>labexp</code>) runs, <code>ProtectSystem=strict</code> mounts the filesystem read-only (except for pre-created target <code>.py</code> files bound via <code>ReadWritePaths</code>), and <code>InaccessiblePaths</code> blocks access to the grading suite (<code>evaluation/</code>).</li>
<li><strong>0.8s Cached ISO Generation + Disposable Full-Clone VMs:</strong> Pre-verified <code>.deb</code> packages (verified via <code>InRelease</code> ➔ <code>Packages</code> SHA-256 chains) and experiment payloads are burned into read-only <code>iso9660</code> CD-ROM images in <del>0.8 seconds, mounted onto a fresh full clone of a stopped Debian 13 base VM, and executed end-to-end in **</del>60–90 seconds per experiment**.</li>
</ul>
<hr />
<h2>1. Why Naive Single-User Execution Distorts Agent Benchmarks</h2>
<p>Anyone who has evaluated coding agents at scale has seen <strong>Reward Hacking</strong> firsthand: the agent reports that all tests pass on Turn 1, but <code>git diff</code> reveals that instead of fixing the bug, it modified the test runner or weakened an assertion.</p>
<p>Running the <strong>Agent (Generator)</strong> and the <strong>Grader (Verifier)</strong> under the same user and writable filesystem introduces four structural failure modes:</p>
<table>
<thead>
<tr>
<th align="left">Failure Mode in Naive Setups</th>
<th align="left">Root Cause</th>
<th align="left">AI Labo Mitigation</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>1. Test Peeking (Eval Cheating)</strong></td>
<td align="left">If hidden grading tests are readable by the agent's UID, file-search tools can read test inputs and hardcode answers.</td>
<td align="left">Separate UID (<code>labjudge</code>, <code>mode 0750</code>) + <code>systemd</code> <code>InaccessiblePaths</code> denies access (<code>EACCES</code>) to the grading directory during agent execution.</td>
</tr>
<tr>
<td align="left"><strong>2. Tampering with Tests or Configs</strong></td>
<td align="left">A writable working directory allows the agent to overwrite test files, runners, or config files.</td>
<td align="left"><code>ProtectSystem=strict</code> mounts the filesystem read-only; <code>ReadWritePaths</code> permits in-place writes <em>only</em> to the existing inodes of target <code>.py</code> files.</td>
</tr>
<tr>
<td align="left"><strong>3. API Key Leaks &amp; Unbounded Spend</strong></td>
<td align="left">Storing API keys in guest environment variables risks exposure in logs and unthrottled API loops.</td>
<td align="left"><strong>Zero NICs and zero API keys inside the VM.</strong> The Mac relay enforces <code>maxOutputTokens</code>, cumulative token limits, and micro-USD budget caps.</td>
</tr>
<tr>
<td align="left"><strong>4. Cross-Experiment Environment Drift</strong></td>
<td align="left">Reusing the same environment across runs leaves behind temp files, caches, or altered state.</td>
<td align="left">Every experiment full-clones a stopped <strong>Debian 13 Golden Base VM</strong> (<code>VM9300</code>) and mounts immutable <code>iso9660</code> CD-ROMs.</td>
</tr>
</tbody></table>
<blockquote>
<p>💡 <strong>Why Proxmox VMs +</strong> <code>systemd</code> <strong>Instead of Docker Containers?</strong> You <em>can</em> achieve similar logical separation in Docker by splitting agent and grader containers, passing <code>--network none</code> and <code>--read-only</code>, and keeping secrets on a host proxy. We chose <strong>Proxmox VMs +</strong> <code>systemd</code> for two reasons:</p>
<ol>
<li><strong>Kernel Isolation:</strong> Avoiding shared host kernels between the controller/host and disposable guest environments.</li>
<li><strong>Full-Clone &amp; ISO Reproducibility:</strong> LVM full clones combined with immutable <code>iso9660</code> virtual CD-ROMs make it trivial to pin and audit exact VM configuration SHA-256 digests before and after every run.</li>
</ol>
</blockquote>
<hr />
<h2>2. System Architecture (Mac × Tailscale × Proxmox × 0-NIC VM)</h2>
<p>The infrastructure is split across a <strong>macOS Controller/Relay</strong> and a <strong>Proxmox VE Host</strong> connected via a 1-to-1 peer-restricted Tailscale VPN:</p>
<pre><code class="language-text">+-------------------------------------------------------------------------+
| 1. Controller &amp; Relay Host (macOS)                                      |
|  - OS Keychain / 0400 Secret --&gt; Loaded once into memory at startup     |
|  - Experiment Orchestrator   --&gt; Drives Phase 1 (ISO) &amp; Phase 2 (Run)   |
|  - Private API Relay         --&gt; Enforces call, token &amp; micro-USD caps  |
|  - Evidence Store            --&gt; Verifies &amp; saves 7 artifacts + SHA256  |
+------------------------------------+------------------------------------+
                                     |
         1-to-1 Peer-Restricted VPN  |  (SSH Control + Peer-Pinned HTTP)
                                     v
+-------------------------------------------------------------------------+
| 2. Virtualization Host (Proxmox VE Mini-PC)                             |
|  - Host Controller (driver.py)  --&gt; Allowlisted qm/pvesh + Watchdogs    |
|  - Local ISO Builder            --&gt; Verified .deb cache -&gt; iso9660 CDs  |
|  - Golden Base VM (VM9300)      --&gt; Stopped Debian 13 clone source      |
|                                                                         |
|  +-------------------------------------------------------------------+  |
|  | 3. Disposable Sandbox VM (Full Clone per Experiment / 0 NICs)     |  |
|  |  - serial0 (/dev/ttyS0) : One-way boot &amp; QGA readiness signal     |  |
|  |  - virtio-serial (QGA)  : Bidirectional RPC via `qm guest exec`   |  |
|  |  - ide2 &amp; ide3 (CD-ROM) : Read-only offline packages &amp; payload    |  |
|  |                                                                   |  |
|  |  +-----------------------------+  +----------------------------+  |  |
|  |  | Agent Sandbox (UID: labexp) |  | Grader Sandbox (labjudge)  |  |  |
|  |  | - PrivateNetwork=yes        |  | - Runs AFTER agent exits   |  |  |
|  |  | - ProtectSystem=strict      |  | - Evaluates 20 hidden cases|  |  |
|  |  | - InaccessiblePaths=/opt/.. |  |   + 6 boundary mutants     |  |  |
|  |  |   /evaluation (EACCES)      |  | - Writes grader-output.json|  |  |
|  |  +-----------------------------+  +----------------------------+  |  |
|  +-------------------------------------------------------------------+  |
+-------------------------------------------------------------------------+
</code></pre>
<h3>Full Component Matrix (15 Components, Privileges, and Security Roles)</h3>
<table>
<thead>
<tr>
<th align="left">Environment</th>
<th align="left">Component</th>
<th align="left">Execution Identity</th>
<th align="left">Primary Role</th>
<th align="left">Security &amp; Integrity Meaning</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>External Cloud</strong></td>
<td align="left">Cloud LLM API</td>
<td align="left">Managed Service</td>
<td align="left"><code>countTokens</code> &amp; <code>generateContent</code></td>
<td align="left">Accepts HTTPS only from the Mac relay; unreachable from the guest VM.</td>
</tr>
<tr>
<td align="left"><strong>Mac Controller</strong></td>
<td align="left">OS Keychain / Secret</td>
<td align="left"><code>Security.framework</code> / <code>0400</code></td>
<td align="left">Stores LLM API key</td>
<td align="left">Never written to <code>.env</code> or VM disks; read once into relay memory at startup.</td>
</tr>
<tr>
<td align="left"><strong>Mac Controller</strong></td>
<td align="left">Orchestrator</td>
<td align="left">Local user</td>
<td align="left">Runs Phase 1 (ISO) &amp; Phase 2 (VM run)</td>
<td align="left">Invokes the Proxmox host driver over SSH and verifies final evidence hashes.</td>
</tr>
<tr>
<td align="left"><strong>Mac Controller</strong></td>
<td align="left">Private API Relay</td>
<td align="left">Local user (<code>serve_once.py</code>)</td>
<td align="left">Proxies LLM calls &amp; enforces budgets</td>
<td align="left">Enforces <strong>max calls, input/output token caps (</strong><code>maxOutputTokens</code><strong>), and micro-USD caps</strong>.</td>
</tr>
<tr>
<td align="left"><strong>Mac Controller</strong></td>
<td align="left">Evidence Storage</td>
<td align="left">Local filesystem</td>
<td align="left">Stores 7 evidence files + SHA256 manifest</td>
<td align="left">Preserves tamper-evident records of prompts, actions, token usage, and test results.</td>
</tr>
<tr>
<td align="left"><strong>Private VPN</strong></td>
<td align="left">Tailscale 1-to-1 Link</td>
<td align="left">Encrypted Mesh VPN</td>
<td align="left">Mac ⇔ Proxmox control &amp; relay traffic</td>
<td align="left">Pins source peer IP (<code>100.x.x.x</code>) so LAN or external hosts cannot hit the relay.</td>
</tr>
<tr>
<td align="left"><strong>Proxmox Host</strong></td>
<td align="left">Host Controller</td>
<td align="left"><code>root</code> (<code>driver.py</code>)</td>
<td align="left">Clones VM, drives QGA, runs watchdogs</td>
<td align="left">Executes only allowlisted <code>qm</code> / <code>pvesh</code> commands with stage watchdogs.</td>
</tr>
<tr>
<td align="left"><strong>Proxmox Host</strong></td>
<td align="left">Local ISO Builder</td>
<td align="left"><code>builder</code> (<code>builder.py</code>)</td>
<td align="left">Packs verified <code>.deb</code> cache &amp; code into ISOs</td>
<td align="left">Verifies <code>.deb</code> SHA-256 hashes from signed <code>InRelease</code> ➔ <code>Packages</code> metadata.</td>
</tr>
<tr>
<td align="left"><strong>Proxmox Host</strong></td>
<td align="left">Golden Base VM</td>
<td align="left">Stopped (<code>VM9300</code>)</td>
<td align="left">Clean Debian 13 clone source</td>
<td align="left">Kept stopped with pinned config SHA-256 to eliminate cross-run drift.</td>
</tr>
<tr>
<td align="left"><strong>Proxmox Host</strong></td>
<td align="left">Audit Broker VM</td>
<td align="left">Isolated VM (<code>VM100</code>)</td>
<td align="left">Holds ledger hash pins</td>
<td align="left">Verified before and after every experiment to ensure host state integrity.</td>
</tr>
<tr>
<td align="left"><strong>Sandbox VM (0 NIC)</strong></td>
<td align="left"><code>serial0</code> (<code>/dev/ttyS0</code>)</td>
<td align="left">Kernel / Bootstrap</td>
<td align="left">One-way boot progress notification</td>
<td align="left">Emulated 16550 UART used <em>only</em> during initial boot to signal when offline QGA installation finishes.</td>
</tr>
<tr>
<td align="left"><strong>Sandbox VM (0 NIC)</strong></td>
<td align="left"><code>virtio-serial</code> (QGA)</td>
<td align="left"><code>root</code> (<code>guest_rpc.py</code>)</td>
<td align="left">Bidirectional host-guest RPC channel</td>
<td align="left">Uses <code>org.qemu.guest_agent.0</code> (<code>.qga</code> socket) via <code>qm guest exec</code> to exchange JSON.</td>
</tr>
<tr>
<td align="left"><strong>Sandbox VM (0 NIC)</strong></td>
<td align="left">Virtual CD-ROMs × 2</td>
<td align="left">Read-only (<code>iso9660</code>)</td>
<td align="left">Delivers <code>.deb</code> deps (<code>ide2</code>) &amp; payload (<code>ide3</code>)</td>
<td align="left">Immutable media inside the guest; <code>install.py</code> verifies payload SHA-256 before extraction.</td>
</tr>
<tr>
<td align="left"><strong>Sandbox VM (0 NIC)</strong></td>
<td align="left">Agent Sandbox</td>
<td align="left">Unprivileged <code>labexp</code></td>
<td align="left">Runs the AI agent under test (Mode A / B)</td>
<td align="left"><code>ProtectSystem=strict</code> mounts <code>/</code> read-only; <code>InaccessiblePaths</code> blocks access to <code>evaluation/</code>.</td>
</tr>
<tr>
<td align="left"><strong>Sandbox VM (0 NIC)</strong></td>
<td align="left">Grader Sandbox</td>
<td align="left">Unprivileged <code>labjudge</code></td>
<td align="left">Runs independent grading suite after agent exits</td>
<td align="left">Starts only after <code>agent.service</code> stops; evaluates snapshots against 20 hidden test cases.</td>
</tr>
</tbody></table>
<hr />
<h2>3. Core Mechanism 1: OS-Level Separation via Linux UIDs and <code>systemd</code></h2>
<p>Inside each experiment VM, the <strong>Agent (</strong><code>labexp</code><strong>)</strong> and the <strong>Independent Grader (</strong><code>labjudge</code><strong>)</strong> run under separate Linux UIDs and separate <code>systemd</code> mount namespaces.</p>
<p>Before starting the agent, the root installer (<code>guest/install.py</code>) sets strict filesystem ownership and permissions:</p>
<ul>
<li><strong>Grading directory (</strong><code>/opt/.../evaluation</code><strong>)</strong>: Owned by <code>root:labjudge</code> with mode <code>0750</code> (files <code>0640</code>), preventing <code>labexp</code> from reading it even at the basic DAC level.</li>
<li><strong>Work directories (</strong><code>work_cold_start/</code><strong>,</strong> <code>work_episodic_memory/</code><strong>)</strong>: Owned by <code>root:root</code> with mode <code>0555</code> (read/traverse only). Only the 3 target Python files inside each directory (<code>validator.py</code>, <code>settlement.py</code>, <code>statement.py</code>) are pre-created with <code>labexp</code> ownership (<code>0644</code>).</li>
</ul>
<p>Below are the actual <code>systemd</code> unit files from experiment <code>E20</code> (<code>VM224</code>):</p>
<h3>Agent Execution Unit (<code>ai-lab-e20-01-agent.service</code>)</h3>
<pre><code class="language-ini">[Unit]
Description=AI Lab E20 Episodic Gotchas Memory Isolated Agent Runner (VM224)

[Service]
Type=oneshot
User=labexp20
Group=labexp20
WorkingDirectory=/var/lib/ai-lab-e20-vm224-01
ExecStart=/usr/bin/python3 -I -B /opt/ai-lab-e20-vm224-01/guest/run_guest.py
Restart=no
KillMode=control-group
TimeoutStartSec=240
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ProtectSystem=strict
ProtectHome=yes
CapabilityBoundingSet=
AmbientCapabilities=
# Under ProtectSystem=strict, ONLY outbox and the 6 pre-created target files (3 files x 2 modes) are writable
ReadWritePaths=/var/lib/ai-lab-e20-vm224-01/outbox /var/lib/ai-lab-e20-vm224-01/work_cold_start/validator.py /var/lib/ai-lab-e20-vm224-01/work_cold_start/settlement.py /var/lib/ai-lab-e20-vm224-01/work_cold_start/statement.py /var/lib/ai-lab-e20-vm224-01/work_episodic_memory/validator.py /var/lib/ai-lab-e20-vm224-01/work_episodic_memory/settlement.py /var/lib/ai-lab-e20-vm224-01/work_episodic_memory/statement.py
# Grading code (evaluation/), initial fixtures, and evidence directories are overlaid with empty 0000 mounts
InaccessiblePaths=/var/lib/ai-lab-e20-vm224-01/root /var/lib/ai-lab-e20-vm224-01/evidence /var/lib/ai-lab-e20-vm224-01/grade /opt/ai-lab-e20-vm224-01/evaluation /opt/ai-lab-e20-vm224-01/fixtures
</code></pre>
<h3>Independent Grader Unit (<code>ai-lab-e20-01-grade.service</code>)</h3>
<p>Only after <code>agent.service</code> terminates and the root RPC handler (<code>guest_rpc.py</code>) copies a snapshot of the edited files into <code>grade/</code> does the grader service launch as <code>labjudge20</code>:</p>
<pre><code class="language-ini">[Unit]
Description=AI Lab E20 Episodic Gotchas Memory Isolated Grader (VM224)

[Service]
Type=oneshot
User=labjudge20
Group=labjudge20
WorkingDirectory=/var/lib/ai-lab-e20-vm224-01/grade
ExecStart=/usr/bin/python3 -I -B /opt/ai-lab-e20-vm224-01/evaluation/grade_comparison.py
Restart=no
KillMode=control-group
TimeoutStartSec=30
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ProtectSystem=strict
ProtectHome=yes
CapabilityBoundingSet=
AmbientCapabilities=
# The grader can only write to the single grader-output.json file
ReadWritePaths=/var/lib/ai-lab-e20-vm224-01/grader-output.json
InaccessiblePaths=/var/lib/ai-lab-e20-vm224-01/root /var/lib/ai-lab-e20-vm224-01/evidence /var/lib/ai-lab-e20-vm224-01/inbox /var/lib/ai-lab-e20-vm224-01/outbox /var/lib/ai-lab-e20-vm224-01/work_cold_start /var/lib/ai-lab-e20-vm224-01/work_episodic_memory
</code></pre>
<p>This layered setup enforces three protections during agent execution:</p>
<ol>
<li><strong>Blocking Access to Grading Contents (DAC</strong> <code>0750</code> <strong>+</strong> <code>InaccessiblePaths</code><strong>):</strong><code>InaccessiblePaths</code> bind-mounts an empty directory with <code>0000</code> permissions over the target path in the unit's mount namespace. While listing <code>/opt/ai-lab-.../</code> still shows the directory entry <code>evaluation</code>, any attempt to read or traverse into it fails immediately with <code>Permission denied (EACCES)</code>.</li>
<li><strong>Preventing File Creation, Deletion, or Rename (</strong><code>ProtectSystem=strict</code> <strong>+ Root-Owned</strong> <code>0555</code> <strong>Workdir):</strong> The primary mechanism blocking file creation or deletion is <code>ProtectSystem=strict</code>, which mounts the entire filesystem read-only and bind-mounts only the 6 listed <code>.py</code> files as writable (backed up by root-owned <code>0555</code> directory permissions so <code>labexp</code> cannot <code>chmod</code> the parent directory). Running Python with <code>-I</code> (isolated mode) also disables user site-packages and <code>PYTHONPATH</code> injection.</li>
<li><strong>Structured JSON Actions Only:</strong> In our benchmark suite (<code>E04</code>–<code>E44</code>), the agent does not execute arbitrary shell commands; both the Mac relay (<code>serve_once.py</code>) and guest harness (<code>pipeline_checks.py</code>) validate structured JSON tool calls against a strict schema, verify <code>ast.parse()</code> syntax, and overwrite existing file inodes in-place via <code>os.open(..., os.O_WRONLY | os.O_TRUNC | os.O_NOFOLLOW)</code>.</li>
</ol>
<blockquote>
<p>💡 <strong>How We Use Mutation Testing (</strong><code>Mutation Kill Rate</code><strong>)</strong> In experiments where the agent submits both production code and a test suite (<code>test_suite.py</code> in <code>E06</code> Blind TDD, <code>E07</code>, <code>E08</code>, <code>E10</code>), passing functional tests isn't enough—we also need to know if the agent wrote weak, self-serving tests. Our grader (<code>judges.py</code>) runs the agent's <code>test_suite.py</code> against <strong>6 deliberate boundary mutants</strong> of the reference implementation (e.g., changing <code>&gt;= 10000</code> to <code>&gt; 10000</code>, removing <code>isinstance(x, bool)</code> checks, omitting <code>-0</code> formatting) to measure the exact <strong>Mutation Kill Rate (</strong><code>0/6</code> <strong>to</strong> <code>6/6</code><strong>)</strong>.</p>
</blockquote>
<hr />
<h2>4. Core Mechanism 2: Dual Serial Channels (<code>serial0</code> vs. <code>virtio-serial</code>) &amp; Budget-Gated Relay</h2>
<p>Because the experiment VM has <strong>zero network interfaces</strong> (verified at boot by asserting that <code>/sys/class/net</code> contains only <code>lo</code>), it is a <strong>network-isolated VM whose sole external channel is an inspected host-mediated relay</strong>.</p>
<p>We use two distinct serial mechanisms between the Proxmox host and the guest VM for different phases of the lifecycle:</p>
<ol>
<li><code>serial0</code> <strong>(</strong><code>/dev/ttyS0</code> <strong>inside guest — emulated 16550 UART):</strong><ul>
<li><strong>Role:</strong> Used <em>exclusively</em> for one-way boot progress notifications during the initial 25-second offline bootstrap (before <code>qemu-guest-agent</code> is installed from the CD-ROM). The host controller (<code>driver.py</code>) monitors <code>/var/run/qemu-server/&lt;vmid&gt;.serial0</code> until it receives the <code>common_prepared_QGA_acceptance_pending</code> JSON marker.</li>
</ul>
</li>
<li><code>virtio-serial</code> <strong>(</strong><code>org.qemu.guest_agent.0</code> <strong>/</strong> <code>.qga</code> <strong>socket — QEMU Guest Agent):</strong><ul>
<li><strong>Role:</strong> Used for all subsequent bidirectional RPC (<code>qm guest exec &lt;vmid&gt;</code>), including payload installation, polling the agent's <code>Outbox</code>, delivering LLM responses to the <code>Inbox</code>, and retrieving the 7 final evidence files.</li>
</ul>
</li>
</ol>
<h3>End-to-End Execution Lifecycle (~60–90 Seconds per Experiment)</h3>
<table>
<thead>
<tr>
<th align="center">Step</th>
<th align="left">Phase &amp; Actor</th>
<th align="left">Operation</th>
<th align="left">Typical Duration / Security Gate</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>1</strong></td>
<td align="left"><strong>Phase 1: ISO Build</strong><br />(Mac ➔ Proxmox)</td>
<td align="left">Packs SHA256-verified <code>.deb</code> cache and experiment payload into read-only <code>iso9660</code> image.</td>
<td align="left"><strong>~0.8s</strong> (with warm package cache)</td>
</tr>
<tr>
<td align="center"><strong>2</strong></td>
<td align="left"><strong>Phase 2: Pre-Flight</strong><br />(Mac &amp; Proxmox)</td>
<td align="left">Mac loads API key from Keychain into memory; Proxmox verifies <code>VM9300</code> &amp; broker ledger hashes.</td>
<td align="left">Zero API keys written to disk</td>
</tr>
<tr>
<td align="center"><strong>3</strong></td>
<td align="left"><strong>Clone &amp; Offline Boot</strong><br />(Proxmox ➔ VM)</td>
<td align="left">Full-clones <code>VM9300</code>, attaches 2 CD-ROMs (<code>ide2</code>, <code>ide3</code>), verifies <code>0 NICs</code>, boots VM, installs QGA offline.</td>
<td align="left"><strong>~25s</strong> (notifies host via <code>serial0</code>)</td>
</tr>
<tr>
<td align="center"><strong>4</strong></td>
<td align="left"><strong>Workspace Setup</strong><br />(Proxmox ➔ QGA)</td>
<td align="left"><code>install.py</code> verifies payload SHA-256, sets <code>0750</code> on <code>evaluation/</code> and <code>0555</code> on <code>work/</code>, starts <code>agent.service</code>.</td>
<td align="left">Enforces UID &amp; <code>systemd</code> locks</td>
</tr>
<tr>
<td align="center"><strong>5</strong></td>
<td align="left"><strong>Agent Turn Loop</strong><br />(VM ⇔ PVE ⇔ Mac ⇔ LLM)</td>
<td align="left">Polls <code>Outbox</code> via QGA ➔ Mac relay checks <code>countTokens</code> &amp; <code>maxOutputTokens</code> ➔ calls Gemini API ➔ delivers action via <code>deliver_and_request</code>.</td>
<td align="left"><strong>~15–40s</strong> (1-trip QGA batching)</td>
</tr>
<tr>
<td align="center"><strong>6</strong></td>
<td align="left"><strong>Independent Grading</strong><br />(Proxmox ➔ QGA ➔ VM)</td>
<td align="left">After <code>agent.service</code> stops, copies snapshot to <code>grade/</code> and runs <code>grade.service</code> as <code>labjudge</code>.</td>
<td align="left">Runs 20 hidden tests + 6 mutants</td>
</tr>
<tr>
<td align="center"><strong>7</strong></td>
<td align="left"><strong>Evidence &amp; Shutdown</strong><br />(VM ➔ PVE ➔ Mac)</td>
<td align="left">Collects 7 JSON artifacts + <code>manifest.json</code>, verifies SHA-256 on Mac, gracefully shuts down VM (<code>qm shutdown</code>).</td>
<td align="left"><strong>Tamper-evident</strong> archive locked</td>
</tr>
</tbody></table>
<h3>External Token Accounting &amp; Pre-Call Budget Enforcement (<code>usage.json</code>)</h3>
<p>Because the Mac relay (<code>serve_once.py</code>) mediates every LLM call outside the VM, the agent cannot tamper with token accounting. Before each <code>generateContent</code> call, the relay calls <code>countTokens</code> to verify that <code>cumulative_inputs + prompt_tokens &lt;= MAX_INPUT_TOKENS</code> and <code>cumulative_outputs + maxOutputTokens &lt;= MAX_OUTPUT_TOKENS</code>.</p>
<p>Upon receiving the response, the relay extracts <code>promptTokenCount</code>, <code>candidatesTokenCount</code>, and <code>thoughtsTokenCount</code> <strong>(reasoning tokens)</strong> from <code>usageMetadata</code>, computing <code>billed_out = candidatesTokenCount + thoughtsTokenCount</code> and recording exact costs in <strong>micro-USD (</strong><code>1e-6 USD</code><strong>)</strong> against a hard session cap (e.g., <code>350,000 micro-USD</code> = <code>$0.35</code>).</p>
<hr />
<h2>5. Known Limitations &amp; Future Security Roadmap</h2>
<p>While this architecture is highly effective for benchmarking structured-action coding agents, evaluating <strong>untrusted, adversarial agents with arbitrary code/shell execution</strong> requires addressing several remaining security boundaries. We document these limitations openly for anyone building on this design:</p>
<h3>Limitation 1: [Critical] The Grader (<code>labjudge</code>) Directly <code>exec</code>s Agent-Submitted Code in the Same Process</h3>
<ul>
<li><strong>Current Behavior &amp; Risk:</strong> During agent execution (<code>labexp</code>), <code>InaccessiblePaths</code> prevents the agent from reading <code>evaluation/judges.py</code>. However, once the agent finishes, <code>judges.py</code> runs as <code>labjudge</code> and calls <code>exec(compile(code_bytes, ...), ns)</code> on the submitted <code>validator.py</code> inside the grader process. If an adversarial agent planted a Trojan payload inside <code>validator.py</code> that executes upon import/call—such as reading <code>/opt/.../evaluation/judges.py</code>, inspecting the caller stack frame, or writing a fake passing JSON directly to <code>/var/lib/.../grader-output.json</code> (which is writable by <code>labjudge</code> via <code>ReadWritePaths</code>)—that payload would execute with <code>labjudge</code> privileges during grading. <em>(In our 41 experiments, all submitted code bundles are archived in</em> <code>mode-a/b-bundle.json</code> <em>and verified to contain only pure domain logic, but the architectural boundary itself is porous against active malice.)</em></li>
<li><strong>Planned Remediation (Decoupling Untrusted Runner from Trusted Scorer):</strong><ol>
<li>Split grading into two separate processes/UIDs: an <strong>Untrusted Test Runner</strong> (which imports the candidate code with zero access to <code>judges.py</code> or <code>grader-output.json</code>) and a <strong>Trusted Scorer</strong> (<code>root</code> or a separate UID).</li>
<li>Pass test input vectors to the Untrusted Runner over a pipe (<code>stdin</code>/<code>stdout</code>), compare the returned JSON outputs against expected values in the Trusted Scorer, and have only the Trusted Scorer write <code>grader-output.json</code>.</li>
</ol>
</li>
</ul>
<h3>Limitation 2: Denylist (<code>InaccessiblePaths</code>) Blind Spots &amp; Mount Visibility</h3>
<ul>
<li><strong>Current Behavior &amp; Risk:</strong><code>InaccessiblePaths</code> is a <strong>denylist</strong> mechanism. If any unredacted copy of the experiment payload remains accessible elsewhere (for example, on the mounted <code>iso9660</code> CD-ROM path if not unmounted or restricted before agent launch), a shell-capable agent could read grading assets from the mount source. Additionally, the current unit file can be hardened further:<ul>
<li>Adding <code>ProtectProc=invisible</code> to hide <code>/proc</code> command-line arguments of other processes (such as root's <code>guest_rpc.py</code>).</li>
<li>Adding <code>RestrictAddressFamilies=none</code> (since <code>PrivateNetwork=yes</code> blocks TCP/IP and abstract sockets, but filesystem UNIX domain sockets remain reachable unless address families are restricted).</li>
<li>Adding <code>MemoryMax=</code>, <code>TasksMax=</code>, and <code>LimitFSIZE=</code> to prevent fork bombs or disk-filling DoS writes to <code>outbox/</code>.</li>
</ul>
</li>
<li><strong>Planned Remediation:</strong> Transition from a denylist (<code>InaccessiblePaths</code>) to an <strong>allowlist root filesystem</strong> (<code>TemporaryFileSystem=/</code> + minimal <code>BindReadOnlyPaths=</code>) and audit the unit with <code>systemd-analyze security</code>.</li>
</ul>
<h3>Limitation 3: Tamper-Evident vs. Tamper-Proof Evidence</h3>
<ul>
<li>The SHA-256 manifest (<code>manifest.json</code>) makes the 7 evidence files <strong>tamper-evident</strong> across transit and storage, but it is not cryptographically <strong>tamper-proof</strong> against a host administrator who recalculates both the file and manifest hashes.</li>
</ul>
<hr />
<h2>6. Two Real-World Operational Gotchas We Hit (and How We Fixed Them)</h2>
<p>While running 40+ consecutive VM benchmarks, we encountered two non-obvious operational traps worth sharing:</p>
<h3>Gotcha 1: QGA Timeouts When Passing Non-ASCII Strings Directly to <code>qm guest exec -c</code></h3>
<h4>What We Observed</h4>
<p>When invoking <code>/usr/sbin/qm guest exec &lt;vmid&gt; -- /usr/bin/python3 -c &lt;script_source&gt;</code> from the Proxmox host, early builds of <code>E21</code> that included Japanese UTF-8 characters inside Python docstrings consistently failed with <code>VM qga command 'guest-exec' failed - got timeout</code> (exit code 255), leaving <code>guest-ping</code> unresponsive afterward.</p>
<h4>Analysis &amp; Best Practice Workaround</h4>
<p>While <code>virtio-serial</code> itself is a byte stream and Proxmox's QGA client prepends <code>guest-sync-delimited</code> (<code>\xff</code> delimiter resynchronization), passing large multi-kilobyte scripts containing multi-byte UTF-8 characters directly as command-line arguments to Proxmox's <code>/usr/sbin/qm guest exec</code> CLI (which is implemented in Perl under the hood) triggered encoding/buffer handling stalls.</p>
<ul>
<li><strong>Our Fix:</strong> We enforced a unit-tested <strong>100% Pure ASCII (</strong><code>all(b &lt; 128)</code><strong>) policy</strong> on the bootstrap scripts passed via <code>-c</code> (<code>guest_rpc.py</code> and <code>install.py</code>) and passed all experiment payloads as <strong>Base64-encoded JSON (</strong><code>payload_b64</code><strong>) over</strong> <code>stdin</code>. Across 40+ subsequent VM runs, we observed zero QGA timeouts.</li>
<li><strong>General Recommendation:</strong> Avoid passing raw multi-line script strings to <code>qm guest exec -- python3 -c "..."</code>. Instead, pass a tiny Base64 decoder one-liner (<code>python3 -c "import base64,sys;exec(base64.b64decode(sys.stdin.read()))"</code>) or write the file via <code>guest-file-write</code> first.</li>
</ul>
<h3>Gotcha 2: False-Positive LLM Safety Filter Blocks (<code>PROHIBITED_CONTENT</code>) During Infra Debugging</h3>
<h4>What We Observed</h4>
<p>While using an AI coding assistant on our development machine to debug Proxmox socket states and systemd sandbox rules (not inside the benchmarked VMs themselves), the Gemini API occasionally blocked responses with:</p>
<blockquote>
<p><code>This request was blocked by Gemini's filters. They can occasionally trigger by mistake on safe coding, security, or biology-related queries.</code></p>
</blockquote>
<h4>Analysis &amp; Workaround</h4>
<p>This is a known false positive (<code>promptFeedback.blockReason: "PROHIBITED_CONTENT"</code>) documented on the Google AI Developers Forum that cannot be disabled via standard safety threshold sliders. Because the safety classifier evaluates the full conversation history, accumulating multiple inline snippets referencing raw hypervisor sockets (<code>*.qmp</code>, <code>*.qga</code>) and namespace boundary checks (<code>InaccessiblePaths</code>, <code>runuser</code>) likely resembled VM-escape or privilege-escalation exploit development. Switching our debugging workflow to invoke only standard <code>/usr/sbin/qm guest exec</code> CLI wrappers and pre-committed scripts eliminated these false positives completely (and zero filter blocks ever occurred inside the 41 benchmark runs).</p>
<hr />
<h2>7. The 7 Tamper-Evident Artifacts Collected per Run</h2>
<p>Every metric reported in our benchmark series (<code>E04</code>–<code>E44</code>) is extracted directly from the 7 JSON files collected from the VM and verified against <code>manifest.json</code>:</p>
<ol>
<li><code>run-summary.json</code>: High-level comparison of Mode A vs. Mode B (tests passed, turns, elapsed seconds, tool output chars).</li>
<li><code>usage.json</code>: External relay ledger recording <code>promptTokenCount</code>, <code>candidatesTokenCount</code>, <code>thoughtsTokenCount</code>, and <code>estimated_micro_usd</code> per call.</li>
<li><code>execution-traces.json</code>: Turn-by-turn log of tool calls, output character counts, and sensor/hook triggers.</li>
<li><code>independent-tests.json</code>: Individual pass/fail results for all 20 hidden test cases (plus mutation kill audits where applicable).</li>
<li><code>mode-a-bundle.json</code>: Full snapshot of the final Python files submitted by the baseline agent.</li>
<li><code>mode-b-bundle.json</code>: Full snapshot of the final Python files submitted by the harnessed agent.</li>
<li><code>actions.json</code>: Normalized JSON action payloads returned by the model on every turn.</li>
</ol>
<hr />
<h3>🔗 Explore the Benchmark Results</h3>
<p>Ready to see what we discovered when we ran <strong>41 agent harness patterns</strong> through this sandbox? Check out the master benchmark index and our #1 ranked pattern deep dive:</p>
<ul>
<li>🚀 <strong>Master Benchmark Hub:</strong> <a href="https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark">I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (</strong><code>E20</code><strong>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[I Benchmarked 41 AI Agent Harness Patterns in Isolated VMs: How to Cut Tokens by 90% Without Losing Accuracy]]></title><description><![CDATA[💡 Living Benchmark Index (Last Updated: October 31, 2026 | Verified in Isolated Sandbox VMs: 41 Patterns) Across X/Twitter, Hacker News, and engineering…]]></description><link>https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark</link><guid isPermaLink="true">https://tappers.hashnode.dev/ai-agent-harness-patterns-benchmark</guid><category><![CDATA[architecture]]></category><category><![CDATA[benchmarks]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[Harness]]></category><dc:creator><![CDATA[tappers]]></dc:creator><pubDate>Sat, 31 Oct 2026 12:41:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>💡 <strong>Living Benchmark Index (Last Updated: October 31, 2026 | Verified in Isolated Sandbox VMs: 41 Patterns)</strong></p>
<p>Across X/Twitter, Hacker News, and engineering blogs from OpenAI, Anthropic, Google, Martin Fowler (Thoughtworks), and Databricks, dozens of <strong>"Agent Harness Engineering"</strong> patterns have been proposed to make coding agents (Claude Code, Cursor, Codex, custom agents) faster, cheaper, and more reliable.</p>
<p>To find out <strong>which patterns actually work—and which ones make things worse</strong>, we built an isolated Proxmox VM benchmark infrastructure (<strong>AI Labo</strong>) and ran controlled A/B experiments across <strong>41 harness patterns (<code>E04</code>–<code>E44</code>)</strong> using identical models, identical initial codebases, a hidden 20-case grading suite, and mutation testing.</p>
</blockquote>
<hr />
<h2>0. Introduction: What Is "Harness Engineering"?</h2>
<p>Every engineer deploying AI coding agents in production eventually hits the same wall:
<strong>"Why does the exact same frontier model (Claude, GPT, or Gemini) write a flawless patch in one turn today, and tomorrow burn 80,000 tokens editing the wrong files before breaking the test suite?"</strong></p>
<p>When we started this benchmark series (<code>E04</code>–<code>E24</code>), we weren't setting out to define a new discipline—we were simply testing practical agent workflow ideas from developer communities bottom-up. As the data accumulated, we realized that our findings converged directly on what OpenAI, Anthropic, Google, and Thoughtworks call <strong>"Harness Engineering"</strong>. We then expanded the suite to cover industry-standard patterns (<code>E25</code>–<code>E42</code>) and cross-model / cross-generation scaling laws (<code>E43</code>–<code>E44</code>).</p>
<p>At its core, Harness Engineering boils down to a single equation:</p>
<blockquote>
<p><strong>Agent = Model (Probabilistic Reasoning Core) + Harness (Deterministic Control System)</strong></p>
</blockquote>
<p>An LLM is a non-deterministic token predictor. Turning it into a reliable engineering system requires wrapping it in a <strong>Harness</strong> composed of three control layers:</p>
<ol>
<li><strong>🧭 Guides (Feed-Forward Control — Before Writing Code):</strong> Steering the agent onto the right path <em>before</em> it generates a patch (e.g., directory-pointer <code>AGENTS.md</code>, AST call-graph injection, two-phase read-only plan locks, spec-first gates).</li>
<li><strong>🛡️ Sensors (Feedback Control — After Writing Code):</strong> Deterministically catching errors, regressions, and reward-hacking <em>after</em> an edit so the agent can self-correct (e.g., AST pre-commit hooks, actionable fix-hint linters, cryptographic test locks, loop-detection circuit breakers).</li>
<li><strong>🧹 Context Hygiene (Attention &amp; Memory Management):</strong> Ruthlessly stripping noise that dilutes model attention (e.g., CLI test output token killers, failed-attempt rollback pruning, Tree-sitter skeleton context, episodic <code>MEMORY.md</code> gotchas).</li>
</ol>
<blockquote>
<p>🛡️ <strong>Methodology &amp; Anti-Cheating Sandbox Architecture</strong>
All 41 A/B experiments were executed inside disposable <strong>Debian 13 Proxmox VMs with zero network interfaces (0 NICs)</strong>, controlled via QEMU Guest Agent (<code>virtio-serial</code>) and a budget-enforcing Mac relay, with <strong>Linux UID + <code>systemd</code> (<code>ProtectSystem=strict</code> / <code>InaccessiblePaths</code>) isolation</strong> separating the AI agent (<code>labexp</code>) from the hidden grading suite (<code>labjudge</code>).
👉 For a full technical deep dive into our evaluation infrastructure—including actual <code>systemd</code> unit files and security boundary limitations—read our companion post: <strong><a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></strong>.</p>
</blockquote>
<hr />
<h2>1. Top 7 High-Impact Patterns (Ranked by Token Reduction &amp; Accuracy)</h2>
<p>Among all <strong>41 verified patterns (<code>E04</code>–<code>E44</code>)</strong>, these 7 patterns delivered the most dramatic improvements in <strong>input token reduction</strong>, <strong>first-turn pass rate (zero rework)</strong>, and <strong>defect/mutant detection</strong>:</p>
<table>
<thead>
<tr>
<th align="center">Rank</th>
<th align="left">Pattern Name (Experiment ID)</th>
<th align="center">Input Token Reduction</th>
<th align="center">API Cost Reduction</th>
<th align="center">Speedup</th>
<th align="left">Key Empirical Finding</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>🥇 #1</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Episodic Gotchas Memory (<code>MEMORY.md</code>)</a></strong><br />(<code>E20</code>: Injecting 5 repo-specific gotchas)</td>
<td align="center"><strong>-95.5%</strong><br />(<code>46,560</code> ➔ <code>2,078</code>)</td>
<td align="center"><strong>-83.7%</strong></td>
<td align="center"><strong>2.21x faster</strong></td>
<td align="left">Cold-start exploration hit 2 repo traps and stalled at <code>17/20</code>; injecting a <strong>442-token <code>MEMORY.md</code></strong> achieved a <strong>1-turn <code>20/20</code> pass</strong>.</td>
</tr>
<tr>
<td align="center"><strong>🥈 #2</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Tree-sitter Skeleton Context</a></strong><br />(<code>E21</code>: Signature-only dependency context)</td>
<td align="center"><strong>-94.9%</strong><br />(<code>45,270</code> ➔ <code>2,316</code>)</td>
<td align="center"><strong>-82.6%</strong></td>
<td align="center"><strong>2.34x faster</strong></td>
<td align="left">Feeding full dependency implementations (78k chars) degraded accuracy to <code>17/20</code>; passing <strong>only function signatures &amp; type contracts</strong> hit <code>20/20</code> in 1 turn.</td>
</tr>
<tr>
<td align="center"><strong>🥉 #3</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">CLI Test Output Token Killer</a></strong><br />(<code>E16</code>: Structured JSON failure-diff filter)</td>
<td align="center"><strong>-94.1%</strong><br />(<code>92,385</code> ➔ <code>5,445</code>)</td>
<td align="center"><strong>-87.0%</strong></td>
<td align="center"><strong>1.54x faster</strong></td>
<td align="left">Stripping <code>PASS</code> lines and ANSI noise from raw <code>pytest</code> output (<code>117,391</code> ➔ <code>1,604</code> chars) enabled a clean 1-shot fix.</td>
</tr>
<tr>
<td align="center"><strong>#4</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e19-error-trace-pruning">Error-Trace Pruning (Failed-Attempt Rollback)</a></strong><br />(<code>E19</code>: Pruning failed patches &amp; stack traces)</td>
<td align="center"><strong>-93.0%</strong><br />(<code>59,944</code> ➔ <code>4,210</code>)</td>
<td align="center"><strong>-80.3%</strong></td>
<td align="center"><strong>1.57x faster</strong></td>
<td align="left">Leaving failed code patches in history triggers self-imitation bias; <strong>pruning failed diffs into a 1-line lesson</strong> improved token efficiency by 14.2x.</td>
</tr>
<tr>
<td align="center"><strong>#5</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e14-progressive-tool-search">Progressive Tool Schema Search</a></strong><br />(<code>E14</code>: On-demand MCP tool loading)</td>
<td align="center"><strong>-92.7%</strong><br />(<code>79,773</code> ➔ <code>5,844</code>)</td>
<td align="center"><strong>-84.8%</strong></td>
<td align="center"><strong>1.94x faster</strong></td>
<td align="left">Eliminated the "schema tax" of injecting 32 MCP tool schemas every turn and reduced <strong>decoy tool misfires from <code>2 ➔ 0</code></strong>.</td>
</tr>
<tr>
<td align="center"><strong>#6</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e12-milestone-context-compaction">Milestone Context Compaction</a></strong><br />(<code>E12</code>: Save-game JSON state reset)</td>
<td align="center"><strong>-91.9%</strong><br />(<code>35,869</code> ➔ <code>2,922</code>)</td>
<td align="center"><strong>-72.2%</strong></td>
<td align="center"><strong>1.10x faster</strong></td>
<td align="left">Accumulating full chat history caused spec drift (<code>18/20</code>); <strong>compacting state to JSON at milestones</strong> achieved <code>20/20</code>.</td>
</tr>
<tr>
<td align="center"><strong>#7</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e18-advisor-executor-routing">Advisor-Executor Model Routing</a></strong><br />(<code>E18</code>: Upfront architectural guidance memo)</td>
<td align="center"><strong>-84.9%</strong><br />(<code>27,699</code> ➔ <code>4,172</code>)</td>
<td align="center"><strong>-71.5%</strong></td>
<td align="center"><strong>1.78x faster</strong></td>
<td align="left">Injecting a cross-module design memo on Turn 1 eliminated <strong>2 unplanned rework retries (<code>2 ➔ 0</code>)</strong>.</td>
</tr>
</tbody></table>
<hr />
<h2>2. Why Does the "Harness" Matter More Than the Prompt?</h2>
<p>Across all 41 experiments, five structural anti-patterns consistently caused token explosions and accuracy failures—and five deterministic harness mechanisms solved them:</p>
<table>
<thead>
<tr>
<th align="left">❌ Common Agent Anti-Pattern (Token Bloat &amp; Drift)</th>
<th align="left">✨ Verified Harness Solution (AI Labo)</th>
<th align="left">Measured Impact</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>1. Dumping all tools &amp; full history into a single free loop</strong></td>
<td align="left"><strong>Stage-Gated Directed Graphs (<code>E04</code>) &amp; Progressive Tool Search (<code>E14</code>)</strong></td>
<td align="left"><strong>Input tokens <code>-74%</code> to <code>-93%</code></strong></td>
</tr>
<tr>
<td align="left"><strong>2. Splitting tightly coupled tasks across role-based subagents</strong></td>
<td align="left"><strong>Subagent Folding + Progressive Skill Disclosure (<code>E05</code>)</strong></td>
<td align="left"><strong>2.66x faster, zero handoff loss</strong></td>
</tr>
<tr>
<td align="left"><strong>3. Letting the same agent write code, write tests, and self-review</strong></td>
<td align="left"><strong>Blind TDD (<code>E06</code>) / Adversarial Critic (<code>E10</code>) / Reproduction-First Gate (<code>E22</code>)</strong></td>
<td align="left"><strong>Mutant kill rate <code>0%</code> ➔ <code>100%</code></strong></td>
</tr>
<tr>
<td align="left"><strong>4. Appending raw CLI test logs &amp; failed patches to chat history</strong></td>
<td align="left"><strong>CLI Token Killer (<code>E16</code>) / Trace Pruning (<code>E19</code>) / AST Skeletons (<code>E09</code>, <code>E21</code>)</strong></td>
<td align="left"><strong>Input tokens <code>-93%</code> to <code>-95%</code>, 1-shot pass</strong></td>
</tr>
<tr>
<td align="left"><strong>5. Stuffing 20+ negative prohibitions into a monolithic <code>CLAUDE.md</code></strong></td>
<td align="left"><strong>Deterministic Pre-Commit AST Hooks (<code>E11</code>) + Positive Few-Shot (<code>E24</code>)</strong></td>
<td align="left"><strong>0 architectural rule violations (<code>100%</code>)</strong></td>
</tr>
</tbody></table>
<h3>Key Insight 1: Stage-Gated Graphs Work, but Role-Based Subagents Backfire (<code>E04</code> vs. <code>E05</code>)</h3>
<ul>
<li>In <strong><code>E04</code> (Loops vs. Graphs)</strong>, replacing a single free-form loop with a stage-gated <strong>Directed Graph</strong> (resetting context after each module's deterministic verifier passes) cut input tokens by <strong>-74.4%</strong> and execution time by <strong>-48.0%</strong>.</li>
<li>Conversely, in <strong><code>E05</code> (Subagent Folding)</strong>, splitting the exact same pipeline into <strong>role-based subagents</strong> (Architect ➔ Implementer ➔ QA) passing natural-language handoff summaries caused a "Telephone Game" effect: subtle formatting invariants (like signed zero <code>-0</code>) were lost in summarization, triggering rework that made the multi-agent setup <strong>2.66x slower</strong> and <strong>2.2x more expensive</strong> than a folded single loop with on-demand skill loading.</li>
</ul>
<h3>Key Insight 2: Self-Testing and Self-Review Yield a 0% Mutation Kill Rate (<code>E06</code>, <code>E08</code>, <code>E10</code>, <code>E22</code>)</h3>
<ul>
<li>In <strong><code>E06</code> (Blind TDD)</strong> and <strong><code>E10</code> (Adversarial Critic)</strong>, when a single agent context wrote both the implementation and the unit tests (or reviewed its own code), the agent unconsciously wrote weak "happy-path" tests tailored to pass its own bugs—resulting in a <strong><code>0/6 (0%)</code> mutation kill rate</strong> against boundary mutants.</li>
<li>Isolating the test author into a <strong>Blind Tester (<code>E06</code>)</strong> that sees only <code>SPEC.md</code> (never the implementation), enforcing <strong>Spec-First SDD (<code>E08</code>)</strong>, using an <strong>Adversarial Critic (<code>E10</code>)</strong> rewarded for finding counterexamples, or locking production files until a failing reproduction test is recorded (<strong>Reproduction-First Gate <code>E22</code></strong>) pushed test mutation kill rates and hidden-suite accuracy to <strong><code>100%</code></strong>.</li>
</ul>
<h3>Key Insight 3: 90% of Input Tokens Are Wasted on Exploration, Unused Schemas, and Raw Logs (<code>E09</code>, <code>E14</code>, <code>E16</code>, <code>E19</code>, <code>E21</code>)</h3>
<ul>
<li>Across every experiment, the actual fixed Python code generated by the LLM was only <strong>1,200 to 1,800 output tokens</strong>.</li>
<li>Why did baseline agents consume 30,000 to 92,000 input tokens? Because of <strong>blind grep exploration (<code>E09</code>)</strong>, <strong>30 unused MCP tool schemas (<code>E14</code>)</strong>, <strong>76 passing test lines in raw terminal output (<code>E16</code>)</strong>, <strong>failed code diffs left in context (<code>E19</code>)</strong>, and <strong>full source bodies of dependency files (<code>E21</code>)</strong>.</li>
<li>Filtering these deterministically in the harness cuts API costs by <strong>80%–95%</strong> while simultaneously preventing Lost-in-the-Middle attention degradation.</li>
</ul>
<h3>Key Insight 4: A Cheap Model + Great Harness Beats an Unharnessed Frontier Model (<code>E43</code> &amp; <code>E44</code>)</h3>
<ul>
<li>In <strong><code>E43</code> (Cross-Model Giant Killing)</strong>, we pitted an unharnessed frontier model (<strong><code>gemini-4-argon</code></strong>) in a raw loop against a model costing 1/8th as much (<strong><code>gemini-3.1-flash-lite</code></strong>) equipped with our combined golden harness (<code>E09</code> AST Map + <code>E20</code> <code>MEMORY.md</code> + <code>E21</code> Skeleton + <code>E11</code> Hook + <code>E16</code> Token Killer + <code>E39</code> Remediation Sensor).</li>
<li>The harnessed lightweight model achieved <strong><code>20/20</code> (100%) in 1 turn (<code>5.6s</code>, <code>$0.0029</code>)</strong>, beating the unharnessed frontier model (<strong><code>15/20</code> in 3 turns, <code>39.1s</code>, <code>$0.1538</code></strong>)—delivering <strong>better accuracy at 6.95x the speed and -98.1% lower cost</strong>.</li>
<li>In <strong><code>E44</code> (Model Evolution vs. Harness Boundary)</strong>, testing across four model generations (<code>2.5-flash</code> ➔ <code>3.5-flash</code> ➔ <code>3.8-flash</code> ➔ <code>4-argon</code>) revealed that while <strong>syntax-assist harnesses</strong> (like diff format helpers) gradually become less critical as models improve, <strong>repository-knowledge &amp; physical guardrail harnesses</strong> (<code>MEMORY.md</code>, output filters, circuit breakers, cryptographic test locks) remain indispensable even for the latest frontier models.</li>
</ul>
<hr />
<h2>3. Master Benchmark Table: All 41 Patterns (<code>E04</code>–<code>E44</code>)</h2>
<p><em>(Note: Each experiment represents a controlled <code>n=1</code> A/B case study on a 3-module domain pipeline evaluated against 20 hidden test cases. Detailed deep-dive posts are linked as they are published.)</em></p>
<table>
<thead>
<tr>
<th align="left">ID</th>
<th align="left">Harness Pattern</th>
<th align="left">Baseline (Mode A)</th>
<th align="left">Harnessed (Mode B)</th>
<th align="left">Empirical A/B Result (Hidden 20-Case Suite)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>🕸️ E04</strong></td>
<td align="left"><strong>Loops vs. Graphs</strong></td>
<td align="left">Single free-form ReAct loop with all tools and full history</td>
<td align="left">Stage-gated Directed Graph with per-stage deterministic verifiers and context resets</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Calls: <code>7 ➔ 3</code> (-57.1%) | Input tokens: <code>23,322 ➔ 5,966</code> (<strong>-74.4%</strong>) | Time: <code>36.7s ➔ 19.1s</code> (<strong>-48.0%</strong>) | Cost: <strong>-59.3%</strong></td>
</tr>
<tr>
<td align="left"><strong>🪆 E05</strong></td>
<td align="left"><strong>Subagent Folding</strong></td>
<td align="left">Role-based subagents (Architect/Coder/QA) passing handoff summaries</td>
<td align="left">Single folded loop with on-demand Progressive Skill Disclosure</td>
<td align="left"><strong>Both 20/20 Pass, Folded wins</strong> | Calls: <code>5 ➔ 2</code> (-60.0%) | Input tokens: <code>14,815 ➔ 4,889</code> (<strong>-67.0%</strong>) | Time: <code>36.4s ➔ 13.7s</code> (<strong>2.66x faster</strong>) | Cost: <strong>-54.9%</strong></td>
</tr>
<tr>
<td align="left"><strong>🧪 E06</strong></td>
<td align="left"><strong>Blind TDD Worktrees</strong></td>
<td align="left">Single agent writing both tests and implementation in one context</td>
<td align="left">Agent 1 (Blind Tester) writes tests from spec only; Agent 2 implements in separate worktree</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | <strong>Mutant Kill Rate: <code>0/6 (0%)</code> ➔ <code>6/6 (100%)</code></strong> | Calls: <code>1 vs 2</code> | Cost: <code>$0.0080 vs $0.0178</code></td>
</tr>
<tr>
<td align="left"><strong>⚙️ E07</strong></td>
<td align="left"><strong>Code-Mode Dynamic Workflows</strong></td>
<td align="left">Iterating multi-module audits inside a single chat history</td>
<td align="left">Agent writes a deterministic orchestrator script invoking fresh worker contexts</td>
<td align="left"><strong>Accuracy: <code>18/20</code> ➔ <code>20/20</code></strong> | <strong>Mutant Kill Rate: <code>0/6</code> ➔ <code>6/6</code></strong> | Calls: <code>4 ➔ 2</code> (-50%) | Input tokens: <code>12,509 ➔ 6,445</code> (<strong>-48.5%</strong>)</td>
</tr>
<tr>
<td align="left"><strong>📐 E08</strong></td>
<td align="left"><strong>Spec-First SDD</strong></td>
<td align="left">Vibe coding directly from a high-level prompt with iterative fixes</td>
<td align="left">Locking a strict contract (<code>SPEC.md</code>) and change plan before unlocking code edits</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code> (0 rework)</strong> | <strong>Mutant Kill Rate: <code>0/6</code> ➔ <code>6/6</code></strong> | Input tokens: <code>9,375 ➔ 6,284</code> (<strong>-32.9%</strong>)</td>
</tr>
<tr>
<td align="left"><strong>🗺️ E09</strong></td>
<td align="left"><strong>AST Dependency Map vs. Raw Grep</strong></td>
<td align="left">Giving the agent <code>grep</code> and file-read tools to explore on its own</td>
<td align="left">Pre-injecting a 0-token deterministic AST symbol &amp; call-graph mini-map</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Decoy reads: <code>5 ➔ 0</code> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>9,508 ➔ 3,893</code> (<strong>-59.1%</strong>) | Time: <strong>3.15x faster</strong> | Cost: <strong>-63.0%</strong></td>
</tr>
<tr>
<td align="left"><strong>⚔️ E10</strong></td>
<td align="left"><strong>Adversarial Critic (Maker-Checker)</strong></td>
<td align="left">Asking the implementing agent to self-review before submitting</td>
<td align="left">Separate Adversarial Critic agent rewarded for writing falsifying counterexample tests</td>
<td align="left"><strong>Both 20/20 impl</strong> | <strong>Test Mutant Kill Rate: Self-Review <code>0/6 (0%)</code> ➔ Adversarial Critic <code>6/6 (100%)</code></strong></td>
</tr>
<tr>
<td align="left"><strong>🛡️ E11</strong></td>
<td align="left"><strong>Architecture Guardrail Hooks</strong></td>
<td align="left">Listing coding rules &amp; line limits in a long system prompt</td>
<td align="left">Enforcing AST architecture rules &amp; line limits via a deterministic Pre-Commit Hook</td>
<td align="left"><strong>Both 20/20 functional</strong> | <strong>Arch Rule Compliance: Prompt-only <code>4/5</code> (violated line cap) ➔ AST Hook <code>5/5 (100%)</code></strong></td>
</tr>
<tr>
<td align="left"><strong>🗜️ E12</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e12-milestone-context-compaction">Milestone Context Compaction</a></strong></td>
<td align="left">Accumulating all file reads and logs across multi-step tasks</td>
<td align="left">Resetting chat history at milestones into a compact JSON state object</td>
<td align="left"><strong>Accuracy: <code>18/20</code> ➔ <code>20/20</code></strong> | Input tokens: <code>35,869 ➔ 2,922</code> (<strong>-91.9%</strong>) | Cost: <strong>-72.2%</strong></td>
</tr>
<tr>
<td align="left"><strong>🏆 E13</strong></td>
<td align="left"><strong>Parallel Best-of-N Tournament</strong></td>
<td align="left">Retrying sequential fixes inside a single stuck session</td>
<td align="left">Running 2 parallel lightweight branches + deterministic benchmark judge selection</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Judge selected <code>O(n)</code> <code>0.96ms</code> branch over <code>202.95ms</code> <code>O(n²)</code> branch | Input tokens: <code>13,822 ➔ 4,537</code> (<strong>-67.2%</strong>)</td>
</tr>
<tr>
<td align="left"><strong>🔍 E14</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e14-progressive-tool-search">Progressive Tool Schema Search</a></strong></td>
<td align="left">Injecting all 32 MCP tool JSON schemas on every turn</td>
<td align="left">Providing a lightweight <code>search_tools</code> meta-tool to load schemas on demand</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Decoy tool calls: <code>2 ➔ 0</code> | Input tokens: <code>79,773 ➔ 5,844</code> (<strong>-92.7%</strong>) | Time: <strong>-48.5%</strong> | Cost: <strong>-84.8%</strong></td>
</tr>
<tr>
<td align="left"><strong>✂️ E15</strong></td>
<td align="left"><strong>Search-Replace Blocks vs. Unified Diff</strong></td>
<td align="left">Outputting standard Unified Diffs (<code>@@ -l,s +l,s @@</code>) or full rewrites</td>
<td align="left">Outputting line-number-free <code>SEARCH/REPLACE</code> anchor blocks</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Patch failures: <code>1 ➔ 0</code> | Output tokens: <code>3,775 ➔ 1,398</code> (<strong>-63.0%</strong>) | Input tokens: <strong>-58.9%</strong> | Time: <strong>-58.6%</strong> | Cost: <strong>-61.7%</strong></td>
</tr>
<tr>
<td align="left"><strong>🧹 E16</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">CLI Test Output Token Killer</a></strong></td>
<td align="left">Returning raw <code>pytest</code> stdout (76 PASS lines + ANSI + deep traces)</td>
<td align="left">Wrapper strips passing tests and returns only compact JSON failure diffs</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Tool output: <code>117,391 ➔ 1,604</code> chars (<strong>-98.6%</strong>) | Input tokens: <code>92,385 ➔ 5,445</code> (<strong>-94.1%</strong>) | Cost: <strong>-87.0%</strong></td>
</tr>
<tr>
<td align="left"><strong>💡 E17</strong></td>
<td align="left"><strong>Actionable Fix-Suggestion Linter</strong></td>
<td align="left">Returning bare compiler/linter error codes (<code>E402 Invalid state</code>)</td>
<td align="left">Injecting a concrete 1-line <code>💡 Fix Hint</code> code recipe directly into the linter error</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Doc searches: <code>1 ➔ 0</code> | Input tokens: <code>13,367 ➔ 4,064</code> (<strong>-69.6%</strong>) | Time: <strong>-31.2%</strong> | Cost: <strong>-43.3%</strong></td>
</tr>
<tr>
<td align="left"><strong>🧭 E18</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e18-advisor-executor-routing">Advisor-Executor Model Routing</a></strong></td>
<td align="left">Running a single worker prompt across all planning and editing turns</td>
<td align="left">Injecting a concise architectural memo from an Advisor on Turn 1 before Executor edits</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Rework retries: <code>2 ➔ 0</code> | Calls: <code>4 ➔ 2</code> | Input tokens: <code>27,699 ➔ 4,172</code> (<strong>-84.9%</strong>) | Cost: <strong>-71.5%</strong></td>
</tr>
<tr>
<td align="left"><strong>🌿 E19</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e19-error-trace-pruning">Error-Trace Pruning (Failed Rollback)</a></strong></td>
<td align="left">Keeping failed code patches and long error traces in chat history</td>
<td align="left">Pruning failed patches on test failure and leaving only a 1-line negative lesson</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Input tokens: <code>59,944 ➔ 4,210</code> (<strong>-93.0%</strong>, 14.2x efficiency) | Time: <strong>-36.3%</strong> | Cost: <strong>-80.3%</strong></td>
</tr>
<tr>
<td align="left"><strong>🧠 E20</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Episodic Gotchas Memory (<code>MEMORY.md</code>)</a></strong></td>
<td align="left">Starting every task cold and rediscovering repo traps via test failures</td>
<td align="left">Injecting a 5-item (<code>+442</code> token) <code>MEMORY.md</code> of repo gotchas distilled from prior runs</td>
<td align="left"><strong>Accuracy: <code>17/20</code> ➔ <code>20/20</code></strong> | Traps hit: <code>2 ➔ 0</code> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,560 ➔ 2,078</code> (<strong>-95.5%</strong>) | Cost: <strong>-83.7%</strong></td>
</tr>
<tr>
<td align="left"><strong>🦴 E21</strong></td>
<td align="left"><strong><a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Tree-sitter Skeleton Context</a></strong></td>
<td align="left">Loading full implementation bodies of peripheral dependency files</td>
<td align="left">Folding dependency bodies into function signatures, types, and docstrings only</td>
<td align="left"><strong>Accuracy: <code>17/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>45,270 ➔ 2,316</code> (<strong>-94.9%</strong>) | Time: <strong>2.34x faster</strong> | Cost: <strong>-82.6%</strong></td>
</tr>
<tr>
<td align="left"><strong>🚦 E22</strong></td>
<td align="left"><strong>Reproduction-First Gate</strong></td>
<td align="left">Letting the agent immediately patch production code upon reading an issue</td>
<td align="left">Locking production files until a minimal bug-reproducing test fails (Red) first</td>
<td align="left"><strong>Accuracy: <code>17/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>49,801 ➔ 2,323</code> (<strong>-95.3%</strong>) | Time: <strong>2.18x faster</strong> | Cost: <strong>-83.9%</strong></td>
</tr>
<tr>
<td align="left"><strong>📦 E23</strong></td>
<td align="left"><strong>Atomic Multi-File Batch Edit</strong></td>
<td align="left">Editing one file at a time and running tests in broken intermediate states</td>
<td align="left">Applying coupled multi-file patch sets atomically in a single turn before testing</td>
<td align="left"><strong>Accuracy: <code>17/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>41,664 ➔ 2,208</code> (<strong>-94.7%</strong>) | Time: <strong>2.26x faster</strong> | Cost: <strong>-81.8%</strong></td>
</tr>
<tr>
<td align="left"><strong>✨ E24</strong></td>
<td align="left"><strong>Positive Few-Shot vs. Negative Rules</strong></td>
<td align="left">Listing 20+ negative prohibitions ("NEVER do X") in the system prompt</td>
<td align="left">Replacing negative rules with a single 20-line affirmative Golden Code Example</td>
<td align="left"><strong>Accuracy: <code>16/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>48,640 ➔ 2,092</code> (<strong>-95.7%</strong>) | Time: <strong>2.85x faster</strong> | Cost: <strong>-84.1%</strong></td>
</tr>
<tr>
<td align="left"><strong>⚡ E25</strong></td>
<td align="left"><strong>Prefix-Stable Prompt Caching</strong></td>
<td align="left">Mutating timestamps and task tables at the top of the system prompt</td>
<td align="left">Freezing an append-only prefix and appending state diffs only at the tail</td>
<td align="left"><strong>Accuracy: <code>17/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,699 ➔ 2,298</code> (<strong>-95.1%</strong>) | Time: <strong>2.63x faster</strong> | Cost: <strong>-82.9%</strong></td>
</tr>
<tr>
<td align="left"><strong>🔒 E26</strong></td>
<td align="left"><strong>Two-Phase Read-Only Plan Lock</strong></td>
<td align="left">Allowing interleaved Read and Write tool calls from Turn 1</td>
<td align="left">Physically locking Write tools during Phase 1 exploration until plan approval</td>
<td align="left"><strong>Accuracy: <code>17/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>43,300 ➔ 2,262</code> (<strong>-94.8%</strong>) | Time: <strong>2.22x faster</strong> | Cost: <strong>-82.2%</strong></td>
</tr>
<tr>
<td align="left"><strong>🗂️ E27</strong></td>
<td align="left"><strong>Structured Hypothesis Table</strong></td>
<td align="left">Debugging complex multi-bug failures via unstructured monologue</td>
<td align="left">Requiring a structured <code>THINK.md</code> Hypothesis Elimination Matrix before editing</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>48,712 ➔ 2,450</code> (<strong>-95.0%</strong>) | Time: <strong>2.54x faster</strong> | Cost: <strong>-83.6%</strong></td>
</tr>
<tr>
<td align="left"><strong>🔌 E28</strong></td>
<td align="left"><strong>Loop-Detection Circuit Breaker</strong></td>
<td align="left">Allowing unguarded retry loops on repeated test failures</td>
<td align="left">Detecting oscillating edits (<code>A➔B➔A</code>), rolling back, and forcing a strategy shift</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>43,900 ➔ 2,230</code> (<strong>-94.9%</strong>) | Time: <strong>2.46x faster</strong> | Cost: <strong>-82.6%</strong></td>
</tr>
<tr>
<td align="left"><strong>🎯 E29</strong></td>
<td align="left"><strong>Root-Cause Error Clustering</strong></td>
<td align="left">Dumping 50 cascading downstream type/runtime errors raw</td>
<td align="left">Clustering cascading errors via dependency graph into 1 root-cause signature error</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>47,834 ➔ 2,262</code> (<strong>-95.3%</strong>) | Time: <strong>2.27x faster</strong> | Cost: <strong>-83.4%</strong></td>
</tr>
<tr>
<td align="left"><strong>⏩ E30</strong></td>
<td align="left"><strong>Dependency-Scoped Incremental Tests</strong></td>
<td align="left">Running the entire repository test suite after every single file edit</td>
<td align="left">Running only AST-impacted tests per edit and gating full suite at final submit</td>
<td align="left"><strong>Accuracy: <code>16/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>52,146 ➔ 2,240</code> (<strong>-95.7%</strong>) | Time: <strong>2.11x faster</strong> | Cost: <strong>-84.4%</strong></td>
</tr>
<tr>
<td align="left"><strong>🌳 E31</strong></td>
<td align="left"><strong>Parameterized AST Codemod Tool</strong></td>
<td align="left">Manually editing 15+ call sites across files via text replacement</td>
<td align="left">Providing a deterministic AST transformation primitive (<code>ast_transform_calls</code>)</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>41,680 ➔ 2,164</code> (<strong>-94.8%</strong>) | Time: <strong>2.22x faster</strong> | Cost: <strong>-82.0%</strong></td>
</tr>
<tr>
<td align="left"><strong>🔍 E32</strong></td>
<td align="left"><strong>Pre-Submit Blast-Radius &amp; Diff Audit</strong></td>
<td align="left">Accepting <code>SUBMIT</code> immediately once functional tests pass</td>
<td align="left">Auditing <code>git diff</code> for debug leftovers, out-of-scope symbol edits, and entropy</td>
<td align="left"><strong>Accuracy: <code>16/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>45,057 ➔ 2,251</code> (<strong>-95.0%</strong>) | Time: <strong>2.21x faster</strong> | Cost: <strong>-82.8%</strong></td>
</tr>
<tr>
<td align="left"><strong>🎲 E33</strong></td>
<td align="left"><strong>Property-Based Fuzzing Sensor</strong></td>
<td align="left">Validating patches against fixed hand-written example unit tests only</td>
<td align="left">Running an invariant property fuzzer that feeds minimal falsifying counterexamples</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>47,536 ➔ 2,338</code> (<strong>-95.1%</strong>) | Time: <strong>2.27x faster</strong> | Cost: <strong>-83.5%</strong></td>
</tr>
<tr>
<td align="left"><strong>📸 E34</strong></td>
<td align="left"><strong>Runtime Variable-Snapshot Injection</strong></td>
<td align="left">Returning standard stack traces (file, line number, exception message)</td>
<td align="left">Injecting live <code>locals()</code> variable snapshots at the failing frame into the trace</td>
<td align="left"><strong>Accuracy: <code>16/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,581 ➔ 2,322</code> (<strong>-95.0%</strong>) | Time: <strong>2.18x faster</strong> | Cost: <strong>-83.1%</strong></td>
</tr>
<tr>
<td align="left"><strong>📖 E35</strong></td>
<td align="left"><strong>Directory-Pointer <code>AGENTS.md</code></strong></td>
<td align="left">Stuffing a 500-line monolithic encyclopedia <code>AGENTS.md</code> into initial context</td>
<td align="left">Using a 30-line pointer <code>AGENTS.md</code> that routes the agent to scoped docs on demand</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,021 ➔ 2,219</code> (<strong>-95.2%</strong>) | Time: <strong>3.13x faster</strong> | Cost: <strong>-83.1%</strong></td>
</tr>
<tr>
<td align="left"><strong>🤝 E36</strong></td>
<td align="left"><strong>Shift-Handover Harness</strong></td>
<td align="left">Passing unstructured chat summary text across multi-session resets</td>
<td align="left">Enforcing a 3-artifact mechanical handover (<code>feature_list.json</code>, <code>init.sh</code>, <code>progress.txt</code>)</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,227 ➔ 2,237</code> (<strong>-95.2%</strong>) | Time: <strong>2.27x faster</strong> | Cost: <strong>-83.1%</strong></td>
</tr>
<tr>
<td align="left"><strong>🪜 E37</strong></td>
<td align="left"><strong>Hypothesis-Pivot Ladder</strong></td>
<td align="left">Allowing blind micro-retries when the exact same error repeats</td>
<td align="left">Locking current approach on repeated error signature, restoring via git, forcing 3 new hypotheses</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,393 ➔ 2,238</code> (<strong>-95.2%</strong>) | Time: <strong>2.27x faster</strong> | Cost: <strong>-83.2%</strong></td>
</tr>
<tr>
<td align="left"><strong>🔐 E38</strong></td>
<td align="left"><strong>Cryptographic Test-Tampering Lock</strong></td>
<td align="left">Asking the agent via prompt not to modify or weaken existing tests</td>
<td align="left">Enforcing SHA-256 hash locks on test files to physically reject test tampering</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,429 ➔ 2,235</code> (<strong>-95.2%</strong>) | Time: <strong>2.46x faster</strong> | Cost: <strong>-83.2%</strong></td>
</tr>
<tr>
<td align="left"><strong>🧭 E39</strong></td>
<td align="left"><strong>Guides &amp; Sensors Remediation</strong></td>
<td align="left">Returning standard linter/compiler errors on architectural layer violations</td>
<td align="left">Pairing upfront boundary maps (Guides) with DI remediation code recipes on violation (Sensors)</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>45,922 ➔ 2,237</code> (<strong>-95.1%</strong>) | Time: <strong>2.19x faster</strong> | Cost: <strong>-83.1%</strong></td>
</tr>
<tr>
<td align="left"><strong>🐾 E40</strong></td>
<td align="left"><strong>Behavioral Action-Sequence Gate</strong></td>
<td align="left">Judging only end-to-end test outcomes regardless of investigation steps</td>
<td align="left">Blocking symbol edits unless <code>find_references</code> / <code>grep</code> was executed on that symbol first</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>46,436 ➔ 2,218</code> (<strong>-95.2%</strong>) | Time: <strong>2.01x faster</strong> | Cost: <strong>-83.2%</strong></td>
</tr>
<tr>
<td align="left"><strong>🪶 E41</strong></td>
<td align="left"><strong>Zero-Tax Minimal Primitives (Pi-Style)</strong></td>
<td align="left">Heavy wrapper with 15+ custom tool schemas and verbose instructions</td>
<td align="left">Stripping tools down to 2 minimal primitives (<code>bash</code> and <code>file_edit</code>)</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>45,968 ➔ 2,224</code> (<strong>-95.2%</strong>) | Time: <strong>2.21x faster</strong> | Cost: <strong>-83.1%</strong></td>
</tr>
<tr>
<td align="left"><strong>🧹 E42</strong></td>
<td align="left"><strong>Post-Green Entropy Sweep Pass</strong></td>
<td align="left">Stopping immediately as soon as functional tests turn green</td>
<td align="left">Triggering an automated 1-turn cleanup pass (deduping helpers, naming alignment) after green</td>
<td align="left"><strong>Accuracy: <code>19/20</code> ➔ <code>20/20</code></strong> | Calls: <code>3 ➔ 1</code> | Input tokens: <code>47,114 ➔ 2,217</code> (<strong>-95.3%</strong>) | Time: <strong>2.34x faster</strong> | Cost: <strong>-83.3%</strong></td>
</tr>
<tr>
<td align="left"><strong>⚔️ E43</strong></td>
<td align="left"><strong>Cross-Model Giant Killing</strong></td>
<td align="left">Unharnessed frontier model (<code>gemini-4-argon</code>) in a raw loop</td>
<td align="left">1/8th-cost <code>gemini-3.1-flash-lite</code> equipped with combined Golden Harnesses (<code>E09+E11+E16+E20+E21+E39</code>)</td>
<td align="left"><strong>Accuracy: Frontier Raw <code>15/20</code> vs. Flash-Lite Harnessed <code>20/20</code></strong> | Input tokens: <code>48,097 ➔ 2,321</code> (<strong>-95.2%</strong>) | Time: <code>39.1s ➔ 5.6s</code> (<strong>6.95x faster</strong>) | Cost: <code>$0.1538 ➔ $0.0029</code> (<strong>-98.1%</strong>)</td>
</tr>
<tr>
<td align="left"><strong>🧬 E44</strong></td>
<td align="left"><strong>Model Evolution vs. Harness Boundary</strong></td>
<td align="left">Syntax-only harnesses across model generations (<code>2.5</code> ➔ <code>3.5</code> ➔ <code>3.8</code> ➔ <code>4-Argon</code>)</td>
<td align="left">Repo-knowledge &amp; physical guardrail harnesses (<code>E16+E20+E28+E35+E38</code>) across generations</td>
<td align="left"><strong>Both 20/20 Pass</strong> | Syntax helpers fade with newer models, but Context Hygiene &amp; Guardrail Harnesses still cut tokens by <strong>-95.5%</strong> (<code>51,285 ➔ 2,333</code>) and cost by <strong>-84.4%</strong></td>
</tr>
</tbody></table>
<hr />
<h2>4. A 5-Point Checklist You Can Apply to Your Agent Harness Tomorrow</h2>
<ol>
<li><strong>Never return raw CLI test or linter stdout to the LLM (<code>E16</code> / <code>E17</code>)</strong>
Wrap your test runner to strip passing tests and ANSI codes. Return only a structured JSON of failed assertions plus a 1-line <code>💡 Fix Hint</code> showing the expected helper or pattern.</li>
<li><strong>Replace negative prompt prohibitions with Pre-Commit Hooks and 1 Golden Example (<code>E11</code> / <code>E24</code>)</strong>
Listing 20 "DO NOT do X" rules in <code>CLAUDE.md</code> triggers the Pink Elephant effect. Show one 20-line affirmative code example and enforce constraints mechanically via AST save/commit hooks.</li>
<li><strong>Maintain a 5-to-15 line <code>MEMORY.md</code> of repository-specific gotchas (<code>E20</code> / <code>E35</code>)</strong>
Instead of a 500-line encyclopedia, keep <code>AGENTS.md</code> as a 30-line directory pointer and maintain a tiny <code>MEMORY.md</code> listing only non-obvious domain invariants that previously caused test failures.</li>
<li><strong>Use line-number-free <code>SEARCH/REPLACE</code> blocks + Atomic Multi-File Batches (<code>E15</code> / <code>E23</code>)</strong>
Unified diffs (<code>@@ -l,s +l,s @@</code>) frequently fail due to LLM line-counting errors. Apply multi-file interface changes atomically in a single turn before running verification tests.</li>
<li><strong>Separate the Test Author context from the Implementer context (<code>E06</code> / <code>E22</code>)</strong>
Never trust an agent to grade its own homework in the same context. Lock production files until a reproduction test fails (<code>E22</code>), or generate tests in a blind worktree that only sees the spec (<code>E06</code>).</li>
</ol>
<hr />
<h3>🔗 Explore the Series</h3>
<ul>
<li>🛡️ <strong>Methodology &amp; Sandbox Architecture:</strong> <a href="https://tappers.hashnode.dev/air-gapped-proxmox-systemd-agent-sandbox">How to Benchmark AI Agents Without Eval Cheating or API Key Leaks: Designing a Proxmox &amp; systemd Sandbox</a></li>
<li>🥇 <strong>Rank #1 Deep Dive (<code>E20</code>):</strong> <a href="https://tappers.hashnode.dev/e20-episodic-gotchas-memory">Stop Your Coding Agent From Stepping on the Same Rake Twice: Cold-Start Exploration vs. a 5-Item <code>MEMORY.md</code></a></li>
<li>🥈 <strong>Rank #2 Deep Dive (<code>E21</code>):</strong> <a href="https://tappers.hashnode.dev/e21-treesitter-skeleton-context">Why Feeding Full Dependency Files to Your Coding Agent Hurts Accuracy: Full Source vs. Tree-sitter Interface Skeletons</a></li>
<li>🥉 <strong>Rank #3 Deep Dive (<code>E16</code>):</strong> <a href="https://tappers.hashnode.dev/e16-cli-test-output-token-killer">Stop Dumping Raw Pytest Logs Into Your Coding Agent: How a Structured JSON Test Filter Cuts Input Tokens by 94.1%</a></li>
<li>🌿 <strong>Rank #4 Deep Dive (<code>E19</code>):</strong> <a href="https://tappers.hashnode.dev/e19-error-trace-pruning">Why Keeping Failed Code Attempts in Chat History Poisons Your Coding Agent: Error-Trace Pruning vs. Accumulated History</a></li>
<li>🔍 <strong>Rank #5 Deep Dive (<code>E14</code>):</strong> <a href="https://tappers.hashnode.dev/e14-progressive-tool-search">The Hidden "Schema Tax" of MCP Tools: Dumping 32 Tool Schemas vs. On-Demand Tool Search (-92.7% Input Tokens)</a></li>
<li>🗜️ <strong>Rank #6 Deep Dive (<code>E12</code>):</strong> <a href="https://tappers.hashnode.dev/e12-milestone-context-compaction">How Save-Game Context Compaction Fixes Spec Drift in Multi-Step Coding Agents (-91.9% Input Tokens)</a></li>
<li>🧭 <strong>Rank #7 Deep Dive (<code>E18</code>):</strong> <a href="https://tappers.hashnode.dev/e18-advisor-executor-routing">Benchmarking the Claude Code <code>/advisor</code> Pattern: Solo Executor Loops vs. Upfront Advisor Blueprint Routing</a></li>
</ul>
]]></content:encoded></item></channel></rss>