<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anish Shrestha</title>
    <description>The latest articles on DEV Community by Anish Shrestha (@anyesh).</description>
    <link>https://dev.to/anyesh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F202575%2F3ff44b38-0271-4c86-9438-14b0ada88277.png</url>
      <title>DEV Community: Anish Shrestha</title>
      <link>https://dev.to/anyesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anyesh"/>
    <language>en</language>
    <item>
      <title>Running a 26B MoE on an 8 GB Jetson by streaming experts from SSD</title>
      <dc:creator>Anish Shrestha</dc:creator>
      <pubDate>Sat, 01 Aug 2026 14:00:35 +0000</pubDate>
      <link>https://dev.to/anyesh/running-a-26b-moe-on-an-8-gb-jetson-by-streaming-experts-from-ssd-2jpf</link>
      <guid>https://dev.to/anyesh/running-a-26b-moe-on-an-8-gb-jetson-by-streaming-experts-from-ssd-2jpf</guid>
      <description>&lt;p&gt;My Jetson Orin Nano has 8 GB of unified memory. Gemma 4 26B-A4B is a 13.3 GiB download at Q4. This post is about the llama.cpp patch that lets the first thing run the second thing anyway, producing logits that are bit-for-bit identical to the standard code path, and why mixture-of-experts models make that possible when dense models never could.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fci4pqpye489dqh8qgfr1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fci4pqpye489dqh8qgfr1.gif" alt="Terminal recording of gemma4-26b explaining MoE routing on the Jetson Orin Nano: raw tegrastats GPU/RAM/power at top, a derived elapsed/token/tok-s/gpu/power line under it, and the decode scrolling below, ending on llama.cpp's timings block at 2.09 tokens per second" width="760" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea is borrowed, and worth borrowing
&lt;/h2&gt;

&lt;p&gt;Credit where it belongs: the architecture comes from &lt;a href="https://github.com/drumih/turbo-fieldfare" rel="noopener noreferrer"&gt;TurboFieldfare&lt;/a&gt;, a Swift and Metal runtime that runs Gemma 4 26B-A4B in about 2 GB of RAM on Apple Silicon. Its trick is to notice that an MoE model only touches a few experts per token. The dense weights (attention, router, shared expert) stay resident, and the routed experts, which are most of the model, live on the SSD. A small per-layer cache of expert slots is filled by parallel reads the moment the router announces which experts the next token needs, and the reads hide behind compute the GPU was doing anyway. The related academic thread is Eliseev and Mazur's expert-offloading work (&lt;a href="https://arxiv.org/abs/2312.17238" rel="noopener noreferrer"&gt;arXiv:2312.17238&lt;/a&gt;), which established that MoE routing is stable enough between tokens for caching to pay off.&lt;/p&gt;

&lt;p&gt;TurboFieldfare proves the idea beautifully, but it is a bespoke runtime: two supported models, Apple platforms only, custom kernels for everything. I wanted the same idea for the other cheap 8 GB machine on my desk, a Jetson Orin Nano, and I wanted it for any MoE model I could quantize. So instead of porting the runtime, I grafted the idea into &lt;a href="https://github.com/ggml-org/llama.cpp" rel="noopener noreferrer"&gt;llama.cpp&lt;/a&gt;, which already runs on the Jetson and already has Ampere-tuned CUDA kernels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming without touching a single GPU kernel
&lt;/h2&gt;

&lt;p&gt;The part I like most about the design is that no CUDA kernel changes were needed. llama.cpp evaluates the routed-expert matmul with &lt;code&gt;ggml_mul_mat_id&lt;/code&gt;, which takes the expert weight tensor plus a small tensor of selected expert ids and addresses each expert as &lt;code&gt;base + id * stride&lt;/code&gt; on the device. The kernel does not care whether &lt;code&gt;base&lt;/code&gt; points at 128 experts or at 32; it just multiplies ids by strides.&lt;/p&gt;

&lt;p&gt;So the patch allocates, per MoE layer, a persistent pool tensor holding N expert-sized slots plus a tiny slot-id tensor, and at load time it records where every expert's bytes sit in the GGUF file. During decode, a scheduler callback fires on the router's top-k node, reads the chosen expert ids, and consults a per-layer LFU cache: experts already in slots are hits, misses are &lt;code&gt;pread&lt;/code&gt; directly from the file into their assigned slots by a small thread pool. The callback writes the slot indices into the slot-id tensor and lets the graph continue. The matmul reads the pool through slot ids and gets exactly the bytes it would have read from the full tensor.&lt;/p&gt;

