<?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: showjihyun</title>
    <description>The latest articles on DEV Community by showjihyun (@jihyunsama).</description>
    <link>https://dev.to/jihyunsama</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3861013%2F5c318e0e-6a91-43cf-8f14-e8f7eba86384.jpg</url>
      <title>DEV Community: showjihyun</title>
      <link>https://dev.to/jihyunsama</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jihyunsama"/>
    <language>en</language>
    <item>
      <title>Article - 1 How Figma stores your design files (and how to read them offline)</title>
      <dc:creator>showjihyun</dc:creator>
      <pubDate>Fri, 15 May 2026 13:08:59 +0000</pubDate>
      <link>https://dev.to/jihyunsama/article-1-how-figma-stores-your-design-files-and-how-to-read-them-offline-1j2f</link>
      <guid>https://dev.to/jihyunsama/article-1-how-figma-stores-your-design-files-and-how-to-read-them-offline-1j2f</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2Fsrg6bacyo6ldg6jdo1l2.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.amazonaws.com%2Fuploads%2Farticles%2Fsrg6bacyo6ldg6jdo1l2.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A 5-minute tour from the bytes on disk to the 35,660-node tree your designer never sees.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;You hit &lt;strong&gt;File → Save as &lt;code&gt;.fig&lt;/code&gt;&lt;/strong&gt; in Figma. You drop the result on your desktop. Open it in your text editor and you get… a wall of garbage. Not even a recognizable header.&lt;/p&gt;

&lt;p&gt;What's actually in there?&lt;/p&gt;

&lt;p&gt;I spent a few weeks taking one apart, byte by byte, and built an open-source parser around it (&lt;a href="https://github.com/showjihyun/figmatojson" rel="noopener noreferrer"&gt;figma-reverse&lt;/a&gt; — TypeScript, MIT, no Figma API). This post is the tour I wish someone had handed me when I started.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — A &lt;code&gt;.fig&lt;/code&gt; file is a ZIP. Inside it lives &lt;code&gt;canvas.fig&lt;/code&gt;, the actual binary, in a format called &lt;strong&gt;fig-kiwi&lt;/strong&gt;. Decode it and you get 568 type definitions plus a flat array of 35,660 nodes. Walk the parents and you get the tree your designer was looking at.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Five layers, each with its own format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.fig (ZIP)
  └── canvas.fig (fig-kiwi)
       └── chunks[0] = compressed schema
       └── chunks[1] = compressed message
                       (35,660 flat nodes)
                          ↓ link by parent GUID
                       Tree of pages → frames → leaves
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's walk down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1 — The outer file is a ZIP
&lt;/h2&gt;

&lt;p&gt;The first 4 bytes give it away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;xxd &lt;span class="nt"&gt;-l&lt;/span&gt; 4 design.fig
00000000: 504b 0304                                PK..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;50 4B 03 04&lt;/code&gt; is the &lt;strong&gt;PKZIP local file header&lt;/strong&gt; — every ZIP starts with it. Figma's &lt;code&gt;.fig&lt;/code&gt; is, prosaically, a ZIP archive in &lt;strong&gt;STORE mode&lt;/strong&gt; (no compression) with four entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;design.fig (ZIP STORE)
├── canvas.fig          ← the real binary, more on this below
├── meta.json           ← file_name, background_color, exported_at
├── thumbnail.png       ← small preview
└── images/
    ├── &amp;lt;sha1-hash-1&amp;gt;   ← raw bytes, no extension
    ├── &amp;lt;sha1-hash-2&amp;gt;
    └── …
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A surprise: the images have no file extension. They're keyed by SHA-1 hash because the same image (a PNG icon, say) might be referenced 50 times across your design. Deduplication for free. To recover the type, you sniff the magic byte: &lt;code&gt;89 50 4E 47&lt;/code&gt; is PNG, &lt;code&gt;FF D8 FF&lt;/code&gt; is JPEG, etc.&lt;/p&gt;

&lt;p&gt;So far, nothing exotic. Standard ZIP, standard PNG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2 — &lt;code&gt;canvas.fig&lt;/code&gt; is in fig-kiwi format
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;canvas.fig&lt;/code&gt; and you see an 8-byte ASCII string at offset 0:&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="go"&gt;00000000: 6669 672d 6b69 7769 6a00 0000 ...        fig-kiwij....
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fig-kiwi&lt;/code&gt; is the magic. After it comes a 4-byte little-endian uint32 — the format version (&lt;code&gt;0x6a&lt;/code&gt; = 106 in modern exports). Then come the chunks.&lt;/p&gt;

