{"slug": "i-replaced-claude-codes-compact-with-dijkstra", "title": "I Replaced Claude Code’s /compact With Dijkstra", "summary": "Developer Abdul M. released context-graph, an open-source Claude Code plugin that replaces the /compact summarization step with a graph-based selection algorithm, after measuring that each /compact in his own long sessions read 437k to 830k tokens, took 134 to 182 seconds, and produced 20k to 28k characters of model-written summary. The plugin records every prompt, tool call and reply as atoms, weights relevance edges by w = -log p, and uses Dijkstra to find the most probable chain of relevance, rebuilding context on /clear without a model; in a 36-turn session it selected 103 of 240 atoms in about 5,700 tokens. The author reports that heat-kernel diffusion, which he expected to outperform shortest-path selection, did not win, and that relevance is further multiplied by first-order decay with a half-life of 8 user turns.", "body_md": "I looked at the compactions in my own long Claude Code sessions. Each /compact read between 437k and 830k tokens, took 134 to 182 seconds, and produced a summary of 20k to 28k characters. That summary is everything the rest of the session knows about what came before. A model wrote it in one pass, deciding on its own what would matter later.\n\nTwo things about that bothered me. First, a summary paraphrases. “Amounts are integer cents end to end” can come back as “be careful with money”, and the difference is a bug. Second, nobody can see what was dropped, or why.\n\nAndrej Karpathy described context engineering as “the delicate art and science of filling the context window with just the right information for the next step”. I wanted to see how far you get if you treat “just the right information” as something you compute rather than something a model writes. So I built [context-graph](https://github.com/abdulm-ai/context-graph), a Claude Code plugin that does exactly that, and measured it against /compact.\n\nThe plugin hooks into the session and records everything as it happens: every prompt word for word, every tool call with its full output, every final reply. It breaks these into atoms: user turns, constraints, decisions, facts, commands, edits, errors and files. Atoms that are related get an edge:\n\nWhen you run /clear, a SessionStart hook rebuilds the context from this graph. No model is involved. The result is a pack of original text, chosen under a hard token budget, with a log of every choice. In a 36-turn session, that was 103 of 240 atoms in about 5,700 tokens.\n\nThat makes it a selection problem, which is something classical algorithms are good at.\n\nEvery edge carries a probability: if atom *i* matters for the next step, how likely is it that atom *j* does too? A shared file might be 0.6. An error and the fix that followed it, 0.9. When several reasons link two atoms, they combine by noisy-OR.\n\nNow set each edge’s weight to w = -log p. A path's length becomes the sum of its surprisals, which is minus the log of the product of its probabilities. So the shortest path from what you're working on now to any atom is its *most probable chain of relevance*, and Dijkstra finds all of them in one pass:\n\n```\nrelevance(i) = max over paths from the focus of  prod p(edge)  =  exp(-dist(i))\n```\n\nThe focus is the latest user prompt, at weight 1, and the one before it at 0.5. Because of this, the plugin can explain every choice it makes. Here is why a root cause stated at turn 17 survives at turn 36:\n\n``` bash\n$ ctx explain d17.1most probable chain from the focus (Dijkstra, w = -log p):  u35            focus weight 0.5  -> p = 0.500  t184           p(edge) = 0.600 [in turn]  -> p = 0.3000  surprisal 1.20  f:test_out.txt p(edge) = 0.600 [file]     -> p = 0.1800  surprisal 1.71  t90            p(edge) = 0.600 [file]     -> p = 0.1080  surprisal 2.23  u17            p(edge) = 0.600 [in turn]  -> p = 0.0648  surprisal 2.74  d17.1          p(edge) = 0.900 [stated in] -> p = 0.0583  surprisal 2.84decision: KEPT (pinned: always carried over, verbatim)\n```\n\nShortest path has a blind spot: it counts only the single best chain. An atom reached by six weak links scores no higher than one reached by the best of those six.\n\nThe alternative is borrowed from physics. Put heat on the focus nodes and let it flow through the graph for a time *t*:\n\n```\nu(t) = u0 exp(-t L),   L = I - P,   P = D^-1 W\n```\n\nThis is the heat equation on a graph, and it gives Fan Chung’s heat kernel pagerank. The plugin computes it in pure Python as a truncated series. Heat adds up over every path, so many weak links can beat one strong link. HippoRAG uses a close relative, personalised PageRank, to give language models a long-term memory.\n\nI expected diffusion to win. It didn’t (see below).\n\nRelevance is multiplied by a decay term: first-order decay, the same law as radioactive decay, with a half-life of 8 user turns:\n\n```\ndN/dt = -lambda N,   lambda = ln 2 / half_life\n```\n\nWhen a later atom links back to an atom by content, the clock resets. That is the Ebbinghaus forgetting curve with rehearsal, which MemoryBank applied to LLM memory. Generative Agents scores memories the same way: decayed recency multiplied by relevance.\n\nChoosing atoms under a token budget is then a 0/1 knapsack problem. Value is relevance × decay, and weight is tokens. Dynamic programming solves it exactly in 5.8 ms for 255 atoms at a 6,000-token budget.\n\nConstraints, decisions, rejected approaches, root causes and specific facts the user states in passing are never scored at all. They are *pinned*: always kept and copied word for word. In a 36-turn session they come to about 1,000 tokens. This is where a paraphrase does the most damage, so this is where the plugin refuses to paraphrase.\n\nThe rule-based detector that finds them is the weakest part of the system. It pinned “Both are now rejected.” as a decision, a sentence that means nothing out of context.\n\n```\nMethod                     Recall  Rules  Tokens carried  Compaction step                Next task input tokens/compact                   100%    100%   10,877          114,587 tokens, $0.19, 24.8 s  491,490/clear + recency, 6k       44%     50%    6,039           $0, 3.0 s                      845,704/clear + graph, Dijkstra   92%     100%   5,755           $0, 3.0 s                      943,321/clear + graph, diffusion  89%     100%   5,349           $0, 3.3 s                      793,925\n```\n\n**Half the tokens, every rule kept.** The graph pack carried 5,755 tokens against 10,877 for /compact, and recalled every rule-like fact in every run. In the follow-up task it broke no rules. /compact broke one: in one run, the model brought back float for money.\n\n**The structure is doing the work.** At the same 6,000-token budget, keeping the most recent turns recalled 44%, and only facts from turn 26 onward. The graph recalled 92%, with facts from every part of the session. That is the “lost in the middle” pattern Liu et al. measured, reproduced by a naive selector and avoided by a graph.\n\n**Summaries fail all at once.** On the held-out facts, one /compact wrote a 1,826-character summary that covered only the last request, and recalled 3 of 12 facts. Its average over three runs was 75%, with a standard deviation of 43 points. A pin cannot be dropped like that. With the current extractor, all twelve held-out facts are in the pack word for word at every budget from 2,000 tokens up. That is an offline check of the pack's contents, not yet confirmed with live probes.\n\n**Compaction itself becomes free.** It takes no model call, costs nothing and runs in 0.1 seconds of selection. In sessions the size of mine, that removes a step that reads up to 830k tokens.\n\n**Diffusion lost to Dijkstra.** It scored 89% against 92%, and dropped the finance contact once. On these graphs, the single strongest chain was a better signal than the sum of weak ones.\n\n**The exact knapsack barely mattered.** Greedy by value per token reached 99.89% of the optimal objective on average, and selected the same facts. The DP is cheap enough to keep, but I wouldn’t claim it as a win.\n\n**Trimming tool output saved nothing measurable.** The plugin can cut long Bash output down to its head, tail and error lines, and page the rest back on request. It fired one to five times per session, because the model mostly kept its own output short.\n\n**The next task cost about twice as much.** This is the result that matters most. After the graph pack, the follow-up task used 943k input tokens on average, against 491k after /compact. Add in what /compact spends on compacting (115k tokens), and the summary still comes out well ahead on total tokens.\n\nThe reason is visible in the transcripts. Claude Code’s summary includes a section describing the current state of the code. The pack holds original turns and decisions, but no map of the files, so the model spent extra calls re-reading code it had already seen.\n\nSo on my two goals, the score is split. On not losing context, the graph is better: it is deterministic, every choice can be explained, and pins don’t get paraphrased away. On saving tokens, a pack that is cheap to build is not the same as a session that is cheap to continue. A summary compresses the state of the work, not just the conversation, and a selection of original text cannot do that.\n\nThe direction this points to is a hybrid: pins and the graph for what must not change, plus a short, regenerable description of the code’s current state. The second part is what a model is genuinely good at writing, and the first part is what it shouldn’t be trusted to rewrite.\n\nThe other honest caveat is scale. Three runs per method, one repository, one model and a scripted user is enough to see these effects, but not enough to settle them.\n\nThe code, the raw benchmark data and every number above are in the repo: [github.com/abdulm-ai/context-graph](https://github.com/abdulm-ai/context-graph).\n\n[I Replaced Claude Code’s /compact With Dijkstra](https://pub.towardsai.net/i-replaced-claude-codes-compact-with-dijkstra-cf456a39c7cb) was originally published in [Towards AI](https://pub.towardsai.net) on Medium, where people are continuing the conversation by highlighting and responding to this story.", "url": "https://wpnews.pro/news/i-replaced-claude-codes-compact-with-dijkstra", "canonical_source": "https://pub.towardsai.net/i-replaced-claude-codes-compact-with-dijkstra-cf456a39c7cb?source=rss----98111c9905da---4", "published_at": "2026-10-23 10:42:56+00:00", "updated_at": "2026-10-23 11:10:02.639232+00:00", "lang": "en", "topics": ["ai-tools", "large-language-models", "ai-agents", "developer-tools"], "entities": ["Claude Code", "context-graph", "Abdul M.", "Andrej Karpathy", "Dijkstra", "HippoRAG", "Fan Chung"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-replaced-claude-codes-compact-with-dijkstra", "markdown": "https://wpnews.pro/news/i-replaced-claude-codes-compact-with-dijkstra.md", "text": "https://wpnews.pro/news/i-replaced-claude-codes-compact-with-dijkstra.txt", "jsonld": "https://wpnews.pro/news/i-replaced-claude-codes-compact-with-dijkstra.jsonld"}}