&lt;p&gt;There is one place to be genuinely careful, and it is the reason this patch has a paranoid test. The same expert ids feed several consumers in the graph: the main matmul, the per-expert scale vectors that quantization-aware checkpoints ship, LoRA adapters, and expert biases. Only the main matmul may see remapped slot ids. Everything else must keep the real expert ids, because those tensors stay fully resident and are indexed by actual expert number. Get this wrong and nothing crashes; the model just gets quietly stupider. The test that guards against it greedy-decodes the same prompt with streaming off and on and compares every logit of every step with &lt;code&gt;memcmp&lt;/code&gt;. Same kernels, same bytes, different addressing, therefore bitwise identity is the correct bar, not "close enough".&lt;/p&gt;

&lt;p&gt;On the Jetson there is a bonus that TurboFieldfare's mmap trick foreshadowed: the Orin's GPU shares memory with the CPU, so the slot pools live in pinned host memory that the disk reads land in directly and the GPU addresses without any copy.&lt;/p&gt;

&lt;p&gt;For the IO-hiding part, Gemma 4's graph has a shared dense FFN that runs for every token alongside the routed experts. The patch reorders the layer so the router's top-k comes first, dispatches the disk reads, lets the GPU compute the shared FFN while the NVMe works, and joins right before the expert matmul needs the data. One synchronization per layer, exactly TurboFieldfare's overlap, expressed as a graph reordering plus a two-phase callback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers from the actual machine
&lt;/h2&gt;

&lt;p&gt;Measured on a Jetson Orin Nano Super (8 GB, MAXN, NVMe) with Gemma 4 26B-A4B QAT Q4, using &lt;code&gt;llama-bench&lt;/code&gt; tg128. The baseline is the best you could previously do in llama.cpp: dense weights on the GPU, experts mmap'd on the CPU with &lt;code&gt;--cpu-moe&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Decode tok/s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--cpu-moe&lt;/code&gt; mmap baseline&lt;/td&gt;
&lt;td&gt;1.94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming, 32 slots x 8 IO threads, overlap on&lt;/td&gt;
&lt;td&gt;2.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same, overlap off&lt;/td&gt;
&lt;td&gt;2.23&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few observations that the table compresses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The overlap alone is worth 32 percent, which is the TurboFieldfare thesis confirmed on entirely different hardware: expert IO really does hide behind compute you were already doing.&lt;/li&gt;
&lt;li&gt;The NVMe's random-read ceiling (about 1.27 GB/s at this block size, per fio) puts the IO-bound limit near 5 tok/s, so there is headroom left in the IO path, not a wall.&lt;/li&gt;
&lt;li&gt;Slot count is a real tuning knob with a cliff. 32 slots per layer (a 3 GB pinned pool) is the sweet spot on 8 GB; 64 slots allocates 6 GB and starves the rest of the system.&lt;/li&gt;
&lt;li&gt;Peak RSS stays at 6.7 GB, and the correctness bar held on device: 32 greedy steps on the real 26B, every logit bitwise identical, with 10.5 GiB streamed through the pools during the comparison.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest tradeoff: cold prefill drops to about a quarter of the baseline's speed, because streaming mode deliberately stops pre-faulting the expert file (pre-faulting 13 GiB into 8 GB of RAM is its own disaster). Prefill-through-the-pool is the obvious next piece of work, along with O_DIRECT reads.&lt;/p&gt;

&lt;p&gt;The GIF at the top is one request against the production &lt;code&gt;gemma4-26b&lt;/code&gt; entry in llama-swap. I sent a warm-up call first to get past cold-load and cold-prefill, then recorded a streamed completion with &lt;code&gt;tegrastats&lt;/code&gt; logging GPU load and power draw as each token arrived, laid out as three regions in the same terminal so the numbers and the text are visible at once. It settled at 2.09 tok/s where the bench measured 2.95, which is about the gap I'd expect between a clean &lt;code&gt;llama-bench&lt;/code&gt; run and one live request with a different prompt. The GPU trace is the interesting part: utilization swings between 0 and 95 percent because for stretches of every layer the GPU waits while the NVMe pulls in the next expert. That is the overlap claim from earlier, on screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  What generalizing bought
&lt;/h2&gt;

