<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Local-Llm on tsvinc</title>
		<link>https://tsvinc.dev/tags/local-llm/</link>
		<description>Recent content in Local-Llm 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/local-llm/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Image input on a 27B coder costs 1.1 GiB, once</title>
				<link>https://tsvinc.dev/posts/qwen38-27b-image-input-costs-1-gib/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/qwen38-27b-image-input-costs-1-gib/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve served a GGUF with llama.cpp. &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;I had the memory risk of image input in the wrong place. On a 24 GB RTX 3090 that already holds a 27B coding model with a 128k-token context, I expected the dangerous request to be the full one: an image plus a context filled to the brim. Adding image input to Qwen3.8-27B makes sense: the base model is a vision-language model, and a coding agent that can read a screenshot of a failing UI or a terminal is worth having. The question was whether it still fits on the card.&lt;/p&gt;</description>
			</item>
			<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>One open PR made MoE prompts 12-30% faster on my 3090</title>
				<link>https://tsvinc.dev/posts/open-pr-moe-prompts-12-30-faster/</link>
				<pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/open-pr-moe-prompts-12-30-faster/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve served a GGUF with llama.cpp. &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;115 upstream llama.cpp commits bought my RTX 3090 at most 2.8%. The next morning, one pull request from outside upstream bought up to 29.7% in prompt speed in &lt;code&gt;llama-bench&lt;/code&gt;, and about 13% through the server that actually feeds the agents, on synthetic prompts. &lt;a href=&#34;https://tsvinc.dev/posts/llamacpp-115-commits-barely-faster/&#34;&gt;Part 25&lt;/a&gt; moved my llama.cpp build forward by those commits and measured the result on the card: prompt reading moved -0.4% to +2.8%, generation 0.0% to +0.6%. I switched anyway, mostly for the parser fixes.&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 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>115 llama.cpp commits later, my 3090 was barely faster</title>
				<link>https://tsvinc.dev/posts/llamacpp-115-commits-barely-faster/</link>
				<pubDate>Sun, 01 Nov 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/llamacpp-115-commits-barely-faster/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve served a GGUF with llama.cpp. &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;This series is about one 24 GB RTX 3090 and the local models I serve from it as coding agents, each run scored by tests the model never sees. The models are served by llama.cpp, built from source, and since 26 October the build was master at &lt;code&gt;caa8191&lt;/code&gt;. By 31 October upstream was 115 commits further on, and the question was the routine one: did any of them do anything for this card and these models?&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>In opencode 1.19.5, my workaround protected nothing</title>
				<link>https://tsvinc.dev/posts/opencode-workaround-protected-nothing/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/opencode-workaround-protected-nothing/</guid>
				<description>&lt;p&gt;The backlog item was small and polite. My LAN proxy for local LLMs writes the config for opencode, a terminal coding agent, and the model names in that config had been slightly wrong on purpose: opencode showed &lt;code&gt;lan/3090-q38-27b-128k&lt;/code&gt; where every other client sees &lt;code&gt;3090-qwen38-27b-128k&lt;/code&gt;. The &lt;code&gt;lan/&lt;/code&gt; is just the provider prefix; the &lt;code&gt;q38&lt;/code&gt; was the deliberate mistake. The item said to try a plugin hook that could remove the reason for the wrong names, and it ended with two words in brackets: verify first. Backlog items don&#39;t usually carry a warning label. This one had earned it, and the person it was warning was me.&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>My drift check said 0 differences. It checked 4 arrays.</title>
				<link>https://tsvinc.dev/posts/drift-check-checked-four-arrays/</link>
				<pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/drift-check-checked-four-arrays/</guid>
				<description>&lt;p&gt;My LAN proxy for local LLMs keeps its own catalog of how to serve each model, &lt;code&gt;models.json&lt;/code&gt;. Each of the two GPU boxes behind it keeps the same settings again in its own &lt;code&gt;models.sh&lt;/code&gt;, a bash script. I wrote a drift check to compare the two, and its first run said &amp;quot;0 differences&amp;quot;.&lt;/p&gt;&#xA;&lt;p&gt;I read that as a sentence about the config files. It was a sentence about my parser, which had pulled four arrays out of two bash scripts of about 500 and 600 lines, compared them, and reported back with total calm.&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>My isolation guard checked itself</title>
				<link>https://tsvinc.dev/posts/guard-checked-its-own-variable/</link>
				<pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/guard-checked-its-own-variable/</guid>
				<description>&lt;p&gt;&lt;em&gt;Follows &lt;a href=&#34;https://tsvinc.dev/posts/mutant-switched-a-real-gpu/&#34;&gt;The mutant was killed after it switched a real GPU&lt;/a&gt;, which built the isolation this post tests.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;I used to think a guard test fails when the thing it guards is gone. That is what the word means. Mine guarded the wall between my Go CLI&#39;s tests and my live LAN proxy, which starts a real model server on a real GPU box whenever a request asks for a model that isn&#39;t running. I wrote the guard, described it in a post, and trusted it the way you would trust an examiner who lets the student bring the answer key: nothing ever comes back wrong. I took that as a good sign. So, I imagine, would the student.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The config said 32k. The engine still ran 64k.</title>
				<link>https://tsvinc.dev/posts/hot-reload-two-clocks/</link>
				<pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/hot-reload-two-clocks/</guid>
				<description>&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; My Go proxy for local LLMs now reloads three settings on SIGHUP and refuses any other change by name. A reload changes what the proxy tells clients at once, but a running model server keeps the preset it was started with, so the two can disagree until the server restarts; the reload log line says so. Overlapping model-list refreshes are numbered, and only a successful read moves the number forward.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The race detector saw my requests in order</title>
				<link>https://tsvinc.dev/posts/race-detector-saw-ordered-requests/</link>
				<pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/race-detector-saw-ordered-requests/</guid>
				<description>&lt;p&gt;I run a small Go proxy on my LAN, in front of the GPU boxes that serve my local LLMs. Clients can ask it for a model by a short alias, and it looks the alias up in a map to decide where the request goes. That map can now be replaced while the proxy is running, by a config reload, so every read of it has to take a lock. A read that skips the lock is the kind of bug that does nothing at all until one request lands in the middle of a reload, and I have no interest in meeting it there.&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 mutant was killed after it switched a real GPU</title>
				<link>https://tsvinc.dev/posts/mutant-switched-a-real-gpu/</link>
				<pubDate>Wed, 28 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/mutant-switched-a-real-gpu/</guid>
				<description>&lt;p&gt;I test my fixes by breaking them on purpose. The method is mutation testing, done by hand: copy the module, undo one condition (that copy is a &amp;quot;mutant&amp;quot;), run the copy&#39;s tests, and record KILLED if a test fails or SURVIVED if none does. KILLED is the result you want; it means the tests noticed. The code in question is a small Go program on my LAN that sits in front of the GPU boxes serving local language models and decides which model server runs where. I like the method because its verdict is one word. It had never occurred to me to ask what a mutant gets up to between being created and being killed.&lt;/p&gt;</description>
			</item>
			<item>
				<title>llama.cpp&#39;s MoE cache wants gigabytes and long prompts</title>
				<link>https://tsvinc.dev/posts/moe-cache-wants-gigabytes/</link>
				<pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/moe-cache-wants-gigabytes/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve served a MoE GGUF with llama.cpp and know what &lt;code&gt;--n-cpu-moe&lt;/code&gt; does. The card and the build bump behind these numbers are in &lt;a href=&#34;https://tsvinc.dev/posts/rtx-3090-unlock-what-it-buys/&#34;&gt;The 3090 unlock is real. Where it lands is the catch&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;My agent bench runs local models on one RTX 3090 with 24 GB of VRAM. A mixture-of-experts (MoE) model too big for that can keep part of its experts (blocks of weights, of which each token uses only a few) in system RAM. llama.cpp now has a flag for that case, &lt;code&gt;--moe-cache-mib&lt;/code&gt;: a slice of VRAM that keeps the popular experts on the GPU while the rest sit in RAM. This part measures what it buys on three MoE models: Flint 40B, Ornith 35B and Flash-Next, the last too big even for VRAM and RAM together.&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>Load gpu_unlock before nvidia on Bazzite for an RTX 3090</title>
				<link>https://tsvinc.dev/guides/gpu-unlock-on-bazzite/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/guides/gpu-unlock-on-bazzite/</guid>
				<description>&lt;p&gt;This installs the &lt;code&gt;gpu_unlock&lt;/code&gt; kernel module on Bazzite so that it runs from the initramfs, before &lt;code&gt;nvidia&lt;/code&gt; binds the card, and leaves an RTX 3090 with full-rate FP16/BF16 (FP32-accumulate) and TF32 tensor math on every boot that carries the kernel option &lt;code&gt;gpu_unlock.targets=all&lt;/code&gt;.&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;Bazzite 44.20261023 (&lt;code&gt;bazzite-nvidia-open&lt;/code&gt; image, kernel 7.2.9-ogc3.1.fc44), NVIDIA open driver 615.78.08, RTX 3090 (&lt;code&gt;10de:2204&lt;/code&gt;) with VBIOS 94.02.26.80.8C, Secure Boot off; gpu-unlock at upstream commit &lt;code&gt;af28cbb&lt;/code&gt;, NVIDIA firmware pin 610.43.03 (2026-10-26).&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 redo after an image update brings a new kernel (none has arrived yet); the file-removal half of Undo; the login check on a real failed boot (tested with its override variables only); Secure Boot; any card other than this one RTX 3090; the boot-menu keys in Recovery come from Bazzite&#39;s docs.&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;a login user that can read the kernel log with &lt;code&gt;journalctl -k&lt;/code&gt; (on the tested box: a member of &lt;code&gt;wheel&lt;/code&gt;); an rpm-ostree image that puts &lt;code&gt;nvidia&lt;/code&gt; in its initramfs (Bazzite&#39;s &lt;code&gt;nvidia&lt;/code&gt; and &lt;code&gt;nvidia-open&lt;/code&gt; images do); a GA102/GA103/GA104/GA106 GeForce card from the upstream list; sudo; about 340 MB of downloads; a keyboard on the box to reach the hidden boot menu.&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;30 min (plus one full power off).&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;if the unlock fails partway, the card can stay unusable until a full power off; on a single-GPU box that is a black screen, recovered from the boot menu (Recovery below). Reversible: the option lives in one deployment, the speed selectors do not survive a power off. Step 11 (optional) removes the rollback entry for good.&lt;/dd&gt;&lt;/div&gt;&lt;/dl&gt;&#xA;&lt;p&gt;The steps replace upstream&#39;s &lt;code&gt;make install&lt;/code&gt; (DKMS plus packages), which cannot work on rpm-ostree: &lt;code&gt;/usr&lt;/code&gt; is read-only, and the image&#39;s prebuilt initramfs loads &lt;code&gt;nvidia&lt;/code&gt; before anything on the root file system runs. Here the module, its firmware and a loader script live in &lt;code&gt;/etc&lt;/code&gt;, &lt;code&gt;rpm-ostree initramfs-etc&lt;/code&gt; adds them to the initramfs as an overlay, and a &lt;code&gt;modprobe.d&lt;/code&gt; &lt;code&gt;install&lt;/code&gt; rule runs the loader before every load of &lt;code&gt;nvidia&lt;/code&gt;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The 3090 unlock is real. Where it lands is the catch</title>
				<link>https://tsvinc.dev/posts/rtx-3090-unlock-what-it-buys/</link>
				<pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/rtx-3090-unlock-what-it-buys/</guid>
				<description>&lt;p&gt;NVIDIA sells the GeForce RTX 3090 with one kind of tensor math running at half speed: FP16 and BF16 multiplies that accumulate in FP32, and TF32, issue at half the rate of the professional cards built on the same silicon. &lt;code&gt;gpu_unlock&lt;/code&gt;, an open-source kernel module, puts the full rate back at every boot. My 3090 runs headless at home as a local LLM box, mostly serving one 27-billion-parameter coding model through llama.cpp, so the question was what doubling that math buys the work I actually do. I approach every free speedup the same way: I assume the bill is somewhere, and I go looking for it.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The benchmark said it was waiting. Once.</title>
				<link>https://tsvinc.dev/posts/bench-said-waiting-once/</link>
				<pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/bench-said-waiting-once/</guid>
				<description>&lt;p&gt;At 12:36 on 25 October the proxy learned to see the A9&#39;s benchmark between two of its jobs. At 12:58 it learned to see the benchmark waiting behind the proxy&#39;s own engine. At 13:14 it learned that the benchmark mentions the waiting exactly once. At 13:59 it learned that once means once for the whole queue, not once per job. Four commits on a feature branch in 83 minutes, and each one found somewhere else the benchmark could be. Four more review passes went over them before any of it shipped, as v0.3.1, at 15:16.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The first engine on the A9 was started to be stopped</title>
				<link>https://tsvinc.dev/posts/started-to-be-stopped/</link>
				<pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/started-to-be-stopped/</guid>
				<description>&lt;p&gt;Stopping a process is the easy half of proving that my LLM proxy gives a GPU back. On the A9 the hard half was letting it take the GPU at all. The one rule I keep about that box is that nothing gets in its benchmark&#39;s way, and the proxy had never started an engine there. The day before, its idle stop had sent the A9 a stop anyway (&lt;a href=&#34;https://tsvinc.dev/posts/cutting-a-stream-doesnt-cancel-the-turn/&#34;&gt;how idle stop managed that&lt;/a&gt;).&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 proxy said unloaded. The model was loading.</title>
				<link>https://tsvinc.dev/posts/status-said-unloaded/</link>
				<pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/status-said-unloaded/</guid>
				<description>&lt;p&gt;Ten times in sixteen seconds the probe asked my proxy for its model list, and ten times it got back the same 54,031 bytes, with the model it had just asked to load listed as &lt;code&gt;unloaded&lt;/code&gt;. After fifteen seconds of that, the probe wrote its verdict and moved on:&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-json&#34; data-lang=&#34;json&#34;&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;nt&#34;&gt;&amp;#34;alias&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;3090-flint-40b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;configured_context&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;131072&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;3090-flint-40b returned to unloaded during load&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;total_s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;18.4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Four of the seven models in that run got the same line. None of them had unloaded. The 3090 box&#39;s router had accepted each load, exactly as asked.&lt;/p&gt;</description>
			</item>
			<item>
				<title>&#34;Terminating immediately&#34; took 90 seconds</title>
				<link>https://tsvinc.dev/posts/terminating-immediately-took-90-seconds/</link>
				<pubDate>Sat, 24 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/terminating-immediately-took-90-seconds/</guid>
				<description>&lt;p&gt;llama-server keeps a message for the moment you run out of patience with it: &lt;code&gt;Received second interrupt, terminating immediately.&lt;/code&gt; On the 3090 box it printed that at 09:45:04. The next thing systemd had to say about the unit came at 09:46:34, and it was &lt;code&gt;Aborting&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;That stop was half of an engine switch. My LAN proxy for local models stops one llama.cpp build and starts another when a request names a model that only the other build has (&lt;a href=&#34;https://tsvinc.dev/posts/cutting-a-stream-doesnt-cancel-the-turn/&#34;&gt;why it does that&lt;/a&gt;). The proxy logged the new engine up after 1m36s. A switch between those two builds takes 2 to 4 seconds. Two signals did this, the second one came from my own script, and the &amp;quot;immediately&amp;quot; path deadlocked inside glibc.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Cutting a stream doesn&#39;t cancel the turn</title>
				<link>https://tsvinc.dev/posts/cutting-a-stream-doesnt-cancel-the-turn/</link>
				<pubDate>Sat, 24 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/cutting-a-stream-doesnt-cancel-the-turn/</guid>
				<description>&lt;p&gt;The plan was a router. One OpenAI- and Anthropic-compatible URL on a mini-PC, two GPU boxes behind it, read the request&#39;s &lt;code&gt;model&lt;/code&gt;, pick the box, forward the bytes. I started it at 23:37 one night. By 22:04 the next evening it was starting and stopping llama.cpp builds on the 3090 box (the A9&#39;s benchmarks kept it out that day), deciding when a request has to wait, and shutting engines down after fifteen idle minutes so a benchmark could have the GPU back.&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>To a coding agent, 503 means try again</title>
				<link>https://tsvinc.dev/posts/503-means-try-again/</link>
				<pubDate>Sat, 24 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/503-means-try-again/</guid>
				<description>&lt;p&gt;&lt;code&gt;opencode run&lt;/code&gt; printed its banner, &lt;code&gt;&amp;gt; build · mock-err503&lt;/code&gt;, and then nothing. No error, no exit code, no output, until the run was killed. The mock server behind it answered every request with a 503 in my LAN proxy&#39;s error format: the status the proxy sent at the time for any model it couldn&#39;t serve, whether the GPU box was down, the engine switch had failed or a benchmark owned the GPU for the next few hours. opencode was retrying, and it had no plans to stop.&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>MTP acceptance is a side effect, not a target</title>
				<link>https://tsvinc.dev/posts/mtp-acceptance-not-a-target/</link>
				<pubDate>Wed, 21 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/mtp-acceptance-not-a-target/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you know what speculative decoding is. &lt;a href=&#34;https://tsvinc.dev/posts/benchmark-ran-without-mtp/&#34;&gt;Part 6&lt;/a&gt; covers how the bench turned on MTP drafting for the Qwen3.8 27B tunes.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The hard-tier table for Swift-1.5, a Qwen3.8-27B tune, ends in a column that reads 0.83: the share of the MTP head&#39;s draft tokens that the model accepted, averaged over every request the server logged. The server drafts three tokens ahead, and the average request keeps 83 percent of them.&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>It fits in 16 GB. That was the easy question</title>
				<link>https://tsvinc.dev/posts/kv-cache-in-system-ram/</link>
				<pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/kv-cache-in-system-ram/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you&#39;ve run llama.cpp and know what the KV cache is.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&amp;quot;Will it fit?&amp;quot; is the question everyone asks about a model and a graphics card, because it has a clean yes-or-no answer. A friend asked it about a 16 GB card: can Qwen3.8-27B, or its Swift-1.5 tune, at IQ4_XS (13.3 and 14.4 GiB of weights) run fully on the GPU if the KV cache goes to system RAM? He sent the flags he&#39;d run it with, &lt;code&gt;-t 8 -ctk q8_0 -ctv q8_0&lt;/code&gt;, and the question was what adding &lt;code&gt;-nkvo&lt;/code&gt; does.&lt;/p&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>Every way my GPU job queue went quiet</title>
				<link>https://tsvinc.dev/posts/gpu-job-queue-went-quiet/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/gpu-job-queue-went-quiet/</guid>
				<description>&lt;p&gt;Six days, one GPU, and every way the queue went quiet: idle with work waiting, stuck with nothing logged, or failing in words that pointed somewhere else.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;A model download on the net lane:&lt;/strong&gt; GPU idle 3 h 56 min, 3 h 32 min of it behind one 56.3 GB file at about 3 MB/s.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;llama-server&lt;/code&gt; left behind by the previous job:&lt;/strong&gt; GPU lane blocked 3 h 44 min. One event, then nothing.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;REQUIRES&lt;/code&gt; naming a file nothing would ever write:&lt;/strong&gt; job 84c could not run for 1 h 07 min and logged nothing. The GPU kept busy; the job just disappeared.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;One model&#39;s launch flags breaking every model&#39;s smoke test:&lt;/strong&gt; the bug lived 16 h 56 min, and three intakes failed their gate, each one looking like the model&#39;s fault.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A seed script that stops at the first bad job:&lt;/strong&gt; every job after it is never written. Caught once by a scratch render and reproduced below; no count of what it would have dropped.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The watcher that reads the queue:&lt;/strong&gt; froze on a hung ssh overnight. Not measured, by construction.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR —&lt;/strong&gt; A queue that logs state changes cannot report a state that doesn&#39;t change. Give every kind of waiting an event or a watcher condition: a download that runs too long, a GPU held by a process you didn&#39;t start, a &lt;code&gt;REQUIRES&lt;/code&gt; that has been false for hours, and nothing running at all. Make jobs kill their own servers. Render the seed before it reaches the box. Put timeouts on the thing that watches.&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>My benchmark served the model at half speed</title>
				<link>https://tsvinc.dev/posts/benchmark-ran-without-mtp/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/benchmark-ran-without-mtp/</guid>
				<description>&lt;p&gt;Nine short agent tasks, one model, an RTX 3090. llama.cpp finished them in 1,521 seconds. open-jet, a megakernel engine, finished them in 629. That is 2.4 times faster, the kind of ratio that gets a post written about a new engine.&lt;/p&gt;&#xA;&lt;p&gt;The engine, on a different quant and drafting one token deeper, accounts for 1.29x of it. The rest came from a flag one of my scripts had and the other didn&#39;t.&lt;/p&gt;</description>
			</item>
			<item>
				<title>One rebuild moved gpt-oss off ROCm. Two models stayed</title>
				<link>https://tsvinc.dev/posts/master-vulkan-beat-rocm-at-depth/</link>
				<pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/master-vulkan-beat-rocm-at-depth/</guid>
				<description>&lt;p&gt;&lt;a href=&#34;https://tsvinc.dev/posts/vulkan-or-rocm-at-which-depth/&#34;&gt;Part 6&lt;/a&gt; settled it by depth. gpt-oss-120b at 64k of context: ROCm prefilled 324 tok/s, Vulkan 131. By 127k the gap was 7×. The serving table put gpt-oss on ROCm, under a rule that fits in four words: ROCm for long context.&lt;/p&gt;&#xA;&lt;p&gt;On the night of 15 October the queue built llama.cpp master for Vulkan, 132 commits past the host build behind parts 5 to 7, and ran both builds back to back. gpt-oss-120b at 64k, old build: 114.7 tok/s. New build: 355.9.&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>IOMMU off buys a 128 GB iGPU 5 %, and costs the NPU</title>
				<link>https://tsvinc.dev/posts/iommu-off-five-percent-no-npu/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/iommu-off-five-percent-no-npu/</guid>
				<description>&lt;p&gt;The mental model went like this: the stock Fedora kernel boots the IOMMU in translated mode, so every GPU read of the 120 GiB of GTT memory on this box pays for an address translation, and &lt;code&gt;iommu=pt&lt;/code&gt; takes that cost away. Round one of the A/B even seemed to agree. gpt-oss-120b&#39;s Vulkan prefill at 64k context came out 14.7 % faster with passthrough, then 4.0 % in a second pair.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Mistral-Small-4 ran one matmul on the CPU, and said nothing</title>
				<link>https://tsvinc.dev/posts/mistral-small-4-one-matmul-on-cpu/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/mistral-small-4-one-matmul-on-cpu/</guid>
				<description>&lt;p&gt;A slow local model has an obvious diagnosis: it is too big for the box. Drop to a smaller quant, or a smaller model, and move on.&lt;/p&gt;&#xA;&lt;p&gt;On the A9 Mega that diagnosis was wrong twice in one afternoon. Mistral-Small-4 generated 3.4 tokens per second, with the model loaded on the GPU and llama-bench sitting at about 300 % CPU. MiniMax-M2.7 held the GPU at 100 % busy for hours and drew about 40 W doing it.&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 benchmark was talking to the previous server</title>
				<link>https://tsvinc.dev/posts/benchmark-talking-to-previous-server/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/benchmark-talking-to-previous-server/</guid>
				<description>&lt;p&gt;Strata v0.1.42 is an inference engine for one model family. Its loader accepts exactly one architecture, &lt;code&gt;qwen4exp&lt;/code&gt;: Qwen3.8-Flash-Next and the models built on it. On the A9 its own &lt;code&gt;setup.sh&lt;/code&gt; compiled the engine against ROCm 7.14 nightly wheels in about seven minutes, no root, and reused the GGUFs already sitting in &lt;code&gt;~/models&lt;/code&gt;. No new model download.&lt;/p&gt;&#xA;&lt;p&gt;So far, a short and happy engine review. Then I asked it to compare its own modes, and the second mode&#39;s result was measured on the first mode&#39;s server.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The deadlock test can&#39;t see a half-held lock</title>
				<link>https://tsvinc.dev/posts/deadlock-test-half-held-lock/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/deadlock-test-half-held-lock/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you have written a two-account transfer under per-account locks, and know what a mutation test is.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;At 128k context and a four-hour budget, four 27B local models ran a Zig ledger task 36 times through OpenCode. All 36 passed all 15 hidden core tests. All 36 had written their own concurrency test with real threads, because the prompt asked for one.&lt;/p&gt;&#xA;&lt;p&gt;Then the scorer removed the second account lock from each &lt;code&gt;transfer&lt;/code&gt; and ran everything again. The hidden suite failed on every mutant it could score. The models&#39; own suites failed in 4 of 11 scoreable spec runs, 0 of 9 clean review runs, and 6 of 9 test-first runs.&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>Vulkan or ROCm on Strix Halo? Ask at which depth</title>
				<link>https://tsvinc.dev/posts/vulkan-or-rocm-at-which-depth/</link>
				<pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/vulkan-or-rocm-at-which-depth/</guid>
				<description>&lt;div class=&#34;tldr&#34;&gt;&#xA;  &lt;span class=&#34;tldr-label&#34;&gt;TL;DR&lt;/span&gt;&#xA;  Which llama.cpp backend should a Strix Halo box (Ryzen AI Max+ 395, Radeon 8060S) run for long-context agent work? It depends on the context depth and on the model&#39;s attention. At an empty context Vulkan won prefill and decode on all four models of the first-day bench. By 32k ROCm prefills faster on both reference models, and at 127k it prefills gpt-oss-120b 7.0× faster (190 vs 27 tok/s). Vulkan still decodes faster on those two, but not on every model: on Laguna-S ROCm decode drops to less than half. On Ornith-1.5-35B at 128k ROCm also drew less power, ran about 10 °C cooler and used less energy per fill. &lt;code&gt;-ub 2048&lt;/code&gt; helps gpt-oss-120b prefill at zero context and halves it at 64k; on Flash-Next it is slower at every depth. The result is one engine per model, picked by the depth the model will actually work at.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;Four models, two backends, eight pairs of numbers. On 9 October, the day the A9 Mega first booted on real hardware, a quick llama-bench pass put Vulkan ahead on all eight. gpt-oss-120b: 1077 tok/s prefill against ROCm&#39;s 675, 55.5 tok/s generation against 51.0. Clean sweep (&lt;a href=&#34;https://tsvinc.dev/posts/gpu-120gib-in-the-kernel/&#34;&gt;part 2&lt;/a&gt; has the full table). The note in the project file said so in one line, and the one line read like a decision.&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>
			<item>
				<title>Zig&#39;s defer ends at the closing brace</title>
				<link>https://tsvinc.dev/posts/zig-defer-ends-at-closing-brace/</link>
				<pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/zig-defer-ends-at-closing-brace/</guid>
				<description>&lt;p&gt;&lt;em&gt;Assumes you have guarded a critical section with a mutex and &lt;code&gt;defer unlock&lt;/code&gt; in Go.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Moving money between two accounts under per-account locks has one famous trap, and it is the order. Lock A then B in one thread, B then A in another, and the two transfers deadlock. The textbook answer is to sort the IDs and always lock the lower one first. It is in every concurrency chapter.&lt;/p&gt;</description>
			</item>
			<item>
				<title>128k context is the floor, and prefill is the wall</title>
				<link>https://tsvinc.dev/posts/128k-floor-prefill-wall/</link>
				<pubDate>Sun, 11 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/128k-floor-prefill-wall/</guid>
				<description>&lt;div class=&#34;tldr&#34;&gt;&#xA;  &lt;span class=&#34;tldr-label&#34;&gt;TL;DR&lt;/span&gt;&#xA;  Is a 128 GB Ryzen AI Max+ 395 box worth it for long-context agent work? Memory isn&#39;t the limit: an 87 GiB model ran a 256k context with GTT peaking under 70 GiB. Prefill is. On gpt-oss-120b the marginal prefill rate falls 48×, from 1186.5 tok/s at an empty context to 24.6 tok/s at 127k, and filling that context once took 29 minutes. Qwen3.8-Flash-Next starts at less than half that prefill, overtakes it between 32k and 64k, and is 4.3× faster at ~128k, though gpt-oss still generates faster there. On kernel 7.2, one model hit a GPU compute ring timeout at 64k on all three attempts; the &lt;code&gt;amdgpu.lockup_timeout=10000,60000,10000,10000&lt;/code&gt; workaround from llama.cpp #25664 let that row complete.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;The arithmetic that sells a 128 GB box goes like this. Weights are 63 GB. The KV cache for a long context is a few more. Add it up, it&#39;s under 128, so it fits. And if it fits, it runs.&lt;/p&gt;</description>
			</item>
			<item>
				<title>120 GiB for the GPU, set in the kernel, not the BIOS</title>
				<link>https://tsvinc.dev/posts/gpu-120gib-in-the-kernel/</link>
				<pubDate>Sat, 10 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/gpu-120gib-in-the-kernel/</guid>
				<description>&lt;div class=&#34;tldr&#34;&gt;&#xA;  &lt;span class=&#34;tldr-label&#34;&gt;TL;DR&lt;/span&gt;&#xA;  On a Ryzen AI Max+ 395 with 128 GB, set the BIOS&#39;s Dedicated Graphics Memory to its minimum (0.5 G) and raise the TTM limit on the kernel command line: &lt;code&gt;ttm.pages_limit=31457280 ttm.page_pool_size=31457280&lt;/code&gt;. The unit is 4 KiB pages, so that is 120 GiB. Linux then sees 124.9 GiB, and the GPU can map up to 120 GiB of it on demand. &lt;code&gt;amdgpu.gttsize&lt;/code&gt; is deprecated and not needed. A firmware reset (F3, Optimized Defaults) quietly puts the carve-out back to 64 G. With the memory fixed, llama.cpp&#39;s Vulkan backend beat Fedora&#39;s ROCm build on every model tested. The two were different llama.cpp builds, though, and the benchmark&#39;s mean-power column measures something other than efficiency.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;The A9 Mega&#39;s setup menu keeps its GPU memory setting under Advanced → GFX Configuration, and it goes from 0.5 G to 96 G. The machine has 128 GB of LPDDR5X soldered to the board, and it was bought to run models whose weights alone are 63 GB. Dedicated Graphics Memory went to 96 G, the top of the list.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The download that contaminated the benchmark</title>
				<link>https://tsvinc.dev/posts/download-contaminated-benchmark/</link>
				<pubDate>Sat, 10 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://tsvinc.dev/posts/download-contaminated-benchmark/</guid>
				<description>&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; git show --name-status --relative --format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%h %ad %s&amp;#39;&lt;/span&gt; --date&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;format:&lt;span class=&#34;s1&#34;&gt;&amp;#39;%H:%M&amp;#39;&lt;/span&gt; dfd9f06 -- a9-results&#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;dfd9f06 01:59 a9-results: gpt-oss long-ctx run overlapped downloads, marked contaminated; rerun queued&#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;err&#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;go&#34;&gt;R100&#x9;a9-results/longctx-gptoss120b-b11538.jsonl&#x9;a9-results/longctx-gptoss120b-b11538.contaminated-by-download.jsonl&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That rename is a confession. The file holds gpt-oss-120b&#39;s long-context sweep on the A9 Mega: llama-bench prefill and generation speed at depth 0, 32k, 64k and 96k tokens. It started at 01:18. The model downloads started at 01:19.&lt;/p&gt;&#xA;&lt;p&gt;R100 means the content didn&#39;t change by a byte. The numbers stayed exactly as they were, and the filename now says they can&#39;t be trusted. The commit subject says the rest: rerun queued. There was no queue running yet. The runner&#39;s first commit had landed 25 seconds before the rename; the service started 94 seconds after it.&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
