<?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: Vitaly Ivasenko</title>
    <description>The latest articles on DEV Community by Vitaly Ivasenko (@vitaly_ivasenko_cd7932e08).</description>
    <link>https://dev.to/vitaly_ivasenko_cd7932e08</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%2F4122516%2Faff04930-b345-4e75-be36-600e75521e24.JPG</url>
      <title>DEV Community: Vitaly Ivasenko</title>
      <link>https://dev.to/vitaly_ivasenko_cd7932e08</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vitaly_ivasenko_cd7932e08"/>
    <language>en</language>
    <item>
      <title>Your coding agent forgets on purpose. A PreCompact hook is where you save it</title>
      <dc:creator>Vitaly Ivasenko</dc:creator>
      <pubDate>Tue, 15 Sep 2026 19:16:18 +0000</pubDate>
      <link>https://dev.to/vitaly_ivasenko_cd7932e08/your-coding-agent-forgets-on-purpose-a-precompact-hook-is-where-you-save-it-3gn</link>
      <guid>https://dev.to/vitaly_ivasenko_cd7932e08/your-coding-agent-forgets-on-purpose-a-precompact-hook-is-where-you-save-it-3gn</guid>
      <description>&lt;p&gt;Your coding agent doesn't have amnesia. It has a budget. When the context window fills up, the host compacts it into a summary — and summaries keep the &lt;em&gt;what&lt;/em&gt; while losing the &lt;em&gt;why&lt;/em&gt;. For engineering decisions, the why is everything.&lt;/p&gt;

&lt;p&gt;This is a write-up of a tool I came across that is built around exactly that moment — the one where a decision can still be saved: &lt;strong&gt;myc&lt;/strong&gt;, an open-source (MIT) local task-and-memory layer for coding agents, built on Bun and TypeScript. Its author describes three days of an agent re-doing the same work before writing it; the design follows from that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug report was the author
&lt;/h2&gt;

&lt;p&gt;The cycle, as the README tells it, looked like this. Early in a session you'd decide something: "we're doing X, not Y, because Z." Hours later the context got compacted, the reason drowned, and the agent — honestly, politely, with no memory of ever thinking otherwise — proposed Y again.&lt;/p&gt;

&lt;p&gt;Notice the failure mode: it's not that the agent forgot. It's that it &lt;em&gt;had no way to know there was something to remember&lt;/em&gt;. Any memory layer you query after the fact is useless if nothing was written into it at the moment the context died.&lt;/p&gt;

&lt;p&gt;So the saving moment is exactly one: &lt;strong&gt;right before compaction&lt;/strong&gt;. Claude Code exposes that moment as the &lt;code&gt;PreCompact&lt;/code&gt; hook event. That's where myc lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the hook does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;myc wire&lt;/code&gt; installs the hook. When compaction is about to happen, two things run, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The session episode is written to disk first&lt;/strong&gt; — raw, into the L0 layer, marked private, with secrets masked (&lt;code&gt;password=&lt;/code&gt;, &lt;code&gt;api_key=&lt;/code&gt;, and friends are matched on word boundaries so prose stays prose). The write happens before anything else: if the hook exceeds its timeout, you lose the summary, not the record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A rescue packet is printed back into the context that survives&lt;/strong&gt; — the short list of things that must not be lost.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myc absorb-session &lt;span class="nt"&gt;--reason&lt;/span&gt; manual &lt;span class="nt"&gt;--transcript&lt;/span&gt; … &lt;span class="nt"&gt;--agent&lt;/span&gt; claude
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myc: context is being compacted — here is what must not be lost
&lt;span class="go"&gt;episode sess-5jh8je4g050m saved (265 B)
NEXT     myc show sess-5jh8je4g050m · myc ready --claim
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Distillation of what was saved is queued, never done on the write path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Candidates, not facts
&lt;/h2&gt;

&lt;p&gt;Here's the design decision that makes myc deliberately a bit inconvenient: decisions the hook lifts from the transcript ("we decided / because" lines) are stored as &lt;em&gt;candidates&lt;/em&gt; in &lt;code&gt;pending_review&lt;/code&gt; state. They do not come back from search, &lt;code&gt;recall&lt;/code&gt;, &lt;code&gt;prime&lt;/code&gt;, or the MCP tools until a human confirms them. &lt;code&gt;prime&lt;/code&gt; says so in its footer: &lt;code&gt;N pending review hidden — myc review&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myc review
&lt;span class="go"&gt;PENDING REVIEW 2 · 1 in this session's prime · 1 from other sessions · session 3f9c21aa
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myc review confirm memory-6k2x…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reasoning: a memory that hallucinates is worse than no memory, because it gets believed. Confirming a candidate turns it into knowledge exactly the way a normal note is born; rejecting it retracts the note, and retracted notes are out of every retrieval path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed is a constraint, not a metric
&lt;/h2&gt;

&lt;p&gt;A memory tool lives in the agent's hot path — it gets called dozens of times per session. If it's slow, the agent starts economizing on it, and economizing on memory &lt;em&gt;is&lt;/em&gt; forgetting. So every hot path in myc has a latency budget enforced by tests.&lt;/p&gt;