&lt;p&gt;Because the hook lives in llama.cpp's graph layer rather than in any model's code, it works for every GGUF MoE architecture. I verified bitwise identity on three families: Qwen3-MoE style graphs with per-expert scale tensors, Gemma 4's merged gate-up projection, and Qwen 3.5/3.6's hybrid DeltaNet layers. The same flag also gets a 22 GiB Qwen 3.6 35B-A3B onto the same 8 GB board.&lt;/p&gt;

&lt;p&gt;Is 3 tokens per second fast? No. It is roughly reading speed, fine for anything asynchronous and painful for chat. But the alternative on this hardware is not a slower 26B, it is no 26B at all. The compromise the patch offers is exactly the one I wanted: model size becomes a choice you pay for in speed instead of a hard out-of-memory wall.&lt;/p&gt;

&lt;p&gt;The work lives on the &lt;a href="https://github.com/Anyesh/llama.cpp/tree/upstream-pr/moe-stream" rel="noopener noreferrer"&gt;&lt;code&gt;moe-stream&lt;/code&gt; branch of my llama.cpp fork&lt;/a&gt;: a standalone expert streamer, a &lt;code&gt;--moe-stream&lt;/code&gt; flag, pool allocation at load, the dual-id graph integration, the Gemma 4 overlap, and the identity test harness, in seven commits against upstream master.&lt;/p&gt;

&lt;p&gt;Thanks again to TurboFieldfare for the architecture. Ideas this good deserve to escape their original hardware.&lt;/p&gt;

&lt;p&gt;The run behind that GIF is scrubbable. I put a &lt;a href="https://learn.anyesh.me/a/moe-stream-jetson-replay" rel="noopener noreferrer"&gt;replay of it on the Learning Lab&lt;/a&gt;, with GPU load and power draw lined up against each token.&lt;/p&gt;

</description>
      <category>moestream</category>
      <category>llamacpp</category>
      <category>mixtureofexperts</category>
      <category>jetsonorin</category>
    </item>
    <item>
      <title>My context selector beat grep. An agent with grep beat it.</title>
      <dc:creator>Anish Shrestha</dc:creator>
      <pubDate>Fri, 31 Jul 2026 14:00:23 +0000</pubDate>
      <link>https://dev.to/anyesh/my-context-selector-beat-grep-an-agent-with-grep-beat-it-5</link>
      <guid>https://dev.to/anyesh/my-context-selector-beat-grep-an-agent-with-grep-beat-it-5</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Anyesh/cognitive-cache" rel="noopener noreferrer"&gt;cognitive-cache&lt;/a&gt; started from an observation I still think is correct: every coding tool decides what goes into the context window, and almost none of them treat that decision as an explicit selection problem. Cursor greps, RAG systems embed and cosine-search, and plenty of harnesses just stuff files until the budget runs out. The OS analogy is hard to unsee once you have it. The context window is RAM, eviction and retrieval are virtual memory, and token budget allocation is an allocator that nobody has written.&lt;/p&gt;

&lt;p&gt;So I wrote one. Six weighted signals score every file in a repo against a task description, and a two-phase greedy submodular selector picks the highest-value set that fits a token budget. Nine languages, no LLM calls, no API keys. It shipped to PyPI and I used it through MCP for a while.&lt;/p&gt;

&lt;p&gt;Then I built a benchmark harness, and the harness spent the next two months dismantling most of what I believed about the tool. This is that sequence, in order, because the order is the interesting part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygvja5qkdelq2ow1nws5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygvja5qkdelq2ow1nws5.gif" alt="Animated forest plot building row by row: cognitive-cache's recall@5 advantage over grep, with paired bootstrap 95% confidence intervals. At n=23 the interval straddles zero. At n=78 and n=91 it clears zero" width="799" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The first honest measurement said it does not beat grep
&lt;/h2&gt;

&lt;p&gt;The original benchmark was bad in the way most self-benchmarks are bad. It coupled retrieval to LLM patch generation, reported rank-blind full-set recall, had no significance test, and I almost never ran it.&lt;/p&gt;

