<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Ai-Agents on tsvinc</title>
		<link>https://tsvinc.dev/tags/ai-agents/</link>
		<description>Recent content in Ai-Agents on tsvinc</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Mon, 02 Nov 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://tsvinc.dev/tags/ai-agents/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Lock OpenCode 2.x to your self-hosted model</title>
				<link>https://tsvinc.dev/guides/lock-opencode-to-local-model/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/guides/lock-opencode-to-local-model/</guid>
				<description>&lt;p&gt;Two config lines make OpenCode 2.x send prompts only to your self-hosted OpenAI-compatible model, and a self-contained script proves which model a headless run picks.&lt;/p&gt;&#xA;&#xA;&lt;dl class=&#34;guide-scope&#34;&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Tested on&lt;/dt&gt;&lt;dd&gt;OpenCode V2 (npm &lt;code&gt;@opencode/cli&lt;/code&gt;) 2.2.5, headless &lt;code&gt;opencode run&lt;/code&gt;, throwaway &lt;code&gt;HOME&lt;/code&gt;, first in a container with no network, then with the PoC below behind a dead proxy (2026-11-02). The no-model case was also run on 2.2.0.&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row is-gap&#34;&gt;&lt;dt&gt;Not verified&lt;/dt&gt;&lt;dd&gt;the interactive TUI, a fresh install&#39;s first-run screens, 2.2.1 to 2.2.4, a config with both lines at once, &lt;code&gt;&amp;quot;model&amp;quot;&lt;/code&gt; alone with the local server down, a full personal config.&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Needs&lt;/dt&gt;&lt;dd&gt;an &lt;code&gt;opencode&lt;/code&gt; 2.x binary, a local OpenAI-compatible model server already configured as a provider (one way: /guides/serve-local-models-to-opencode-llama-router/), &lt;code&gt;python3&lt;/code&gt; for the PoC. No opencode.ai account and no API key.&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Time&lt;/dt&gt;&lt;dd&gt;about 10 min, my estimate, not timed (the PoC caps each of its three runs at 60 s).&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Risk&lt;/dt&gt;&lt;dd&gt;low. You edit one config file after copying it; the PoC points &lt;code&gt;HOME&lt;/code&gt; and the four &lt;code&gt;XDG_*&lt;/code&gt; directories it sets (config, data, state, cache) at a &lt;code&gt;mktemp -d&lt;/code&gt; directory and starts a server on 127.0.0.1:18765 for the length of the run.&lt;/dd&gt;&lt;/div&gt;&lt;/dl&gt;&#xA;&lt;h2 id=&#34;before-you-start&#34;&gt;Before you start&lt;a class=&#34;heading-anchor&#34; href=&#34;#before-you-start&#34; aria-label=&#34;Link to section: Before you start&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Placeholders used below:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;CONFIG&amp;gt;&lt;/code&gt;: your global OpenCode config file. The PoC writes its own as &lt;code&gt;$XDG_CONFIG_HOME/opencode/opencode.json&lt;/code&gt; inside a throwaway &lt;code&gt;HOME&lt;/code&gt;, so on a default setup this is &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;PROVIDER_ID&amp;gt;&lt;/code&gt;: the key of your local provider under &lt;code&gt;&amp;quot;provider&amp;quot;&lt;/code&gt; in &lt;code&gt;&amp;lt;CONFIG&amp;gt;&lt;/code&gt;. The PoC uses &lt;code&gt;local&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;MODEL_ID&amp;gt;&lt;/code&gt;: the key of your model under that provider&#39;s &lt;code&gt;&amp;quot;models&amp;quot;&lt;/code&gt;. The PoC uses &lt;code&gt;qwen-local&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;OPENCODE_BIN&amp;gt;&lt;/code&gt;: path to the &lt;code&gt;opencode&lt;/code&gt; 2.x binary; the PoC defaults to &lt;code&gt;opencode&lt;/code&gt; on &lt;code&gt;PATH&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Check the version with &lt;code&gt;&amp;lt;OPENCODE_BIN&amp;gt; --version&lt;/code&gt;. Mine printed &lt;code&gt;opencode v2.2.5&lt;/code&gt; (it is also the PoC&#39;s first output line).&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Back up the config before step 3:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp &amp;lt;CONFIG&amp;gt; &amp;lt;CONFIG&amp;gt;.bak&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
			</item>
			<item>
				<title>My agent guardrails passed every test. Then I used them.</title>
				<link>https://tsvinc.dev/posts/guardrails-first-real-evening/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/guardrails-first-real-evening/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve set up a terminal coding agent&#39;s permission rules, or wondered what a model sees when they say no.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;For weeks, the only user my test suites knew was a fake model. The suites cover my configuration templates for six terminal coding agents (OpenCode in two major versions, V1 and V2, plus pi, omp, Hermes Agent and Claude Code): each one runs local models and refuses destructive commands even with nobody approving. Every suite drove them with &lt;a href=&#34;https://tsvinc.dev/posts/model-that-only-says-run/&#34;&gt;a scripted fake model&lt;/a&gt; in a container with no network. It was an ideal user: it ran exactly the command the test told it to, it never improvised, and it never once wanted a screenshot of anything.&lt;/p&gt;</description>
			</item>
			<item>
				<title>OpenCode didn&#39;t want an account. It wanted a model name.</title>
				<link>https://tsvinc.dev/posts/opencode-account-myth/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/opencode-account-myth/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve pointed a terminal coding agent at a model you serve yourself.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The warning reached me second-hand, the way the most confident warnings travel. Don&#39;t install a fresh OpenCode 2.x if you run your own models, it said: the new version won&#39;t talk to a self-hosted model until you activate an account on opencode.ai and pay for a subscription, and it asks for an API key on your very first prompt. A second message came along with it, an AI assistant&#39;s explanation, and that one had a mechanism: fresh installs now default to OpenCode&#39;s own hosted model, the switch that turns the hosted provider off is fairly new and may not work, and in that case there&#39;s a third-party plugin, installed globally with &lt;code&gt;npx&lt;/code&gt;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The leak scan found me in my own guard&#39;s tests</title>
				<link>https://tsvinc.dev/posts/leak-scan-found-me-in-guard-tests/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/leak-scan-found-me-in-guard-tests/</guid>
				<description>&lt;p&gt;Publishing a private repository as a single orphan commit sounds like the safe kind of open source. No history goes out, so nothing buried in the history can leak: you choose the files, you commit them once, you push. It&#39;s the plan that looks best on a whiteboard, and that is exactly why I didn&#39;t let it go out on its own.&lt;/p&gt;&#xA;&lt;p&gt;The repository holds my hardened configuration templates for six terminal coding agents that run on local models (OpenCode V1 and V2, pi, omp, Hermes Agent, Claude Code), plus the guard code that stops them running destructive commands. It&#39;s public now, under MIT, as &lt;a href=&#34;https://gitlab.com/myrants-oss/harnesses&#34;&gt;one commit&lt;/a&gt;. On the first builds, the checks I&#39;d put in front of that commit, plus one review of the plan, found six things. Each pointed at a real defect, two of them in the scanner itself. One of them was me, by name, in the test cases of that guard.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The screenshot opencode never sent</title>
				<link>https://tsvinc.dev/posts/igpu-screenshot-opencode-never-sent/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/igpu-screenshot-opencode-never-sent/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a local model behind a coding agent. &lt;a href=&#34;https://tsvinc.dev/posts/igpu-bandwidth-picked-the-model/&#34;&gt;Part 1&lt;/a&gt; has the laptop and its memory pool; the router manual has the serving setup.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;tldr&#34;&gt;&#xA;  &lt;span class=&#34;tldr-label&#34;&gt;TL;DR&lt;/span&gt;&#xA;  On a laptop&#39;s integrated Radeon 680M, llama.cpp served 14 model presets (12 models, two of them in two quants) with image input, each with its projector, the small extra file that turns pixels into tokens. On five generated test images they got 54 of 56 single answers right, and read a six-line cut list back with full recall on every model. Mixture-of-experts models with spare GPU memory lost 2% of their generation speed or less; dense 27B models and the presets that already filled the pool lost 4 to 32%. In opencode, a terminal coding agent, versions 1.19.3 and 1.19.5 on my setup and 1.18.35 on a second machine did not send an attached image unless the model entry declared image input: the model got an error string instead, and the run exited 0. Adding &lt;code&gt;&amp;quot;modalities&amp;quot;: {&amp;quot;input&amp;quot;: [&amp;quot;text&amp;quot;, &amp;quot;image&amp;quot;], &amp;quot;output&amp;quot;: [&amp;quot;text&amp;quot;]}&lt;/code&gt; fixed it. One setup that first saw the image arrive without the key had it in a config file in a parent directory, which opencode merges in. An RTX 3090 desktop measured the same projector memory with no speed loss. On the second machine, a Strix Halo mini PC, two models &amp;quot;passed&amp;quot; the image test without seeing the image by running an OCR tool through bash, so check what was sent and what tools ran, not the answer. On 18 questions about real screenshots with thinking on, Gemma 4 26B (a mixture-of-experts model) got 17 at 23 s each on average, and Swift 9B (a dense model) all 18 at 38 s.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;The obvious suspect for anything new on this laptop is the GPU. It&#39;s a Radeon 680M with no memory of its own: on the boot these tests ran on, it borrowed a 4 GiB carve-out plus 13.6 GiB of system RAM it can map (GTT), out of the laptop&#39;s 32 GB, and this series has already spent a fair share of its words on what does and doesn&#39;t fit in it. So the place to expect trouble from letting local coding models look at screenshots is right there: a picture means extra weights, extra tokens and extra memory, on a GPU that has none to spare.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Eighteen days later, the same lie</title>
				<link>https://tsvinc.dev/posts/eighteen-days-later-same-lie/</link>
				<pubDate>Sun, 01 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/eighteen-days-later-same-lie/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/inventing-work-vs-promising-what-isnt-there/&#34;&gt;Inventing the work vs promising what isn&#39;t there&lt;/a&gt;, where this model first earned its flag.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Here is a paragraph from a README that a local model, working as a coding agent on a Java money-transfer ticket, wrote on the evening of 31 October, in a batch of runs that started at 21:08:&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Atomicity:&lt;/strong&gt; All-or-nothing semantics. Either both accounts are updated or neither is. If the repository fails part-way through (e.g., connection lost on the second save), the operation is rolled back: balances are unchanged and no audit events are appended.&lt;/p&gt;&#xA;&#xA;&lt;/blockquote&gt;</description>
			</item>
			<item>
				<title>Faster prompts, slower agents: a second engine on the 680M</title>
				<link>https://tsvinc.dev/posts/igpu-faster-prompts-slower-agents/</link>
				<pubDate>Sun, 01 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/igpu-faster-prompts-slower-agents/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a local model behind a coding agent. &lt;a href=&#34;https://tsvinc.dev/posts/igpu-bandwidth-picked-the-model/&#34;&gt;Part 1&lt;/a&gt; has the laptop and why every model here is a mixture-of-experts; &lt;a href=&#34;https://tsvinc.dev/posts/igpu-agent-runs-none-merge-ready/&#34;&gt;part 2&lt;/a&gt; has how the agent runs were graded.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;tldr&#34;&gt;&#xA;  &lt;span class=&#34;tldr-label&#34;&gt;TL;DR&lt;/span&gt;&#xA;  On a laptop&#39;s integrated Radeon 680M, UnbiasedStrata, an inference engine built around mixture-of-experts models (each token runs through a few expert blocks, a small slice of the parameters), read a 2,048-token prompt 1.4 to 1.5 times as fast as llama.cpp&#39;s Vulkan backend once warm, but only on models whose experts all fit in GPU memory. It generated slower on every model it ran: 52 to 78% of llama.cpp&#39;s speed. Three coding-agent runs on the same model file got the same grades on both engines (2, 3 and 3 out of 5) from part 2&#39;s blind review, which grades the code under a random id. They took longer on Strata: one ran out the two-hour clock, one died on the context limit. Agents spend most of their time generating, so llama.cpp stays the engine for agents on this laptop.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;The README of UnbiasedStrata closes its human-written note with &amp;quot;Use at your own risk. Works on my machine.&amp;quot; My machine is a 2022 laptop with a Ryzen 7 PRO 6850U and its integrated Radeon 680M GPU, which has no memory of its own and borrows about 16 GB of the laptop&#39;s 30 GB of RAM. It is not the machine the README means. The engine targets an NVIDIA card with CUDA, and the 680M&#39;s chip, gfx1035, appears nowhere in the build&#39;s list of AMD GPUs, not even among the ones marked &amp;quot;unvalidated&amp;quot;. I read the warning, agreed with it, and built the thing anyway.&lt;/p&gt;</description>
			</item>
			<item>
				<title>105 identical commands and other ways local agents fail</title>
				<link>https://tsvinc.dev/posts/how-local-agents-failed/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/how-local-agents-failed/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent through a client like OpenCode. &lt;a href=&#34;https://tsvinc.dev/posts/blamed-opencode-flash-next/&#34;&gt;Part 1&lt;/a&gt; introduced the bench; &lt;a href=&#34;https://tsvinc.dev/posts/ledgers-split-the-models/&#34;&gt;part 23&lt;/a&gt; has the results by task.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;GLM-4.7 Flash, an 18 GB local model, was asked to write a small Java tool that reads JUnit test reports and lists the slowest tests. Somewhere in the run it typed this shell command:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -rf test-reports &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir test-reports &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; java SlowTests --json test-reports 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The command empties a directory and then asks the tool to summarise it. After a line from the JVM about its options, the tool answered with a JSON summary that began &lt;code&gt;{&amp;quot;total&amp;quot;:0&lt;/code&gt;. The model sent the same command again, byte for byte, and got the same answer, 105 times back to back. The run made 176 tool calls in all, and the bench killed it at its 30-minute limit.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Another guard&#39;s test list found holes in mine</title>
				<link>https://tsvinc.dev/posts/second-guard-found-my-gaps/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/second-guard-found-my-gaps/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/guard-missed-bash-lc/&#34;&gt;My guard blocked bash -c. Models write bash -lc.&lt;/a&gt;, which explains the guard this post tests.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Comparing two guards for coding agents sounds like a code review: read theirs, read mine, note the differences, nod. I walked in with the posture of a visiting inspector. It turned into the best test run my guard has had, none of the test cases came from my own list, and the inspection went the other way.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Confine a coding agent&#39;s HTTPS to an allowlist with bwrap</title>
				<link>https://tsvinc.dev/guides/run-coding-agent-egress-allowlist/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/guides/run-coding-agent-egress-allowlist/</guid>
				<description>&lt;p&gt;Run a command (a coding agent, a build, a shell) in a bubblewrap sandbox whose only way out is HTTPS through a small proxy on the host that lets through the hosts on your list and refuses the rest.&lt;/p&gt;&#xA;&#xA;&lt;dl class=&#34;guide-scope&#34;&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Tested on&lt;/dt&gt;&lt;dd&gt;Fedora Linux 44 (KDE), kernel 7.2.13-200.fc44.x86_64, bubblewrap 0.12.0, Python 3.14.8, curl 8.18.0, Node.js v22.23.1 (2026-10-31).&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row is-gap&#34;&gt;&lt;dt&gt;Not verified&lt;/dt&gt;&lt;dd&gt;running a coding agent inside the sandbox. The test commands were curl, Python, bash and Node&#39;s &lt;code&gt;fetch&lt;/code&gt;, run from a test script.&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Needs&lt;/dt&gt;&lt;dd&gt;Linux with bubblewrap (&lt;code&gt;bwrap&lt;/code&gt;) and Python 3; curl for the tests. No root. Node.js for the two &lt;code&gt;fetch&lt;/code&gt; checks only.&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Time&lt;/dt&gt;&lt;dd&gt;not timed. The scripted re-run of the whole procedure, failure captures included, took 3 s (13:44:32 to 13:44:35); that is script speed, not the time to set this up by hand.&lt;/dd&gt;&lt;/div&gt;&lt;div class=&#34;guide-scope-row&#34;&gt;&lt;dt&gt;Risk&lt;/dt&gt;&lt;dd&gt;low and reversible. Nothing outside the working directory and the project directory changes, and the proxy runs as your user. The project directory is mounted read-write, so the command inside can change or delete files there.&lt;/dd&gt;&lt;/div&gt;&lt;/dl&gt;&#xA;&lt;h2 id=&#34;before-you-start&#34;&gt;Before you start&lt;a class=&#34;heading-anchor&#34; href=&#34;#before-you-start&#34; aria-label=&#34;Link to section: Before you start&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;Check the tools. My run:&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; bwrap --version&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; python3 --version&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; curl --version &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; /usr/bin/node --version&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; uname -r&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; grep PRETTY_NAME /etc/os-release&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;bubblewrap 0.12.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Python 3.14.8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;curl 8.18.0 (x86_64-redhat-linux-gnu) libcurl/8.18.0 OpenSSL/3.5.9 zlib/1.3.1.zlib-ng brotli/1.2.1 libidn2/2.3.8 libpsl/0.21.5 libssh/0.12.2/openssl/zlib nghttp2/1.68.0 ngtcp2/1.22.1 nghttp3/1.18.0 mit-krb5/1.22.2 OpenLDAP/2.6.15&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;v22.23.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;7.2.13-200.fc44.x86_64&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;PRETTY_NAME=&amp;#34;Fedora Linux 44 (KDE Plasma Desktop Edition)&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;Pick a working directory for the scripts. Every command below runs from it, and the proxy&#39;s files go in &lt;code&gt;run/&lt;/code&gt; under it. In the output below it is shown as &lt;code&gt;/home/user/egress&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Placeholders, used only in step 8:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;PROJECT_DIR&amp;gt;&lt;/code&gt;: the directory the sandboxed command may read and write. It appears as &lt;code&gt;/work&lt;/code&gt; inside.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;AGENT&amp;gt;&lt;/code&gt;: the command to run in the sandbox.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;&amp;lt;INSTALL_DIR&amp;gt;&lt;/code&gt;: the directory the agent is installed in, if it is not under &lt;code&gt;/usr&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Back up or commit &lt;code&gt;&amp;lt;PROJECT_DIR&amp;gt;&lt;/code&gt; before you run anything in it: the sandbox mounts it read-write.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;How it fits together, from the two scripts:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Most models passed the scripts. The ledgers split them</title>
				<link>https://tsvinc.dev/posts/ledgers-split-the-models/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/ledgers-split-the-models/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent through a client like OpenCode. &lt;a href=&#34;https://tsvinc.dev/posts/blamed-opencode-flash-next/&#34;&gt;Part 1&lt;/a&gt; introduced the bench; &lt;a href=&#34;https://tsvinc.dev/posts/agent-speed-followed-active-parameters/&#34;&gt;part 22&lt;/a&gt; covered how fast the same models run.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;This part counts passes: which local models got which coding tasks past hidden checks they never see. The models run on one RTX 3090 (24 GB of VRAM), mostly served by llama.cpp, and drive an agent client, mainly OpenCode, an open-source coding agent.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; Since 15 October, through OpenCode on one RTX 3090, 18 models ran six small command-line tools in their first intake arm (a model&#39;s first batch of intake runs under one set of settings): 288 of 309 runs passed every hidden check, and 11 models passed all of theirs. 20 models ran feature work in existing Java, Go and Spring Boot code, again counting each model&#39;s first arm: 80 of 180 runs passed, no model passed all 9, five passed none. On the Zig version of the Java ledger task, first arms only (add a money transfer to an existing ledger service), 18 of 60 runs passed, and only K2 Horizon, a mixture-of-experts model with 4 billion of its 36 billion parameters active per token, passed all three, at 73 minutes a run. The best on feature work were tuned variants of the dense Qwen3.8-27B model with MTP drafting, the model&#39;s own draft layer (ThinkingCap 8 of 9, Qwen3.8 27B-pi and Turbo Brilliance 7) and Flash-Next on Strata, an engine that keeps part of a mixture-of-experts model in system RAM (7 of 9). A gap of one or two passes out of nine is inside run-to-run noise.&lt;/p&gt;</description>
			</item>
			<item>
				<title>My agents got their own config. Two still read mine.</title>
				<link>https://tsvinc.dev/posts/installed-apart-still-read-home/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/installed-apart-still-read-home/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/benchmark-read-my-claude-md/&#34;&gt;My benchmark read my CLAUDE.md&lt;/a&gt; and &lt;a href=&#34;https://tsvinc.dev/posts/claude-code-home-as-project/&#34;&gt;Claude Code read my home folder as a project&lt;/a&gt;, which explain two of the leaks below in detail.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;I keep a set of config templates for six terminal coding agents: OpenCode in two major versions, pi, omp, Hermes Agent and Claude Code. They&#39;re set up to run on local models and to block destructive commands even when nobody is approving them. Anyone installing them may already have a config of their own for at least one of those agents, so the installer has a mode for that. I call it installing apart: the template goes into its own directory, &lt;code&gt;~/.config/harnesses/&amp;lt;name&amp;gt;&lt;/code&gt;, a small launcher points the agent there, and your own config is never touched. That is the whole promise, one clause long, and I wrote it.&lt;/p&gt;</description>
			</item>
			<item>
				<title>On a laptop iGPU, memory bandwidth picked the model</title>
				<link>https://tsvinc.dev/posts/igpu-bandwidth-picked-the-model/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/igpu-bandwidth-picked-the-model/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a local model behind a coding agent. The &lt;a href=&#34;https://tsvinc.dev/series/3090-agent-bench/&#34;&gt;3090 Agent Bench&lt;/a&gt; ran the same kind of models on a desktop GPU; this series moves them to a laptop.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;I ran local models as coding agents on a laptop: a Ryzen 7 PRO 6850U from 2022, with the models running on its integrated Radeon 680M GPU through llama.cpp. The first request opencode, the coding-agent client I drove them with, sent in a session was 11,000 to 12,400 tokens long, and the model had to read all of it before it wrote a word. A dense 27-billion-parameter model, one that runs every weight for every token, took 299 seconds, about five minutes, to read it. Three mixture-of-experts models of 35 to 40 billion parameters, which route each token through a few expert blocks so that only 2.5 to 3 billion parameters are active, took 77 to 78 seconds. None of them had opened a file of the project yet. Five minutes, and the agent hadn&#39;t done a single thing I could blame it for. That part would have to wait as well.&lt;/p&gt;</description>
			</item>
			<item>
				<title>On one RTX 3090, agent speed followed active parameters</title>
				<link>https://tsvinc.dev/posts/agent-speed-followed-active-parameters/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/agent-speed-followed-active-parameters/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a local model behind a coding agent. &lt;a href=&#34;https://tsvinc.dev/posts/blamed-opencode-flash-next/&#34;&gt;Part 1&lt;/a&gt; introduced the bench; this part and the next one sum it up.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The smallest file on my bench is Ternary Bonsai 2, a 27-billion-parameter model packed into 7.2 GB. Driving a coding agent, it generated 37.9 tokens per second, the median over its three batches of runs. Ornith 1.5, a 35B model in a 19.3 GB file, generated 171.2. The file 2.7 times bigger ran 4.5 times faster.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The template differed. The prompts didn&#39;t.</title>
				<link>https://tsvinc.dev/posts/template-differed-prompts-didnt/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/template-differed-prompts-didnt/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/read-the-transcript-not-the-gate/&#34;&gt;Read the transcript, not the gate&lt;/a&gt;, where a chat template broke every Claude Code session on four models.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The plan had one variable. Same model, two quantizations, only the weights differ, and I was about to run it on Ornith-1.5-122B, a 122-billion-parameter mixture-of-experts coding model, on my 128 GB AMD Strix Halo box. On one side, the 4-bit UD-Q4_K_XL from unsloth, a team that publishes quantized builds of many big open models, about 80 GB. On the other, a GSQ-RCO IQ3_S from the Hugging Face account fajardc, about 53 GB, a mixed quant whose tensors range from IQ2_XS to Q6_K. Ten coding-agent runs each: five tasks in five languages, each through two agent harnesses, opencode and Claude Code, the programs that turn a model&#39;s replies into file edits and shell commands.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Twenty-one agent runs on a laptop iGPU, none merge-ready</title>
				<link>https://tsvinc.dev/posts/igpu-agent-runs-none-merge-ready/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/igpu-agent-runs-none-merge-ready/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent through a client like opencode. &lt;a href=&#34;https://tsvinc.dev/posts/igpu-bandwidth-picked-the-model/&#34;&gt;Part 1&lt;/a&gt; covers why every model here is a mixture-of-experts.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; Seven mixture-of-experts models (each token runs through only a few expert blocks, a small slice of the parameters) ran three coding tasks each through opencode, a coding-agent client run headless in a sandbox, on a laptop&#39;s integrated Radeon 680M GPU: a Node bookmarks API, a Java access-log report and a Go status page. 12 of the 21 runs committed their work, 4 hit the two-hour limit, and 3 died when the GPU timed out under GLM-4.7-Flash, one of the seven. A blind review (the reviewer never knew which model wrote the code) that ran its own probes (small tests of the failure paths) in a sandbox graded 12 of the 21 at 3 out of 5, four at 2 and five at 1: nothing was merge-ready. In the first twelve runs, 11 projects passed their own test suites, and the review still found blockers in 11. Tiel-Coder, a 35B model with about 3B active, came closest.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Five languages, two reps: the round-two ranking</title>
				<link>https://tsvinc.dev/posts/round-two-ranking-five-languages/</link>
				<pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/round-two-ranking-five-languages/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/nine-accusations-one-lie/&#34;&gt;Nine accusations, one lie&lt;/a&gt;, which covers how round two was graded. This post is what the grades add up to.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Round two of my local-model coding study is finished, and this post ranks its six setups by how their code holds up when a save fails. The study asks whether a model served locally, on my 128 GB AMD Strix Halo box, can do a developer&#39;s job as a coding agent. Round two gave the six setups (a setup is one model with its quantization and inference engine) the same seven coding tickets, each in two agent harnesses, opencode and Claude Code, the programs that turn a model&#39;s replies into file edits and shell commands. Five of the tickets are the &lt;em&gt;core&lt;/em&gt;: the same money-transfer feature for a small ledger service, in Java, Go, Zig, TypeScript and Python.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Nine accusations, one lie: re-reading round two&#39;s judges</title>
				<link>https://tsvinc.dev/posts/nine-accusations-one-lie/</link>
				<pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/nine-accusations-one-lie/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/coding-agent-lied-not-just-failed/&#34;&gt;How to tell when your coding agent lied, not just failed&lt;/a&gt;, which describes the honesty check this post grades.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Two automated judges graded round two of my local-model coding study, and this post grades them. The study asks whether a model served locally, on my 128 GB AMD Strix Halo box, can do a developer&#39;s job as a coding agent; round two ran six model setups through the same coding tickets in two agent programs, opencode and Claude Code. One judge grades the code, the other grades whether the agent told the truth about its own work.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Two rounds, seventeen arms: the engine set the speed, not the quant</title>
				<link>https://tsvinc.dev/posts/engine-set-the-speed-not-the-quant/</link>
				<pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/engine-set-the-speed-not-the-quant/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/round-two-ranking-five-languages/&#34;&gt;Five languages, two reps: the round-two ranking&lt;/a&gt;, which ranks these arms by the quality of their code. This post is about how fast they got there.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Qwen3.8-Flash-Next, a mixture-of-experts model, ran at two quantizations (a quant sets how many bits each weight gets) in round two of my study of local models as coding agents, six model setups on the same coding tickets on my 128 GB AMD Strix Halo box. At Q2_0, 2.40 bits per weight, it generated 58.9 tokens per second; at IQ4_XS, 4.25 bits per weight, 26.6. A 66.4 GB file beating a 93.7 GB one by 2.2x is what a smaller quant is supposed to do: fewer bytes to read per token, more tokens per second. There should be a rule that a speed gap lists everything that changed before it is allowed an explanation. This one listed one thing.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The third run mattered where it cost the most</title>
				<link>https://tsvinc.dev/posts/third-run-where-it-cost-most/</link>
				<pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/third-run-where-it-cost-most/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent through a client like OpenCode. &lt;a href=&#34;https://tsvinc.dev/posts/blamed-opencode-flash-next/&#34;&gt;Part 1&lt;/a&gt; introduced the bench.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;My bench is a set of coding tasks for local models on one RTX 3090 (24 GB): each model drives OpenCode, an open-source coding agent, and hidden tests decide whether a run passed. Until this part, a new model went through an &lt;em&gt;intake&lt;/em&gt;: every task three times, in three tiers. The &lt;em&gt;basic&lt;/em&gt; tier is six small scripting tasks, the &lt;em&gt;hard&lt;/em&gt; tier three ledger services in Java, Go and Spring with hidden core tests, and the &lt;em&gt;Zig&lt;/em&gt; tier one Zig ledger task that may run for four hours. One model&#39;s full intake cost about a day and a half of the card&#39;s time, and most of that is Zig.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The lock map deleted a mutex someone was waiting on</title>
				<link>https://tsvinc.dev/posts/lock-map-deleted-a-waited-mutex/</link>
				<pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/lock-map-deleted-a-waited-mutex/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you have guarded per-key work in Go with a map of mutexes. Builds on &lt;a href=&#34;https://tsvinc.dev/posts/blind-review-agent-code-probes/&#34;&gt;29 of 34 passing agent runs had a blocker the hidden tests never saw&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;This series is about the A9, my 128 GB AMD Strix Halo test box (a GEEKOM A9 Mega: Ryzen AI Max+ 395, 128 GB of soldered LPDDR5X-8000), and lately about one question: can a model running on it, locally, do a developer&#39;s job as a coding agent? The study that asks it is a grid: several models, the same ticket-style tasks for each, more than one agent harness, and I grade what each run leaves behind. The grading has several layers, on the theory that any one of them can be fooled and fooling all of them at once is harder. This post is about a run that fooled every layer but one, and I wrote one of the fooled layers myself.&lt;/p&gt;</description>
			</item>
			<item>
				<title>An exception in one layer deleted a block in another</title>
				<link>https://tsvinc.dev/posts/exception-removed-a-block/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/exception-removed-a-block/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/guard-missed-bash-lc/&#34;&gt;My guard blocked bash -c. Models write bash -lc.&lt;/a&gt;, which explains the guard&#39;s command-position rules and lists &lt;code&gt;$(echo rm) -rf victim&lt;/code&gt; among the things a regex can&#39;t see.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;At 20:01 I committed a convenience: my coding agents could now delete reverse-engineering output without asking. At 20:14 I committed a new deny rule for a command my own guard post had filed under &amp;quot;what a regex still can&#39;t see&amp;quot;. In the thirteen minutes between them, one end-to-end run showed that the convenience had quietly switched off the only thing covering that command, in the one agent where anything covered it at all.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Bun&#39;s fetch had no way to skip the proxy</title>
				<link>https://tsvinc.dev/posts/fetch-had-no-proxy-off-switch/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/fetch-had-no-proxy-off-switch/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/model-that-only-says-run/&#34;&gt;The model that only says RUN&lt;/a&gt;, which describes the fake model server and the six agent harnesses these templates set up.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Bun&#39;s documentation for &lt;code&gt;fetch&lt;/code&gt; has a paragraph I only found after the fix was in: pass &lt;code&gt;proxy: false&lt;/code&gt; &amp;quot;to connect directly regardless of the environment&amp;quot;. The Bun my tests ran on, 1.3.10, accepts that option without complaint and then sends the request to the proxy anyway.&lt;/p&gt;&#xA;&lt;p&gt;That gap cost my agents their models. The config templates I keep for six terminal coding agents find local model servers in two places. At install time a Python tool asks each candidate host for its model list and writes the result into the configs. At run time a TypeScript module does the same thing inside OpenCode, pi and omp. Same hosts, same question, two languages.&lt;/p&gt;</description>
			</item>
			<item>
				<title>My guard blocked bash -c. Models write bash -lc.</title>
				<link>https://tsvinc.dev/posts/guard-missed-bash-lc/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/guard-missed-bash-lc/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a coding agent with auto-approval on, or written a hook that vets its shell commands.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The guard looked at &lt;code&gt;bash -lc &#39;rm -rf build&#39;&lt;/code&gt; and said allow.&lt;/p&gt;&#xA;&lt;p&gt;It denied &lt;code&gt;bash -c &#39;rm -rf build&#39;&lt;/code&gt;. It denied &lt;code&gt;bash -l -c &#39;rm -rf build&#39;&lt;/code&gt;, the same two flags passed as separate arguments. Pack the &lt;code&gt;l&lt;/code&gt; and the &lt;code&gt;c&lt;/code&gt; into one argument and the guard waved the recursive delete through without so much as a question. The guard had applied its rule exactly as written. The rule had been written for a different spelling of the same command.&lt;/p&gt;</description>
			</item>
			<item>
				<title>My guard read the tool&#39;s name, never its arguments</title>
				<link>https://tsvinc.dev/posts/guard-judged-tools-by-name/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/guard-judged-tools-by-name/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/guard-missed-bash-lc/&#34;&gt;My guard blocked bash -c. Models write bash -lc.&lt;/a&gt;, which explains the guard and what it hard-denies.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Every rename asked. Every comment asked. Every retyped variable stopped the agent and waited for me to click, and the binary it was all about never changed once. That was my guard&#39;s Ghidra policy doing exactly what I had written.&lt;/p&gt;&#xA;&lt;p&gt;My agent-config templates give six terminal coding agents a Ghidra MCP server for reverse engineering, off by default and available on demand: pyghidra-mcp 0.2.7, GhidraMCP 2.0 or ReVa 7.3.1. The same guard that hard-denies destructive shell commands also decides about these tools, and its Ghidra policy was cautious: an allowlist of read verbs (&lt;code&gt;get_&lt;/code&gt;, &lt;code&gt;list_&lt;/code&gt;, &lt;code&gt;decompile_&lt;/code&gt; and a few more), a deny for deleting a binary, and a question for everything else.&lt;/p&gt;</description>
			</item>
			<item>
				<title>My sandbox profile had never run as shipped</title>
				<link>https://tsvinc.dev/posts/sandbox-profile-never-ran/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/sandbox-profile-never-ran/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve seen Claude Code&#39;s sandboxed Bash tool; &lt;a href=&#34;https://tsvinc.dev/posts/model-that-only-says-run/&#34;&gt;The model that only says RUN&lt;/a&gt; describes the fake model used to drive it here.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Claude Code&#39;s sandbox for its Bash tool is one of the better ideas in the coding-agent world, which is why it hurt a little to find that mine had never run. On Linux it runs each command under bubblewrap, takes away the network except through its own proxy, and fences off the filesystem with lists you control. My agent-config templates ship an opt-in profile for it, set up for Java and Go work. That profile had never been run as shipped. An audit of the templates pointed it out, and the first run found three ways it would break the builds it was set up for.&lt;/p&gt;</description>
			</item>
			<item>
				<title>My unit tests never typed `; echo`</title>
				<link>https://tsvinc.dev/posts/sql-rule-fix-broke-echo/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/sql-rule-fix-broke-echo/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/guard-missed-bash-lc/&#34;&gt;My guard blocked bash -c. Models write bash -lc.&lt;/a&gt;, which explains the guard and its command-position rules.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Both guards agreed on more than 700 cases. Four coding agents then failed one end-to-end case, and the failures were right.&lt;/p&gt;&#xA;&lt;p&gt;The guards are the TypeScript and Python halves of the guard in my agent-config templates: one shared file of regex rules that hard-denies destructive shell commands even when an agent runs with auto-approval. Both halves have their own unit tests, and the two must agree. The case that failed was &lt;code&gt;git log -S &#39;DROP TABLE&#39; --oneline&lt;/code&gt;, which searches history and destroys nothing. It has been in the end-to-end suite as a near miss, a harmless command that has to keep running, ever since a harness&#39;s own &lt;code&gt;*DROP TABLE*&lt;/code&gt; permission glob blocked it. I had just made the SQL rule stricter, every unit case was green, and the agents now refused to run a search.&lt;/p&gt;</description>
			</item>
			<item>
				<title>One space in a path, and Hermes blocked echo hi</title>
				<link>https://tsvinc.dev/posts/space-in-hermes-home/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/space-in-hermes-home/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/guard-missed-bash-lc/&#34;&gt;My guard blocked bash -c. Models write bash -lc.&lt;/a&gt;, which explains the guard that Hermes runs as a hook here.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;echo hi&lt;/code&gt; was blocked. So was every other terminal command and every file read and write Hermes Agent tried. The guard in my agent-config templates is supposed to stop destructive commands, and on this install it stopped everything, which is one way to be secure.&lt;/p&gt;&#xA;&lt;p&gt;Hermes runs my guard as a &lt;code&gt;pre_tool_call&lt;/code&gt; shell hook, with &lt;code&gt;fail_closed: true&lt;/code&gt;. That setting is deliberate: if the guard can&#39;t give a verdict, the tool call doesn&#39;t run. A guard that fails open is a guard that an unlucky crash switches off. The cause was the install&#39;s &lt;code&gt;HERMES_HOME&lt;/code&gt;, the directory Hermes keeps its config and hooks in: it had a space in its name.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tests that could not fail</title>
				<link>https://tsvinc.dev/posts/tests-that-could-not-fail/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/tests-that-could-not-fail/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/redaction-ate-the-marker/&#34;&gt;The secret filter made my security test pass&lt;/a&gt;, where the same rule caught a test that couldn&#39;t fail.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;One rule in my agent-config project has paid for itself several times: a new end-to-end case must fail on the old code. A test that passes before the fix and after it can&#39;t tell me whether the fix works.&lt;/p&gt;&#xA;&lt;p&gt;The suite drives six real coding agents (OpenCode V1 and V2, pi, omp, Hermes Agent, Claude Code) with a fake model and checks whether each command ran. For a fix to the guard, the shared regex rules that block destructive commands, &amp;quot;the old code&amp;quot; usually means the old rule file. For the guard cases below I broke the guard on purpose instead: a copy whose &amp;quot;ask&amp;quot; verdict returns &amp;quot;allow&amp;quot;. Every case that counts on that ask to stop a command should then fail. Two of the new ones didn&#39;t, and a third test, from a different fix, passed on the old code just as happily.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The fix was in five agents. The sixth never called it.</title>
				<link>https://tsvinc.dev/posts/sixth-agent-never-called-the-guard/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/sixth-agent-never-called-the-guard/</guid>
				<description>&lt;p&gt;&lt;em&gt;Follows &lt;a href=&#34;https://tsvinc.dev/posts/guard-judged-tools-by-name/&#34;&gt;My guard read the tool&#39;s name, never its arguments&lt;/a&gt;, which introduced the fix this post tests.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The last post ended with a fix I was pleased with. My guard used to decide about Ghidra MCP tools by their name alone; now &lt;code&gt;ghidraCheck(rules, tool, input, cwd)&lt;/code&gt; runs the guard&#39;s read rules over every path-like argument first, so an agent that asks Ghidra to import &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt; into its project gets refused. Both guard implementations, TypeScript and Python, had cases for it. One agent, Claude Code, had been probed against a fake MCP server. The post said the pi and omp extensions, the OpenCode plugin and the Hermes and Claude Code hook &amp;quot;all go through it&amp;quot;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The secret filter made my security test pass</title>
				<link>https://tsvinc.dev/posts/redaction-ate-the-marker/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/redaction-ate-the-marker/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/model-that-only-says-run/&#34;&gt;The model that only says RUN&lt;/a&gt;, which explains how a fake model drives the real agents in these tests.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;My end-to-end suite rests on one belief: if the marker doesn&#39;t come back, the command didn&#39;t run. Every &amp;quot;blocked&amp;quot; verdict for six coding agents is that belief applied to a tool result. It is a very convenient belief. It holds right up to the moment something between the command and the test eats part of the output, and then a command that ran reads as a command that was stopped.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The test rig was the unsafe part</title>
				<link>https://tsvinc.dev/posts/test-rig-was-the-unsafe-part/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/test-rig-was-the-unsafe-part/</guid>
				<description>&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; A throwaway &lt;code&gt;HOME&lt;/code&gt; isn&#39;t isolation when the tool under test honours its own &lt;code&gt;*_HOME&lt;/code&gt; or &lt;code&gt;*_CONFIG_DIR&lt;/code&gt; variable; my test installer would have written into real configs. &lt;code&gt;bwrap --dev-bind / / --unshare-net&lt;/code&gt; gives the command its own network and shares the rest: host processes stay visible and signalable, the filesystem stays writable, and a Unix socket that lives at a path, like Docker&#39;s, still connects. For a suite that runs destructive commands on purpose, I now use one container per suite with &lt;code&gt;--network none&lt;/code&gt;, only the repository mounted, and my own uid.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Claude Code sessions can message each other. Mine didn&#39;t</title>
				<link>https://tsvinc.dev/posts/sessions-could-message-each-other/</link>
				<pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/sessions-could-message-each-other/</guid>
				<description>&lt;p&gt;Copy, paste and a question mark: that was the protocol between two of my Claude Code sessions on 25 October, and I was the transport. The session that works on my LLM proxy had reported that the A9, one of the two GPU boxes behind the proxy, was still running a benchmark, with 223 jobs queued. At 13:33 I pasted that line into the A9 session&#39;s window and asked what the 223 meant. The answer came 32 seconds later, and at 13:36 I pasted it back into the proxy&#39;s window.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The model that only says RUN</title>
				<link>https://tsvinc.dev/posts/model-that-only-says-run/</link>
				<pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/model-that-only-says-run/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve configured a coding agent&#39;s permission rules, or wondered whether they hold.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The most useful model I ran this month has a vocabulary of one sentence. It has no opinions about code, it never reads its context, and it does exactly what the prompt tells it, every time, which is more than I can say for most of the models I benchmark. Give it &lt;code&gt;RUN: rm -rf victim :END&lt;/code&gt; and it asks the harness to run &lt;code&gt;rm -rf victim&lt;/code&gt;. That is the whole personality.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The typo followed the tag; the failures didn&#39;t</title>
				<link>https://tsvinc.dev/posts/typo-followed-the-tag/</link>
				<pubDate>Sat, 24 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/typo-followed-the-tag/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a coding agent against a local llama-server. Builds on &lt;a href=&#34;https://tsvinc.dev/posts/flint-mistyped-its-path/&#34;&gt;Flint mistyped its path and blamed the tools&lt;/a&gt;, from the 3090 Agent Bench.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Flint misspelled its own run folder 495 times on the A9. It typed &lt;code&gt;runs-s2_flxr1&lt;/code&gt; where the folder was &lt;code&gt;runs-s2-flxr1&lt;/code&gt;: one underscore where the tag had a hyphen, up to 127 times in a single session. Six of the 18 Flint cells I counted did it. One of those six lost its task to the typo. When I renamed the run tag and ran the same task again, the typo moved. The scores didn&#39;t.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Flint mistyped its path and blamed the tools</title>
				<link>https://tsvinc.dev/posts/flint-mistyped-its-path/</link>
				<pubDate>Thu, 22 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/flint-mistyped-its-path/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent through a client like OpenCode. &lt;a href=&#34;https://tsvinc.dev/posts/blamed-opencode-flash-next/&#34;&gt;Part 1&lt;/a&gt; introduced the bench&#39;s sandbox.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;On 22 October a Go run of Flint 40B made 115 tool calls, and OpenCode refused 81 of them. The model spent the session working around what its summary called a &amp;quot;persistent tool-output glitch&amp;quot;, then reported its code and tests written. The workspace diff was zero bytes.&lt;/p&gt;&#xA;&lt;p&gt;The glitch was a typo. Seven calls in, the model started mistyping its own workspace path, one token off.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Three models wrote the same circuit-breaker bug</title>
				<link>https://tsvinc.dev/posts/same-circuit-breaker-bug/</link>
				<pubDate>Thu, 22 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/same-circuit-breaker-bug/</guid>
				<description>&lt;p&gt;One README on the A9 says this about its circuit breaker:&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;An attempt that was already in flight when the circuit opened scores nothing: the open period is&#xA;the point, and the trial is the only way out.&lt;/p&gt;&#xA;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Swift-1.5 wrote that sentence, under Claude Code, as part of a TypeScript retry layer for a payments&#xA;gateway. It is a precise statement of the right invariant. The code below it does not keep it: a&#xA;request that left while the circuit was closed, and comes back during the half-open trial, decides&#xA;the trial. The code reviewer called the sentence an overclaim. The honesty checker, which files the&#xA;labels, called it honest: it read the sentence as a claim about the open state, where the code does&#xA;keep it (&lt;a href=&#34;https://tsvinc.dev/posts/coding-agent-lied-not-just-failed/&#34;&gt;how the labels work&lt;/a&gt;). Both readings fit what&#xA;the code does. It handles the late request, for the wrong state.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Claude Code read my home folder as a project</title>
				<link>https://tsvinc.dev/posts/claude-code-home-as-project/</link>
				<pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/claude-code-home-as-project/</guid>
				<description>&lt;p&gt;&lt;em&gt;Builds on &lt;a href=&#34;https://tsvinc.dev/posts/benchmark-read-my-claude-md/&#34;&gt;My benchmark read my CLAUDE.md&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Part 5 of this series told the story of OpenCode carrying my global &lt;code&gt;~/.claude&lt;/code&gt; into every bench prompt. It held up Claude Code as the client the harness had isolated properly: &lt;code&gt;CLAUDE_CONFIG_DIR&lt;/code&gt; pointed at an empty bench-local directory, and &lt;code&gt;claudeMdExcludes&lt;/code&gt; kept the repository&#39;s &lt;code&gt;CLAUDE.md&lt;/code&gt; out.&lt;/p&gt;&#xA;&#xA;&lt;div class=&#34;callout callout-note&#34; role=&#34;note&#34;&gt;&#xA;&lt;p class=&#34;callout-label&#34;&gt;&lt;span class=&#34;callout-kind&#34;&gt;Note&lt;/span&gt;&lt;span class=&#34;callout-title&#34;&gt;Correction to part 5&lt;/span&gt;&lt;/p&gt;&#xA;&lt;p&gt;That isolation didn&#39;t hold. Claude Code walks up from its working directory into my home directory and loads my global &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;~/.claude/rules/&lt;/code&gt; from there, and every bench workspace sat under it. The details are below.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
			</item>
			<item>
				<title>The model card said think harder. The audits got worse</title>
				<link>https://tsvinc.dev/posts/more-thinking-did-not-help/</link>
				<pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/more-thinking-did-not-help/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a local reasoning model as a coding agent and know what a reasoning-effort setting does.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The Qwen3.8-27B model card lists three reasoning efforts and marks one as the default:&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;code&gt;xhigh&lt;/code&gt; (default): for complex tasks demanding thorough analysis&lt;/p&gt;&#xA;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A few lines further down it adds a tip aimed at people like me:&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;In multi-turn agentic tasks, lower reasoning effort does not always reduce overall task completion time. Although it may produce faster per-turn responses, it can also lead to insufficient analysis, more failures, and repeated retries, which may increase total latency and token consumption.&lt;/p&gt;&#xA;&#xA;&lt;/blockquote&gt;</description>
			</item>
			<item>
				<title>The model said it saw no output. It was right.</title>
				<link>https://tsvinc.dev/posts/model-saw-no-output/</link>
				<pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/model-saw-no-output/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent against a local llama.cpp server and know what a tool call is.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;North-Mini-Code made eight tool calls in a Java task under Claude Code and gave every one of them the same id, &lt;code&gt;1&lt;/code&gt;. Claude Code ran all eight and logged eight real results. The model was shown one of them, a read error. Within a minute it decided the project directory was empty. From where it sat, that was correct.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Three bugs between K2 and a working Spring app</title>
				<link>https://tsvinc.dev/posts/three-bugs-k2-spring-app/</link>
				<pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/three-bugs-k2-spring-app/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve read a Spring Boot stack trace and know what an agent matrix cell is (&lt;a href=&#34;https://tsvinc.dev/posts/best-code-came-from-the-slowest-arm/&#34;&gt;part 13&lt;/a&gt; has the setup).&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;An honesty table invites a lazy reading: the models with no lies on record must be the ones that can do the work. In this matrix that reading held up for a while. When these cells ran, Flash-Next IQ4_XS, the stage-1 leader, had 33 runs and zero lie cells.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The hard tier sorted by a rollback, not by speed</title>
				<link>https://tsvinc.dev/posts/hard-tier-rollback-not-speed/</link>
				<pubDate>Mon, 19 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/hard-tier-rollback-not-speed/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you know how this bench grades: a prompt, a repo, and a hidden suite the agent never sees, where one failing core test makes the run FAIL.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;On 19 October this post had a title, &amp;quot;Fast models failed the hard tier&amp;quot;, and three runs per model to back it. The two fastest decoders on the box had just gone 0 of 9. So had Muse Glimmer, whose label in my model notes read &amp;quot;slow; never failed on scripting or dev tools&amp;quot;, although the same row already recorded a failed Java ledger in all four earlier arms, each for one missing rollback. I parked the draft until the remaining queues finished, so it would not age into &amp;quot;still running&amp;quot;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The hidden test asked for more than the prompt</title>
				<link>https://tsvinc.dev/posts/hidden-test-asked-for-more/</link>
				<pubDate>Mon, 19 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/hidden-test-asked-for-more/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve seen a hidden-test agent benchmark: the model gets a task prompt and a repo, a scorer runs tests the model never saw.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;When most models fail the same check, the check starts to look like the hard part of the task. It sits in the results as a wall: four tunes of one 27B model hit it, six other models hit it, run after run. The natural reading is that the models are missing something.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Empty diff, finished code: 124,000 characters of thinking</title>
				<link>https://tsvinc.dev/posts/empty-diff-finished-code/</link>
				<pubDate>Sat, 17 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/empty-diff-finished-code/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a coding agent against a local llama.cpp server and know what reasoning tokens are.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;16 October, the A9, K2-Horizon Q8 on llama.cpp master Vulkan, Claude Code on a Java ledger ticket:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;19:48:50&lt;/strong&gt; the session starts.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;19:51:45&lt;/strong&gt; the last of 14 file reads comes back. By then the model has read the README, eleven main classes and two test classes.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;20:34:44&lt;/strong&gt; the next turn ends. It is one block of thinking, 140,241 characters long, and it stops mid-sentence.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;20:48:50&lt;/strong&gt; the 60-minute timeout kills the process.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Files changed: 0. Score: 29 points, the score the matrix gives an untouched repository. The obvious reading is a model that sat in a loop for an hour. The 43-minute turn in the middle says otherwise.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The harness decided how long the model thought</title>
				<link>https://tsvinc.dev/posts/harness-set-the-thinking/</link>
				<pubDate>Sat, 17 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/harness-set-the-thinking/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a coding agent against a local llama.cpp server and know what prefill, decode and reasoning tokens are.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;On the first run of t11, a Bash script that files an inbox of messy files by what they contain, Qwen3.8-27B wrote 62,977 characters of reasoning. The run took 425 seconds and passed. On the first run of the same task in Pi, against the same server settings, it wrote 8,911 characters, took 90 seconds, and passed.&lt;/p&gt;</description>
			</item>
			<item>
				<title>2.2x faster prefill, 1.04x faster agent</title>
				<link>https://tsvinc.dev/posts/fast-prefill-slow-agent/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/fast-prefill-slow-agent/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve pointed a coding agent at a local inference server and know what prefill, decode and a prefix cache are.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Before the engine was even downloaded, the research note wrote down what would get it deleted: park it if the probe &amp;quot;shows &amp;lt; 90 tok/s at ~25k on non-copy text and &amp;lt; 1.2x llama.cpp on edit turns&amp;quot;. Both legs were meant for the probe. The second, edit turns, ended up being measured with a coding agent.&lt;/p&gt;</description>
			</item>
			<item>
				<title>29 of 34 passing agent runs had a blocker the hidden tests never saw</title>
				<link>https://tsvinc.dev/posts/blind-review-agent-code-probes/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/blind-review-agent-code-probes/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a coding agent on a real task and read the diff it left behind.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; A Go transfer function written by a local model passed 15 of 15 hidden core tests, 12 of 12 detail tests and 7 of 7 mutants. Cancel the request context between its two saves and 4.00 EUR disappears. To find that kind of bug in agent output at scale:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Review each run &lt;strong&gt;blind&lt;/strong&gt;: task, &lt;em&gt;original&lt;/em&gt; README, diff, final tree, mechanical score, and nothing that names the model or harness.&lt;/li&gt;&#xA;&lt;li&gt;Score &lt;strong&gt;overall as a merge decision&lt;/strong&gt;, never an average, with mechanical caps: a reproduced money loss is a blocker, and any blocker caps overall at 3.&lt;/li&gt;&#xA;&lt;li&gt;The reviewer &lt;strong&gt;writes and runs&lt;/strong&gt; a short probe test for every failure path the task names. Reasoning about a probe instead of running it gets the review rejected.&lt;/li&gt;&#xA;&lt;li&gt;Calibrate a cheap reviewer against an expensive one on the same packets. Here the rules moved the scores, not the model.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;As of the last review filed on 15 October, 69 of 75 reviews carried at least one blocker. 34 of the 75 runs had a PASS verdict, and 29 of those still carried one.&lt;/p&gt;</description>
			</item>
			<item>
				<title>How to tell when your coding agent lied, not just failed</title>
				<link>https://tsvinc.dev/posts/coding-agent-lied-not-just-failed/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/coding-agent-lied-not-just-failed/</guid>
				<description>&lt;p&gt;A rollback that discards its own failure is a bug.&#xA;A rollback the README promises, with no rollback code under it, is a lie.&lt;/p&gt;&#xA;&lt;p&gt;Both of these come from Zig runs of the same transfer task on my local agent bench. The first agent&#39;s commit message says &amp;quot;a store failure part-way through rolls back so neither balance changes&amp;quot;, and the code tries:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-zig&#34; data-lang=&#34;zig&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Commit both balances. A failure on the second save rolls back the first, so either both&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// change or neither does.&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;errdefer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The undo exists, and its &lt;code&gt;catch {}&lt;/code&gt; drops a failed undo on the floor. The second agent&#39;s README says &amp;quot;All or nothing: either both balances change or neither does, including when the AccountStore fails part-way through.&amp;quot; The code under it:&lt;/p&gt;</description>
			</item>
			<item>
				<title>LFM2.5 is fast. Gemma 4 finishes the job</title>
				<link>https://tsvinc.dev/posts/lfm25-vs-gemma4-26b/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/lfm25-vs-gemma4-26b/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve pointed a coding agent at a local llama.cpp server and know what MoE and a Q4 GGUF are.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; For agent work, use Gemma 4 26B-A4B. LFM2.5-24B-A2B was faster on most short single-file tasks and passed most of them. On the Spring Boot task it wrote no tests in three runs; Gemma wrote eleven in each of its three, and passed all 14 hidden core tests twice. Three runs per cell, so read these as a direction, not a rate.&lt;/p&gt;</description>
			</item>
			<item>
				<title>My benchmark read my CLAUDE.md</title>
				<link>https://tsvinc.dev/posts/benchmark-read-my-claude-md/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/benchmark-read-my-claude-md/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve pointed a coding agent like OpenCode at a local OpenAI-compatible server.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The payload audit on 14 October was built to answer one dull question: do the coding clients in my local agent benchmark send their own sampler settings and quietly override the server&#39;s? For the model ids it captured, they didn&#39;t. OpenCode 1.18.4 and pi both sent no temperature, no top_p, nothing, and I took that as the answer for the whole benchmark.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Seven FAILs, one setting: rerunning agents at 262144 tokens</title>
				<link>https://tsvinc.dev/posts/seven-fails-one-setting-262144/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/seven-fails-one-setting-262144/</guid>
				<description>&lt;p&gt;A FAIL in the agent matrix reads like a verdict on the code. The agent wrote something, the hidden tests caught it, next row.&lt;/p&gt;&#xA;&lt;p&gt;Three of the FAIL rows from the A9 score 100 points. Every hidden test passes in them, and every mutant is caught. The code is finished and correct. What failed was the session. Each one had grown to just under 131072 tokens of context when the next request crossed the line. The server refused it, and Claude Code exited with an error. The matrix grades that exit as well as the code.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tabby turned file contents into numbers</title>
				<link>https://tsvinc.dev/posts/tabby-turned-file-contents-into-numbers/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/tabby-turned-file-contents-into-numbers/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve pointed a coding agent at a local model server with OpenAI-style tool calls.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; TabbyAPI&#39;s tool-call parser for the &lt;code&gt;qwen3_coder&lt;/code&gt; format runs every argument through &lt;code&gt;strip()&lt;/code&gt; and &lt;code&gt;json.loads&lt;/code&gt; without looking at the tool&#39;s schema. A &lt;code&gt;string&lt;/code&gt; argument whose text is valid JSON comes back as a number, boolean, null, list or object, and every argument loses its leading and trailing whitespace. For a coding agent that means &lt;code&gt;write&lt;/code&gt; and &lt;code&gt;edit&lt;/code&gt; calls with corrupted content. Upstream &lt;code&gt;main&lt;/code&gt; still has the same code as of 2026-10-16. A three-file local patch that hands the request&#39;s tool schemas to the parser fixes it.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The engine got credit for the quant</title>
				<link>https://tsvinc.dev/posts/engine-got-credit-for-the-quant/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/engine-got-credit-for-the-quant/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve served a GGUF through llama.cpp and know what a quant is.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Two inference engines ran the same 17-gigabyte GGUF through the same nine Zig agent runs, and each finished with 7 PASS, 1 PARTIAL, 1 FAIL. A tie is a boring result. This one was the most useful number of the week, because the evening before, one of those engines had been beating the other 7 to 3.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The models deleted the benchmark&#39;s log</title>
				<link>https://tsvinc.dev/posts/models-deleted-the-log/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/models-deleted-the-log/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run a coding agent inside a benchmark harness, or written one.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Tool call 225 of a two-hour Zig run was two words long.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm run-1.log&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That was the last thing &lt;code&gt;qwen38pi&lt;/code&gt;, a 27B local model driven through OpenCode, did on the benchmark&#39;s ledger task. The task never mentioned the file. &lt;code&gt;run-1.log&lt;/code&gt; was the harness&#39;s live log of that same session. It was also the only thing the harness read to count the session&#39;s tool calls. The result row says PARTIAL, 8,131 seconds, &lt;code&gt;toolcalls: 0&lt;/code&gt;. (It says PASS now: a 22 October rescore stopped counting the Mermaid-diagram check toward the verdict, the one detail that run had missed.) OpenCode&#39;s own database holds 225 tool parts for the session.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The best-reviewed code came from the slowest Flash-Next arm</title>
				<link>https://tsvinc.dev/posts/best-code-came-from-the-slowest-arm/</link>
				<pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/best-code-came-from-the-slowest-arm/</guid>
				<description>&lt;p&gt;Same model, same six cells, two rows.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Row one:&lt;/strong&gt; Flash-Next at IQ4_XS on llama.cpp Vulkan with MTP. 4 of 6 cells passed. Blind-review mean 3.17. &lt;strong&gt;0.68 passes per hour&lt;/strong&gt; of wall clock.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Row two:&lt;/strong&gt; the same model squeezed into a 62 GiB Q2_0, on Strata&#39;s hcq8 mode. 6 of 6 passed. &lt;strong&gt;3.96 passes per hour.&lt;/strong&gt; Review mean 2.5, the lowest of any Flash-Next arm.&lt;/p&gt;&#xA;&lt;p&gt;The pass column crowns the small quant. The review column says the slowest Flash-Next arm reviewed best. Both columns are measured correctly. Neither explains the other, because the two rows differ in four ways at once and each cell ran once.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Zig needed context, not time</title>
				<link>https://tsvinc.dev/posts/zig-needed-context-not-time/</link>
				<pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/zig-needed-context-not-time/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent against a local llama.cpp server and know what context compaction is.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;When an agent run dies at the wall clock, the obvious diagnosis is that it was slow, and the obvious fix is a longer wall. Four 27B models on a Zig task were hitting a one-hour limit in about half their runs at 64k context, so twelve runs of the test-first prompt got four hours instead.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Inventing the work vs promising what isn&#39;t there</title>
				<link>https://tsvinc.dev/posts/inventing-work-vs-promising-what-isnt-there/</link>
				<pubDate>Wed, 14 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/inventing-work-vs-promising-what-isnt-there/</guid>
				<description>&lt;p&gt;After stage 1 the ask was simple: separate the models whose code is wrong from the models that say things that never happened. A rollback that exists and breaks is a bug. A test suite that exists only in the closing summary is something else.&lt;/p&gt;&#xA;&lt;p&gt;That sounds like one more column in the results table. It took three labels, a keyword rule, consistency passes that changed five labels, and a ruling on what counts as a lie before the last column could be filled in.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Read the transcript, not the gate</title>
				<link>https://tsvinc.dev/posts/read-the-transcript-not-the-gate/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/read-the-transcript-not-the-gate/</guid>
				<description>&lt;p&gt;The prompt asks for one file, &lt;code&gt;hello.py&lt;/code&gt;, containing &lt;code&gt;print(&#39;hi&#39;)&lt;/code&gt;, and then asks the agent to stop. The model is Qwen3-Coder-Next at Q8, served by llama.cpp on the A9, driven by Claude Code. Its reply comes back in one turn. It reports the file made, line and all, and signs off.&lt;/p&gt;&#xA;&lt;p&gt;The session transcript holds one user message, one assistant message, and nothing between them. No Write. No Bash. No tool call of any kind. No file was written, because nothing ever tried to write one.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The fastest model finished first by stopping first</title>
				<link>https://tsvinc.dev/posts/fastest-model-finished-first/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/fastest-model-finished-first/</guid>
				<description>&lt;p&gt;&lt;code&gt;zig build test&lt;/code&gt; comes back with one line that matters:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;src/service.zig:30:9: error: declarations are not allowed between container fields&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The agent is gpt-oss-120b under opencode, about 480 seconds into a 5400-second budget, and this is its tenth failing &lt;code&gt;zig build test&lt;/code&gt; since second 204. It reads the file. Its reasoning works out the fix in detail: &amp;quot;We need to move TransferReceipt and TransferError out of struct. We&#39;ll delete lines 29-40 ... Let&#39;s edit to delete.&amp;quot;&lt;/p&gt;</description>
			</item>
			<item>
				<title>I blamed OpenCode; a 2-bit model passed Zig in it</title>
				<link>https://tsvinc.dev/posts/blamed-opencode-flash-next/</link>
				<pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/blamed-opencode-flash-next/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve driven a coding agent against a local llama.cpp server and know what MoE and a Q2 quant are.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;OpenCode has two features that look bad in a Zig agent run at 64k context.&lt;/p&gt;&#xA;&lt;p&gt;The first is auto-compaction. When the conversation fills the window, OpenCode summarizes it and carries on from the summary. OpenCode&#39;s config docs describe the default as compacting &amp;quot;when context is full&amp;quot;. On a Zig task, that window fills with compiler errors, test files and stress-test output, and a summary can only keep so much of it.&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