&lt;p&gt;Measured on 100,000 nodes (2026-09-11, darwin-arm64, myc 0.3.6, p99):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;operation&lt;/th&gt;
&lt;th&gt;p99&lt;/th&gt;
&lt;th&gt;budget&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;prime&lt;/code&gt; (session context packet)&lt;/td&gt;
&lt;td&gt;0.608 ms&lt;/td&gt;
&lt;td&gt;30 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;search&lt;/code&gt; (hybrid)&lt;/td&gt;
&lt;td&gt;8.215 ms&lt;/td&gt;
&lt;td&gt;25 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.327 ms&lt;/td&gt;
&lt;td&gt;5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cold start&lt;/td&gt;
&lt;td&gt;21.337 ms&lt;/td&gt;
&lt;td&gt;60 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Reproduce it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aistastudio/myc
bun run scripts/bench-latency.ts   &lt;span class="c"&gt;# from a source checkout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honest footnote: those absolute numbers are calibrated on one machine, and CI doesn't enforce them on other hardware. CI &lt;em&gt;does&lt;/em&gt; enforce structural claims (query plans, prefiltering) and relative ones (the healthy path measured against a deliberately degraded one, interleaved so hardware cancels out). Ranking is measured too: boosts take MRR@10 from 0.520 to 0.867, two-hop graph expansion from 0.193 to 0.422 — on corpora that contain a control group which gets &lt;em&gt;worse&lt;/em&gt; when the feature works, so you can't fake a win by shaping the corpus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory anchored to code — and honest when the code is gone
&lt;/h2&gt;

&lt;p&gt;Since 0.3.10 a note can be anchored to a span of code, and the anchor follows the code as it moves — across a refactor, even into another file (verified on TypeScript and Python). When the code is no longer where the anchor put it, the knowledge is not deleted: it ranks lower and says why. &lt;code&gt;recall&lt;/code&gt; marks the row &lt;code&gt;[code moved ×0.64]&lt;/code&gt;, &lt;code&gt;[code unverified ×0.5]&lt;/code&gt; or &lt;code&gt;[code gone ×0.2]&lt;/code&gt;; knowledge whose every anchor is lost stays out of &lt;code&gt;prime&lt;/code&gt;, and the footer counts it: &lt;code&gt;N with code gone hidden&lt;/code&gt;. That is the answer to the question every memory tool eventually faces — "will it start lying to me in a month?" — and it is the part I have not seen elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the toolbox, one line each
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tasks&lt;/strong&gt;: a graph with dependencies, blockers inherited down the parent chain, and atomic claims — two agents never get the same task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt;: hybrid (BM25 + vectors fused by RRF). Semantics are opt-in: the embedding model (129 MB) downloads only when you ask (&lt;code&gt;myc models fetch&lt;/code&gt;); until then search is lexical and &lt;em&gt;says so on every answer&lt;/em&gt;. No silent fallbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code index&lt;/strong&gt;: built-in tree-sitter — symbols, callers, search by meaning for TypeScript/TSX/JS/Python. Grammars are fetched once; indexing never touches the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-agent sanity&lt;/strong&gt;: &lt;code&gt;myc run -- bun test&lt;/code&gt; puts heavy commands through one machine-wide queue, so parallel agents stop strangling each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sync&lt;/strong&gt;: only the oplog goes to git, merged per field — two machines editing the same note converge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration from beads&lt;/strong&gt;: &lt;code&gt;myc import-beads&lt;/code&gt; brought over a live project — 796 tasks, 972 dependencies, 265 notes — in 889 ms; re-running it syncs instead of duplicating. (The first release silently dropped the export's comments; found the next day on a real project, fixed in 0.2.0, and described by the author in the README.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The limits, stated before you hit them
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bun only.&lt;/strong&gt; The runtime binds to &lt;code&gt;bun:sqlite&lt;/code&gt; — SQLite and sqlite-vec ship inside Bun with no native Node bindings. It will not start on plain Node.js or Deno. That's the deal: one runtime, one binary, zero native build steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS and Linux&lt;/strong&gt;; on Windows use WSL — the launcher doesn't start in cmd or PowerShell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single user.&lt;/strong&gt; No server, no team mode, no ACL. Those are designed and tracked (the team milestone has open subtasks; the live count is on the site), not implemented. Swarm routing and memory distillation haven't been started.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it and break it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aistastudio/myc   &lt;span class="c"&gt;# 3.42 MB, pulls nothing&lt;/span&gt;
myc init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; myc wire              &lt;span class="c"&gt;# hooks + MCP for Claude Code, Codex, opencode, Kimi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is about a week old and ships a release close to every day, which is another way of saying: now is the cheapest time to tell the author what's wrong. If you run agents daily, the question worth answering in the issues is whether decisions survive your sessions today — and if not, whether a compaction hook feels like the right place to save them.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/aistastudio/myc" rel="noopener noreferrer"&gt;https://github.com/aistastudio/myc&lt;/a&gt;&lt;br&gt;
Site (every number printed next to the command that reproduces it): &lt;a href="https://aistastudio.github.io/myc/" rel="noopener noreferrer"&gt;https://aistastudio.github.io/myc/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cli</category>
      <category>bunjs</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