&lt;p&gt;The replacement is LLM-free and deterministic. For each issue it clones the repo at the pre-fix commit, produces a full file ranking per strategy, and scores that ranking against the files the gold patch actually modified, using recall@k and MRR. Every head-to-head comparison carries a paired bootstrap 95% confidence interval, so a difference that excludes zero is real and a difference that straddles zero is not a result.&lt;/p&gt;

&lt;p&gt;On the first dataset, 23 hand-curated issues across 8 repos:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;strategy&lt;/th&gt;
&lt;th&gt;R@1&lt;/th&gt;
&lt;th&gt;R@5&lt;/th&gt;
&lt;th&gt;R@10&lt;/th&gt;
&lt;th&gt;MRR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cognitive-cache&lt;/td&gt;
&lt;td&gt;0.170&lt;/td&gt;
&lt;td&gt;0.428&lt;/td&gt;
&lt;td&gt;0.511&lt;/td&gt;
&lt;td&gt;0.540&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep (keyword count)&lt;/td&gt;
&lt;td&gt;0.170&lt;/td&gt;
&lt;td&gt;0.409&lt;/td&gt;
&lt;td&gt;0.559&lt;/td&gt;
&lt;td&gt;0.533&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lexical (TF-IDF)&lt;/td&gt;
&lt;td&gt;0.159&lt;/td&gt;
&lt;td&gt;0.362&lt;/td&gt;
&lt;td&gt;0.496&lt;/td&gt;
&lt;td&gt;0.487&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The recall@5 difference over grep was +0.020 with a 95% CI of [-0.067, +0.109]. Grep won at &lt;a href="mailto:recall@10"&gt;recall@10&lt;/a&gt;. Whatever the tool was doing, at that sample size it was indistinguishable from counting keywords.&lt;/p&gt;

&lt;p&gt;Two other things fell out of the same run. The per-signal ablation suggested &lt;code&gt;graph_distance&lt;/code&gt;, carrying 0.20 of the weight budget, was net negative, and &lt;code&gt;file_role_prior&lt;/code&gt; was doing nothing at 0.07. And the budget selector was leaking badly: ranking recall@5 was 0.428 while end-to-end recall under a 12k budget was 0.282, so the selector was discarding relevant files its own ranking had already surfaced.&lt;/p&gt;

&lt;p&gt;I nearly acted on the ablation immediately. A weight sweep stopped me, because every candidate reweighting had a bootstrap lower bound of +0.000. The "graph_distance is net negative" reading was itself noise at n=23. The real blocker was not the weights, it was that 23 narrow issues cannot detect or drive an improvement in either direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Widening the dataset flipped the result
&lt;/h2&gt;

&lt;p&gt;I moved to &lt;a href="https://www.swebench.com/" rel="noopener noreferrer"&gt;SWE-bench&lt;/a&gt;, which solves the curation problem by construction: real GitHub issues paired with the gold patch that fixed them, pulled over the HuggingFace datasets-server with no auth. Ground truth is the set of source files the patch modified, and because patches only touch files present at the base commit, every ground-truth file is retrievable.&lt;/p&gt;

&lt;p&gt;On a balanced 78-instance subset of SWE-bench Lite across 12 repos:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;strategy&lt;/th&gt;
&lt;th&gt;R@1&lt;/th&gt;
&lt;th&gt;R@5&lt;/th&gt;
&lt;th&gt;R@10&lt;/th&gt;
&lt;th&gt;MRR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cognitive-cache&lt;/td&gt;
&lt;td&gt;0.231&lt;/td&gt;
&lt;td&gt;0.538&lt;/td&gt;
&lt;td&gt;0.654&lt;/td&gt;
&lt;td&gt;0.375&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep&lt;/td&gt;
&lt;td&gt;0.103&lt;/td&gt;
&lt;td&gt;0.359&lt;/td&gt;
&lt;td&gt;0.487&lt;/td&gt;
&lt;td&gt;0.236&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lexical (TF-IDF)&lt;/td&gt;
&lt;td&gt;0.141&lt;/td&gt;
&lt;td&gt;0.410&lt;/td&gt;
&lt;td&gt;0.462&lt;/td&gt;
&lt;td&gt;0.253&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Recall@5 over grep: +0.179, CI [+0.064, +0.295]. Over TF-IDF: +0.128, CI [+0.013, +0.244]. Both exclude zero. On a harder 91-issue subset restricted to fixes touching two or more files, the edge held at 0.365 vs 0.252, CI [+0.056, +0.174].&lt;/p&gt;

