<?xml version='1.0' encoding='utf-8'?>
<?xml-stylesheet type="text/xsl" href="/sheet.xsl"?><rss version="2.0"><channel><title>Planet Haskell</title><link>http://planet.haskell.org/</link><language>en</language><description>Planet Haskell - http://planet.haskell.org/</description><item><title>Eric Kidd: Redoubtful: Linux agent sandbox progress</title><guid isPermaLink="true">http://www.randomhacks.net/2026/05/17/redoubtful-agent-sandbox-progress/</guid><link>http://www.randomhacks.net/2026/05/17/redoubtful-agent-sandbox-progress/</link><description>&lt;p&gt;I’ve also been experimenting with agent sandboxes lately. &lt;a href="https://github.com/emk/redoubtful"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;redoubtful&lt;/code&gt;&lt;/a&gt; is a work-in-progress sandbox that supports:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
&lt;strong&gt;Linux-only sandboxes:&lt;/strong&gt; I’m focusing on what Linux supports, specifically, rather than trying to support the lowest-common-denominator features that work cross platform.&lt;/li&gt;
  &lt;li&gt;
&lt;strong&gt;Modular configuration profiles:&lt;/strong&gt; See below.&lt;/li&gt;
  &lt;li&gt;Isolation using &lt;code class="language-plaintext highlighter-rouge"&gt;pasta&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;bwrap&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;A shadow filesystem that &lt;em&gt;looks&lt;/em&gt; like your home directory, so things like &lt;code class="language-plaintext highlighter-rouge"&gt;git worktree&lt;/code&gt; actually work correctly. You can also selectively mount existing parts of your filesystem in read-only or read-write mode.&lt;/li&gt;
  &lt;li&gt;Network port forwarding and filtering proxy server.&lt;/li&gt;
  &lt;li&gt;
&lt;strong&gt;TODO:&lt;/strong&gt; Proxy credential support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But first, a warning: Nearly 100% of this code was written by coding agents, much of it by a local Qwen3.6 27B. I am, however, keeping a &lt;em&gt;very&lt;/em&gt; close eye on the output—one of my goals here is to see just what a small agent like this can do. This is maybe only 80% as good as my handwritten code would be a similar point in a project.&lt;/p&gt;

&lt;p&gt;And finally, &lt;strong&gt;this is an incomplete work-in-progress&lt;/strong&gt;, and it has not been packaged nicely for anyone besides me yet.&lt;/p&gt;

&lt;h3 id="modular-configuration-profiles"&gt;Modular configuration “profiles”&lt;/h3&gt;

&lt;p&gt;One of the &lt;em&gt;slightly&lt;/em&gt; novel parts of all this is the ability to define modular configuration. This allows us to invoke a sandbox with a specific set of credentials:&lt;/p&gt;

&lt;div class="language-sh highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;redoubtful run &lt;span class="nt"&gt;--uses&lt;/span&gt; pi &lt;span class="nt"&gt;--uses&lt;/span&gt; llama-server pi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we’re running the &lt;a href="https://pi.dev/"&gt;pi.dev&lt;/a&gt; coding agent with a locally-served Qwen3.6 27B via &lt;code class="language-plaintext highlighter-rouge"&gt;llama-server&lt;/code&gt;. Qwen3.6 27B is a fantastic lightweight coding model, and it works very well with pi.dev’s minimalist prompt. And since we’re running in a sandbox, we don’t care that pi.dev provides no sandbox and no confirmation before acting.&lt;/p&gt;

&lt;p&gt;To set up these two profiles, we first define a &lt;code class="language-plaintext highlighter-rouge"&gt;node&lt;/code&gt; profile:&lt;/p&gt;

&lt;div class="language-toml highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# Standard Node setup. If you're using `nvm`, you'll need to fix the path_add&lt;/span&gt;
&lt;span class="c"&gt;# entry to point to the correct nvm version.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# We might want some kind of plugin system to handle messy things like nvm.&lt;/span&gt;
&lt;span class="nn"&gt;[profile.node]&lt;/span&gt;
&lt;span class="py"&gt;mounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.npm-global"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.local/share/nvm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rw"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;path_add&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"~/.npm-global/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"~/.local/share/nvm/v24.15.0/bin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then let’s make Rust work:&lt;/p&gt;

&lt;div class="language-toml highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# A Rust setup, with optional rustup and advisory support.&lt;/span&gt;
&lt;span class="nn"&gt;[profile.rust]&lt;/span&gt;
&lt;span class="py"&gt;mounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.rustup"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.cargo"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c"&gt;# Cargo audit/deny support, which needs to take a lock to update the&lt;/span&gt;
    &lt;span class="c"&gt;# advisory database.&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.cargo/advisory-dbs/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rw"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;path_add&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["~/.cargo/bin"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then basic &lt;code class="language-plaintext highlighter-rouge"&gt;git&lt;/code&gt; is easy—we just need enough config to read &lt;code class="language-plaintext highlighter-rouge"&gt;user.name&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;user.email&lt;/code&gt;:&lt;/p&gt;

&lt;div class="language-toml highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# Things you will likely want for git.&lt;/span&gt;
&lt;span class="nn"&gt;[profile.git]&lt;/span&gt;
&lt;span class="py"&gt;mounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.gitconfig"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then finally, we can set up &lt;code class="language-plaintext highlighter-rouge"&gt;pi&lt;/code&gt; itself:&lt;/p&gt;

&lt;div class="language-toml highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# Profile for the pi coding agent. Run with:&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#     redoubtful run -u pi pi&lt;/span&gt;
&lt;span class="nn"&gt;[profile.pi]&lt;/span&gt;
&lt;span class="py"&gt;uses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rust"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;mounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"~/.pi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rw"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="language-toml highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pass through llama-server connections.&lt;/span&gt;
&lt;span class="nn"&gt;[profile.llama-server]&lt;/span&gt;
&lt;span class="py"&gt;forwards&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;proxies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id="whats-left"&gt;What’s left?&lt;/h3&gt;

&lt;p&gt;The biggest missing piece is teaching the proxy server how to inject real credentials into network connections. This isn’t a new idea. The goal is to provide access to things like GitHub without giving an agent actual credentials.&lt;/p&gt;

&lt;p&gt;After that, it’s just packaging everything up nicely and writing some docs, so that other people (or agents) can easily configure it for different purposes.&lt;/p&gt;</description><pubDate>Sun, 17 May 2026 18:50:36 +0000</pubDate><ns0:encoded xmlns:ns0="http://purl.org/rss/1.0/modules/content/">&lt;div class="post" morss_own_score="4.92822966507177" morss_score="11.373284610126715"&gt;

&lt;h1&gt;Redoubtful: Linux agent sandbox progress&lt;/h1&gt;
&lt;p&gt;May 17, 2026 • by Eric Kidd&lt;/p&gt;