&lt;p&gt;The structure is dead simple — like TLV without the T:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[8 bytes  ] "fig-kiwi" magic
[4 bytes  ] version (LE uint32)              ← 106
[4 bytes  ] chunk[0].size (LE uint32)
[N bytes  ] chunk[0].data
[4 bytes  ] chunk[1].size
[N bytes  ] chunk[1].data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just two chunks. You'd think the first would be a header and the second the payload. Half right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3 — Two compressed blocks, two algorithms
&lt;/h2&gt;

&lt;p&gt;This is where it got weird.&lt;/p&gt;

&lt;p&gt;I assumed both chunks were compressed the same way. The existing OSS &lt;a href="https://www.npmjs.com/package/fig-kiwi" rel="noopener noreferrer"&gt;&lt;code&gt;fig-kiwi&lt;/code&gt;&lt;/a&gt; npm package assumes &lt;code&gt;deflate-raw&lt;/code&gt;. I plugged it in, decoded chunk 0 — got something. Decoded chunk 1 — silent garbage.&lt;/p&gt;

&lt;p&gt;Looked at the magic bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;xxd &lt;span class="nt"&gt;-l&lt;/span&gt; 4 chunk0.bin   &lt;span class="c"&gt;# chunk 0&lt;/span&gt;
00000000: 78 da xx xx                           x...     &lt;span class="c"&gt;# zlib's standard prefix&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;xxd &lt;span class="nt"&gt;-l&lt;/span&gt; 4 chunk1.bin   &lt;span class="c"&gt;# chunk 1&lt;/span&gt;
00000000: 28 b5 2f fd                           &lt;span class="o"&gt;(&lt;/span&gt;./.    &lt;span class="c"&gt;# zstd's magic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chunk 1 is zstd.&lt;/strong&gt; Not deflate. Not gzip. Facebook's modern &lt;code&gt;zstd&lt;/code&gt;. Most existing &lt;code&gt;.fig&lt;/code&gt; parsers don't know.&lt;/p&gt;

&lt;p&gt;I'll come back to &lt;em&gt;why&lt;/em&gt; in &lt;a href="//./02-two-tiered-compression-mystery.md"&gt;the next article&lt;/a&gt;, but for now: auto-detect the algorithm from the magic byte and fork. In figma-reverse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;detectCompression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zstd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deflate-zlib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deflate-raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xB5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x2F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xFD&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zstd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mh"&gt;0x78&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deflate-zlib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deflate-raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After decompression, chunk 0 is &lt;strong&gt;64 KB of binary type definitions&lt;/strong&gt;. Chunk 1 is &lt;strong&gt;20 MB of binary message data&lt;/strong&gt;, encoded against those definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 4 — Kiwi: a self-describing binary schema
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/evanw/kiwi" rel="noopener noreferrer"&gt;Kiwi&lt;/a&gt; is a binary serialization format by Evan Wallace (one of Figma's founders). It's like Protocol Buffers, but the schema definitions ship inside the same stream as the data — perfect for clients that get pushed schema updates.&lt;/p&gt;

&lt;p&gt;Decoded, chunk 0 contains &lt;strong&gt;568 type definitions&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="n"&gt;NODE_CHANGES&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;nodeChanges&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NodeChange&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;blobs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;NodeChange&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;guid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NodeType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;GUID&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sessionID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;localID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;Vector2&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;Transform&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;m00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m12&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;Paint&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaintType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="mi"&gt;562&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In TypeScript, decoding a &lt;code&gt;.fig&lt;/code&gt; is three lines once you've got the schema and data buffers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kiwi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeBinarySchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schemaBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 568 type defs&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kiwi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compileSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;             &lt;span class="c1"&gt;// generate decoder class&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// root: NODE_CHANGES&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;message&lt;/code&gt; is now a JavaScript object. Specifically, it has a &lt;code&gt;nodeChanges&lt;/code&gt; property — an array. And here's the next surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 5 — The "tree" is a flat array
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;message.nodeChanges&lt;/code&gt; is &lt;strong&gt;flat&lt;/strong&gt;. For my test fixture (a real, mid-sized Figma file): &lt;strong&gt;35,660 entries&lt;/strong&gt;. Not a tree. A list.&lt;/p&gt;

&lt;p&gt;Each entry has a &lt;code&gt;parentIndex.guid&lt;/code&gt; pointing at another node and a &lt;code&gt;parentIndex.position&lt;/code&gt; string. Reconstruction is two passes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pass 1: index every node by GUID&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nc&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodeChanges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allNodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;nc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Pass 2: link children → parents, then sort siblings&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;allNodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allNodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentGuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentGuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOCUMENT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Sort by parentIndex.position string (Figma's fractional indexing)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sortChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;sortChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;sortChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two oddities here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Why a flat array?&lt;/strong&gt; Because Figma's wire format is built for &lt;em&gt;streaming edits&lt;/em&gt;. Every change a designer makes is a &lt;code&gt;NodeChange&lt;/code&gt; record appended to a multiplayer message. The "saved file" is just the materialized end state of that stream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The sort key is a string.&lt;/strong&gt; &lt;code&gt;parentIndex.position&lt;/code&gt; looks like &lt;code&gt;'~)Wxs'&lt;/code&gt;, &lt;code&gt;'~)Wxs#'&lt;/code&gt;, &lt;code&gt;'~)Wxs#0'&lt;/code&gt;. That's &lt;a href="https://news.ycombinator.com/item?id=16635440" rel="noopener noreferrer"&gt;fractional indexing&lt;/a&gt; — a string-based ordering that lets two clients insert between any two siblings without conflict. It's a CRDT trick, and it shows up because Figma is collaborative-first.&lt;/p&gt;

&lt;p&gt;After sorting you finally have a tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOCUMENT
├── CANVAS "Page 1"
│   ├── FRAME "Header"
│   │   ├── TEXT "logo"
│   │   └── …
│   └── FRAME "Body"
│       └── …
└── CANVAS "Page 2"
    └── …
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shape every designer recognizes. Six pages, hundreds of frames, thousands of leaves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do with this
&lt;/h2&gt;

&lt;p&gt;Once you have the tree in memory, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Export to JSON&lt;/strong&gt; — &lt;code&gt;JSON.stringify(documentTree)&lt;/code&gt; and you're done. Caveat: &lt;code&gt;Uint8Array&lt;/code&gt; and &lt;code&gt;BigInt&lt;/code&gt; need special encoding (see &lt;a href="//./03-round-trip-safe-parser.md"&gt;article 3&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate Pencil &lt;code&gt;.pen&lt;/code&gt;&lt;/strong&gt; — Pencil.dev is an OSS Figma alternative; their &lt;code&gt;.pen&lt;/code&gt; format is JSON-shaped, and the conversion is mostly a 1:1 type mapping plus visibility composition. figma-reverse ships a &lt;code&gt;pen-export&lt;/code&gt; subcommand that does this in ~1.8 s for 35K nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a RAG index&lt;/strong&gt; — flatten to per-page JSON (~140 KB per page in the sample), embed each page or each frame, and you can ask questions like "where does this button get its primary color from?" with grounded retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit and re-encode&lt;/strong&gt; — change a value in JSON, then re-pack via the kiwi codec → ZIP STORE → and you have a new &lt;code&gt;.fig&lt;/code&gt; Figma will open. (&lt;a href="//./03-round-trip-safe-parser.md"&gt;Article 3&lt;/a&gt; has the details on what makes this round-trip-safe vs lossy.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/showjihyun/figmatojson.git
&lt;span class="nb"&gt;cd &lt;/span&gt;figmatojson &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install
&lt;/span&gt;npx tsx src/cli.ts extract docs/bvp.fig
&lt;span class="nb"&gt;cat &lt;/span&gt;output/bvp/verification_report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repo has 162 unit tests covering every stage and a 5-layer harness (&lt;a href="https://github.com/showjihyun/figmatojson/blob/main/docs/HARNESS.md" rel="noopener noreferrer"&gt;&lt;code&gt;docs/HARNESS.md&lt;/code&gt;&lt;/a&gt;) that proves the round-trip equality. There's a single-file bilingual developer guide at &lt;a href="https://github.com/showjihyun/figmatojson/blob/main/docs/dev-guide.html" rel="noopener noreferrer"&gt;&lt;code&gt;docs/dev-guide.html&lt;/code&gt;&lt;/a&gt; with eight mermaid diagrams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next
&lt;/h2&gt;

&lt;p&gt;This was the &lt;em&gt;what&lt;/em&gt;. &lt;a href="//./02-two-tiered-compression-mystery.md"&gt;Article 2 — The two-tiered compression mystery in Figma's &lt;code&gt;.fig&lt;/code&gt; format&lt;/a&gt; digs into &lt;em&gt;why&lt;/em&gt; one of the two chunks is zstd and the other is deflate. (Hint: it's not a mistake.)&lt;/p&gt;

&lt;p&gt;&lt;a href="//./03-round-trip-safe-parser.md"&gt;Article 3 — Building a round-trip-safe parser&lt;/a&gt; covers what it takes to go &lt;em&gt;back&lt;/em&gt;: from a JSON tree to a byte-identical &lt;code&gt;.fig&lt;/code&gt;, and what &lt;code&gt;Uint8Array&lt;/code&gt; / &lt;code&gt;NaN&lt;/code&gt; / &lt;code&gt;BigInt&lt;/code&gt; do to your &lt;code&gt;JSON.stringify&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;⭐ If this saved you a Figma API headache or a few hours of binary archaeology, &lt;a href="https://github.com/showjihyun/figmatojson" rel="noopener noreferrer"&gt;give the repo a star&lt;/a&gt;. It's how I'll know whether to keep covering edge cases.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>figma</category>
      <category>web</category>
    </item>
    <item>
      <title>"Memory Is the Unsolved Problem of AI Agents — Here's Why Everyone's Getting It Wrong"</title>
      <dc:creator>showjihyun</dc:creator>
      <pubDate>Wed, 15 Apr 2026 13:27:42 +0000</pubDate>
      <link>https://dev.to/jihyunsama/memory-is-the-unsolved-problem-of-ai-agents-heres-why-everyones-getting-it-wrong-4066</link>
      <guid>https://dev.to/jihyunsama/memory-is-the-unsolved-problem-of-ai-agents-heres-why-everyones-getting-it-wrong-4066</guid>
      <description>&lt;p&gt;Every AI coding agent on the market ships with amnesia by design.&lt;/p&gt;

&lt;p&gt;Claude Code starts each session with a blank context window. Cursor loses your architecture when you close the tab. The most capable models ever built can't recall what you said 10 minutes ago once the context fills up.&lt;/p&gt;

&lt;p&gt;The industry's answer? A markdown file loaded at startup.&lt;/p&gt;

&lt;p&gt;I spent the past month studying how agent memory actually works across the major systems — Claude Code's recently exposed internals, Mem0's vector store, Zep's temporal graph, Letta's OS-inspired tiers. The benchmarks are public. The architectures are documented. And every one of them is solving the wrong problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three paradigms, three failure modes
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2Fdp115k2xf19zrpf7lxmz.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.amazonaws.com%2Fuploads%2Farticles%2Fdp115k2xf19zrpf7lxmz.png" alt=" " width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. File-based: Claude Code
&lt;/h3&gt;

&lt;p&gt;Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt; from your project root at session start. The entire file goes into the context window. No vector DB. No embeddings. No search.&lt;/p&gt;

&lt;p&gt;Since v2.1.59, "auto memory" writes notes to &lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/memory/&lt;/code&gt; — things like build commands, debugging patterns, style preferences. Still markdown. Still loaded wholesale. &lt;code&gt;MEMORY.md&lt;/code&gt; has a hard cap at 200 lines. Anything beyond gets silently truncated.&lt;/p&gt;

&lt;p&gt;This is intentionally simple. And for small projects it works. The failure mode is obvious: &lt;strong&gt;no selective retrieval.&lt;/strong&gt; A 200-line cap on a monorepo with years of decisions means most knowledge is discarded. You either load everything or nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Vector search: Mem0 and Zep
&lt;/h3&gt;

&lt;p&gt;Mem0 (48K+ GitHub stars) decomposes interactions into facts and preferences, stores them as embeddings, and retrieves via semantic similarity. Zep builds a temporal knowledge graph — entities as nodes, relationships as edges, with timestamps tracking when facts were valid.&lt;/p&gt;

&lt;p&gt;The benchmark data is revealing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;LongMemEval&lt;/th&gt;
&lt;th&gt;Memory per conversation&lt;/th&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mem0&lt;/td&gt;
&lt;td&gt;49.0%&lt;/td&gt;
&lt;td&gt;~1,764 tokens&lt;/td&gt;
&lt;td&gt;Vector + Graph + KV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zep&lt;/td&gt;
&lt;td&gt;63.8%&lt;/td&gt;
&lt;td&gt;~600,000 tokens&lt;/td&gt;
&lt;td&gt;Temporal KG + Vector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Letta&lt;/td&gt;
&lt;td&gt;~83.2%&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Tiered (Core/Recall/Archival)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mem0 fails to recall the right information more than half the time. Zep is 15 points better — but uses 340x more memory per conversation. The Zep team disputes the Mem0 paper's configuration, claiming a corrected score of 75.1%. Even so, the tradeoff is steep.&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.amazonaws.com%2Fuploads%2Farticles%2Fqllnb8reghptvha4sxxt.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.amazonaws.com%2Fuploads%2Farticles%2Fqllnb8reghptvha4sxxt.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;![Scatter plot showing accuracy vs memory footprint: Mem0 at 49% with minimal tokens, Zep at 64% with 600K tokens, Letta at 83% in between]&lt;/p&gt;

&lt;p&gt;The deeper problem: both assume &lt;strong&gt;memory = retrieval.&lt;/strong&gt; Store everything, search when needed. But retrieval accuracy is only useful if you're retrieving at the right moment. Neither system has a mechanism for deciding &lt;em&gt;"does this agent need memory at all for this task?"&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. OS metaphor: Letta (MemGPT)
&lt;/h3&gt;

&lt;p&gt;Letta models memory like an operating system. The context window is RAM. External storage is disk. The agent pages information in and out via explicit tool calls: &lt;code&gt;core_memory_append&lt;/code&gt;, &lt;code&gt;archival_memory_search&lt;/code&gt;, &lt;code&gt;conversation_search&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On benchmarks, Letta leads the open-source field at ~83.2%. The agent makes nuanced decisions about what to keep in its working set. Architecturally, this is the most ambitious approach.&lt;/p&gt;

&lt;p&gt;The cost: &lt;strong&gt;every memory operation burns inference tokens.&lt;/strong&gt; The agent reasons about what to store, what to retrieve, how to organize — and that overhead compounds. Simple tasks get expensive because the agent spends a significant portion of its token budget on memory management rather than on the actual task.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real problem nobody's solving: forgetting
&lt;/h2&gt;

&lt;p&gt;Ebbinghaus mapped the human forgetting curve in 1885. We don't remember everything. We forget most things. What survives is what got reinforced — through repetition, emotional weight, or retrieval practice.&lt;/p&gt;

&lt;p&gt;AI agents have no forgetting strategy. They either hoard everything (vector stores growing without bound) or lose everything (session boundaries that wipe the slate). There's no equivalent of "this decision from 3 months ago is probably stale" or "this debugging pattern surfaced 4 times — promote it to permanent storage."&lt;/p&gt;

&lt;p&gt;![Chart comparing human forgetting curve with agent memory patterns: session-based drops to zero, vector stores grow unbounded, active curation follows a healthy decay]&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fdv84u85s0asy160ben65.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.amazonaws.com%2Fuploads%2Farticles%2Fdv84u85s0asy160ben65.png" alt=" " width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude Code's source, briefly exposed via an npm packaging error on March 31, contains a hint of the right direction. A module called &lt;code&gt;DreamTask&lt;/code&gt; runs during idle time — consolidating memories, merging duplicates, archiving stale entries. The codebase literally calls it "dreaming."&lt;/p&gt;

&lt;p&gt;But it's primitive. A separate module &lt;code&gt;memoryAge.ts&lt;/code&gt; calculates memory age and appends a text warning: &lt;em&gt;"This memory is 47 days old. Claims may be outdated."&lt;/em&gt; Just a string appended to the content. The system doesn't reduce the memory's weight, doesn't trigger re-verification, doesn't decay its relevance score. The warning exists. The system doesn't act on it.&lt;/p&gt;

&lt;p&gt;What's needed isn't better storage or retrieval. It's &lt;strong&gt;active curation&lt;/strong&gt; — continuous evaluation of what's worth keeping, what should decay, and what should be promoted based on actual usage patterns.&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.amazonaws.com%2Fuploads%2Farticles%2Fizdonzf2qpzjg3fnxnwj.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.amazonaws.com%2Fuploads%2Farticles%2Fizdonzf2qpzjg3fnxnwj.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it compounds: multi-agent memory
&lt;/h2&gt;

&lt;p&gt;Everything above assumes one agent, one memory. Now multiply.&lt;/p&gt;

&lt;p&gt;In multi-agent setups — Claude Code's subagents, CrewAI's role-based teams, AutoGen's collaborative agents — memory becomes a coordination problem.&lt;/p&gt;

&lt;p&gt;A concrete scenario: Agent A decides to use the repository pattern for data access. Agent B, working on a separate module, implements raw SQL queries. Agent C reviews both and sees contradictory patterns. None of them know about the others' decisions because each agent's memory is private.&lt;/p&gt;

&lt;p&gt;Claude Code's current solution: all agents read the same &lt;code&gt;CLAUDE.md&lt;/code&gt;. When one writes, others pick it up on their next file read. This handles 2-3 agents. At 20+ agents making concurrent decisions, you get write conflicts, stale reads, and contradictory entries that nobody reconciles.&lt;/p&gt;

&lt;p&gt;Research in agent-based social simulation — Stanford's Generative Agents, Tsinghua's AgentSociety, CAMEL-AI's OASIS — has been hitting these problems at scale for years. When hundreds of agents interact over time, questions emerge that single-agent memory never encounters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Social reinforcement&lt;/strong&gt; — If 50 agents independently store the same fact, is it more durable or just more popular?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory conflict&lt;/strong&gt; — When two agents hold contradictory memories of the same event, what resolves the disagreement?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Information decay&lt;/strong&gt; — When does a group "forget" something? When every individual forgets, or when it stops being referenced?&lt;/p&gt;

&lt;p&gt;These aren't academic curiosities. They're the exact problems that any production multi-agent system will face once it scales past a handful of agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  A hypothesis, not a solution
&lt;/h2&gt;

&lt;p&gt;I don't have a working system to show. What I have is a hypothesis after studying all of these:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory in multi-agent systems is a coordination problem, not a storage problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Claude Code is right about simplicity. Mem0 is right about searchability. Letta is right about agent autonomy. None of them address the coordination dimension — the fact that agents need to share, negotiate, and reconcile memories, not just store and retrieve them privately.&lt;/p&gt;

&lt;p&gt;The components that seem necessary:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tiered personal memory&lt;/strong&gt; — episodic (what happened) + semantic (what I know), with explicit promotion and demotion rules between tiers. Letta has the right shape here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared state protocol&lt;/strong&gt; — not a shared file, but a structured mechanism for propagating decisions across agents. Something closer to a distributed log than a wiki.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active forgetting&lt;/strong&gt; — relevance decay weighted by access frequency, age, and cross-agent reinforcement. Things that multiple agents reference stay. Things only one agent cares about fade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conflict as data&lt;/strong&gt; — when memories contradict, maintain the disagreement as a first-class object rather than silently picking a winner.&lt;/p&gt;

&lt;p&gt;The Meta-Harness paper (Stanford &amp;amp; MIT, March 2026) demonstrated that harness design alone produces a 6x performance gap on the same underlying model. Memory is arguably the highest-leverage harness component that remains wide open. The current state of the art is either "load a markdown file" or "search a vector store and hope for 49% accuracy."&lt;/p&gt;

&lt;p&gt;There's a lot of room to do better. The agent that wins won't be the one that remembers the most. It'll be the one that knows what to forget.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your setup for agent memory? If you've found something that actually works across sessions — or something that failed spectacularly — I'd like to hear about it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Prophet: We Built a Wind Tunnel for Marketing Campaigns</title>
      <dc:creator>showjihyun</dc:creator>
      <pubDate>Mon, 13 Apr 2026 16:11:29 +0000</pubDate>
      <link>https://dev.to/jihyunsama/prophet-we-built-a-wind-tunnel-for-marketing-campaigns-25id</link>
      <guid>https://dev.to/jihyunsama/prophet-we-built-a-wind-tunnel-for-marketing-campaigns-25id</guid>
      <description>&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%2Fimg.shields.io%2Fbadge%2FLicense-MIT-7F77DD%3Fstyle%3Dfor-the-badge" 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%2Fimg.shields.io%2Fbadge%2FLicense-MIT-7F77DD%3Fstyle%3Dfor-the-badge" width="124" height="28"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2FAgents-10%252C000-1D9E75%3Fstyle%3Dfor-the-badge" 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%2Fimg.shields.io%2Fbadge%2FAgents-10%252C000-1D9E75%3Fstyle%3Dfor-the-badge" width="143" height="28"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2FCost-Under%2520%25245-D85A30%3Fstyle%3Dfor-the-badge" 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%2Fimg.shields.io%2Fbadge%2FCost-Under%2520%25245-D85A30%3Fstyle%3Dfor-the-badge" width="147" height="28"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Focus groups lie. A/B tests are too late. Brand-lift studies take 6 weeks, cost $50K, and tell you nothing about &lt;em&gt;why&lt;/em&gt; a message failed.&lt;/p&gt;

&lt;p&gt;So we built &lt;strong&gt;Prophet&lt;/strong&gt; — an open-source platform that simulates how your message spreads through &lt;strong&gt;10,000 AI agents&lt;/strong&gt; organized into the communities you actually care about.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Think of it as a wind tunnel, but for campaigns instead of airplanes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔄 The Core Loop
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;⚙️&lt;br&gt;&lt;b&gt;1. Set up&lt;/b&gt;&lt;br&gt;Message · communities · dials
&lt;/td&gt;
    &lt;td&gt;▶️&lt;br&gt;&lt;b&gt;2. Run&lt;/b&gt;&lt;br&gt;10K agents · real-time 3D graph
&lt;/td&gt;
    &lt;td&gt;⚡&lt;br&gt;&lt;b&gt;3. Intervene&lt;/b&gt;&lt;br&gt;Pause · inject crisis · replay
&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;📊&lt;br&gt;&lt;b&gt;4. Analyze&lt;/b&gt;&lt;br&gt;Adoption curves · sentiment
&lt;/td&gt;
    &lt;td&gt;🔍&lt;br&gt;&lt;b&gt;5. Drill in&lt;/b&gt;&lt;br&gt;Read actual conversation threads
&lt;/td&gt;
    &lt;td&gt;⚖️&lt;br&gt;&lt;b&gt;6. Compare&lt;/b&gt;&lt;br&gt;Clone · change variable · diff
&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧠 What's Under the Hood
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6-layer cognitive stack: &lt;code&gt;perception → memory → emotion → cognition → decision → influence&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hybrid Watts-Strogatz + Barabási-Albert with validated clustering &amp;amp; modularity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Opinion Dynamics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deffuant bounded-confidence model for endogenous polarization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Emergent Detectors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Viral cascade · Slow adoption · Polarization · Collapse · Echo chamber&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3-tier LLM inference — full 10K-agent run under &lt;strong&gt;$5&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uncertainty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monte Carlo sweep with &lt;strong&gt;P5 / P50 / P95&lt;/strong&gt; quantification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🚀 Get Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/showjihyun/Prophet
&lt;span class="nb"&gt;cd &lt;/span&gt;Prophet
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# No API keys required to start&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✅ Reproducibility Guarantee
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Every claim in the README has per-step JSON you can audit.&lt;br&gt;&lt;br&gt;
Every equation traces back to a published paper.&lt;br&gt;&lt;br&gt;
Everything is reproducible. Runs on a laptop.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🤝 We're Looking for Contributors
&lt;/h2&gt;

&lt;p&gt;Especially:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐛 Bug reports&lt;/li&gt;
&lt;li&gt;🧪 Test cases &amp;amp; validation studies&lt;/li&gt;
&lt;li&gt;💡 New use cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're simulating something interesting, &lt;strong&gt;we want to hear about it.&lt;/strong&gt;&lt;/p&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/showjihyun" rel="noopener noreferrer"&gt;
        showjihyun
      &lt;/a&gt; / &lt;a href="https://github.com/showjihyun/Prophet" rel="noopener noreferrer"&gt;
        Prophet
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Prophet
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/showjihyun/Prophet/docs/assets/hero.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fshowjihyun%2FProphet%2FHEAD%2Fdocs%2Fassets%2Fhero.gif" alt="Prophet — 3D social simulation spreading in real time" width="720"&gt;&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔮 Prophet&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;The wind tunnel for marketing campaigns&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Test your campaign on 10,000 AI agents before you spend a dollar on the launch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/showjihyun/prophet/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/40178740d6ff953e5f1b9ae567b8ffb83352c15d810bbb058a1e16374fb657d4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f73686f776a696879756e2f70726f706865743f7374796c653d666f722d7468652d6261646765266c6f676f3d67697468756226636f6c6f723d663563353138" alt="GitHub stars"&gt;&lt;/a&gt;
&lt;a href="https://github.com/showjihyun/Prophet/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2434e2ec34c11ec786ae7bc1520aad553d56162407000352a960807330358957/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f73686f776a696879756e2f70726f706865743f7374796c653d666f722d7468652d626164676526636f6c6f723d626c7565" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://github.com/showjihyun/Prophet/CHANGELOG.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6f9bece63651626508d087448e3eb7d5f7dabf32000ad331eff9cc6fa37ab998/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d302e312e312e302d3861326265323f7374796c653d666f722d7468652d6261646765" alt="Version"&gt;&lt;/a&gt;
&lt;a href="https://github.com/showjihyun/prophet/commits" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/92c4d53e7c89d4e80564a96e1f891ded958161abc3ca3903ded3df93c4ac836c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f73686f776a696879756e2f70726f706865743f7374796c653d666f722d7468652d626164676526636f6c6f723d323861373435" alt="Last commit"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.python.org/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d79b3db457806421e2eb09d2d98c84af94dfb4141078f3e045c9c007a285bed7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e31322b2d3337373661623f6c6f676f3d707974686f6e266c6f676f436f6c6f723d7768697465" alt="Python"&gt;&lt;/a&gt;
&lt;a href="https://fastapi.tiangolo.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/990fc4a3f2c07019530e00ff2beb0c9041b1f76af67cd8ea9beb2b8c310ae2da/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f466173744150492d3030393638383f6c6f676f3d66617374617069266c6f676f436f6c6f723d7768697465" alt="FastAPI"&gt;&lt;/a&gt;
&lt;a href="https://react.dev/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a9084302dcd151eca34083f7b8bb1ec8c11a946e714c64a8c41789bbb4fb2cb8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742d31382d3631646166623f6c6f676f3d7265616374266c6f676f436f6c6f723d7768697465" alt="React"&gt;&lt;/a&gt;
&lt;a href="https://www.typescriptlang.org/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b9873a28dbd9710d7617388ea3499246b92271b2d3804f0744c11680f5ce6f6d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d352d3331373863363f6c6f676f3d74797065736372697074266c6f676f436f6c6f723d7768697465" alt="TypeScript"&gt;&lt;/a&gt;
&lt;a href="https://www.docker.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4a9016d5947e53ebf36fb131b44cc58254be69bbd1f4ccbd9b9462a2867a6fcb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f636b65722d72656164792d3234393665643f6c6f676f3d646f636b6572266c6f676f436f6c6f723d7768697465" alt="Docker"&gt;&lt;/a&gt;
&lt;a href=""&gt;&lt;img src="https://camo.githubusercontent.com/7e1c0abc09cb1c8b58e1e243f94d3ae0a3ce949dd702e4f97b385703165d9836/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d313033315f42455f2537435f3733365f46452d627269676874677265656e" alt="Tests"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/showjihyun/Prophet#-quick-start" rel="noopener noreferrer"&gt;🚀 Quick Start&lt;/a&gt;&lt;/strong&gt; ·
&lt;strong&gt;&lt;a href="https://github.com/showjihyun/Prophet#-features" rel="noopener noreferrer"&gt;✨ Features&lt;/a&gt;&lt;/strong&gt; ·
&lt;strong&gt;&lt;a href="https://github.com/showjihyun/Prophet#-use-cases" rel="noopener noreferrer"&gt;🎯 Use Cases&lt;/a&gt;&lt;/strong&gt; ·
&lt;strong&gt;&lt;a href="https://github.com/showjihyun/Prophet#-how-prophet-compares" rel="noopener noreferrer"&gt;📊 Comparison&lt;/a&gt;&lt;/strong&gt; ·
&lt;strong&gt;&lt;a href="https://github.com/showjihyun/Prophet#-documentation" rel="noopener noreferrer"&gt;📖 Docs&lt;/a&gt;&lt;/strong&gt; ·
&lt;strong&gt;&lt;a href="https://github.com/showjihyun/Prophet#-contributing" rel="noopener noreferrer"&gt;🤝 Contributing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/showjihyun/prophet.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; prophet &lt;span class="pl-k"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose up -d
open http://localhost:5173&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;That's it.&lt;/strong&gt; 5 minutes from clone to your first simulation. No API keys required to start — Prophet runs fully locally on a laptop.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;💡 Why Prophet?&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Focus groups lie — 10 humans in a room cannot tell you how a message spreads through a community.
A/B tests are too late — by the time you have data, you're already paying for the launch.
Brand-lift studies take 6 weeks, cost $50K, and tell you nothing about &lt;em&gt;why&lt;/em&gt; a message failed.&lt;/p&gt;
&lt;p&gt;Every discipline that ships things at scale — &lt;strong&gt;aerospace, civil engineering, software&lt;/strong&gt; — gets to simulate…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/showjihyun/Prophet" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>opensource</category>
      <category>ai</category>
      <category>marketing</category>
      <category>simulation</category>
    </item>
    <item>
      <title>Hi Guys. First Greetings</title>
      <dc:creator>showjihyun</dc:creator>
      <pubDate>Sat, 04 Apr 2026 13:13:55 +0000</pubDate>
      <link>https://dev.to/jihyunsama/hi-guys-first-greetings-m2m</link>
      <guid>https://dev.to/jihyunsama/hi-guys-first-greetings-m2m</guid>
      <description>&lt;p&gt;Hi~ Nice to Meet you guys!!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