&lt;p&gt;The earlier negative was a small-sample artifact. That cuts both ways, and it is worth sitting with: if I had published the n=23 result I would have been wrong, and if I had published the n=78 result as the final word I would also have been wrong, for the reason in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  The baseline I had not run was an agent
&lt;/h2&gt;

&lt;p&gt;"Beats grep" means beats a naive keyword count. It says nothing about the thing my tool would actually be competing with, which is a coding agent running its own search loop.&lt;/p&gt;

&lt;p&gt;So I ran that. Five single-file SWE-bench issues, one each from flask, requests, xarray, pylint, and pytest. Fresh Claude subagents, cold in the repo at the base commit, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;glob&lt;/code&gt;, and &lt;code&gt;read&lt;/code&gt; only, cognitive-cache explicitly forbidden.&lt;/p&gt;

&lt;p&gt;The agent got 5 of 5, averaging about 3.4 tool calls and 19k tokens, and in each case it pinpointed the mechanism rather than just the file. cognitive-cache got 4 of 5, missing pylint #5859, which the agent found in two calls. Naive keyword grep got 3 of 5.&lt;/p&gt;

&lt;p&gt;The token argument inverted at the same time. The agent read a handful of targeted files. A cognitive-cache call returns file contents up to the full token budget, so on these tasks it front-loads more context than a surgical agent consumes. It was costing context, not saving it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flijfipkigkpkhi25v5r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flijfipkigkpkhi25v5r5.png" alt="Three stat tiles: an agent with only grep and read localized the fix file in 5 of 5 issues at about 3.4 tool calls each, cognitive-cache in 4 of 5, naive keyword grep in 3 of 5" width="799" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is n=5 on hand-picked single-file issues, so it is directional rather than definitive. But it moved the honest positioning from "more accurate than the agent" to "fewer roundtrips on a cold start in unfamiliar code", which is a much smaller claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  The signal that was worth zero, and why
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;graph_distance&lt;/code&gt; measures import-graph distance from the files the task names. It carried 0.20 weight from the day I wrote it, on the reasoning that a fix often lives one hop away from the file the issue mentions.&lt;/p&gt;

&lt;p&gt;Across both datasets it earned nothing. On the 91-issue multi-file set, zeroing it changed recall@5 by +0.000 and MRR by -0.006. That was the pre-registered kill criterion, so I was ready to retire it.&lt;/p&gt;

&lt;p&gt;Then I pushed back on my own experiment, which turned out to be the useful move. Multi-file is not the same as multi-hop, a regex-based import graph is not a reference graph, and testing a signal as one term in a drowned weighted sum is not a fair test of the underlying idea. All three critiques were fair, so instead of assuming, I wrote a diagnostic over the 91-issue set to check whether the ground truth was even reachable.&lt;/p&gt;

&lt;p&gt;The answer killed the revival rather than supporting it. 93% of issues have all their ground-truth files reachable from the entry points. But &lt;strong&gt;87% of fix files are themselves entry points, at graph distance 0&lt;/strong&gt;, with only about 10% sitting at one to three hops and none further out. The mechanism was never starvation, it was redundancy: real issue text names the files it touches, so &lt;code&gt;symbol_overlap&lt;/code&gt; has already ranked those files highly and import distance adds nothing on top.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0cn1xnbm8n4tlrl9y7es.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0cn1xnbm8n4tlrl9y7es.png" alt="A single bar split into three segments: 87% of ground-truth fix files sit at import-graph distance 0 from the files the issue names, 10% at one to three hops, 3% unreachable" width="799" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is not a bug in the experiment. It is a fact about the task distribution, and it explains the agent result too. On issue-shaped work the target is usually named in the prompt, which is exactly the regime where an agentic grep loop is excellent and a static ranker has little room to add value. Claude Code shipping no code index looks less like an omission and more like the same finding, reached earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The delivery problem, measured
&lt;/h2&gt;

&lt;p&gt;One thread survived long enough to be worth reporting separately. I built a jedi-backed neighborhood tool (definition, callers, in-repo callees, with real scope-aware resolution rather than name matching) and tested it two ways.&lt;/p&gt;