&lt;article class="post-content" morss_own_score="9.89010989010989" morss_score="50.2317186289381"&gt;
&lt;p&gt;I’ve also been experimenting with agent sandboxes lately. &lt;a href="https://github.com/emk/redoubtful"&gt;&lt;code&gt;redoubtful&lt;/code&gt;&lt;/a&gt; is a work-in-progress sandbox that supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Linux-only sandboxes:&lt;/strong&gt; I’m focusing on what Linux supports, specifically, rather than trying to support the lowest-common-denominator features that work cross platform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modular configuration profiles:&lt;/strong&gt; See below.&lt;/li&gt;
&lt;li&gt;Isolation using &lt;code&gt;pasta&lt;/code&gt; and &lt;code&gt;bwrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A shadow filesystem that &lt;em&gt;looks&lt;/em&gt; like your home directory, so things like &lt;code&gt;git worktree&lt;/code&gt; actually work correctly. You can also selectively mount existing parts of your filesystem in read-only or read-write mode.&lt;/li&gt;
&lt;li&gt;Network port forwarding and filtering proxy server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TODO:&lt;/strong&gt; Proxy credential support.&lt;/li&gt;
&lt;/ul&gt;
&lt;p morss_own_score="7.0" morss_score="9.0"&gt;But first, a warning: Nearly 100% of this code was written by coding agents, much of it by a local Qwen3.6 27B. I am, however, keeping a &lt;em&gt;very&lt;/em&gt; close eye on the output—one of my goals here is to see just what a small agent like this can do. This is maybe only 80% as good as my handwritten code would be a similar point in a project.&lt;/p&gt;
&lt;p&gt;And finally, &lt;strong&gt;this is an incomplete work-in-progress&lt;/strong&gt;, and it has not been packaged nicely for anyone besides me yet.&lt;/p&gt;
&lt;h3&gt;Modular configuration “profiles”&lt;/h3&gt;
&lt;p&gt;One of the &lt;em&gt;slightly&lt;/em&gt; novel parts of all this is the ability to define modular configuration. This allows us to invoke a sandbox with a specific set of credentials:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redoubtful run &lt;span&gt;--uses&lt;/span&gt; pi &lt;span&gt;--uses&lt;/span&gt; llama-server pi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we’re running the &lt;a href="https://pi.dev/"&gt;pi.dev&lt;/a&gt; coding agent with a locally-served Qwen3.6 27B via &lt;code&gt;llama-server&lt;/code&gt;. Qwen3.6 27B is a fantastic lightweight coding model, and it works very well with pi.dev’s minimalist prompt. And since we’re running in a sandbox, we don’t care that pi.dev provides no sandbox and no confirmation before acting.&lt;/p&gt;
&lt;p&gt;To set up these two profiles, we first define a &lt;code&gt;node&lt;/code&gt; profile:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;# Standard Node setup. If you're using `nvm`, you'll need to fix the path_add&lt;/span&gt;
&lt;span&gt;# entry to point to the correct nvm version.&lt;/span&gt;
&lt;span&gt;#&lt;/span&gt;
&lt;span&gt;# We might want some kind of plugin system to handle messy things like nvm.&lt;/span&gt;
&lt;span&gt;[profile.node]&lt;/span&gt;
&lt;span&gt;mounts&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;
    &lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.npm-global"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.local/share/nvm"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;access&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"rw"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;path_add&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;"~/.npm-global/bin"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"~/.local/share/nvm/v24.15.0/bin"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then let’s make Rust work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;# A Rust setup, with optional rustup and advisory support.&lt;/span&gt;
&lt;span&gt;[profile.rust]&lt;/span&gt;
&lt;span&gt;mounts&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;
    &lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.rustup"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.cargo"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;# Cargo audit/deny support, which needs to take a lock to update the&lt;/span&gt;
    &lt;span&gt;# advisory database.&lt;/span&gt;
    &lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.cargo/advisory-dbs/"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;access&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"rw"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;path_add&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;["~/.cargo/bin"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p morss_own_score="5.0" morss_score="9.5"&gt;And then basic &lt;code&gt;git&lt;/code&gt; is easy—we just need enough config to read &lt;code&gt;user.name&lt;/code&gt; and &lt;code&gt;user.email&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;# Things you will likely want for git.&lt;/span&gt;
&lt;span&gt;[profile.git]&lt;/span&gt;
&lt;span&gt;mounts&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.gitconfig"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then finally, we can set up &lt;code&gt;pi&lt;/code&gt; itself:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;# Profile for the pi coding agent. Run with:&lt;/span&gt;
&lt;span&gt;#&lt;/span&gt;
&lt;span&gt;#     redoubtful run -u pi pi&lt;/span&gt;
&lt;span&gt;[profile.pi]&lt;/span&gt;
&lt;span&gt;uses&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;"node"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"rust"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"git"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;mounts&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"~/.pi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;access&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"rw"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;# Pass through llama-server connections.&lt;/span&gt;
&lt;span&gt;[profile.llama-server]&lt;/span&gt;
&lt;span&gt;forwards&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;{&lt;/span&gt; &lt;span&gt;host_port&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;8080&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;proxies&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;{&lt;/span&gt; &lt;span&gt;host&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"127.0.0.1"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;What’s left?&lt;/h3&gt;
&lt;p&gt;The biggest missing piece is teaching the proxy server how to inject real credentials into network connections. This isn’t a new idea. The goal is to provide access to things like GitHub without giving an agent actual credentials.&lt;/p&gt;
&lt;p&gt;After that, it’s just packaging everything up nicely and writing some docs, so that other people (or agents) can easily configure it for different purposes.&lt;/p&gt;
&lt;/article&gt;

&lt;h3&gt;More posts&lt;/h3&gt;


&lt;p&gt;Want to &lt;a href="https://www.randomhacks.net/contact/"&gt;contact me&lt;/a&gt; about this article?  Or if you're looking for something else to read, here's a &lt;a href="https://www.randomhacks.net/greatest-hits/"&gt;list of popular posts&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
</ns0:encoded></item><item><title>Eric Kidd: Lab notebook: Edit completion #1</title><guid isPermaLink="true">http://www.randomhacks.net/2026/05/16/lab-notebook-edit-completion-1/</guid><link>http://www.randomhacks.net/2026/05/16/lab-notebook-edit-completion-1/</link><description>&lt;p&gt;I &lt;a href="https://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/"&gt;continue to be interested&lt;/a&gt; in late-2024-era edit completion, the “Fill in the Middle” (FIM) models. You know, what Copilot used to do, back before it started generating “mini diffs.” Why?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I like and use agentic workflows. But as many people are realizing, &lt;a href="https://blog.k10s.dev/im-going-back-to-writing-code-by-hand/"&gt;it’s &lt;em&gt;super&lt;/em&gt; easy to lose track of what’s happening in your code, with terrible consequences&lt;/a&gt;. So I want to reinvest in human-in-the-loop tools, too. (And slightly weaker agentic models, but more on that later.)&lt;/li&gt;
  &lt;li&gt;The new-school edit completion offered by Copilot and &lt;a href="https://zed.dev/blog/zeta2"&gt;Zed’s Zeta2&lt;/a&gt; actually slows me down. It overlays diffs on my buffer, which is visually disorienting at speed. And it proposes edits further from the current cursor, which take me longer to mentally process. Personally, the new style feels like hunt-and-peck. The older style felt like &lt;em&gt;really fast&lt;/em&gt; touch typing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mind you, I’m a very specific sort of user. I want to know how my code works. I want my code to be clean. And I can read a half-page code completion in moments, thanks to way too many years of reading PRs.&lt;/p&gt;

&lt;h3 id="initial-experiments"&gt;Initial experiments&lt;/h3&gt;

&lt;p&gt;All experiments performed in Zed, which does less post-processing of the raw model output than some tools. All evaluations are purely subjective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New-school models (generating diffs).&lt;/strong&gt; Zeta2 is honestly pretty underwhelming right now. The completions are very generic. And Zeta2 seems to be bad about taking the context into account. It will complete a function, sure. But I’d swap Zeta2 for late 2024 Copilot in a heartbeat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old-school models (FIM, inserting at cursor).&lt;/strong&gt; Let’s go down the list so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
&lt;code class="language-plaintext highlighter-rouge"&gt;ggml-org/Qwen2.5-Coder-7B-Q8_0-GGUF:Q8_0&lt;/code&gt;: The classic, default choice. This isn’t &lt;em&gt;terrible&lt;/em&gt;, and it gives more context-aware completions than Zeta2. But it’s generations old, and I want to know if anything is new and shiny.&lt;/li&gt;
  &lt;li&gt;
&lt;code class="language-plaintext highlighter-rouge"&gt;mradermacher/Seed-Coder-8B-Base-i1-GGUF:Q6_K&lt;/code&gt;. This is the raw base that went into Zeta2, I think? It doesn’t seem to be useful in Zed, because the inserted text feels pretty raw. This might work better in a smarter harness. But I’m dropping it for now.&lt;/li&gt;
  &lt;li&gt;
&lt;code class="language-plaintext highlighter-rouge"&gt;JetBrains/Mellum-4b-base-gguf:Q8_0&lt;/code&gt;. Downloaded, but not yet tested.&lt;/li&gt;
  &lt;li&gt;
&lt;code class="language-plaintext highlighter-rouge"&gt;unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_XS&lt;/code&gt;. This is &lt;a href="https://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/"&gt;unexpectedly good&lt;/a&gt;! Worth further experimentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="refining-qwen36-35b-a3b-changing-order-from-psm-to-spm"&gt;Refining Qwen3.6 35B A3B: Changing order from PSM to SPM&lt;/h2&gt;

&lt;p&gt;Qwen typically uses FIM, “Fill in the Middle” completion. This uses 3 magic tokens:&lt;/p&gt;

&lt;div class="language-rust highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// Qwen FIM prefix marker.&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PRE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;|fim_prefix|&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cd"&gt;/// Qwen FIM suffix marker.&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SUF&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;|fim_suffix|&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cd"&gt;/// Qwen FIM middle marker (model generates after this).&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;|fim_middle|&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have two possible flavors. The original is “PSM” compeletion, “prefix, suffix, middle”:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-txt"&gt;{PRE}{prefix}{SUF}{suffix}{MID}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But since the prefix grows with each keystroke, we can’t cache the entire message. We could get much better caching with “SPM” order:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-txt"&gt;{SUF}{suffix}{PRE}{prefix}{MID}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here, we can cache everything up to the final {MID} character, and resume generation with a longer prefix. Whooo, speed!&lt;/p&gt;

&lt;p&gt;But Zed doesn’t support SPM completion, only PSM. So I fired up a copy of Claude Code (as one does), and asked, “Hey, write me a Rust proxy server (using my standard conventions) that intercepts &lt;code class="language-plaintext highlighter-rouge"&gt;/completion&lt;/code&gt;, and translates PSM to SPM please.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt; Extremely disappointing. SPM format confuses Qwen3.6 35B A3B pretty badly. But then I thought, “Hey, even if we’re running in &lt;code class="language-plaintext highlighter-rouge"&gt;/completion&lt;/code&gt; mode, this is still an instruction-tuned model. Can we prompt it?” One unscientific tweak later:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-txt"&gt;You are a code-completion tool. You receive input in
fim_suffix+fim_prefix+fim_middle order, and your job
is to generate what the user would be likely to type
next. When in doubt, keep it short. Think of this like
generating a diff in agentic coding mode. You're trying
to insert the right text to make a working program that
does what the user wants. If there's no obvious next
step, generate nothing.

{SUF}{suffix}{PRE}{prefix}{MID}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is &lt;em&gt;still&lt;/em&gt; pretty bad, but it’s better. You can tell it’s &lt;em&gt;trying&lt;/em&gt; to be an SPM autocompleter, though it’s still the worst of the bunch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible next steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;What if we modify the proxy to transform &lt;code class="language-plaintext highlighter-rouge"&gt;/completion&lt;/code&gt; into a &lt;code class="language-plaintext highlighter-rouge"&gt;/chat/completions&lt;/code&gt; request, with a real prompt, real text inputs, and tool for &lt;code class="language-plaintext highlighter-rouge"&gt;insert_at_cursor(text)&lt;/code&gt;? Can we access more of the model’s intelligence?&lt;/li&gt;
  &lt;li&gt;Qwen3.6 35B A3B is small enough to fine-tune! We could look up file completion data sets, and try to create a LoRA adapter. We could even use something like &lt;a href="https://tree-sitter.github.io/tree-sitter/"&gt;tree-sitter&lt;/a&gt; to generate custom completion examples. Would that give us something useful?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I also notice that FIM-style models are &lt;em&gt;notoriously&lt;/em&gt; bad at choosing a good stopping place. This can be fixed with a lot of regexes. But what if our fine-tuning data took care to demonstrate &lt;em&gt;good&lt;/em&gt; stopping places?&lt;/p&gt;</description><pubDate>Sat, 16 May 2026 15:49:55 +0000</pubDate><ns0:encoded xmlns:ns0="http://purl.org/rss/1.0/modules/content/">&lt;div class="post" morss_own_score="4.882075471698113" morss_score="11.043533805031446"&gt;

&lt;h1&gt;Lab notebook: Edit completion #1&lt;/h1&gt;
&lt;p&gt;May 16, 2026 • by Eric Kidd&lt;/p&gt;

&lt;article class="post-content" morss_own_score="9.322916666666666" morss_score="68.04055058026832"&gt;
&lt;p&gt;I &lt;a href="https://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/"&gt;continue to be interested&lt;/a&gt; in late-2024-era edit completion, the “Fill in the Middle” (FIM) models. You know, what Copilot used to do, back before it started generating “mini diffs.” Why?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I like and use agentic workflows. But as many people are realizing, &lt;a href="https://blog.k10s.dev/im-going-back-to-writing-code-by-hand/"&gt;it’s &lt;em&gt;super&lt;/em&gt; easy to lose track of what’s happening in your code, with terrible consequences&lt;/a&gt;. So I want to reinvest in human-in-the-loop tools, too. (And slightly weaker agentic models, but more on that later.)&lt;/li&gt;
&lt;li&gt;The new-school edit completion offered by Copilot and &lt;a href="https://zed.dev/blog/zeta2"&gt;Zed’s Zeta2&lt;/a&gt; actually slows me down. It overlays diffs on my buffer, which is visually disorienting at speed. And it proposes edits further from the current cursor, which take me longer to mentally process. Personally, the new style feels like hunt-and-peck. The older style felt like &lt;em&gt;really fast&lt;/em&gt; touch typing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mind you, I’m a very specific sort of user. I want to know how my code works. I want my code to be clean. And I can read a half-page code completion in moments, thanks to way too many years of reading PRs.&lt;/p&gt;
&lt;h3&gt;Initial experiments&lt;/h3&gt;
&lt;p&gt;All experiments performed in Zed, which does less post-processing of the raw model output than some tools. All evaluations are purely subjective.&lt;/p&gt;
&lt;p morss_own_score="7.0" morss_score="9.0"&gt;&lt;strong&gt;New-school models (generating diffs).&lt;/strong&gt; Zeta2 is honestly pretty underwhelming right now. The completions are very generic. And Zeta2 seems to be bad about taking the context into account. It will complete a function, sure. But I’d swap Zeta2 for late 2024 Copilot in a heartbeat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Old-school models (FIM, inserting at cursor).&lt;/strong&gt; Let’s go down the list so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ggml-org/Qwen2.5-Coder-7B-Q8_0-GGUF:Q8_0&lt;/code&gt;: The classic, default choice. This isn’t &lt;em&gt;terrible&lt;/em&gt;, and it gives more context-aware completions than Zeta2. But it’s generations old, and I want to know if anything is new and shiny.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mradermacher/Seed-Coder-8B-Base-i1-GGUF:Q6_K&lt;/code&gt;. This is the raw base that went into Zeta2, I think? It doesn’t seem to be useful in Zed, because the inserted text feels pretty raw. This might work better in a smarter harness. But I’m dropping it for now.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JetBrains/Mellum-4b-base-gguf:Q8_0&lt;/code&gt;. Downloaded, but not yet tested.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_XS&lt;/code&gt;. This is &lt;a href="https://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/"&gt;unexpectedly good&lt;/a&gt;! Worth further experimentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Refining Qwen3.6 35B A3B: Changing order from PSM to SPM&lt;/h2&gt;
&lt;p&gt;Qwen typically uses FIM, “Fill in the Middle” completion. This uses 3 magic tokens:&lt;/p&gt;
&lt;pre class="highlight" morss_own_score="7.0" morss_score="8.5"&gt;&lt;code&gt;&lt;span&gt;/// Qwen FIM prefix marker.&lt;/span&gt;
&lt;span&gt;const&lt;/span&gt; &lt;span&gt;PRE&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;str&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"&amp;lt;|fim_prefix|&amp;gt;"&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;/// Qwen FIM suffix marker.&lt;/span&gt;
&lt;span&gt;const&lt;/span&gt; &lt;span&gt;SUF&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;str&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"&amp;lt;|fim_suffix|&amp;gt;"&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;/// Qwen FIM middle marker (model generates after this).&lt;/span&gt;
&lt;span&gt;const&lt;/span&gt; &lt;span&gt;MID&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;str&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"&amp;lt;|fim_middle|&amp;gt;"&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have two possible flavors. The original is “PSM” compeletion, “prefix, suffix, middle”:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{PRE}{prefix}{SUF}{suffix}{MID}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But since the prefix grows with each keystroke, we can’t cache the entire message. We could get much better caching with “SPM” order:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{SUF}{suffix}{PRE}{prefix}{MID}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we can cache everything up to the final {MID} character, and resume generation with a longer prefix. Whooo, speed!&lt;/p&gt;
&lt;p morss_own_score="7.0" morss_score="8.5"&gt;But Zed doesn’t support SPM completion, only PSM. So I fired up a copy of Claude Code (as one does), and asked, “Hey, write me a Rust proxy server (using my standard conventions) that intercepts &lt;code&gt;/completion&lt;/code&gt;, and translates PSM to SPM please.”&lt;/p&gt;
&lt;p morss_own_score="7.0" morss_score="10.5"&gt;&lt;strong&gt;Results:&lt;/strong&gt; Extremely disappointing. SPM format confuses Qwen3.6 35B A3B pretty badly. But then I thought, “Hey, even if we’re running in &lt;code&gt;/completion&lt;/code&gt; mode, this is still an instruction-tuned model. Can we prompt it?” One unscientific tweak later:&lt;/p&gt;
&lt;pre morss_own_score="7.0" morss_score="8.5"&gt;&lt;code&gt;You are a code-completion tool. You receive input in
fim_suffix+fim_prefix+fim_middle order, and your job
is to generate what the user would be likely to type
next. When in doubt, keep it short. Think of this like
generating a diff in agentic coding mode. You're trying
to insert the right text to make a working program that
does what the user wants. If there's no obvious next
step, generate nothing.

{SUF}{suffix}{PRE}{prefix}{MID}
&lt;/code&gt;&lt;/pre&gt;
&lt;p morss_own_score="6.0" morss_score="10.0"&gt;This is &lt;em&gt;still&lt;/em&gt; pretty bad, but it’s better. You can tell it’s &lt;em&gt;trying&lt;/em&gt; to be an SPM autocompleter, though it’s still the worst of the bunch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Possible next steps:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What if we modify the proxy to transform &lt;code&gt;/completion&lt;/code&gt; into a &lt;code&gt;/chat/completions&lt;/code&gt; request, with a real prompt, real text inputs, and tool for &lt;code&gt;insert_at_cursor(text)&lt;/code&gt;? Can we access more of the model’s intelligence?&lt;/li&gt;
&lt;li&gt;Qwen3.6 35B A3B is small enough to fine-tune! We could look up file completion data sets, and try to create a LoRA adapter. We could even use something like &lt;a href="https://tree-sitter.github.io/tree-sitter/"&gt;tree-sitter&lt;/a&gt; to generate custom completion examples. Would that give us something useful?&lt;/li&gt;
&lt;/ol&gt;
&lt;p morss_own_score="7.0" morss_score="11.0"&gt;I also notice that FIM-style models are &lt;em&gt;notoriously&lt;/em&gt; bad at choosing a good stopping place. This can be fixed with a lot of regexes. But what if our fine-tuning data took care to demonstrate &lt;em&gt;good&lt;/em&gt; stopping places?&lt;/p&gt;
&lt;/article&gt;

&lt;h3&gt;More posts&lt;/h3&gt;


&lt;p&gt;Want to &lt;a href="https://www.randomhacks.net/contact/"&gt;contact me&lt;/a&gt; about this article?  Or if you're looking for something else to read, here's a &lt;a href="https://www.randomhacks.net/greatest-hits/"&gt;list of popular posts&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
</ns0:encoded></item><item><title>Oskar Wickström: Coding on Paper</title><guid isPermaLink="true">https://wickstrom.tech/2026-05-16-coding-on-paper.html</guid><link>https://wickstrom.tech/2026-05-16-coding-on-paper.html</link><description>&lt;p&gt;About three months ago, I bought the &lt;a href="https://shop.boox.com/products/boox-mira-procolor-version"&gt;Onyx BOOX 25.3” Mira Pro Color&lt;/a&gt;, an e-ink monitor for desktop use. I’ve used it as my primary monitor since, and I’ve had a lot of questions about it. This is my experience report, from the perspective of a working, still mostly typing, programmer.&lt;/p&gt; &lt;p&gt;This is &lt;em&gt;not&lt;/em&gt; a sponsored post, and it is &lt;em&gt;not&lt;/em&gt; a product review. I wrote a very similar post about the &lt;a href="https://wickstrom.tech/2025-10-10-programming-in-the-sun-a-year-with-the-daylight-computer.html"&gt;Daylight DC-1&lt;/a&gt; last year.&lt;/p&gt; &lt;figure&gt; &lt;img alt="Neovim in the morning sunlight." src="https://wickstrom.tech/assets/mira-pro-1.webp" /&gt; &lt;figcaption&gt;Neovim in the morning sunlight.&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;As explained in last year’s post, the reason I persist with these monitors is because it makes me energetic and happy. Sunlight, direct or indirect, helps me stay clear and focused during my workday. I find spaces illuminated by natural light beautiful and inspiring.&lt;/p&gt; &lt;p&gt;I’m not going to recommend that you buy one of these devices. They’re expensive, about $2000, and the experience is quite different from LCD. Even if this looks cool, it seems to me very possible that most people would not like it in practice. With that said, I am happy with it, and I’ll probably keep investing in these tools as they get even better with time.&lt;/p&gt; &lt;figure&gt; &lt;img alt="Spending a workday in the garden." src="https://wickstrom.tech/assets/mira-pro-2.webp" /&gt; &lt;figcaption&gt;Spending a workday in the garden.&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;Using the Mira Pro as a primary monitor is a continuation of the experiments with my e-ink tablets and Termux as coding environments. But now, with far fewer compromises. I’m running my regular NixOS environment on my work laptop. No SSH and tmux needed, no Android terminal emulator to customize.&lt;/p&gt; &lt;p&gt;What I have done, though, is spent quite some time on making my system more suited for this monitor. The Mira Pro does not work well with dark themes. In fact, it only works well with high contrast light themes.&lt;/p&gt; &lt;p&gt;Luckily, I’m bent towards minimalism, so I already used near-monochrome themes, relying more on typographic syntax highlighting rather than coloring. I now have custom themes for Neovim, Zed, and Ghostty with a few vivid colors for things like selection, comments, and constants. Otherwise it’s largely black on white.&lt;/p&gt; &lt;p&gt;It’s trickier with other applications. In Firefox, I’ve started using the high contrast setting. That works pretty much like an inverse of DarkReader. I now run Spotify in the browser in order to avoid its dark theme.&lt;/p&gt; &lt;p&gt;The monitor has a clunky menu system with which you can change rendering modes; things like contrast and speed. I found &lt;a href="https://github.com/ipodnerd3019/mira-js"&gt;an open-source reverse-engineered NodeJS package&lt;/a&gt; that I use with Hyprland keybindings to easily change rendering modes and manually refresh. No need for the built-in menu.&lt;/p&gt; &lt;p&gt;In practice I use two modes:&lt;/p&gt; &lt;dl&gt; &lt;dt&gt;Reading:&lt;/dt&gt; &lt;dd&gt; &lt;p&gt;This mode renders colors most vividly and text sharply, but typing with it is agony. I use it when reading text documents, web pages, or code diffs.&lt;/p&gt; &lt;/dd&gt; &lt;dt&gt;Writing:&lt;/dt&gt; &lt;dd&gt; &lt;p&gt;This is by far the most commonly used mode, which compromises colors and sharpness for way better latency. I use this for everything in the terminal, chat, general web browsing, and probably most other things not covered by the reading mode.&lt;/p&gt; &lt;/dd&gt; &lt;/dl&gt; &lt;p&gt;See the following photos for a close-up comparison:&lt;/p&gt; &lt;figure&gt; &lt;img alt="Reading mode, where colored regions are pretty smooth and text looks sharp." src="https://wickstrom.tech/assets/mira-pro-5.webp" /&gt; &lt;figcaption&gt;Reading mode, where colored regions are pretty smooth and text looks sharp.&lt;/figcaption&gt; &lt;/figure&gt; &lt;figure&gt; &lt;img alt="Writing mode, where colored regions (light gray, red, green) are grainy and text is a bit blurry." src="https://wickstrom.tech/assets/mira-pro-6.webp" /&gt; &lt;figcaption&gt;Writing mode, where colored regions (light gray, red, green) are grainy and text is a bit blurry.&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;What about latency? Here’s the two short clips of me typing with the reading and writing modes:&lt;/p&gt; &lt;figure&gt; &lt;video controls="" src="https://wickstrom.tech/assets/mira-pro-slow.mp4"&gt;&lt;a href="https://wickstrom.tech/assets/mira-pro-slow.mp4"&gt;Reading mode, with horrible latency for typing.&lt;/a&gt;&lt;/video&gt; &lt;figcaption&gt;Reading mode, with horrible latency for typing.&lt;/figcaption&gt; &lt;/figure&gt; &lt;figure&gt; &lt;video controls="" src="https://wickstrom.tech/assets/mira-pro-fast.mp4"&gt;&lt;a href="https://wickstrom.tech/assets/mira-pro-fast.mp4"&gt;Writing mode, with some but acceptable latency.&lt;/a&gt;&lt;/video&gt; &lt;figcaption&gt;Writing mode, with some but acceptable latency.&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;Ghosting? In my writing mode it’s minimal. It really doesn’t bother me.&lt;/p&gt; &lt;p&gt;About the color panel: I don’t like it very much to be honest. It was the only version of the Mira Pro available from the Swedish retailer at the time, so I went with it. I think I would’ve been happier with a monochrome panel, because the coloring technology makes it considerably darker.&lt;/p&gt; &lt;p&gt;Here’s a comparison between the Palma 2 Pro (using a similar but smaller Kaleido color panel) and my old Tab Ultra (with a monochrome panel):&lt;/p&gt; &lt;figure&gt; &lt;img alt="Color vs Monochrome e-ink panels without backlight." src="https://wickstrom.tech/assets/mira-pro-7.webp" /&gt; &lt;figcaption&gt;Color vs Monochrome e-ink panels without backlight.&lt;/figcaption&gt; &lt;/figure&gt; &lt;p&gt;Unless the room has great diffuse lighting, natural or otherwise, the color panel does require &lt;em&gt;some&lt;/em&gt; backlight. In direct sunlight or outdoors it works without. I might spend more time optimizing the lighting in my office to make this work during the winter months.&lt;/p&gt; &lt;p&gt;So, what’s to make of it? Personally, I enjoy using this monitor a lot, even if it’s not perfect. Should you buy an expensive 25” e-ink monitor? I cannot say. But if you do, let me know how it works out.&lt;/p&gt; &lt;p&gt;My custom themes and keybindings can be found &lt;a href="https://github.com/owickstrom/nixos/tree/master"&gt;here&lt;/a&gt;.&lt;/p&gt;</description><pubDate>Fri, 15 May 2026 22:00:00 +0000</pubDate><ns0:encoded xmlns:ns0="http://purl.org/rss/1.0/modules/content/">&lt;body morss_own_score="2.664" morss_score="67.43487646652865"&gt;

&lt;h1&gt;Coding on Paper&lt;/h1&gt;
&lt;p&gt;Oskar Wickström&lt;/p&gt;
&lt;p&gt;May 16, 2026&lt;/p&gt;

&lt;p&gt;About three months ago, I bought the &lt;a href="https://shop.boox.com/products/boox-mira-procolor-version"&gt;Onyx
BOOX 25.3” Mira Pro Color&lt;/a&gt;, an e-ink monitor for desktop use. I’ve
used it as my primary monitor since, and I’ve had a lot of questions
about it. This is my experience report, from the perspective of a
working, still mostly typing, programmer.&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;not&lt;/em&gt; a sponsored post, and it is &lt;em&gt;not&lt;/em&gt; a
product review. I wrote a very similar post about the &lt;a href="https://wickstrom.tech/2025-10-10-programming-in-the-sun-a-year-with-the-daylight-computer.html"&gt;Daylight
DC-1&lt;/a&gt; last year.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://wickstrom.tech/assets/mira-pro-1.webp"&gt;
&lt;figcaption&gt;Neovim in the morning
sunlight.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As explained in last year’s post, the reason I persist with these
monitors is because it makes me energetic and happy. Sunlight, direct or
indirect, helps me stay clear and focused during my workday. I find
spaces illuminated by natural light beautiful and inspiring.&lt;/p&gt;
&lt;p&gt;I’m not going to recommend that you buy one of these devices. They’re
expensive, about $2000, and the experience is quite different from LCD.
Even if this looks cool, it seems to me very possible that most people
would not like it in practice. With that said, I am happy with it, and
I’ll probably keep investing in these tools as they get even better with
time.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://wickstrom.tech/assets/mira-pro-2.webp"&gt;
&lt;figcaption&gt;Spending a workday in the
garden.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Using the Mira Pro as a primary monitor is a continuation of the
experiments with my e-ink tablets and Termux as coding environments. But
now, with far fewer compromises. I’m running my regular NixOS
environment on my work laptop. No SSH and tmux needed, no Android
terminal emulator to customize.&lt;/p&gt;
&lt;p&gt;What I have done, though, is spent quite some time on making my
system more suited for this monitor. The Mira Pro does not work well
with dark themes. In fact, it only works well with high contrast light
themes.&lt;/p&gt;
&lt;p&gt;Luckily, I’m bent towards minimalism, so I already used
near-monochrome themes, relying more on typographic syntax highlighting
rather than coloring. I now have custom themes for Neovim, Zed, and
Ghostty with a few vivid colors for things like selection, comments, and
constants. Otherwise it’s largely black on white.&lt;/p&gt;
&lt;p&gt;It’s trickier with other applications. In Firefox, I’ve started using
the high contrast setting. That works pretty much like an inverse of
DarkReader. I now run Spotify in the browser in order to avoid its dark
theme.&lt;/p&gt;
&lt;p&gt;The monitor has a clunky menu system with which you can change
rendering modes; things like contrast and speed. I found &lt;a href="https://github.com/ipodnerd3019/mira-js"&gt;an open-source
reverse-engineered NodeJS package&lt;/a&gt; that I use with Hyprland
keybindings to easily change rendering modes and manually refresh. No
need for the built-in menu.&lt;/p&gt;
&lt;p&gt;In practice I use two modes:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Reading:&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;This mode renders colors most vividly and text sharply, but typing
with it is agony. I use it when reading text documents, web pages, or
code diffs.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;Writing:&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;This is by far the most commonly used mode, which compromises colors
and sharpness for way better latency. I use this for everything in the
terminal, chat, general web browsing, and probably most other things not
covered by the reading mode.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;See the following photos for a close-up comparison:&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://wickstrom.tech/assets/mira-pro-5.webp"&gt;
&lt;figcaption&gt;Reading mode, where colored regions are
pretty smooth and text looks sharp.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src="https://wickstrom.tech/assets/mira-pro-6.webp"&gt;
&lt;figcaption&gt;Writing mode, where colored regions
(light gray, red, green) are grainy and text is a bit
blurry.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;What about latency? Here’s the two short clips of me typing with the
reading and writing modes:&lt;/p&gt;
&lt;figure&gt;
&lt;video src="https://wickstrom.tech/assets/mira-pro-slow.mp4"&gt;&lt;a href="https://wickstrom.tech/assets/mira-pro-slow.mp4"&gt;Reading mode, with horrible latency for
typing.&lt;/a&gt;&lt;/video&gt;
&lt;figcaption&gt;Reading mode, with horrible latency for
typing.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;video src="https://wickstrom.tech/assets/mira-pro-fast.mp4"&gt;&lt;a href="https://wickstrom.tech/assets/mira-pro-fast.mp4"&gt;Writing mode, with some but acceptable
latency.&lt;/a&gt;&lt;/video&gt;
&lt;figcaption&gt;Writing mode, with some but acceptable
latency.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Ghosting? In my writing mode it’s minimal. It really doesn’t bother
me.&lt;/p&gt;
&lt;p&gt;About the color panel: I don’t like it very much to be honest. It was
the only version of the Mira Pro available from the Swedish retailer at
the time, so I went with it. I think I would’ve been happier with a
monochrome panel, because the coloring technology makes it considerably
darker.&lt;/p&gt;
&lt;p&gt;Here’s a comparison between the Palma 2 Pro (using a similar but
smaller Kaleido color panel) and my old Tab Ultra (with a monochrome
panel):&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://wickstrom.tech/assets/mira-pro-7.webp"&gt;
&lt;figcaption&gt;Color vs Monochrome e-ink panels without
backlight.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Unless the room has great diffuse lighting, natural or otherwise, the
color panel does require &lt;em&gt;some&lt;/em&gt; backlight. In direct sunlight or
outdoors it works without. I might spend more time optimizing the
lighting in my office to make this work during the winter months.&lt;/p&gt;
&lt;p&gt;So, what’s to make of it? Personally, I enjoy using this monitor a
lot, even if it’s not perfect. Should you buy an expensive 25” e-ink
monitor? I cannot say. But if you do, let me know how it works out.&lt;/p&gt;
&lt;p&gt;My custom themes and keybindings can be found &lt;a href="https://github.com/owickstrom/nixos/tree/master"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/body&gt;
</ns0:encoded></item><item><title>Eric Kidd: I should blog more</title><guid isPermaLink="true">http://www.randomhacks.net/2026/05/13/i-should-blog-more/</guid><link>http://www.randomhacks.net/2026/05/13/i-should-blog-more/</link><description>&lt;p&gt;This blog is ancient, in blog years. The first post was on June 30, 1998, and it featured a &lt;a href="https://www.randomhacks.net/1998/06/30/randomhacks-online-mathmap/"&gt;randomized emboss for MathMap&lt;/a&gt;. Back in those days, it was a mix of neat little snippets like that and interesting links. The site was a single, hand-edited HTML file in reverse chronological order. It ran on a Linux mini-tower built from parts from &lt;a href="https://w1mx.mit.edu/flea-at-mit/"&gt;the MIT Swapfest&lt;/a&gt;, and it lived under my desk.&lt;/p&gt;

&lt;p&gt;Google hadn’t been incoporated yet. The Internet bubble was still inflating.&lt;/p&gt;

&lt;p&gt;Over the years, the tech stack changed: for a while, this site used SGML-based rendering via a custom script (or was it XML?), then it was a nice interactive Typo site with comments, and then eventually it migrated to the current Jekyll architecture. Which seems to be about 12 years old. I’m pretty proud to have kept nearly all the inbound links working for decades now.&lt;/p&gt;

&lt;p&gt;Around 2007 or so, I did a fun series of high effort posts about &lt;a href="https://www.randomhacks.net/probability-monads/"&gt;probability monads&lt;/a&gt;. But high-effort posts are a trap. Soon I started feeling like &lt;em&gt;every&lt;/em&gt; post ought to be high effort. And then I wrote less and less.&lt;/p&gt;

&lt;p&gt;But blogs are a bit of a retro endeavour these days. RSS readers still exist, but I imagine nearly all my subscribers have disappeared since the heady days of 2007. And apparently it’s trendy to &lt;a href="https://notes.andymatuschak.org/Work_with_the_garage_door_up"&gt;work with the garage door up&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So maybe it’s time to get back this site’s roots. I don’t have any &lt;a href="https://www.complang.tuwien.ac.at/schani/mathmap/dist/"&gt;MathMap&lt;/a&gt; snippets for you today, sadly, because the last release seems to have been in 2004. &lt;a href="http://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/"&gt;But here’s a cool trick!&lt;/a&gt;&lt;/p&gt;</description><pubDate>Wed, 13 May 2026 05:04:52 +0000</pubDate><ns0:encoded xmlns:ns0="http://purl.org/rss/1.0/modules/content/">&lt;div class="post" morss_own_score="3.180952380952381" morss_score="8.29859943977591"&gt;

&lt;h1&gt;I should blog more&lt;/h1&gt;
&lt;p&gt;May 13, 2026 • by Eric Kidd&lt;/p&gt;

&lt;article class="post-content" morss_own_score="8.235294117647058" morss_score="24.27986541308042"&gt;
&lt;p&gt;This blog is ancient, in blog years. The first post was on June 30, 1998, and it featured a &lt;a href="https://www.randomhacks.net/1998/06/30/randomhacks-online-mathmap/"&gt;randomized emboss for MathMap&lt;/a&gt;. Back in those days, it was a mix of neat little snippets like that and interesting links. The site was a single, hand-edited HTML file in reverse chronological order. It ran on a Linux mini-tower built from parts from &lt;a href="https://w1mx.mit.edu/flea-at-mit/"&gt;the MIT Swapfest&lt;/a&gt;, and it lived under my desk.&lt;/p&gt;
&lt;p&gt;Google hadn’t been incoporated yet. The Internet bubble was still inflating.&lt;/p&gt;
&lt;p morss_own_score="7.0" morss_score="7.0"&gt;Over the years, the tech stack changed: for a while, this site used SGML-based rendering via a custom script (or was it XML?), then it was a nice interactive Typo site with comments, and then eventually it migrated to the current Jekyll architecture. Which seems to be about 12 years old. I’m pretty proud to have kept nearly all the inbound links working for decades now.&lt;/p&gt;
&lt;p morss_own_score="5.8" morss_score="7.8"&gt;Around 2007 or so, I did a fun series of high effort posts about &lt;a href="https://www.randomhacks.net/probability-monads/"&gt;probability monads&lt;/a&gt;. But high-effort posts are a trap. Soon I started feeling like &lt;em&gt;every&lt;/em&gt; post ought to be high effort. And then I wrote less and less.&lt;/p&gt;
&lt;p&gt;But blogs are a bit of a retro endeavour these days. RSS readers still exist, but I imagine nearly all my subscribers have disappeared since the heady days of 2007. And apparently it’s trendy to &lt;a href="https://notes.andymatuschak.org/Work_with_the_garage_door_up"&gt;work with the garage door up&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So maybe it’s time to get back this site’s roots. I don’t have any &lt;a href="https://www.complang.tuwien.ac.at/schani/mathmap/dist/"&gt;MathMap&lt;/a&gt; snippets for you today, sadly, because the last release seems to have been in 2004. &lt;a href="https://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/"&gt;But here’s a cool trick!&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;

&lt;h3&gt;More posts&lt;/h3&gt;


&lt;p&gt;Want to &lt;a href="https://www.randomhacks.net/contact/"&gt;contact me&lt;/a&gt; about this article?  Or if you're looking for something else to read, here's a &lt;a href="https://www.randomhacks.net/greatest-hits/"&gt;list of popular posts&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
</ns0:encoded></item><item><title>Eric Kidd: Edit completion works with Qwen3.6 35B A3B!</title><guid isPermaLink="true">http://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/</guid><link>http://www.randomhacks.net/2026/05/13/edit-completion-with-qwen36/</link><description>&lt;p&gt;Do you miss the old-style Copilot completions? The ones where it inserted grey text at the cursor? There’s an open version of this called &lt;a href="https://api-docs.deepseek.com/guides/fim_completion"&gt;“FIM completion”&lt;/a&gt;. And the classic model for doing this is &lt;a href="https://huggingface.co/Qwen/Qwen2.5-Coder-7B"&gt;Qwen2.5 Coder 7B&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But it turns out that Qwen3.6 35B A3B is can also do autocompletion! The fact that it has 3B active parameters means that it’s fast. And the 35B total parameters means it’s smarter than the smaller models.&lt;/p&gt;

&lt;p&gt;So let’s fire it up using &lt;a href="https://github.com/ggml-org/llama.cpp"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;llama-server&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class="language-sh highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;llama-server &lt;span class="nt"&gt;-hf&lt;/span&gt; unsloth/Qwen3.6-35B-A3B-GGUF:UD-IQ4_XS &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cache-type-k&lt;/span&gt; q8_0 &lt;span class="nt"&gt;--cache-type-v&lt;/span&gt; q8_0 &lt;span class="nt"&gt;--no-mmproj&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--ctx-size&lt;/span&gt; 4000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;--no-mmproj&lt;/code&gt; says to disable the vision mode. &lt;code class="language-plaintext highlighter-rouge"&gt;--cache-type-k q8_0 --cache-type-v q8_0&lt;/code&gt; reduces the cache precision, since we’re not really using the cache. You might also need to &lt;a href="https://huggingface.co/unsloth/Qwen3.6-35B-A3B-GGUF"&gt;grab a smaller quant&lt;/a&gt;, depending on your available VRAM.&lt;/p&gt;

&lt;p&gt;Then, we can configure it using &lt;a href="https://zed.dev/"&gt;Zed&lt;/a&gt;:&lt;/p&gt;

&lt;div class="language-json highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"edit_predictions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open_ai_compatible_api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"open_ai_compatible_api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"api_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080/v1/completions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_XS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt_format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"max_output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So how good is this? Well, the completions aren’t too bad at all, but Zed doesn’t seem to do much post-processing. So the completions to be too long. At lot of this could likely be improved with a proxy that did some pre- and post-processing, and maybe a bit of fine tuning.&lt;/p&gt;

&lt;p&gt;But this is an actual, working, 100% local autocomplete. And it’s &lt;em&gt;close&lt;/em&gt; to being actually good.&lt;/p&gt;</description><pubDate>Wed, 13 May 2026 05:04:52 +0000</pubDate><ns0:encoded xmlns:ns0="http://purl.org/rss/1.0/modules/content/">&lt;div class="post" morss_own_score="3.7337461300309602" morss_score="9.743950211663613"&gt;

&lt;h1&gt;Edit completion works with Qwen3.6 35B A3B!&lt;/h1&gt;
&lt;p&gt;May 13, 2026 • by Eric Kidd&lt;/p&gt;

&lt;article class="post-content" morss_own_score="9.020408163265307" morss_score="27.41689939133548"&gt;
&lt;p&gt;Do you miss the old-style Copilot completions? The ones where it inserted grey text at the cursor? There’s an open version of this called &lt;a href="https://api-docs.deepseek.com/guides/fim_completion"&gt;“FIM completion”&lt;/a&gt;. And the classic model for doing this is &lt;a href="https://huggingface.co/Qwen/Qwen2.5-Coder-7B"&gt;Qwen2.5 Coder 7B&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But it turns out that Qwen3.6 35B A3B is can also do autocompletion! The fact that it has 3B active parameters means that it’s fast. And the 35B total parameters means it’s smarter than the smaller models.&lt;/p&gt;
&lt;p&gt;So let’s fire it up using &lt;a href="https://github.com/ggml-org/llama.cpp"&gt;&lt;code&gt;llama-server&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;llama-server &lt;span&gt;-hf&lt;/span&gt; unsloth/Qwen3.6-35B-A3B-GGUF:UD-IQ4_XS &lt;span&gt;\&lt;/span&gt;
    &lt;span&gt;--cache-type-k&lt;/span&gt; q8_0 &lt;span&gt;--cache-type-v&lt;/span&gt; q8_0 &lt;span&gt;--no-mmproj&lt;/span&gt; &lt;span&gt;\&lt;/span&gt;
    &lt;span&gt;--ctx-size&lt;/span&gt; 4000
&lt;/code&gt;&lt;/pre&gt;
&lt;p morss_own_score="5.526315789473684" morss_score="8.526315789473685"&gt;&lt;code&gt;--no-mmproj&lt;/code&gt; says to disable the vision mode. &lt;code&gt;--cache-type-k q8_0 --cache-type-v q8_0&lt;/code&gt; reduces the cache precision, since we’re not really using the cache. You might also need to &lt;a href="https://huggingface.co/unsloth/Qwen3.6-35B-A3B-GGUF"&gt;grab a smaller quant&lt;/a&gt;, depending on your available VRAM.&lt;/p&gt;
&lt;p&gt;Then, we can configure it using &lt;a href="https://zed.dev/"&gt;Zed&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="highlight" morss_own_score="7.0" morss_score="8.5"&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"edit_predictions"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"provider"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"open_ai_compatible_api"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;"open_ai_compatible_api"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"api_url"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"http://localhost:8080/v1/completions"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"model"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_XS"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"prompt_format"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"qwen"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"max_output_tokens"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;256&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;},&lt;/span&gt;
  &lt;span&gt;},&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So how good is this? Well, the completions aren’t too bad at all, but Zed doesn’t seem to do much post-processing. So the completions to be too long. At lot of this could likely be improved with a proxy that did some pre- and post-processing, and maybe a bit of fine tuning.&lt;/p&gt;
&lt;p&gt;But this is an actual, working, 100% local autocomplete. And it’s &lt;em&gt;close&lt;/em&gt; to being actually good.&lt;/p&gt;
&lt;/article&gt;

&lt;h3&gt;More posts&lt;/h3&gt;


&lt;p&gt;Want to &lt;a href="https://www.randomhacks.net/contact/"&gt;contact me&lt;/a&gt; about this article?  Or if you're looking for something else to read, here's a &lt;a href="https://www.randomhacks.net/greatest-hits/"&gt;list of popular posts&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
</ns0:encoded></item></channel></rss>