&lt;p&gt;Against agents, on two hard cross-file issues where naive grep recall was 0, I ran a 2x2: agents with grep and read, versus agents offered the neighborhood tool as well. Every arm scored 0.5, finding the file tied to the named concept and missing the cross-file one. &lt;strong&gt;The agents that had the tool invoked it zero times out of two&lt;/strong&gt;, despite being told to. Checking the tool's output directly against the symbols those same agents had reasoned about, it surfaced both missed files. The data would have lifted recall from 0.5 to 1.0 if it had been used.&lt;/p&gt;

&lt;p&gt;At scale the value is real but modest: across all 91 multi-file issues, neighborhood-of-seeds alone scores 0.164 (worse than grep's 0.252 standalone), while grep-or-neighborhood combined reaches 0.330, recovering 20 of grep's 257 missed fix files. That is +7.8 absolute points, in line with RepoGraph's own reported 9-12%.&lt;/p&gt;

&lt;p&gt;Put the two halves together and the conclusion is about delivery, not capability. A passive tool that competes with the grep reflex for invocation loses the invocation. Value like that only lands if it is injected into results the agent is already reading, not offered as one more thing it could call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it ended
&lt;/h2&gt;

&lt;p&gt;I set &lt;code&gt;graph_distance&lt;/code&gt; to weight 0, rewrote the README around the scoreboard instead of the pitch, and stopped building. The remaining pitch would have required a forced-injection layer to capture a modest gain, in a niche that already has direct clones, running against the current of agents that search well on their own.&lt;/p&gt;

&lt;p&gt;What survives is the measurement rig: the SWE-bench importer that needs no token, the retrieval evaluator, the weight tuner, the multi-hop diagnostic, the neighbor-recovery harness, and the bootstrap CIs that make each of those trustworthy. In a category that ships on anecdotes, that is the part I would build again first.&lt;/p&gt;

&lt;p&gt;The generalizable lesson is not "context selection is a dead end". It is that the baseline you skip is the one that decides whether your work matters. I measured against grep for two months because grep was the baseline that let the tool win. The agent was two hours of work and it reframed the entire project.&lt;/p&gt;

</description>
      <category>contextengineering</category>
      <category>retrieval</category>
      <category>swebench</category>
      <category>benchmarking</category>
    </item>
    <item>
      <title>J-space in practice: using Anthropic's Jacobian lens to decide what an LLM can forget</title>
      <dc:creator>Anish Shrestha</dc:creator>
      <pubDate>Wed, 29 Jul 2026 12:07:06 +0000</pubDate>
      <link>https://dev.to/anyesh/j-space-in-practice-using-anthropics-jacobian-lens-to-decide-what-an-llm-can-forget-14h1</link>
      <guid>https://dev.to/anyesh/j-space-in-practice-using-anthropics-jacobian-lens-to-decide-what-an-llm-can-forget-14h1</guid>
      <description>&lt;p&gt;Anthropic published &lt;a href="https://transformer-circuits.pub/2026/workspace/index.html" rel="noopener noreferrer"&gt;Verbalizable Representations Form a Global Workspace in Language Models&lt;/a&gt; on July 6, and the vocabulary it introduced is suddenly everywhere: J-space, the Jacobian lens, a global workspace inside Claude. Most of the discussion so far is about interpretability and alignment auditing, which is fair, since that is what the paper is about. I had a narrower and more mercenary question: can the workspace tell an inference runtime which parts of the KV cache it is safe to throw away?&lt;/p&gt;

&lt;p&gt;Three days after the paper landed, the first pre-registered gate on that question passed. As of this week the signal has replicated on three models and ships inside &lt;a href="https://github.com/Anyesh/EVOKE" rel="noopener noreferrer"&gt;EVOKE&lt;/a&gt;, my KV cache memory manager built on a forked llama.cpp. This post covers what J-space is, why it makes a good KV cache eviction signal, the numbers across Qwen2.5-7B, Qwen3-8B, and Qwen3-4B, and the caveat that comes with them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frh1hfff9leh8ua2rp18x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frh1hfff9leh8ua2rp18x.gif" alt="Animation of the two-stage pipeline: the Jacobian lens sweeps a session's KV blocks assigning workspace scores at prefill, then under a 25% cache budget the workspace policy keeps the planted fact and answers the turn-14 probe while SnapKV evicts it and fails" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What J-space is, in one paragraph
&lt;/h2&gt;

&lt;p&gt;The Jacobian lens is the instrument and J-space is the phenomenon. The lens isolates directions in a model's residual stream that encode a token the model could verbalize next, and those directions form a low-dimensional workspace: roughly 10% of activation variance, concentrated in the middle layers, carrying whatever the model is "holding in mind" at each position. Anthropic's headline application is alignment auditing, reading reasoning the model never voices. What makes independent work possible is that they released &lt;a href="https://github.com/anthropics/jacobian-lens" rel="noopener noreferrer"&gt;companion code&lt;/a&gt; under Apache-2.0 along with fitted lens matrices for open Qwen models on &lt;a href="https://huggingface.co/neuronpedia/jacobian-lens" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;, so anyone can apply the lens to an open-weights model on a single GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  The systems problem: KV cache eviction
&lt;/h2&gt;

&lt;p&gt;Every long-running LLM session eventually outgrows its KV cache budget. An agent session in a coding harness crosses tens of thousands of cached tokens within a few turns, and something has to decide which entries stay in GPU memory. The standard answers, H2O and SnapKV, rank cache blocks by accumulated attention history: keep what the model has been attending to, evict the rest. StreamingLLM adds protected sink tokens and a recency window.&lt;/p&gt;

&lt;p&gt;These policies share a structural weakness. They are backward-looking, so they can only rank blocks the model has already used. A fact planted at turn 1 that will matter at turn 14 looks cold the whole time, and it gets evicted exactly when the budget tightens.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hypothesis: workspace content predicts KV reuse
&lt;/h2&gt;

&lt;p&gt;The lens readout suggested a forward-looking alternative. If a position carries workspace content, meaning the model is holding something in mind there, then its KV entries should be disproportionately likely to be read by later reasoning. That is a content signal, computable at prefill time from the residual stream alone, before any attention history exists.&lt;/p&gt;

&lt;p&gt;The offline validation lives in &lt;a href="https://github.com/Anyesh/j-space" rel="noopener noreferrer"&gt;j-space&lt;/a&gt;: lens application, a family of workspace statistics over the readout (kurtosis of block means, transport ratios, energy concentration, computed across layers), block aggregation, eviction simulation, and probe distillation. Because a signal this convenient deserved suspicion, I pre-registered three kill gates before running anything:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fact ranking.&lt;/strong&gt; The best workspace statistic has to beat SnapKV at ranking the task-critical block, by a pre-set AUC margin, or the project dies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Masked eviction.&lt;/strong&gt; With only 25% of the cache retained under the workspace policy, the planted fact has to stay answerable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probe distillation.&lt;/strong&gt; Applying the full lens at inference time is too expensive, so a ridge probe distilled from the workspace readout has to reproduce the ranking at Spearman 0.8 or better. The deployable artifact is one dot product per position.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Results across three Qwen models
&lt;/h2&gt;

&lt;p&gt;All three gates passed on all three models, with no hand-tuning on the replications.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Fact-AUC (workspace)&lt;/th&gt;
&lt;th&gt;Fact-AUC (SnapKV)&lt;/th&gt;
&lt;th&gt;Eviction @ 25% budget&lt;/th&gt;
&lt;th&gt;Probe Spearman&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-7B-Instruct&lt;/td&gt;
&lt;td&gt;0.891&lt;/td&gt;
&lt;td&gt;0.622&lt;/td&gt;
&lt;td&gt;35/36 vs 25/36&lt;/td&gt;
&lt;td&gt;0.959&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-8B&lt;/td&gt;
&lt;td&gt;0.865&lt;/td&gt;
&lt;td&gt;0.563&lt;/td&gt;
&lt;td&gt;35/36 vs 14/36&lt;/td&gt;
&lt;td&gt;0.944&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-4B&lt;/td&gt;
&lt;td&gt;0.939&lt;/td&gt;
&lt;td&gt;0.541&lt;/td&gt;
&lt;td&gt;36/36 vs 13/36&lt;/td&gt;
&lt;td&gt;0.946&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdoeyae6nxq4pkk2aapdl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdoeyae6nxq4pkk2aapdl.png" alt="Grouped bar chart of fact-AUC on three Qwen models: the workspace signal scores 0.891, 0.865, and 0.939 while SnapKV scores 0.622, 0.563, and 0.541, barely above the 0.5 chance line" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The eviction column reads: episodes where the planted fact survived eviction and was answered correctly, workspace policy vs SnapKV at the same budget. On Qwen3-4B the workspace policy saturates at 36/36 at every budget tested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvd8l6tg4fswy6q877sea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvd8l6tg4fswy6q877sea.png" alt="Grouped bar chart of eviction survival at a 25% cache budget: the workspace policy keeps the fact answerable in 35, 35, and 36 of 36 episodes while SnapKV manages 25, 14, and 13" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The distilled probe also holds up live. Inside EVOKE's agent bench, the workspace signal passes the fact probe at every cache budget (512, 1024, and 2048 tokens) with recovery disabled, while H2O fails all three budgets and SnapKV passes only at 2048. Measured decode overhead was -0.6%, which is noise; the probe is a dot product per position at prefill, and nothing runs during decode.&lt;/p&gt;

&lt;h2&gt;
  
  
  What transfers and what does not
&lt;/h2&gt;

&lt;p&gt;Here is the caveat, and I think it is the most interesting finding in the project. The winning statistic is model-dependent. On Qwen2.5-7B and Qwen3-8B it is kurtosis of block-mean workspace scores at deep layers (layer 23 and layer 30 respectively). On Qwen3-4B it is a transport-ratio statistic at layer 12 of 36. If I had frozen the 7B statistic and shipped it as "the" signal, the 4B replication would have failed. What transfers is the statistic family plus the automated procedure that selects the winner per model on held-out episodes, not any single magic number.&lt;/p&gt;

&lt;p&gt;The other scope limits are stated plainly in the paper: three models so far, all Qwen; the probes are fit on the benchmark's own episode family; and the task family is long-horizon fact recall. LongBench-style suites and non-Qwen architectures are open work. As far as I can tell this is the first interpretability-derived KV eviction signal, and I would much rather over-state the limits than the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it ships: EVOKE v2.0
&lt;/h2&gt;

&lt;p&gt;EVOKE treats the KV cache the way an operating system treats memory. Blocks are evicted under budget pressure and recovered recompute-free through save/restore primitives added to a forked llama.cpp, 20 to 32 times faster than re-prefilling the same tokens. &lt;a href="https://github.com/Anyesh/EVOKE/releases/tag/v2.0" rel="noopener noreferrer"&gt;Version 2.0&lt;/a&gt; adds the workspace probe as a first-class eviction signal alongside live attention capture, harness priority tags, task-focus coherence, and recency, and includes the paper PDF with the full three-model evaluation.&lt;/p&gt;

&lt;p&gt;You can watch the signal work in the &lt;a href="https://huggingface.co/spaces/anish-shrestha/evoke-demo" rel="noopener noreferrer"&gt;live demo on Hugging Face Spaces&lt;/a&gt;, which runs three arms side by side on Qwen3-4B: a plain baseline, EVOKE with its attention scorer, and EVOKE with workspace eviction. There is also a benchmark walkthrough on my &lt;a href="https://anyesh.github.io" rel="noopener noreferrer"&gt;learning lab&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://transformer-circuits.pub/2026/workspace/index.html" rel="noopener noreferrer"&gt;The Anthropic paper&lt;/a&gt;, the primary source on J-space and the Jacobian lens&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anthropics/jacobian-lens" rel="noopener noreferrer"&gt;anthropics/jacobian-lens&lt;/a&gt;, the official lens code, with fitted matrices at &lt;a href="https://huggingface.co/neuronpedia/jacobian-lens" rel="noopener noreferrer"&gt;neuronpedia/jacobian-lens&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Anyesh/j-space" rel="noopener noreferrer"&gt;j-space&lt;/a&gt;, the offline validation and probe distillation pipeline behind this post&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Anyesh/EVOKE" rel="noopener noreferrer"&gt;EVOKE&lt;/a&gt;, the runtime that consumes the signal, plus the &lt;a href="https://huggingface.co/spaces/anish-shrestha/evoke-demo" rel="noopener noreferrer"&gt;demo Space&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work on KV cache compression, long-context inference, or interpretability and want to poke holes in any of this, the repos are public and the gates were registered before the runs. I would genuinely like to know where it breaks.&lt;/p&gt;

</description>
      <category>jspace</category>
      <category>jacobianlens</category>
      <category>interpretability</category>
      <category>kvcache</category>
    </item>
  </channel>
</rss>
