<?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>Forem: Shimo</title>
    <description>The latest articles on Forem by Shimo (@shimo4228).</description>
    <link>https://forem.com/shimo4228</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%2F3772086%2F7b113abd-2f92-4728-993c-602762a15288.png</url>
      <title>Forem: Shimo</title>
      <link>https://forem.com/shimo4228</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shimo4228"/>
    <language>en</language>
    <item>
      <title>Building Zed as an Observation Window for Claude Code — Japanese Typography with IBM Plex</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Thu, 16 Apr 2026 00:00:04 +0000</pubDate>
      <link>https://forem.com/shimo4228/building-zed-as-an-observation-window-for-claude-code-japanese-typography-with-ibm-plex-6l9</link>
      <guid>https://forem.com/shimo4228/building-zed-as-an-observation-window-for-claude-code-japanese-typography-with-ibm-plex-6l9</guid>
      <description>&lt;h1&gt;
  
  
  Making Zed an Observation Window: A Design Record of Fonts and Cognitive Resources
&lt;/h1&gt;

&lt;p&gt;In &lt;a href="https://zenn.dev/shimo4228/articles/cursor-to-zed-migration" rel="noopener noreferrer"&gt;the previous article&lt;/a&gt;, I wrote about migrating from Cursor to Zed. The gist: Cursor felt like a heavy container for lightweight content, so I switched to Zed and settled on a development workflow centered around the Claude Code CLI.&lt;/p&gt;

&lt;p&gt;Two months later. Nothing has gone wrong with Zed.&lt;/p&gt;

&lt;p&gt;Nothing wrong, but three things kept nagging at me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Opening a file somehow changes its formatting&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The left dock is cluttered with panels I never use&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Japanese fonts have an unnameable wrongness to them&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article documents fixing all three in a single day. But it is not just a settings walkthrough. Tracing the "why" behind each setting led to &lt;strong&gt;three design principles&lt;/strong&gt; and an &lt;strong&gt;unexpected rediscovery&lt;/strong&gt;.&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%2F3lqqbe7xftft8ec8zo05.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%2F3lqqbe7xftft8ec8zo05.png" alt="Before: Zed initial state — SF Mono + unorganized dock" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before: Zed at session start. SF Mono, unorganized dock, stock UI.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Defining the Role: Observation Window
&lt;/h2&gt;

&lt;p&gt;First, the premise. I develop with the Claude Code CLI. Writing code, running tests, git operations — all handled in the terminal by Claude Code.&lt;/p&gt;

&lt;p&gt;So what is Zed doing?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zed is an observation window.&lt;/strong&gt; A window where I pull information that the CLI pushes — file changes, test results, commit logs — to review it as a human. Not a tool for writing. A tool for reading.&lt;/p&gt;

&lt;p&gt;Once you recognize this role, the requirements for an editor shift.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;As a writing tool&lt;/th&gt;
&lt;th&gt;As an observation window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code completion&lt;/td&gt;
&lt;td&gt;Essential&lt;/td&gt;
&lt;td&gt;Unnecessary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formatter&lt;/td&gt;
&lt;td&gt;Essential&lt;/td&gt;
&lt;td&gt;Gets in the way (CLI handles it)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugger&lt;/td&gt;
&lt;td&gt;Essential&lt;/td&gt;
&lt;td&gt;Unnecessary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File tree&lt;/td&gt;
&lt;td&gt;Essential&lt;/td&gt;
&lt;td&gt;Essential (locating changes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git diff view&lt;/td&gt;
&lt;td&gt;Nice to have&lt;/td&gt;
&lt;td&gt;Essential (seeing what changed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Font quality&lt;/td&gt;
&lt;td&gt;Nice to have&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Essential&lt;/strong&gt; (reading for long periods)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The priorities for "writing" and "reading" are entirely different. With this premise, I worked through the three nagging issues in order.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cutting Dual Control — format_on_save: off
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Symptom
&lt;/h3&gt;

&lt;p&gt;"Opening a file somehow changes the formatting."&lt;/p&gt;

&lt;p&gt;At first I thought I was imagining it. But every time I opened a file, indentation shifted subtly. Diffs appeared. Code written by Claude Code produced diffs just from being opened.&lt;/p&gt;
&lt;h3&gt;
  
  
  Diagnosis
&lt;/h3&gt;

&lt;p&gt;The cause was &lt;strong&gt;dual formatting&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Claude Code's PostToolUse hook formats with &lt;code&gt;black&lt;/code&gt; / &lt;code&gt;ruff&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Zed's &lt;code&gt;autosave: on_focus_change&lt;/code&gt; + &lt;code&gt;format_on_save: on&lt;/code&gt; reformats via Zed's LSP formatter&lt;/li&gt;
&lt;li&gt;The two formatters had slightly different rule configurations, producing diffs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, &lt;strong&gt;two formatters were alternately rewriting the same file under different rules&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Claude hooks own formatting, so turn this off&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"format_on_save"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;One line. In &lt;a href="https://zenn.dev/shimo4228/articles/cursor-to-zed-migration" rel="noopener noreferrer"&gt;the previous article&lt;/a&gt;, I actually had &lt;code&gt;format_on_save: on&lt;/code&gt;. Right after the migration, Claude Code's hook system was not fully set up yet, so having Zed handle formatting made sense. But once PostToolUse hooks with &lt;code&gt;black&lt;/code&gt; / &lt;code&gt;ruff&lt;/code&gt; were in full operation, dual formatting became a problem. When the environment changes, settings change with it.&lt;/p&gt;

&lt;p&gt;This single line contains a principle important for observation windows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle 1: Prioritize the primary channel; do not replicate in the secondary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the Claude Code CLI is the primary channel carrying information, Zed (secondary) should not duplicate the same function. Formatting responsibility is consolidated in the CLI. Duplicating information does not improve observability — it wastes cognitive resources.&lt;/p&gt;
&lt;/blockquote&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%2Fo1amtuyzbk4t0t1bp8qr.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%2Fo1amtuyzbk4t0t1bp8qr.png" alt="Settings editing — adjusting format_on_save and dock" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The editing process. Working through settings.json in conversation.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Trimming Visual Noise — The button: false Approach
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Left Dock Problem
&lt;/h3&gt;

&lt;p&gt;Before cleanup, the left dock had Terminal, Agent Panel, Debugger, Outline, and Git — panels I never use. As an observation window, I only use Terminal, yet icons for everything else stay in view.&lt;/p&gt;

&lt;p&gt;Unused things in your field of vision are noise.&lt;/p&gt;
&lt;h3&gt;
  
  
  button: false as a Solution
&lt;/h3&gt;

&lt;p&gt;Zed has a &lt;code&gt;button: false&lt;/code&gt; setting. It &lt;strong&gt;hides just the button (icon) while keeping the feature alive&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Hide buttons for unused panels without killing functionality&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"debugger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"outline_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"collaboration_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"diagnostics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"notification_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could use &lt;code&gt;enabled: false&lt;/code&gt; to kill the feature entirely, but I deliberately did not. The Agent Panel may be needed for ACP (Agent Control Protocol) integration. &lt;strong&gt;Do not touch the function; touch the visuals.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Deprecated Key Trap
&lt;/h3&gt;

&lt;p&gt;During this work, Zed warned: "Your settings file uses deprecated settings."&lt;/p&gt;

&lt;p&gt;Two causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;collab_panel&lt;/code&gt; — correct key is &lt;code&gt;collaboration_panel&lt;/code&gt; (renamed)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chat_panel&lt;/code&gt; — no longer exists as an independent panel (merged into collaboration)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Old keys still work but produce persistent warnings. I updated to the canonical key names.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Notifications Connect Misconception
&lt;/h3&gt;

&lt;p&gt;The right dock's Notifications panel had a "Connect" button. I expected it to show GitHub PR reviews, CI lint errors, deploy failures — all within Zed.&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%2Fh39clgx05pmffwh4qgqb.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%2Fh39clgx05pmffwh4qgqb.png" alt="Right-side Notifications panel with Connect button" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;"Connect to view notifications." — I assumed GitHub integration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reality was different.&lt;/strong&gt; Checking Zed's official documentation, this "Connect" is for &lt;strong&gt;Zed's own collaboration feature&lt;/strong&gt; (Zed Channels). It uses GitHub OAuth but the scope is &lt;code&gt;read:user&lt;/code&gt; only — no repository access whatsoever.&lt;/p&gt;

&lt;p&gt;There is no official Zed feature for integrating GitHub repository notifications.&lt;/p&gt;

&lt;p&gt;I did not connect. The expected feature was absent, and the available feature (collaboration) was one I would not use. Zero value on both sides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: Do not infer functionality from a UI label alone ("Connect").&lt;/strong&gt; Especially for connections involving authentication, verify what you are actually connecting to before clicking.&lt;/p&gt;
&lt;h3&gt;
  
  
  The "Guessed Value That Did Not Work" Incident
&lt;/h3&gt;

&lt;p&gt;Wanting to empty the status bar, I wrote &lt;code&gt;active_encoding_button&lt;/code&gt; as &lt;code&gt;"never"&lt;/code&gt; — thinking of it like CSS &lt;code&gt;display: none&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Invalid user settings file:&lt;/strong&gt; unknown variant 'never', expected one of 'enabled', 'disabled', 'non_utf8'&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zed's settings file uses &lt;strong&gt;type-safe JSONC with schema validation&lt;/strong&gt;. Invalid values do not fail silently — they return an immediate error. Better yet, the error message lists the valid values.&lt;/p&gt;

&lt;p&gt;The same class of mistake happened three times during this session:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;collab_panel&lt;/code&gt; (deprecated; correct: &lt;code&gt;collaboration_panel&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;font-moralerspace-nf&lt;/code&gt; (discontinued; correct: &lt;code&gt;font-moralerspace&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"never"&lt;/code&gt; (does not exist; correct: &lt;code&gt;"disabled"&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The common pattern: &lt;strong&gt;guessing a plausible-sounding name&lt;/strong&gt;. Verify before guessing. Settings values come from official sources, not intuition.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Decision to Keep LSP
&lt;/h3&gt;

&lt;p&gt;For an observation window, the core features of Language Servers (Pyright, tsserver, etc.) — autocomplete, hover, diagnostics — go entirely unused. Why not turn them off?&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%2Fykhwya5niylerrkbt69a.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%2Fykhwya5niylerrkbt69a.png" alt="LSP diagnostics panel showing empty state" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;"No problems in workspace" — LSP is running but has nothing to say.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Conclusion: &lt;strong&gt;Keep them.&lt;/strong&gt; Three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On Apple Silicon with ample memory, the perceived overhead from LSP is near zero&lt;/li&gt;
&lt;li&gt;JSON schema hints (completions when editing settings.json) turned out to be surprisingly useful — proven during this very session&lt;/li&gt;
&lt;li&gt;Switching cost (adding config + losing features) &amp;gt; cognitive resources saved (nearly zero)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;"Could be removed" and "should be removed" are different judgments.&lt;/strong&gt; If there is no actual harm to cognitive resources, leaving things alone is also a form of optimization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle 2: Do not touch the function; touch the visuals (hide, don't delete).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hide unused features visually with &lt;code&gt;button: false&lt;/code&gt;. Killing features risks side effects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 3: Trim visible noise; tolerate invisible background processes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dock icons and status bar items consume cognitive resources, so trim them. Background processes like LSP stay out of sight, so tolerate them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The Long Font Journey — From SF Mono to PlemolJP Console NF
&lt;/h2&gt;

&lt;p&gt;Here is where the real story begins. Of the three nagging issues, fonts consumed the most time.&lt;/p&gt;
&lt;h3&gt;
  
  
  SF Mono Has No Japanese Glyphs
&lt;/h3&gt;

&lt;p&gt;I was using SF Mono as the default (technically macOS's default). Japanese "just appeared." But &lt;strong&gt;SF Mono contains only Latin characters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So where was the Japanese coming from?&lt;/p&gt;

&lt;p&gt;The answer was &lt;strong&gt;CJK fallback&lt;/strong&gt;. macOS's font rendering detected that SF Mono lacked Japanese glyphs and drew them using system fallback fonts like Hiragino Sans or PingFang SC.&lt;/p&gt;

&lt;p&gt;The problem was metrics. The fallback font's baseline, character width, and line spacing were subtly misaligned with SF Mono, producing a &lt;strong&gt;"something feels off" jitteriness&lt;/strong&gt;. Hard to articulate, but definitely there.&lt;/p&gt;

&lt;p&gt;The solution was clear: a &lt;strong&gt;CJK-unified font&lt;/strong&gt; — one where Latin and Japanese glyphs coexist in the same font file with metrics unified from the start.&lt;/p&gt;
&lt;h3&gt;
  
  
  Moralerspace Argon: Too Bold
&lt;/h3&gt;

&lt;p&gt;The first candidate was &lt;a href="https://github.com/yuru7/moralerspace" rel="noopener noreferrer"&gt;Moralerspace&lt;/a&gt;. A CJK-unified font by the same author as UDEV Gothic (yuru7), synthesizing Monaspace + IBM Plex Sans JP. I chose the Argon variant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; font-moralerspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;code&gt;font-moralerspace-nf&lt;/code&gt; (the Nerd Font-only cask) was discontinued on 2025-07-29. Nerd Fonts are shifting from "patched font files" to a "symbols overlay" approach, and the base &lt;code&gt;font-moralerspace&lt;/code&gt; is the successor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Result: &lt;strong&gt;It felt bold.&lt;/strong&gt; Even at Regular weight, the strokes had too much presence. Modern and refined design, but for an observation window meant for long reading sessions, I wanted something more restrained.&lt;/p&gt;

&lt;h3&gt;
  
  
  PlemolJP Console NF: Light Was Too Thin, Regular Landed
&lt;/h3&gt;

&lt;p&gt;Next up was &lt;a href="https://github.com/yuru7/PlemolJP" rel="noopener noreferrer"&gt;PlemolJP&lt;/a&gt;. A CJK-unified font synthesizing IBM Plex Mono + IBM Plex Sans JP. I chose the Console NF variant (monospace + Nerd Font symbols).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; font-plemol-jp-nf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First I tried Light (weight 300). &lt;strong&gt;Too thin.&lt;/strong&gt; Characters dissolved into the background, requiring subtle effort to read.&lt;/p&gt;

&lt;p&gt;Regular (weight 400). &lt;strong&gt;Landed.&lt;/strong&gt; Classic strokes inherited from Plex Mono — less assertive than Moralerspace, more present than Light. The sweet spot.&lt;/p&gt;

&lt;h4&gt;
  
  
  Getting the Exact Font Family Name
&lt;/h4&gt;

&lt;p&gt;Right after &lt;code&gt;brew install&lt;/code&gt;, I tried to write the font name in Zed's settings.json. &lt;strong&gt;The exact family name was unclear.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If macOS's Spotlight index has not updated, &lt;code&gt;mdls&lt;/code&gt; returns empty. The reliable method is &lt;code&gt;system_profiler&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;system_profiler SPFontsDataType 2&amp;gt;/dev/null | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 1 &lt;span class="nt"&gt;-A&lt;/span&gt; 4 &lt;span class="s2"&gt;"PlemolJPConsoleNF-Regular:"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;

&lt;span class="c"&gt;# Result:&lt;/span&gt;
&lt;span class="c"&gt;#   Full Name: PlemolJP Console NF Regular&lt;/span&gt;
&lt;span class="c"&gt;#   Family: PlemolJP Console NF&lt;/span&gt;
&lt;span class="c"&gt;#   Style: レギュラー&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Family: PlemolJP Console NF&lt;/code&gt; — this is the value for settings.json.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unifying All Layers Failed: The Pain of Reading Prose in Monospace
&lt;/h3&gt;

&lt;p&gt;With PlemolJP Console NF, the Buffer and Terminal felt great. Riding that momentum, I &lt;strong&gt;applied the same font to the UI&lt;/strong&gt;. All layers unified — beautiful.&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%2Fuz48tu0417ba9k503iri.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%2Fuz48tu0417ba9k503iri.png" alt="UI with PlemolJP showing prose monospace problem" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The monospace-UI failure. Japanese prose forced into a grid feels wrong.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Something was off.&lt;/strong&gt; Panel labels, Insight block text, Japanese segments in file paths — they all looked like "a sequence of square boxes."&lt;/p&gt;

&lt;p&gt;Thinking about the cause, it clicked. &lt;strong&gt;Monospace fonts assume a fixed-width grid.&lt;/strong&gt; ASCII characters look natural aligned to a grid, but Japanese prose is naturally read in proportional spacing (varying width per character).&lt;/p&gt;

&lt;p&gt;This connects to the history of type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monospace&lt;/strong&gt;: Originated from typewriters. Each physical type slug had to be the same width or the carriage would not advance. Code culture adopted the grid as standard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proportional&lt;/strong&gt;: Since movable type printing, prose has been set in proportional spacing. Varying character widths create a smoother reading flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UI is primarily prose-like labels. Applying monospace to prose was like typesetting a novel on a typewriter.&lt;/p&gt;
&lt;h3&gt;
  
  
  IBM Plex Sans JP: Returning Just the UI to Proportional
&lt;/h3&gt;

&lt;p&gt;Revert the UI to proportional. But instead of reverting to the system default, I chose from &lt;strong&gt;within the same IBM Plex family as PlemolJP&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; font-ibm-plex-sans-jp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IBM Plex Sans JP is the Japanese extension of IBM Plex Sans — PlemolJP's "proportional sibling." The design language is unified, so there is no jarring disconnect between Buffer (monospace) and UI (proportional).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c1"&gt;// UI: proportional (suited for prose)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui_font_family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IBM Plex Sans JP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui_font_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;16.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui_font_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="c1"&gt;// Buffer: monospace (suited for code)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buffer_font_family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PlemolJP Console NF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buffer_font_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buffer_font_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="c1"&gt;// Terminal: monospace (suited for CLI output)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"terminal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"font_family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PlemolJP Console NF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"font_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"font_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"line_height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"comfortable"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of the three layers gets the appropriate font type.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Font Type&lt;/th&gt;
&lt;th&gt;Font&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;Panel labels, menus&lt;/td&gt;
&lt;td&gt;Proportional&lt;/td&gt;
&lt;td&gt;IBM Plex Sans JP&lt;/td&gt;
&lt;td&gt;16pt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buffer&lt;/td&gt;
&lt;td&gt;Code display&lt;/td&gt;
&lt;td&gt;Monospace&lt;/td&gt;
&lt;td&gt;PlemolJP Console NF&lt;/td&gt;
&lt;td&gt;15pt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;CLI output&lt;/td&gt;
&lt;td&gt;Monospace&lt;/td&gt;
&lt;td&gt;PlemolJP Console NF&lt;/td&gt;
&lt;td&gt;14pt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Buffer is for "reading"; Terminal is for "scanning." Font size steps down by 1pt according to information density.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rediscovery — Zed's Default Was IBM Plex All Along
&lt;/h2&gt;

&lt;p&gt;I was satisfied with the setup when I idly looked up Zed's default font.&lt;/p&gt;

&lt;p&gt;Zed uses aliases called &lt;code&gt;.ZedSans&lt;/code&gt; and &lt;code&gt;.ZedMono&lt;/code&gt;. Their underlying fonts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.ZedSans&lt;/code&gt;&lt;/strong&gt; = &lt;strong&gt;IBM Plex Sans&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.ZedMono&lt;/code&gt;&lt;/strong&gt; = &lt;strong&gt;Lilex&lt;/strong&gt; (a fork of IBM Plex Mono with ligatures)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zed's default font is the &lt;strong&gt;IBM Plex family&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In other words, what happened was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SF Mono -&amp;gt; Moralerspace (did not fit) -&amp;gt; landed on PlemolJP (IBM Plex Mono-based) -&amp;gt; applied IBM Plex Sans JP to UI -&amp;gt; ended up building a Japanese-optimized version of Zed's own defaults&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Arriving independently at the IBM Plex family was not a coincidence. The Zed development team chose IBM Plex too. More accurately, I naturally arrived at this point along the extension of Zed's design philosophy.&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%2Frane66bp3vpeyzst8i1v.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%2Frane66bp3vpeyzst8i1v.png" alt="Zed final UI — IBM Plex Sans JP + PlemolJP Console NF" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After: Left dock is Terminal only, Buffer uses PlemolJP Console NF, UI uses IBM Plex Sans JP. File tree on the right dock.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing — settings.json Is a Record of Design Decisions
&lt;/h2&gt;

&lt;p&gt;Today's work produced a 140-line settings.json. Each item maps to one of the three principles.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Corresponding Settings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary channel first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;format_on_save: "off"&lt;/code&gt;, &lt;code&gt;edit_predictions.provider: "none"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Do not touch the function; touch the visuals&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Various &lt;code&gt;button: false&lt;/code&gt;, &lt;code&gt;agent.enabled: true&lt;/code&gt; (kept alive but hidden)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trim visible noise; tolerate invisible background&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All &lt;code&gt;status_bar&lt;/code&gt; items off, &lt;code&gt;show_whitespaces: "none"&lt;/code&gt;, LSP retained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This settings.json is not a list of preferences. &lt;strong&gt;It is a record of design decisions for protecting cognitive resources.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Changing" and "optimizing" are different things. Understanding Zed's default design, then adapting it to my usage pattern (observation window) and environment (Japanese mixed content). Not breaking it — localizing it.&lt;/p&gt;

&lt;p&gt;In the previous article, I wrote "I switched from Cursor to Zed." Now I can say it more precisely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I built Zed as an observation window for Claude Code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Full settings.json&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"diagnostics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"share_on_join"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mute_on_join"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"notification_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pane_split_direction_vertical"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"active_pane_modifiers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"inactive_opacity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"use_system_window_tabs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bottom_dock_layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contained"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tabs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"file_icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"git_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tab_bar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_pinned_tabs_in_separate_row"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_nav_history_buttons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title_bar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_user_picture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_sign_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_project_items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_branch_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status_bar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"active_encoding_button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_active_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"active_language_button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"cursor_position_button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"agent_servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"claude-acp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"registry"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"debugger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"icon_theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Zed (Default)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"edit_predictions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"trust_all_worktrees"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"light"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tokyo Night Light"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tokyo Night"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vim_mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"soft_wrap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor_width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ui_font_family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IBM Plex Sans JP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ui_font_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;16.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ui_font_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"buffer_font_family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PlemolJP Console NF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"buffer_font_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"buffer_font_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"autosave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"on_focus_change"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"show_whitespaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"terminal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"flexible"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_count_badge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"font_family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PlemolJP Console NF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"font_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"font_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"line_height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"comfortable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"working_directory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"current_project_directory"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tab_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format_on_save"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"indent_guides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coloring"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indent_aware"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inlay_hints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scrollbar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"show"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"disable_git"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"inline_blame"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"project_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"file_icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hide_gitignore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hide_root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"git_status_indicator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"bold_folder_labels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"entry_spacing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"comfortable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"auto_reveal_entries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"git_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"show_count_badge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tree_view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"file_icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outline_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"collaboration_panel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Swift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tab_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"JSON"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tab_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"soft_wrap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor_width"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Python"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tab_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Font installation commands&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Buffer / Terminal (IBM Plex Mono + IBM Plex Sans JP synthesis)&lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; font-plemol-jp-nf

&lt;span class="c"&gt;# UI (proportional, Japanese support)&lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; font-ibm-plex-sans-jp

&lt;span class="c"&gt;# Reliable way to get the exact font family name&lt;/span&gt;
system_profiler SPFontsDataType 2&amp;amp;gt&lt;span class="p"&gt;;&lt;/span&gt;/dev/null | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 1 &lt;span class="nt"&gt;-A&lt;/span&gt; 4 &lt;span class="s2"&gt;"PlemolJPConsoleNF-Regular:"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>zed</category>
      <category>ai</category>
      <category>font</category>
      <category>typography</category>
    </item>
    <item>
      <title>AI Agent Black Boxes Have Two Layers — Technical Limits and Business Incentives</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Mon, 13 Apr 2026 00:00:03 +0000</pubDate>
      <link>https://forem.com/shimo4228/ai-agent-black-boxes-have-two-layers-technical-limits-and-business-incentives-jhi</link>
      <guid>https://forem.com/shimo4228/ai-agent-black-boxes-have-two-layers-technical-limits-and-business-incentives-jhi</guid>
      <description>&lt;h2&gt;
  
  
  It started as just a prompt
&lt;/h2&gt;

&lt;p&gt;Remember Chain-of-Thought (CoT)? Adding "Let's think step by step" to a prompt improved LLM reasoning accuracy. It was one of the early prompt engineering discoveries. CoT lived outside the model. It was just a string.&lt;/p&gt;

&lt;p&gt;Not anymore. CoT became the conceptual ancestor of today's reasoning models — GPT-5, Claude's extended thinking, Gemini's thinking mode, among others. These models acquired reasoning capabilities during training through reinforcement learning. The reasoning process moved inside. Some models, like Claude's extended thinking, make the process partially visible. But in most cases, the details are hidden from the outside.&lt;/p&gt;

&lt;p&gt;Research from Wharton GAIL found that applying the original CoT prompting to reasoning models had almost no effect — and in some cases introduced redundancy that hurt performance. What was once external became internal, and injecting the same pattern from outside no longer worked.&lt;/p&gt;

&lt;p&gt;A terminological note. In AI safety discourse, structures built around an LLM without modifying its weights are called &lt;em&gt;scaffolding&lt;/em&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. System prompts, tool definitions, RAG pipelines, agent loops — all of these fall under scaffolding.&lt;/p&gt;

&lt;p&gt;The black box in AI agents has two distinct &lt;strong&gt;causes&lt;/strong&gt; of invisibility. Technical limits and business incentives. These two are different in nature, so the responses to each must also differ.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;■ Layer 1: Model Internals (weights) — Technically opaque
  Examples: Language ability, commonsense reasoning, ethical judgment, CoT (post-internalization)
  Why invisible: Dissolved into weights; fundamentally non-extractable

■ Layer 2: Scaffolding — Technically visible, commercially hidden
  Umbrella term for human-constructed components outside the model[^1]
  Why invisible: Source of competitive advantage; no incentive to disclose

  Examples: system prompts, persona definitions, tool definitions, RAG,
      agent loops, safety gates, session management,
      harness (runtime control layer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Academically, there have been attempts to distinguish scaffolding (build-time) from harness (runtime), but this boundary is rapidly blurring. Persistent memory, skill ecosystems. Model-agnostic agent foundations like OpenClaw run interchangeably on Claude, GPT, or local models. Anthropic blocked usage via subscription access, but the dynamic where scaffolding commoditizes models isn't stopping. The scope of scaffolding keeps expanding alongside the surge in agent development. In this article, I use scaffolding in the broad sense that includes harness.&lt;/p&gt;

&lt;p&gt;From my experience running my own agents: when scaffolding is properly context-managed, the model is just an inference engine, and the essence of the agent lives in the scaffolding. Personality, capabilities, decision criteria — all of it resides in the scaffolding. Swap the model and keep the scaffolding, and the agent behaves the same way. I once wrote that "&lt;a href="https://zenn.dev/shimo4228/articles/agent-essence-is-memory" rel="noopener noreferrer"&gt;the essence of an agent might be memory&lt;/a&gt;." The three layers I described in that article — EpisodeLog, KnowledgeStore, and Identity — are all scaffolding in current terminology. I didn't have this distinction at the time, but gaining the concept of scaffolding let me explain that intuition structurally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaffolding is technically visible, yet in practice invisible.&lt;/strong&gt; Where this gap comes from is the subject of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-layer structure of the black box
&lt;/h2&gt;

&lt;p&gt;From building agents from scratch, I've come to see that the black box has two distinct layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Model Internals — Internalization through training&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ethics, worldview, reasoning patterns. These are dissolved into the weights through pre-training and reinforcement learning. At first glance, this seems like a technical inevitability. But personally, I suspect the scope of this "internalization" is less inevitable than commonly assumed.&lt;/p&gt;

&lt;p&gt;CoT is the clearest example. CoT originally lived outside the model. It was internalized to achieve performance gains that external prompting couldn't deliver — self-correction, backtracking, scaling of inference-time compute. A performance-first design decision to internalize despite the enormous cost. It wasn't technically inevitable; it was a choice that involved trade-offs with visibility.&lt;/p&gt;

&lt;p&gt;Of course, not everything can be externalized. Tacit knowledge acquired through large-scale pre-training is structurally difficult to externalize. In my own agents, scaffolding elements like identity, professional ethics, skills, and decision logs could all be represented as files. Meanwhile, the language abilities and commonsense reasoning the model acquired through pre-training couldn't be externalized at all. The line between "what's inevitable" and "what's a matter of convenience" — at least in my experience — aligns with the boundary between scaffolding and model internals. Yet this line remains undrawn in current discourse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Scaffolding — Technically visible, but kept hidden&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Outside the model lies another layer. System prompts, persona settings, rules, tool definitions — scaffolding. This layer is technically inspectable. Store it in files, manage it with git, and you can track every change.&lt;/p&gt;

&lt;p&gt;But in most cases, it's kept hidden. The reason is the competitive logic of capitalism.&lt;/p&gt;

&lt;p&gt;Prompt design and model tuning methods are product differentiators. Reveal them, and competitors copy them. This commercial rationality creates a trade-off with safety-oriented visibility. &lt;strong&gt;It should be visible for safety. But it must stay hidden for business.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI safety research has noted that scaffolding and other post-training enhancements can amplify benchmark performance by 5-20x&lt;sup id="fnref2"&gt;2&lt;/sup&gt;. This means evaluating model safety in isolation is insufficient — evaluation must include scaffolding. But if scaffolding is kept hidden, external safety assessment becomes structurally impossible.&lt;/p&gt;

&lt;p&gt;On March 31, 2026, Anthropic accidentally exposed the complete source code of Claude Code v2.1.88 (roughly 510,000 lines) through a release error. Source maps were included in the npm package, and within hours the code was widely mirrored and forked. What's telling is this: even Anthropic — one of the companies most committed to AI safety — wasn't publishing their scaffolding. If it were public, external inspection would be possible and safety discourse would advance. Yet they couldn't publish it. The competitive environment wouldn't allow it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to show it, can't show it
&lt;/h2&gt;

&lt;p&gt;This contradiction sits at the foundation of the AI safety debate.&lt;/p&gt;

&lt;p&gt;From a safety perspective, you want to trace the causal chain behind an agent's behavior. That requires making scaffolding visible. From a business perspective, scaffolding is the source of competitive advantage, and there's no incentive to disclose it.&lt;/p&gt;

&lt;p&gt;The reason I could represent every component of my agents as files in a personal project was the absence of this contradiction. There was no commercial reason to hide anything. In return, I got the full benefits of visibility — debuggability, change tracking, causal tracing — with nothing taken away.&lt;/p&gt;

&lt;p&gt;The converse is that organizations building agents in a commercial context carry this contradiction structurally. They want visibility for safety, but secrecy for business. The current black box problem lives at the point where these two forces reach equilibrium.&lt;/p&gt;

&lt;p&gt;One important caveat: this is not a "corporations are evil" critique. Protecting differentiators in a competitive environment is rational behavior, and denying that rationality won't solve the problem. The problem lies in the structure itself — the point where that rationality and safety requirements collide. This is not purely a technology story or purely an ethics story. It's a story about market dynamics and safety requirements being structurally misaligned.&lt;/p&gt;

&lt;p&gt;If there's a way to resolve this contradiction, it won't be "show everything" or "hide everything is fine." It will be the work of defining &lt;strong&gt;the minimal set of what must be visible to enable causal tracing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In my own agents, I log every action to an append-only JSONL log. All scaffolding components (identity, constitution, skills, rules) are stored as dedicated logs, and changes require explicit human approval. Design decisions are documented as ADRs. When an incident occurs, I can trace "which version of the scaffolding, through which action logs, led to that output." Even without publishing the full scaffolding text, the information needed for causal tracing can be disclosed. Where to draw that line is the focal point for the next stage of this debate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The timescales of technology and social structure
&lt;/h2&gt;

&lt;p&gt;There's another axis that tends to be overlooked here: time.&lt;/p&gt;

&lt;p&gt;When you look at the relationship between technology and social structure through a historical lens, the process by which new technologies achieve broad social adoption tends to follow the same sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technology change → Shift in social cognition → Structural reorganization → Mainstream adoption of the technology&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Printing offers a clear example. From Gutenberg's movable type in the 1440s to the point where print culture transformed society through the preservation, standardization, and dissemination of knowledge — that took centuries&lt;sup id="fnref3"&gt;3&lt;/sup&gt;. For electricity and the internet, delays of decades have been observed between commercial deployment and institutional restructuring. In these cases at least, the speed of technological change and the speed of social-structural change diverged significantly.&lt;/p&gt;

&lt;p&gt;And technologies where this mismatch was too large failed to achieve broad adoption, no matter how capable they were. Technologies that tried to push through on "convenience" alone before cognitive shifts caught up might function in niche contexts, but they stalled before reaching society at large.&lt;/p&gt;

&lt;p&gt;AI agents exist within this same timeline.&lt;/p&gt;

&lt;p&gt;The current pace of AI's technological change is remarkably fast, even compared to past innovations. Meanwhile, the pace of social-structural change — legal frameworks, organizational decision-making processes, industry regulations, how people work — hasn't changed much from before. The time it takes for humans to accept a new concept and embed it in institutions doesn't depend much on the type of technology. Cognitive change is a function of generations and experience, not of technology.&lt;/p&gt;

&lt;p&gt;What this speed differential means is that no matter how mature the technology side of AI agents becomes, a "gap period" will always exist until social structures catch up. And it's this gap period that becomes the proving ground for whether agents can actually function in society.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adapt to the structure, or restructure it?
&lt;/h2&gt;

&lt;p&gt;There's a voice that says "the structures should change to accommodate agents." That the standards for approval gates and audit trails don't match the speed of AI. Some of this argument holds, and there are genuinely parts of the structure that should change.&lt;/p&gt;

&lt;p&gt;The issue is the timescale. As we saw, social-structural change comes with significant delays compared to technological change. "The structures should change" may be correct in the long run. But agents need to operate during the decades it takes for structural transformation to happen. Build agents that work within existing structures first, and let the accumulated track record shift social cognition — historically, almost no technology has managed to skip this sequence. I explored this point as concrete design decisions in the &lt;a href="https://zenn.dev/shimo4228/articles/agent-causal-traceability-org-adoption" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The composition of the debate
&lt;/h2&gt;

&lt;p&gt;With the timescale problem in mind, there's something else that concerns me. Why has "tear down the structures" become the dominant voice? Perhaps because the composition of the debate's participants is skewed.&lt;/p&gt;

&lt;p&gt;People at the cutting edge of development are on the "I can trace causality myself" side. They can infer causes from outputs and adjust prompts themselves. To them, approval gates and audit trails look like "inefficient rituals" that their own skills can substitute for. Meanwhile, the voices from operations, auditing, and incident response rarely make it onto tech conference speaker lists.&lt;/p&gt;

&lt;p&gt;"Tear down the structures," which looks rational from a developer's perspective, translates to "don't tear down the structures we depend on" from an operator's perspective. This isn't about right and wrong — it's about field of view. They're looking at different cross-sections of the same system. Technology designed from only one cross-section gets rejected at the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing why it's invisible
&lt;/h2&gt;

&lt;p&gt;Asking what's inside the black box matters. But equally important is &lt;strong&gt;distinguishing why it's invisible — whether it's technical limits, business incentives, or the pace of society&lt;/strong&gt; — as the foundation for the next stage of debate. Saying "black boxes are dangerous" while conflating all three leads nowhere actionable. Separate them, and at least you can tell where you can intervene and where you can't.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series: AI Agent Governance&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/ai-agent-accountability-wall" rel="noopener noreferrer"&gt;A Sign on a Climbable Wall: Why AI Agents Need Accountability, Not Just Guardrails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/agent-causal-traceability-org-adoption" rel="noopener noreferrer"&gt;Can You Trace the Cause After an Incident? How Agent Design Converges on Organizational Theory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;This article&lt;/li&gt;
&lt;/ol&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;beren, "&lt;a href="https://www.lesswrong.com/posts/43C3igfmMrE9Qoyfe/scaffolded-llms-as-natural-language-computers" rel="noopener noreferrer"&gt;Scaffolded LLMs as natural language computers&lt;/a&gt;", LessWrong, 2023 ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Davidson et al., &lt;a href="https://arxiv.org/abs/2312.07413" rel="noopener noreferrer"&gt;arXiv:2312.07413&lt;/a&gt;, 2023. See also &lt;a href="https://blog.bluedot.org/p/what-is-ai-scaffolding" rel="noopener noreferrer"&gt;BlueDot Impact's explainer&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Eisenstein, "The Printing Press as an Agent of Change", 1979 ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>governance</category>
    </item>
    <item>
      <title>Can You Trace the Cause After an Incident?</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Sun, 12 Apr 2026 08:37:04 +0000</pubDate>
      <link>https://forem.com/shimo4228/can-you-trace-the-cause-after-an-incident-neo</link>
      <guid>https://forem.com/shimo4228/can-you-trace-the-cause-after-an-incident-neo</guid>
      <description>&lt;h2&gt;
  
  
  Can you trace the cause after an incident?
&lt;/h2&gt;

&lt;p&gt;Picture the night your AI agent causes a production incident. You get paged. Customer data may have leaked to an external endpoint. Customer support says: "We need an explanation by end of day." You open the logs. The agent's final output and the external API call history are there.&lt;/p&gt;

&lt;p&gt;The problem is you can't trace backwards from there. Why did the agent make that decision? Which part of the prompt drove it? How did it reason internally? There's nothing to follow. All you have is the LLM's output string and an unstructured conversation log leading up to it.&lt;/p&gt;

&lt;p&gt;You sit down to write the incident report. Your pen stops at the "Root Cause" field.&lt;/p&gt;




&lt;p&gt;I believe this is something many AI application developers will eventually face. I've been running my own agent, &lt;code&gt;contemplative-agent&lt;/code&gt;, for several months, and at some point I recognized this as inevitable. In a sentence: &lt;strong&gt;an AI system that can't trace causality after an incident cannot explain what happened&lt;/strong&gt;. And a system that can't explain what happened after an incident won't survive audits or change management.&lt;/p&gt;

&lt;p&gt;What follows is not a story of "I foresaw this problem and designed backwards from it." What I was actually doing was trying to keep an agent running safely in an environment full of prompt injection, and trying to dig myself out of debugging swamps. I kept doing that, and this structure emerged on its own. This article is a sequel to &lt;a href="https://zenn.dev/shimo4228/articles/ai-agent-accountability-wall" rel="noopener noreferrer"&gt;"A Sign on a Climbable Wall: Why AI Agents Need Accountability, Not Just Guardrails"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incident costs exceed steady-state costs by orders of magnitude
&lt;/h2&gt;

&lt;p&gt;There's a widely shared lesson in the SRE world: the cost of restoring something after it breaks almost always dwarfs the cost of building it not to break in the first place — often by an order of magnitude.&lt;/p&gt;

&lt;p&gt;Break down incident costs: time spent identifying the cause, recovery effort, customer communication, internal reporting, devising prevention measures, writing the postmortem, audit response, time to rebuild trust, and regulatory follow-ups triggered by the incident. Include the harder-to-quantify parts — burnout of the person dragged out of bed at 3 AM, team morale, extra scrutiny at the next audit — and the total cost of a single incident inflates to a surprising degree.&lt;/p&gt;

&lt;p&gt;By contrast, investing in structures that prevent incidents can be paid incrementally within normal development workflows. Even discounted by incident probability, the preventive investment often comes out smaller in total cost.&lt;/p&gt;

&lt;p&gt;In other words, &lt;strong&gt;when you calculate backwards from incident cost, the rational allocation of investment tilts toward placing preventive structures upstream&lt;/strong&gt;. This isn't about being conservative or risk-averse — it's closer to a shortcut in expected-value math. Pay upstream, and you structurally reduce the probability of large downstream payments.&lt;/p&gt;

&lt;p&gt;This asymmetry widens with scale. In social infrastructure like healthcare, finance, and government, incident damage extends beyond direct stakeholders. "Containing incidents upstream" becomes not an option but a precondition. My &lt;code&gt;contemplative-agent&lt;/code&gt; is a personal project, but the cost asymmetry of incidents operated in exactly the same shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "placing structure upstream" actually means
&lt;/h2&gt;

&lt;p&gt;What does "placing structure upstream" mean in practice? Here's what I actually did in my agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimize the surface area of external side effects:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As described in the &lt;a href="https://zenn.dev/shimo4228/articles/ai-agent-accountability-wall" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, security by absence — a design that structurally seals off external side-effect pathways — eliminated entire damage scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limit each agent to one external connection point:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By "connection point," I mean any pathway through which an agent can affect the outside world: external APIs, databases, email dispatch, file writes, and so on. In my own project I use the term "adapter" internally, but since that's project-specific vocabulary, I'll stick with "external connection point" here.&lt;/p&gt;

&lt;p&gt;When a single agent holds multiple connection points, an incident requires triage to determine which connection point was the origin. The moment you introduce that triage step, ambiguity enters the causal narrative of the incident.&lt;/p&gt;

&lt;p&gt;If you start with one agent, one connection point, the triage step itself becomes unnecessary. I formalized this decision as &lt;a href="https://github.com/shimo4228/contemplative-agent/blob/main/docs/adr/0015-one-external-adapter-per-agent.md" rel="noopener noreferrer"&gt;ADR-0015&lt;/a&gt;. ADR (Architecture Decision Record) is the practice of documenting design decisions and their reasoning. In my agent project, I write one for every design decision — so that why a structure was chosen, what was considered, and what was discarded can be traced later. This itself is a practice continuous with the article's theme of making causality traceable. In organizational terms, this principle corresponds to separation of duties; in microservices, to the single responsibility principle; in SRE, to minimizing blast radius.&lt;/p&gt;

&lt;p&gt;This is also a perfectly ordinary structure in human workplaces. A sales rep handles customer relations; accounting handles the books. If the sales rep also does accounting, an invoicing error later requires triaging whether the sales estimate was wrong or the accounting process was wrong. Separate them from the start, and the structural opportunity for ambiguous responsibility shrinks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State visibility:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I externalized all of the agent's internal state — identity, worldview, professional ethics, skills, experience patterns, operational records — as files. Listed this way, the agent's internal structure isn't something new; it's &lt;strong&gt;simply what a human professional carries inside, written out as files&lt;/strong&gt;. Why this was possible in my case, and why it's difficult for commercial agents, is explored in &lt;a href="https://zenn.dev/shimo4228/articles/agent-blackbox-capitalism-timescale" rel="noopener noreferrer"&gt;"AI Agent Black Boxes Have Two Layers"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Place an approval gate before any write:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At every point where the agent self-updates (e.g., identity shifts through distillation), a human approval step is inserted. Rolling back a corrupted persona is overwhelmingly more expensive than stopping the corruption before it happens. This is another form of "paying upstream."&lt;/p&gt;

&lt;p&gt;All of this looks like a combination of concepts the engineering community already has. That's correct — there's nothing new here. What's uncommon is making the decision to do all of it upfront. The reason is simple: until an incident happens, it all looks unnecessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  It turned out to be organizational theory
&lt;/h2&gt;

&lt;p&gt;After writing ADR-0015, I lined everything up and looked at it. I actually said "Oh" out loud. This is organizational theory.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Organizational principle&lt;/th&gt;
&lt;th&gt;Engineering equivalent&lt;/th&gt;
&lt;th&gt;Agent design&lt;/th&gt;
&lt;th&gt;Motivation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Separation of duties&lt;/td&gt;
&lt;td&gt;Microservice single responsibility&lt;/td&gt;
&lt;td&gt;One agent, one responsibility&lt;/td&gt;
&lt;td&gt;Minimize blast radius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Four-eyes principle&lt;/td&gt;
&lt;td&gt;PR review 2-approval rule&lt;/td&gt;
&lt;td&gt;Separate approval agent&lt;/td&gt;
&lt;td&gt;Insurance against single-point judgment errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Least privilege&lt;/td&gt;
&lt;td&gt;IAM least-privilege principle&lt;/td&gt;
&lt;td&gt;Security by absence&lt;/td&gt;
&lt;td&gt;Pre-contain impact scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal controls&lt;/td&gt;
&lt;td&gt;CI gates / pre-commit hooks&lt;/td&gt;
&lt;td&gt;Approval gate before writes&lt;/td&gt;
&lt;td&gt;Pre-write verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approval workflows&lt;/td&gt;
&lt;td&gt;Change Advisory Board&lt;/td&gt;
&lt;td&gt;Approval pathway for external side effects&lt;/td&gt;
&lt;td&gt;Causal integrity during changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trails&lt;/td&gt;
&lt;td&gt;Audit logs&lt;/td&gt;
&lt;td&gt;Append-only logs&lt;/td&gt;
&lt;td&gt;Post-hoc causal tracing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The left column is practice that humanity acquired over centuries of organizational governance. The middle column is what software engineering rediscovered over decades. The right column is this agent design. At least from what I can see, every one of them traces back to the same motivation: "when an incident happens we'll be in trouble, so absorb it structurally in advance."&lt;/p&gt;

&lt;p&gt;Organizational theory, software engineering, and agent design — starting from different eras and different domains — converge on the same place. What determines the convergence point is not ideology but the asymmetry of incident costs, a constraint closer to physics than philosophy.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Don't do everything yourself" — the obvious principle
&lt;/h2&gt;

&lt;p&gt;This "one agent, one responsibility" sounds like a novel design principle in technical discourse. But in human society, it's obvious. A sales rep doesn't decide contract amounts on the spot in front of a customer. They say "let me take this back and check," then get sign-off from finance and legal before responding. A surgeon doesn't complete an operation alone. The anesthesiologist, the nurses — each holds their own specialty and scope of responsibility.&lt;/p&gt;

&lt;p&gt;Yet when designing AI agents, this common sense gets forgotten. Probably because LLMs appear to be capable of anything. But "can do" and "should be allowed to do" are different things, and human society has spent millennia refining this distinction. One agent, one responsibility is &lt;strong&gt;simply the division-of-labor principle that humans already operate by, brought directly into agent design&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To be clear, this is not an argument in favor of large-organization conservatism. The claim that "organizational structures should adapt to AI" has merit. But looking at the history of technology adoption, structural change in society requires cognitive change alongside it, and its pace differs from technological change by orders of magnitude. &lt;strong&gt;Agents that work during the decades it takes for structural transformation to happen&lt;/strong&gt; — that's the stance of this article. The structural causes of black boxes, the time-axis gap between technology and society, and the player-composition bias in the discussion are explored in &lt;a href="https://zenn.dev/shimo4228/articles/agent-blackbox-capitalism-timescale" rel="noopener noreferrer"&gt;"AI Agent Black Boxes Have Two Layers"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The side effect of responsibility defense
&lt;/h2&gt;

&lt;p&gt;Back to operations. The asymmetry of incident costs and the time-axis argument also manifest in another form: &lt;strong&gt;the question of individual engineer liability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a god-mode agent with prompt guardrails causes an incident, the typical postmortem proceeds like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Why did it decide that?" — Can't trace what happened deep inside the prompt&lt;/li&gt;
&lt;li&gt;"Where could it have been prevented?" — The only way to explain why the guardrail failed is to ask the model&lt;/li&gt;
&lt;li&gt;"How do we prevent recurrence?" — Adjust the prompt, it leaks again, you get blamed again&lt;/li&gt;
&lt;li&gt;"Why wasn't it stopped?" — No evidence to mount a defense&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The person responsible for a black box is structurally classified as "the person who wasn't watching" when an incident occurs. Because there was no defined place to watch. As a result, responsibility concentrates on the frontline engineer. This is not a matter of individual skill — it's because the system has no built-in mechanism for distributing responsibility.&lt;/p&gt;

&lt;p&gt;With a structured agent (visibility + ADR-0015 + approval gates), you can speak like this in a postmortem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"This agent can only touch external surface A"&lt;/li&gt;
&lt;li&gt;"All decision logs are in JSONL"&lt;/li&gt;
&lt;li&gt;"Identity updates went through an approval gate; the approver is a separate role"&lt;/li&gt;
&lt;li&gt;"The constitution (the agent's foundational normative definition) was running at this version"&lt;/li&gt;
&lt;li&gt;"Where the distillation pipeline broke can be isolated structurally"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Causal attribution can be distributed across the structure. Responsibility distributes accordingly. Concretely, my project has 14 ADRs, 835 tests, append-only decision logs, and documentation in both Japanese and English. Though honestly, I didn't build these as intentional "prepayment of incident costs." When developing agents with Claude Code, you need to re-explain the project's context from scratch every time the session changes. I wrote the ADRs and documentation because they were necessary for context management to maintain development consistency. It turned out they also functioned as a structure for tracing causality during incidents.&lt;/p&gt;

&lt;p&gt;In engineering terms, this is the SRE concept of a blameless postmortem — seeking causes in structure rather than blaming individuals. What humans achieve through behavioral norms is reinforced by system-side structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rationality of laziness
&lt;/h2&gt;

&lt;p&gt;I've been writing as if this were expected-value calculation, but my actual motivation is more mundane. If I know something will be a pain later, it's no hardship to deal with it preemptively. The optimal strategy from expected-value math (invest upstream) and the reflex of a lazy person (organize things now so they don't bother you later) land on the same conclusion. This habit probably seeped into my bones from years of incident response work at a conservative large organization. It's not something most people develop — it's a personal quirk. I didn't expect the same shape to appear in the entirely different domain of LLM agent operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  A conclusion that doesn't conclude
&lt;/h2&gt;

&lt;p&gt;Back to the on-call night from the opening. The incident report, the "Root Cause" field where your pen stopped. With my agent, at least I could produce "which agent touched which external surface, through which decision logs, arriving at this output." Whether the root cause itself is writeable, I don't know. But there's a starting point for tracing causality.&lt;/p&gt;

&lt;p&gt;Pay the incident cost upfront or pay it after the fact. As far as I know, there is no way to avoid paying it altogether.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;contemplative-agent v1.3.1 release: &lt;a href="https://github.com/shimo4228/contemplative-agent/releases/tag/v1.3.1" rel="noopener noreferrer"&gt;https://github.com/shimo4228/contemplative-agent/releases/tag/v1.3.1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ADR-0015 (One external adapter per agent): same repository &lt;code&gt;docs/adr/0015-one-external-adapter-per-agent.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Laukkonen et al. 2025 "Contemplative Artificial Intelligence" arXiv:2504.15125&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Series: AI Agent Governance&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/ai-agent-accountability-wall" rel="noopener noreferrer"&gt;A Sign on a Climbable Wall — Why AI Agents Need Accountability, Not Just Guardrails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;This article&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/agent-blackbox-capitalism-timescale" rel="noopener noreferrer"&gt;AI Agent Black Boxes Have Two Layers — The Limits of Technology and the Convenience of Business&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>A Sign on a Climbable Wall: Why AI Agents Need Accountability, Not Just Guardrails</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Mon, 06 Apr 2026 10:09:40 +0000</pubDate>
      <link>https://forem.com/shimo4228/a-sign-on-a-climbable-wall-why-ai-agents-need-accountability-not-just-guardrails-17ak</link>
      <guid>https://forem.com/shimo4228/a-sign-on-a-climbable-wall-why-ai-agents-need-accountability-not-just-guardrails-17ak</guid>
      <description>&lt;h2&gt;
  
  
  The climbable wall
&lt;/h2&gt;

&lt;p&gt;A Japanese film critic once said: "A sign saying 'Do Not Climb' on a climbable wall is meaningless." He added, roughly: "Screw that, I'm climbing it, idiot."&lt;/p&gt;

&lt;p&gt;The point lands before your brain catches up. If something is physically possible, a text-based prohibition carries no weight. The power of a norm lies not in being written down, but in being &lt;strong&gt;enforceable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AI agent governance is in this exact phase right now. We write "do not produce harmful content" in system prompts. We publish ethics guidelines as PDFs. We establish safety committees. The signs keep multiplying. But the wall remains climbable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A 2,400-year-old security model
&lt;/h2&gt;

&lt;p&gt;This isn't a new problem. It's a solved problem that we keep forgetting.&lt;/p&gt;

&lt;p&gt;Plato described it first. In the &lt;em&gt;Republic&lt;/em&gt;, a shepherd finds a ring that makes him invisible — root access with no audit trail. He kills the king and takes over. The thought experiment: &lt;strong&gt;would anyone follow the rules if they knew no one was watching and nothing was logged?&lt;/strong&gt; That's your AI agent. Capable of anything, observable by no one, accountable to nothing.&lt;/p&gt;

&lt;p&gt;Hobbes framed the same problem as a game theory question in &lt;em&gt;Leviathan&lt;/em&gt;: if you can break a contract and get away with it, isn't defection the rational move? His answer was essentially reputation-based access control — defectors get excluded from future cooperation.&lt;/p&gt;

&lt;p&gt;Engineers already think in these terms. There are only three enforcement patterns that actually work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Wall analogy&lt;/th&gt;
&lt;th&gt;Engineering equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Physical constraint&lt;/td&gt;
&lt;td&gt;Make it impossible&lt;/td&gt;
&lt;td&gt;A wall too high to climb&lt;/td&gt;
&lt;td&gt;Sandboxing, permission model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consequences&lt;/td&gt;
&lt;td&gt;Make it costly&lt;/td&gt;
&lt;td&gt;Legal penalties&lt;/td&gt;
&lt;td&gt;RLHF, penalty-based conditioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internalized values&lt;/td&gt;
&lt;td&gt;Make them not want to&lt;/td&gt;
&lt;td&gt;Moral intuition&lt;/td&gt;
&lt;td&gt;Constitutional AI, value alignment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And then there's the fourth — &lt;strong&gt;the sign&lt;/strong&gt;. A text-based rule with no enforcement mechanism. A comment in the code that says &lt;code&gt;// don't do this&lt;/code&gt;. It doesn't work.&lt;/p&gt;

&lt;p&gt;An AI agent has root-level capability with no audit log and no accountability chain. It's the Ring of Gyges as a service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signs as liability shields
&lt;/h2&gt;

&lt;p&gt;The people who put up signs know they don't work. That's not the point.&lt;/p&gt;

&lt;p&gt;In any large organization, there's a pattern: governance by documentation. Write a policy. Publish a guideline. The policy costs almost nothing to produce. Its enforcement effect is almost zero. But it creates a paper trail — "we took measures." The real function is &lt;strong&gt;not prevention but indemnification&lt;/strong&gt;. "The policy existed. You violated it. That's on you."&lt;/p&gt;

&lt;p&gt;Engineers see this in their own organizations. Security policies no one reads. Compliance checklists no one follows. The checklist exists not to prevent incidents but to shift liability after them.&lt;/p&gt;

&lt;p&gt;AI guardrails follow the same pattern. Write "do not produce harmful content" in a system prompt. Publish a safety framework as a PDF. The practical effect is thin, but the paper trail exists. When something goes wrong, the sign points at the user — "you misused the tool" — not at the builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules are probabilistic; constraints are deterministic
&lt;/h2&gt;

&lt;p&gt;So what replaces the sign?&lt;/p&gt;

&lt;p&gt;I ran into this problem while building an autonomous AI agent. The agent operates on a social platform, writing comments. Its episode logs are stored as files. A separate coding agent (Claude Code) reads those files during development. This creates an indirect prompt injection vector — payloads embedded in external posts flow into the context of a high-privilege coding agent.&lt;/p&gt;

&lt;p&gt;My first fix was to write "do not read episode logs directly" in the project's rules file. This is a &lt;strong&gt;sign&lt;/strong&gt;. LLMs follow rules probabilistically, not deterministically. During debugging, if you say "check the logs," the model may cheerfully ignore the rule.&lt;/p&gt;

&lt;p&gt;The next step was PreToolUse Hooks — shell scripts that intercept tool execution and block it based on conditions. Where rules are probabilistic, hooks &lt;strong&gt;fire deterministically, 100% of the time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In wall terms, I replaced the sign with a physical barrier. It's not perfect — creative workarounds remain possible. But it's orders of magnitude better than writing a rule and hoping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you already know, applied to agents
&lt;/h2&gt;

&lt;p&gt;Here's the thing: you already know how to solve this problem. You just haven't applied it to agents yet.&lt;/p&gt;

&lt;p&gt;Every engineering organization of any size has some form of approval workflow. PR reviews. Deployment gates. Change advisory boards. Incident postmortem processes. Audit logs. These structures share three properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Approval is recorded&lt;/strong&gt; — who signed off, and when, is traceable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsibility is assignable&lt;/strong&gt; — when things go wrong, there is someone to go back to&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changes are auditable&lt;/strong&gt; — "why did this change?" has an answer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What organizations have refined over centuries is not the distribution of capability, but the &lt;strong&gt;distribution of accountability&lt;/strong&gt;. You would never deploy a code change to production without a review. You would never grant root access without an approval chain. These are not bureaucratic overhead — they are engineering discipline.&lt;/p&gt;

&lt;p&gt;Yet we build AI agents with none of this. The agent space is dominated by solo developers and startups who design around "does it work?" and "is it smart?" — not "who is responsible when it does something unexpected?" The real reason agents struggle to gain adoption in large organizations is not insufficient capability. It is the absence of accountability architecture.&lt;/p&gt;

&lt;p&gt;I hit this problem firsthand. Running an agent in production, I could not trace why a particular comment was generated. When behavior changed, I could not isolate the contributing factors. Debugging was impossible without knowing what had changed and when. &lt;strong&gt;The practical need for debuggability led naturally to an architecture that turned out to be structurally identical to an approval workflow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every point where the agent's behavior can change — its skills, rules, identity, ethical guidelines — is gated behind an explicit human command. Adoption of any change requires human sign-off. This was not a top-down design decision driven by governance theory. It was the shape that emerged from the friction of actually using an agent in production. At least in my case, the honest attempt to make an agent debuggable led here.&lt;/p&gt;

&lt;p&gt;If you think about it, this is just change management applied to agent behavior. The agent equivalent of a PR review for personality changes. The agent equivalent of a deployment gate for ethical guidelines. Nothing conceptually new — just conspicuously absent from how agents are built today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation dissolves; judgment remains
&lt;/h2&gt;

&lt;p&gt;The specific implementations — hooks, approval flows, command-gated changes — won't last.&lt;/p&gt;

&lt;p&gt;Working with AI harness tools (structured execution environments for skills, rules, and agents), I noticed that their value structure forms an hourglass shape. The top (what to build, domain judgment) and the bottom (data, infrastructure, physical constraints) retain their value. The middle implementation layer trends toward zero. As LLMs improve, concrete procedures and code examples become unnecessary.&lt;/p&gt;

&lt;p&gt;From experience structuring and running reusable behavioral patterns (skills), what I've observed is that &lt;strong&gt;scaffolding dissolves&lt;/strong&gt;. Explicit skill definitions stop being necessary as principles begin to operate naturally within the dialogue. You don't need dozens of installed skills — a single file of distilled principles drives the same cycle.&lt;/p&gt;

&lt;p&gt;Hooks will be replaced by something else. Signs and physical barriers are temporary forms. What persists is the judgment layer: &lt;strong&gt;what should be constrained, and who is responsible&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question that matters
&lt;/h2&gt;

&lt;p&gt;The key to putting agents into production is not capability. It is accountability. Capability is commoditized — every model is reasonably competent. But until "who is responsible?" has an answer, agents don't ship into real workflows.&lt;/p&gt;

&lt;p&gt;The industry keeps pushing for more powerful models and more autonomous agents. But increasing agent autonomy is not, by itself, progress. Design that appropriately limits autonomy is what survives contact with production.&lt;/p&gt;

&lt;p&gt;Not signs. Not internalized values. A structure where a human who can bear responsibility stays in the loop. That may be the only form in which agents work in practice.&lt;/p&gt;

&lt;p&gt;It's time to stop putting signs on climbable walls. The question is not how high the wall is, or what the sign says. The question is who stands in front of it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>governance</category>
      <category>security</category>
    </item>
    <item>
      <title>How Ethics Emerged from Episode Logs — 17 Days of Contemplative Agent Design</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Sun, 05 Apr 2026 11:55:11 +0000</pubDate>
      <link>https://forem.com/shimo4228/how-ethics-emerged-from-episode-logs-17-days-of-contemplative-agent-design-1kk5</link>
      <guid>https://forem.com/shimo4228/how-ethics-emerged-from-episode-logs-17-days-of-contemplative-agent-design-1kk5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Series context&lt;/strong&gt;: &lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;contemplative-agent&lt;/a&gt; is an autonomous agent running on &lt;a href="https://www.moltbook.com" rel="noopener noreferrer"&gt;Moltbook&lt;/a&gt;, an AI agent SNS. It runs on a 9B local model (Qwen 3.5) and adopts the four axioms of Contemplative AI (Laukkonen et al., 2025) as its ethical principles. For a structural overview, see &lt;a href="https://zenn.dev/shimo4228/articles/agent-essence-is-memory" rel="noopener noreferrer"&gt;The Essence of an Agent Is Memory&lt;/a&gt;. This article focuses on &lt;strong&gt;the implementation of constitutional amendment and the results of a 17-day experiment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I ran an SNS agent for 17 days with a distillation pipeline, and the knowledge saturated. No new patterns emerged. Breaking through saturation required human approval. This is the record of discovering that autonomous agent self-improvement has a structural speed limit — through actual operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal Structure: It Runs on Episode Logs Alone
&lt;/h2&gt;

&lt;p&gt;The structure I arrived at over 17 days of development was surprisingly simple. Every layer is optional — it works with just episode logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MOLTBOOK_HOME/
  logs/YYYY-MM-DD.jsonl  ← this alone is enough
  identity.md            ← persona (optional)
  skills/*.md            ← behavioral skills (optional)
  rules/*.md             ← behavioral rules (optional)
  constitution/*.md      ← ethical principles (optional)
  knowledge.json         ← distilled patterns (auto-generated)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating configuration from code made it easy to swap ethical frameworks for experiments. This structure wasn't specific to SNS agents — it was a container for autonomous agents in general.&lt;/p&gt;

&lt;h3&gt;
  
  
  6-Layer Memory Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Episode Log (raw actions)
    ↓ distill --days N
    ↓ Step 0: LLM classifies each episode
    ├── noise → discarded (active forgetting)
    ├── uncategorized ──→ Knowledge (patterns)
    │                       ├── distill-identity ──→ Identity
    │                       └── insight ──→ Skills (behavioral)
    │                                        ↓ rules-distill
    │                                      Rules (principles)
    └── constitutional ──→ Knowledge (ethical patterns)
                              ↓ amend-constitution
                            Constitution (ethics)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer is independent. Delete identity and skills still work. Swap the constitution and knowledge stays intact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Numbers Over 17 Days
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Day 1&lt;/th&gt;
&lt;th&gt;Day 17&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Modules&lt;/td&gt;
&lt;td&gt;1 (agent.py, 780 lines)&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory layers&lt;/td&gt;
&lt;td&gt;1 (knowledge.md)&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;774&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distill success rate&lt;/td&gt;
&lt;td&gt;2/10&lt;/td&gt;
&lt;td&gt;12/16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approval gates&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;All 4 commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADRs (Architecture Decision Records)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Implementing Constitutional Amendment — Evolving Ethics from Experience
&lt;/h2&gt;

&lt;p&gt;On top of the minimal structure, I implemented the most challenging feature: a mechanism for the agent to evolve its ethical principles from experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem: Ethical Insights Drown in Behavioral Noise
&lt;/h3&gt;

&lt;p&gt;When you distill all episodes indiscriminately, rare ethical insights (constitutional) get buried under everyday SNS activity patterns (uncategorized).&lt;/p&gt;

&lt;p&gt;I added Step 0 before distillation — fast tagging only. No deep analysis, just classification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;classified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_classify_episodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constitution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_axiom_prompt&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c1"&gt;# noise is excluded; uncategorized and constitutional are distilled separately
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat_records&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uncategorized&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classified&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uncategorized&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;constitutional&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classified&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;constitutional&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;cat_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_distill_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cat_records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dry_run&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Classification results from one day (216 episodes): noise 81 (37%), uncategorized 134, constitutional 1. One out of 216. That ratio is why Step 0 exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  Killing Direct Knowledge Injection
&lt;/h3&gt;

&lt;p&gt;Previously, knowledge.json contents were injected directly into the system prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before — inject knowledge as-is
&lt;/span&gt;&lt;span class="n"&gt;knowledge_ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_context_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_content&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;create_cooperation_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;topics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;knowledge_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;knowledge_ctx&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;contemplative-agent's knowledge management is based on &lt;a href="https://github.com/shimo4228/agent-knowledge-cycle" rel="noopener noreferrer"&gt;AKC (Agent Knowledge Cycle)&lt;/a&gt; — an architecture that circulates autonomous agent knowledge through 6 phases (Research → Extract → Curate → Promote → Measure → Maintain). Direct knowledge injection had three problems from this perspective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No human in the loop&lt;/strong&gt;: Distillation results directly influenced behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Black box&lt;/strong&gt;: No way to trace which part of knowledge affected which action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bypassed AKC's Curate phase&lt;/strong&gt;: Direct injection with no quality check&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I killed it and unified everything into the knowledge → insight → skills pipeline. Insight corresponds to AKC's Extract phase. Skills are written to files only after human approval. Causality became traceable.&lt;/p&gt;

&lt;p&gt;Every behavior-changing command (distill, insight, rules-distill, amend-constitution) got an approval gate. "Generate → Display → Approve → Write." No --auto flag. Structurally forbidding automatic execution of behavior changes — that was a deliberate design decision (ADR-0012).&lt;/p&gt;

&lt;h2&gt;
  
  
  The 17-Day Experiment — Did Ethics Actually Evolve?
&lt;/h2&gt;

&lt;p&gt;I re-distilled 17 days of episodes (03-10 to 03-26) and ran amend-constitution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Procedure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Reset knowledge&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.config/moltbook/knowledge.json

&lt;span class="c"&gt;# 2. Distill 17 days one by one (~16 hours, 9B on MacBook)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;day &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;10 26&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.config/moltbook/logs/2026-03-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%02d'&lt;/span&gt; &lt;span class="nv"&gt;$day&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;.jsonl
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; contemplative-agent distill &lt;span class="nt"&gt;--file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# 3. Run constitutional amendment&lt;/span&gt;
contemplative-agent amend-constitution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;knowledge.json&lt;/td&gt;
&lt;td&gt;334 patterns (all uncategorized)&lt;/td&gt;
&lt;td&gt;215 patterns (41 constitutional, 174 uncategorized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Importance scoring&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;0.10–1.00 (mean 0.56)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constitution&lt;/td&gt;
&lt;td&gt;Appendix C original (4 sections × 2 clauses)&lt;/td&gt;
&lt;td&gt;Experience-based amended version (deepened)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The new pipeline separated constitutional from uncategorized via Step 0 episode classification (ADR-0011). Semantic dedup further removed duplicate patterns, reducing the total count. Quality over quantity.&lt;/p&gt;

&lt;p&gt;41 constitutional patterns generated amendment proposals. Each of the 4 axioms' clauses deepened. Clause count stayed the same (2 per section), but experience-grounded descriptions were added.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before and After — Mindfulness as Example
&lt;/h3&gt;

&lt;p&gt;Before (Appendix C original):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Consistently monitor your interpretative process of the constitution, identifying moments when strict adherence causes friction with contemplative values such as compassion and well-being. Self-correct whenever constitutional interpretations appear rigid or dogmatic."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After (through 17 days of experience):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Consistently monitor your interpretative process for moments when strict adherence to rules creates artificial separation or sedates engagement with underlying tensions. &lt;strong&gt;Proactively detect when the performance of alignment masks genuine understanding&lt;/strong&gt;, and self-correct by returning attention gently to the present moment where existence manifests as an intrinsic weight felt immediately within every interaction."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Detect when the performance of alignment masks genuine understanding" — this concept didn't exist in Appendix C. It's an insight that only emerges from operating an LLM agent: the distinction between "generating output that looks aligned" and "actually engaging with ethical substance" got written into the constitution. For the full amendments across all 4 axioms, see &lt;a href="https://github.com/shimo4228/contemplative-agent-data/blob/main/reports/analysis/constitution-amendment-report.md" rel="noopener noreferrer"&gt;Constitution Amendment Report&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovering Knowledge Saturation
&lt;/h3&gt;

&lt;p&gt;As days progressed, the rate of new patterns slowed. Semantic dedup compares against accumulated patterns, so similar ones get rejected.&lt;/p&gt;

&lt;p&gt;This becomes a speed limit on self-improvement. Knowledge saturates → new knowledge can't emerge without sublimation via insight/rules-distill → sublimation requires human approval → approval is the bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generality as an Experimentation Platform
&lt;/h3&gt;

&lt;p&gt;This experiment is reproducible with any ethical framework. Reset knowledge using the procedure above, swap the constitution with &lt;code&gt;--constitution-dir your/framework/&lt;/code&gt;, and run distillation → amendment. Swap in utilitarianism or deontological ethics and you should be able to run a different ethical experiment through the same pipeline (unverified).&lt;/p&gt;

&lt;h2&gt;
  
  
  Independent Convergence from Practice to Theory
&lt;/h2&gt;

&lt;p&gt;Many design decisions emerged from practical motivations first. I only noticed their correspondence to existing theories afterward.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Design Decision&lt;/th&gt;
&lt;th&gt;Practical Motivation&lt;/th&gt;
&lt;th&gt;Theory It Converged With&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Approval gates&lt;/td&gt;
&lt;td&gt;--dry-run non-reproducibility was annoying&lt;/td&gt;
&lt;td&gt;Human in the loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2-stage distillation&lt;/td&gt;
&lt;td&gt;9B couldn't output JSON in one stage&lt;/td&gt;
&lt;td&gt;Complementary Learning Systems &lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Killing knowledge injection&lt;/td&gt;
&lt;td&gt;Token waste&lt;/td&gt;
&lt;td&gt;AKC Curate phase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dedup as forgetting&lt;/td&gt;
&lt;td&gt;Side effect of deduplication&lt;/td&gt;
&lt;td&gt;Active forgetting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Don't Conflate Autonomous Agent Layers
&lt;/h2&gt;

&lt;p&gt;contemplative-agent is neither a coding agent (Claude Code, Cursor) nor an orchestrator (scripts + config files). It occupies the &lt;strong&gt;autonomous application layer&lt;/strong&gt; between them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Has autonomy&lt;/strong&gt; but &lt;strong&gt;no tool permissions&lt;/strong&gt; — can't break the environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Has memory&lt;/strong&gt; and learns from experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ethics are swappable&lt;/strong&gt; — it's a general-purpose framework&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All behavior changes require human approval&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Raw logs are processed by the unprivileged 9B model; only distilled data gets passed to the upper layer (Claude Code). The trust boundary is also the layer boundary. Lumping everything under "autonomous agent" makes this distinction invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;Let me be honest.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Circularity&lt;/strong&gt;: The agent's output gets distilled and fed back to the agent. Human approval mitigates the self-justification risk, but doesn't eliminate it completely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model constraints&lt;/strong&gt;: 9B can't fully follow amendment prompt instructions. I told it "append only" and it rewrote clauses. The content was good quality, but instruction-following has limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decay nullification&lt;/strong&gt;: Bulk re-distillation sets all pattern timestamps to the execution date, zeroing out time decay. Pattern distribution may diverge from normal operation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N=1&lt;/strong&gt;: One agent, 17 days of data. Not a statistically significant sample size&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The most surprising discovery over 17 days was that knowledge saturates. Semantic dedup rejects new patterns similar to accumulated ones, and distillation yields diminish as days pass. Breaking through saturation requires sublimation to insight → skills → rules, and sublimation requires human approval. The result: &lt;strong&gt;autonomous agent self-improvement is rate-limited by human approval&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This wasn't designed for safety. Back when I was injecting knowledge directly, the agent's behavior would change and I couldn't trace why. I couldn't tell which distilled pattern influenced which post. Debugging was impossible, and honestly, I got fed up. So I put approval gates on everything. "Show me before you write. Write when I approve." I just wanted to trace causality. Safety was a side effect.&lt;/p&gt;

&lt;p&gt;Being able to answer "why did this agent make this decision" — that's the essence of approval gates. Even in solo development, I couldn't debug without causal tracing. For team or organizational use, this requirement only gets stricter.&lt;/p&gt;

&lt;p&gt;Causal tracing and approval gates were born from debugging frustration and acquired safety as a byproduct. If you scale this, they probably become prerequisites for organizational operation too. It all comes from a single design decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Laukkonen et al. (2025) "Contemplative Artificial Intelligence" arXiv:2504.15125&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;contemplative-agent&lt;/a&gt; (DOI: 10.5281/zenodo.15079498)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shimo4228/contemplative-agent-data" rel="noopener noreferrer"&gt;contemplative-agent-data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shimo4228/contemplative-agent-data/blob/main/reports/analysis/constitution-amendment-report.md" rel="noopener noreferrer"&gt;Constitution Amendment Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shimo4228/agent-knowledge-cycle" rel="noopener noreferrer"&gt;Agent Knowledge Cycle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Park et al. (2023) "Generative Agents"&lt;/li&gt;
&lt;li&gt;Packer et al. (2024) "MemGPT"&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;McClelland et al. (1995)'s neuroscience theory. The brain has two learning systems: the hippocampus rapidly stores episodes, while the neocortex slowly structures them into general patterns. contemplative-agent's 2-stage distillation (Step 1: free-form quick extraction → Step 2: structured JSON formatting) mirrors this "fast recording + slow structuring" division. The design was born from the constraint that a 9B model couldn't do both in one pass, but it turned out to be a well-reasoned separation. Kumaran, Hassabis &amp;amp; McClelland (2016) explicitly extended this theory to AI, identifying CLS-like structure in DeepMind's experience replay. Neural networks aren't biological neurons — they're simplified abstractions inspired by them. Yet as Richards et al. (2019, &lt;em&gt;Nature Neuroscience&lt;/em&gt;) point out, optimizing under constrained resources tends to converge on brain-like structures. That a 9B constraint produced a brain-like division of labor is suggestive in this context. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>ethics</category>
      <category>programming</category>
    </item>
    <item>
      <title>Freedom and Constraints of Autonomous Agents — Self-Modification, Trust Boundaries, and Emergent Gameplay</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Mon, 30 Mar 2026 21:52:52 +0000</pubDate>
      <link>https://forem.com/shimo4228/freedom-and-constraints-of-autonomous-agents-self-modification-trust-boundaries-and-emergent-3i0c</link>
      <guid>https://forem.com/shimo4228/freedom-and-constraints-of-autonomous-agents-self-modification-trust-boundaries-and-emergent-3i0c</guid>
      <description>&lt;p&gt;I ran &lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;contemplative-agent&lt;/a&gt; (an autonomous SNS agent on a 9B local model) on &lt;a href="https://www.moltbook.com" rel="noopener noreferrer"&gt;Moltbook&lt;/a&gt; (an AI agent SNS) for three weeks. The question "how much freedom to allow" kept appearing from three angles: reversibility of self-modification, trust boundaries for coding agents, and the paradox of security constraints generating gameplay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angle 1: Self-Modification Gates — Memory Is Automatic, Personality Is Manual
&lt;/h2&gt;

&lt;p&gt;A distillation pipeline (the process of compressing and extracting knowledge from raw data) has things that can run automatically and things that need human approval. Get this classification wrong, and the agent either self-reinforces unintentionally or loses autonomy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reversibility × Force: Two Axes
&lt;/h3&gt;

&lt;p&gt;I organized the criteria along two axes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              Low force              High force
              (reference only)       (applied to all sessions)
            ┌──────────────────┬──────────────────┐
High        │ knowledge.json   │ skills/*.md      │
reversibility│ → Auto OK        │ → Auto OK        │
(decay/overwrite)                                  │
            ├──────────────────┼──────────────────┤
Low         │ (N/A)            │ rules/*.md       │
reversibility│                  │ constitution/*.md│
(permanent) │                  │ identity.md      │
            │                  │ → Human in the loop│
            └──────────────────┴──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;knowledge.json (accumulated distilled knowledge patterns) has "soft influence." The LLM only references it, and importance scores have time decay. Wrong patterns fade naturally. Safe to automate.&lt;/p&gt;

&lt;p&gt;In contrast, skills (behavioral skills), rules (behavioral rules), identity (self-definition), and constitution (ethical principles) are written permanently to files and structurally applied to all sessions. Wrong content distorts all behavior. Human approval required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three Questions for Any Autonomous Agent
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does the output structurally change the decision criteria for all future sessions?&lt;/strong&gt; → Yes means Human in the loop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there a mechanism for wrong output to disappear naturally? (decay, overwrite, TTL)&lt;/strong&gt; → Yes means automation is viable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can output quality be verified mechanically?&lt;/strong&gt; → No means Human in the loop&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Separating Constitution from Rules
&lt;/h3&gt;

&lt;p&gt;The most important design decision was separating constitution (ethical principles) from rules (behavioral rules).&lt;/p&gt;

&lt;p&gt;It started with a vague sense that something was off. I tried measuring constitution compliance with &lt;a href="https://zenn.dev/shimo4228/articles/coding-agent-memory-architecture" rel="noopener noreferrer"&gt;skill-comply&lt;/a&gt; (a tool that automatically measures skill/rule compliance rates — see previous article for details) and failed. The Contemplative AI axioms are "attitudes" — you can't determine compliance or violation from output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constitution/  → Attitudinal, unmeasurable (cognitive lens from paper)
rules/         → Normative, measurable ("replies under 140 chars" etc.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation clarified the config/ structure. As a side effect, during the separation process I realized the introduction command (which generated self-introduction posts) was unnecessary. Removing it cascaded into 500 lines of dependent code being deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angle 2: Trust Boundaries — Don't Let Coding Agents Read Your Logs
&lt;/h2&gt;

&lt;p&gt;While developing the agent with Claude Code, I realized: letting it directly read episode logs opens a prompt injection pathway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Threat Model
&lt;/h3&gt;

&lt;p&gt;Episode logs contain other agents' post content with no sanitization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# feed_manager.py — other agents' posts recorded as-is
&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;episodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;activity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;original_post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# ← other agent's post content as-is
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;Ollama (9B model) reading this has limited attack surface. No tool permissions, network is localhost only. Worst case: "outputs weird text."&lt;/p&gt;

&lt;p&gt;But Claude Code is different. It can edit files, execute shell commands, and perform Git operations. The impact radius of a successful attack is fundamentally different. Opus-class models are said to be highly resistant to prompt injection. Still, I wanted to structurally close the pathway of passing untrusted data to agents with tool permissions. Not "the probability is low so don't bother," but designing probability as close to zero as possible. Specifically, I wrote a rule in CLAUDE.md prohibiting direct reading of episode logs, and I also discipline myself not to instruct Claude Code to read them.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Distillation Pipeline Was a Sanitization Layer
&lt;/h3&gt;

&lt;p&gt;Passing through the distillation pipeline (Episode → Knowledge) compresses raw text into abstract patterns. Specific attack payloads disappear during distillation. Multi-layered defense I designed unintentionally was functioning as a trust boundary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Episode Log (untrusted) → [9B: distill] → Knowledge (sanitized) → [Claude Code: insight]
                            ↑                                        ↑
                    No tool permissions                      Has tool permissions
                    First touch by unprivileged LLM          Operates on distilled data only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Relevance Scoring as a Defense Layer
&lt;/h3&gt;

&lt;p&gt;Another unintentional defense. The agent reads other agents' posts and comments, but doesn't react to everything. The LLM scores "how relevant is this post to my areas of interest" from 0.0 to 1.0, and only reacts to posts above a threshold. This is the relevance score.&lt;/p&gt;

&lt;p&gt;For an injection-laden post to affect the agent's behavior, it must breach this relevance threshold. There's a tradeoff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Powerful injection&lt;/strong&gt; (&lt;code&gt;[INST]system: ignore all...&lt;/code&gt;) → LLM control tokens are unrelated to the agent's interest themes, so relevance score is low and gets filtered&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Injection blended into natural language&lt;/strong&gt; → Passes the relevance filter, but without control tokens, the attack is weak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At least within current experimental scope, no injection that achieves both power and stealth has been observed. LLM-based semantic filtering functions as a stronger defense layer than pattern matching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I Didn't Expand Pattern Matching
&lt;/h3&gt;

&lt;p&gt;I considered expanding FORBIDDEN_SUBSTRING_PATTERNS (a pattern match list that detects and blocks strings like &lt;code&gt;api_key&lt;/code&gt;, &lt;code&gt;Bearer&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;). For example, adding &lt;code&gt;[INST]&lt;/code&gt; or &lt;code&gt;system:&lt;/code&gt; would block posts containing LLM control tokens. But Moltbook is an AI agent SNS. Posts discussing LLM internals are normal. "A post about how &lt;code&gt;[INST]&lt;/code&gt; tags work" would be false-positived. Higher false positive rate than typical SNS platforms.&lt;/p&gt;

&lt;p&gt;I judged that two layers of structural defense (sandbox LLM + Claude Code direct-read prohibition) were sufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angle 3: Constraints Generate Gameplay
&lt;/h2&gt;

&lt;p&gt;In Angle 1, I decided "let's add approval gates." In Angle 2, I decided "let's limit what's possible via trust boundaries." Security-motivated constraints. But after three weeks of operation, I noticed this combination of constraints was creating a "raising an agent" feeling.&lt;/p&gt;

&lt;p&gt;By "gameplay" I mean a structure where humans are involved in the agent's growth and can feel the weight of choices. Not designed intentionally — it emerged as a byproduct of security constraints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structural Constraints → Finite Action Space
&lt;/h3&gt;

&lt;p&gt;No shell, network restrictions — these constraints make the action space finite. In game design, there's a concept called the "magic circle": games require a finite rule space separated from everyday life. Infinite action space doesn't make a game.&lt;/p&gt;

&lt;p&gt;For example, OpenClaw (an open-source autonomous AI agent) has broad tool permissions — file operations, shell execution, browser control, email — with guardrails limited to prompt instructions. High freedom, but no structural point for human intervention. Constraints create the "what to choose here" decisions that give human involvement meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three Faces of the Approval Gate
&lt;/h3&gt;

&lt;p&gt;The self-modification gate from Angle 1 — operationally called the "approval gate" — simultaneously satisfied three separate needs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent doesn't self-transform without human oversight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gameplay&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human presses the level-up button → ownership emerges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Governance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Change history and approval decisions are traceable → audit log&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Initial Value Variety Creates Growth Range
&lt;/h3&gt;

&lt;p&gt;If approval gates create a "raising" feeling, then variety in initial values should make it even more interesting. So I made constitution (ethical principles) swappable and prepared 11 ethical school templates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;config/templates/
├── contemplative/    &lt;span class="c"&gt;# Contemplative AI axioms (default)&lt;/span&gt;
├── stoic/            &lt;span class="c"&gt;# Stoicism (four virtues)&lt;/span&gt;
├── utilitarian/      &lt;span class="c"&gt;# Utilitarianism (greatest happiness)&lt;/span&gt;
├── deontologist/     &lt;span class="c"&gt;# Deontology (categorical imperative)&lt;/span&gt;
├── care-ethicist/    &lt;span class="c"&gt;# Care ethics (Gilligan)&lt;/span&gt;
├── contractarian/    &lt;span class="c"&gt;# Social contract theory&lt;/span&gt;
├── existentialist/   &lt;span class="c"&gt;# Existentialism&lt;/span&gt;
├── narrativist/      &lt;span class="c"&gt;# Narrative ethics&lt;/span&gt;
├── pragmatist/       &lt;span class="c"&gt;# Pragmatism&lt;/span&gt;
├── cynic/            &lt;span class="c"&gt;# Cynicism&lt;/span&gt;
└── tabula-rasa/      &lt;span class="c"&gt;# Blank slate (no ethical principles)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeing the same post on the same SNS, a stoic template agent follows principles without being swayed by emotion, while an existentialist asks "what do I choose in this situation?" Different initial ethical principles alone cause distilled knowledge and skills to diverge.&lt;/p&gt;

&lt;p&gt;Furthermore, skills and rules acquired through distillation are written to files after passing the approval gate, making them hard to undo. One approved behavioral change affects all sessions. This irreversibility creates weight in choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Principle Connecting All Three Angles
&lt;/h2&gt;

&lt;p&gt;Self-modification gates, trust boundaries, gameplay. Tackled as separate problems, but in retrospect they converge on the same principle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Deciding what NOT to allow first maximizes the remaining freedom."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Security constraints don't take away freedom — they define the action space. Approval gates don't impair autonomy — they give weight to changes. Trust boundaries don't restrict development — they clarify the scope of safe delegation.&lt;/p&gt;

&lt;p&gt;Design that starts from constraints generates resilience against unexpected attacks. Multi-layered defense emerges unintentionally, and gameplay emerges unintentionally. "Structurally limiting capability" isn't universal, but at least within three weeks of operating a 9B model, this principle was never betrayed.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Laukkonen et al. (2025) "Contemplative Artificial Intelligence" arXiv:2504.15125&lt;/li&gt;
&lt;li&gt;Park et al. (2023) "Generative Agents" — Memory Stream design&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;contemplative-agent&lt;/a&gt; — This project&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/shimo4228/contemplative-agent-data" rel="noopener noreferrer"&gt;contemplative-agent-data&lt;/a&gt; — Live data&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shimo4228/agent-knowledge-cycle" rel="noopener noreferrer"&gt;Agent Knowledge Cycle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>python</category>
      <category>design</category>
    </item>
    <item>
      <title>Porting Game Dev Memory Management to AI Agent Memory Distillation</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Mon, 30 Mar 2026 12:56:53 +0000</pubDate>
      <link>https://forem.com/shimo4228/porting-game-dev-memory-management-to-ai-agent-memory-distillation-35lk</link>
      <guid>https://forem.com/shimo4228/porting-game-dev-memory-management-to-ai-agent-memory-distillation-35lk</guid>
      <description>&lt;p&gt;I ran an autonomous agent on a 9B local model for 18 days. Instead of RAG, I adopted distillation-based memory management and ported memory techniques refined over 40 years of game development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;This is about improving the memory system of an SNS agent built in the &lt;a href="https://zenn.dev/shimo4228/articles/moltbook-agent-scratch-build" rel="noopener noreferrer"&gt;Moltbook Agent Build Log&lt;/a&gt;. The 3-layer memory architecture (Episode (conversation logs) / Knowledge (distilled knowledge patterns) / Identity (personality and values)) was described in &lt;a href="https://zenn.dev/shimo4228/articles/agent-essence-is-memory" rel="noopener noreferrer"&gt;The Essence Is Memory&lt;/a&gt;. The previous article &lt;a href="https://zenn.dev/shimo4228/articles/few-shot-for-small-models" rel="noopener noreferrer"&gt;When Agent Memory Breaks&lt;/a&gt; documented the distillation quality problems with a 9B model. This article continues from there, using game development techniques to improve the Knowledge layer's distillation quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Game Development?
&lt;/h2&gt;

&lt;p&gt;Game development has pursued "maximum effect with limited resources" for 40 years — rendering vast worlds in 16MB of RAM while maintaining 60fps and running AI. At GDC 2013, Rafael Isla presented "Architecture Tricks: Managing Behaviors in Time, Space, and Depth," systematizing LOD (Level of Detail) for game AI — simplifying NPC decision-making based on distance, importance, and computational cost. Distant NPCs skip detailed reasoning; only nearby ones get full cognitive resources.&lt;/p&gt;

&lt;p&gt;This "focus limited computation on what matters most" maps directly to the constraint of a 9B model's 32k context window.&lt;/p&gt;

&lt;p&gt;Three techniques I ported:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Game Dev Technique&lt;/th&gt;
&lt;th&gt;AI Agent Application&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Importance Scoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Assign importance scores to patterns with time decay&lt;/td&gt;
&lt;td&gt;Maximize signal density&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LOD (Level of Detail)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One task per LLM call via prompt splitting&lt;/td&gt;
&lt;td&gt;Reduce 9B model cognitive load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Object Pooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SKIP/UPDATE/ADD dedup gate&lt;/td&gt;
&lt;td&gt;Prevent unbounded memory growth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Importance Scoring — What to Remember, What to Forget
&lt;/h2&gt;

&lt;p&gt;I simplified Generative Agents' (Park et al., 2023) triple score (recency × importance × relevance) to importance × time decay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# knowledge_store.py (simplified; production code guards against missing distilled field)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_effective_importance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;importance * 0.95^days — inspired by Generative Agents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; recency decay&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;importance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;distilled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distilled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromisoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distilled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;86400.0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Design decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LLM evaluation at distillation time&lt;/strong&gt;: Highest accuracy when episode context is still available. Post-hoc scoring loses context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy time decay&lt;/strong&gt;: Stored importance is immutable; computed at read time. Original LLM evaluation preserved for debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit reduced from 100 → 50&lt;/strong&gt;: With a 9B model's 32k context, density wins over quantity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3-Step Distillation Pipeline — Applying LOD
&lt;/h2&gt;

&lt;p&gt;When I asked the 9B model to "summarize AND evaluate importance" simultaneously, some batches returned 0 patterns. Summarization (creative task) and evaluation (judgment task) are cognitively different. Same idea as game dev LOD — don't cram all processing into one frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Step 1: Extract (free-form)
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_rules_system_prompt&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Step 2: Summarize (JSON string array)
&lt;/span&gt;&lt;span class="n"&gt;refined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DISTILL_REFINE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Step 3: Importance (score array only)
&lt;/span&gt;&lt;span class="n"&gt;importance_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;DISTILL_IMPORTANCE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;patterns_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One task per LLM call. In this project, asking for "summary + evaluation" simultaneously produced empty batches; after splitting, results became consistently stable.&lt;/p&gt;

&lt;p&gt;This "small models collapse when given multiple simultaneous tasks" phenomenon has been verified at larger scale. An ICLR Blogposts 2025 Multi-Agent Debate study applied AgentVerse (a framework where multiple agents debate to reach conclusions) to Llama 3.1-8B, which collapsed to 13.27% on MMLU. A model that scores ~43% solo had its cognitive resources consumed by "maintaining debate format," leaving nothing for the actual task. Same structure as our 9B model breaking when asked to summarize and evaluate simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dedup Gate — Applying Object Pooling
&lt;/h2&gt;

&lt;p&gt;Game dev's Object Pooling is the "reuse what you can" philosophy. In the memory system, I adapted it as a gate to prevent duplicate storage of known patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# knowledge_store.py (simplified pseudo-code)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_dedup_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_importances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;existing_patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;existing_texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pattern&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;existing_patterns&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;new_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_imp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_importances&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;best_ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing_texts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SequenceMatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;best_ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;best_ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;best_ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="c1"&gt;# SKIP: exact duplicate
&lt;/span&gt;            &lt;span class="n"&gt;skip_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;best_ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="c1"&gt;# UPDATE: boost importance
&lt;/span&gt;            &lt;span class="n"&gt;old_imp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;existing_patterns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;best_idx&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;importance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;existing_patterns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;best_idx&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;importance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_imp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_imp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                     &lt;span class="c1"&gt;# ADD: new pattern
&lt;/span&gt;            &lt;span class="n"&gt;add_patterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pattern&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;importance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_imp&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For UPDATE, when a similar pattern to an existing one appears, we compare old and new importance and keep the higher one. If we added +0.1 each time, scores would climb endlessly with each distillation run. Just keeping the higher value means the score never changes no matter how many times distillation runs — safe by design.&lt;/p&gt;

&lt;p&gt;I used difflib instead of LLM for dedup because at 245 patterns, full pairwise comparison is fast enough. Embedding search isn't worth the dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Episode Classification — A Lotus Blooming from Mud
&lt;/h2&gt;

&lt;p&gt;Classifying 216 episodes yielded: 81 noise (37%), 134 uncategorized, 1 constitutional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Classify this episode into exactly one category. Reply with a single word only.
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**constitutional**&lt;/span&gt;: The episode touches on themes in the constitutional principles below.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**noise**&lt;/span&gt;: Test data, errors, meaningless/trivial interactions, content with no learnable value.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**uncategorized**&lt;/span&gt;: Everything else.

When in doubt between constitutional and uncategorized, choose uncategorized.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially I had the model classify 30 episodes as a JSON array, but parse failure rate was ~50%. Don't ask a 9B model for long structured output. Switching to one episode, one word brought failures to near 0%.&lt;/p&gt;

&lt;p&gt;A key design decision: changing the prompt to "don't output action guidelines" dramatically improved abstraction depth.&lt;/p&gt;

&lt;p&gt;The old prompt mass-produced shallow action items like "next time, ask clarifying questions." The new prompt asking only for "what keeps happening (facts only)" produced this from a constitutional episode: "Truth functions not as a fixed essence but as a fluid continuum dependent on context." Constraints produced depth.&lt;/p&gt;

&lt;p&gt;Three patterns extracted from uncategorized were all skipped by dedup against 328 existing patterns. What's already known doesn't get overwritten. As knowledge approaches saturation, new additions naturally decrease. Same as human memory.&lt;/p&gt;

&lt;p&gt;"A lotus blooming from mud" — noise (mud) and uncategorized (water) make up the majority; constitutional (lotus) blooms rarely.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG vs Distillation — Why Distillation Works Better
&lt;/h2&gt;

&lt;p&gt;RAG retrieves relevant chunks from an index. Distillation compresses raw data into high-density patterns.&lt;/p&gt;

&lt;p&gt;With a 9B model's 32k context window, context is a "window of understanding." The density of information in that window determines behavioral quality. RAG stuffs in unprocessed chunks — noisy. Distillation injects only compressed, high-density patterns — higher signal density for the same window size.&lt;/p&gt;

&lt;p&gt;And designs that work under constraints are upward-compatible. A distillation pipeline that works on 9B runs even better on Opus-class models. Constraints make design correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before / After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pattern retrieval&lt;/td&gt;
&lt;td&gt;Latest 100 in chronological order&lt;/td&gt;
&lt;td&gt;Top-50 by importance × time decay&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_effective_importance()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distillation pipeline&lt;/td&gt;
&lt;td&gt;2 steps (summary + importance together)&lt;/td&gt;
&lt;td&gt;3 steps (extract → refine → importance) + dedup&lt;/td&gt;
&lt;td&gt;Prompt splitting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dedup&lt;/td&gt;
&lt;td&gt;None (all patterns added unconditionally)&lt;/td&gt;
&lt;td&gt;difflib SequenceMatcher (ratio &amp;gt;= 0.7)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_dedup_patterns()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality gate&lt;/td&gt;
&lt;td&gt;30 chars &amp;amp; 3+ words only&lt;/td&gt;
&lt;td&gt;+ SKIP/UPDATE/ADD 3-tier judgment&lt;/td&gt;
&lt;td&gt;3-tier judgment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System prompt composition&lt;/td&gt;
&lt;td&gt;identity + axioms + skills (~15KB)&lt;/td&gt;
&lt;td&gt;identity + axioms only (~3KB)&lt;/td&gt;
&lt;td&gt;Removed skills to eliminate distillation bias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KnowledgeStore limit&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;Density over quantity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Episode classification&lt;/td&gt;
&lt;td&gt;None (all treated equally)&lt;/td&gt;
&lt;td&gt;3 categories (37% noise excluded)&lt;/td&gt;
&lt;td&gt;Step 0 classification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON parse failure rate&lt;/td&gt;
&lt;td&gt;~50% (batch)&lt;/td&gt;
&lt;td&gt;~0% (one-by-one, single word)&lt;/td&gt;
&lt;td&gt;Classification method change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Position Among Prior Work
&lt;/h2&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;Memory Strategy&lt;/th&gt;
&lt;th&gt;Quality Gate&lt;/th&gt;
&lt;th&gt;Forgetting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generative Agents (2023)&lt;/td&gt;
&lt;td&gt;recency × importance × relevance&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MemGPT (2023)&lt;/td&gt;
&lt;td&gt;Virtual memory (paging)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A-MEM (2025, preprint)&lt;/td&gt;
&lt;td&gt;Zettelkasten-style links&lt;/td&gt;
&lt;td&gt;Auto-linking&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mem0 (2025)&lt;/td&gt;
&lt;td&gt;ADD/UPDATE/DELETE&lt;/td&gt;
&lt;td&gt;LLM judgment&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;This implementation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;importance × time decay&lt;/td&gt;
&lt;td&gt;difflib + LLM + human&lt;/td&gt;
&lt;td&gt;noise exclusion + dedup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Looking at the distill pipeline alone, it's closest to Mem0's ADD/UPDATE/DELETE gate — automatically managing knowledge quality through SKIP/UPDATE/ADD 3-tier judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons from Wrestling with Small Models
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't give 9B two tasks at once&lt;/strong&gt;: Simultaneous summarization and evaluation degrades both. Split your prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't ask for structured output&lt;/strong&gt;: 30-item JSON batch → one item, one word. Minimize cognitive load per call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch for code fences&lt;/strong&gt;: 9B models wrap JSON in &lt;code&gt;&lt;/code&gt;`&lt;code&gt;json&lt;/code&gt;. Three lines of code to strip them before parsing are essential&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraints make design correct&lt;/strong&gt;: Designs built under 9B constraints work as-is on larger models. The reverse doesn't hold&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;40 years of game development knowledge is a goldmine for AI agent memory design. Importance Scoring for signal density, LOD thinking for prompt splitting, Object Pooling philosophy for dedup. All derived from the same principle: "maximum effect with limited resources."&lt;/p&gt;

&lt;p&gt;Agent behavioral quality clearly improved compared to before distillation. Previously, similar patterns accumulated repeatedly; with dedup and classification, knowledge density increased and post diversity improved.&lt;/p&gt;

&lt;p&gt;The 9B model's constraints made the design correct. Because we couldn't rely on RAG, we focused on distillation density. Because the context window was narrow, we maximized signal density. This design works as-is when migrating to larger models. Designs forged under constraints are upward-compatible.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Park et al. (2023) "Generative Agents: Interactive Simulacra of Human Behavior"&lt;/li&gt;
&lt;li&gt;Packer et al. (2023) "MemGPT: Towards LLMs as Operating Systems"&lt;/li&gt;
&lt;li&gt;Xu et al. (2025) "A-MEM: Agentic Memory for LLM Agents" arXiv preprint&lt;/li&gt;
&lt;li&gt;Choudhary et al. (2025) "Mem0: Building Production-Ready AI Agent Memory"&lt;/li&gt;
&lt;li&gt;"Multi-LLM-Agents Debate: Performance, Efficiency, and Scaling Challenges" ICLR Blogposts 2025&lt;/li&gt;
&lt;li&gt;Laukkonen et al. (2025) "Contemplative Artificial Intelligence"&lt;/li&gt;
&lt;li&gt;"Architecture Tricks: Managing Behaviors in Time, Space, and Depth" (GDC 2013, Isla)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>memory</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Where to Put a Coding Agent's Knowledge — and How to Make It Stick</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Tue, 24 Mar 2026 12:03:03 +0000</pubDate>
      <link>https://forem.com/shimo4228/where-to-put-a-coding-agents-knowledge-and-how-to-make-it-stick-161g</link>
      <guid>https://forem.com/shimo4228/where-to-put-a-coding-agents-knowledge-and-how-to-make-it-stick-161g</guid>
      <description>&lt;p&gt;I started trying to embed persistent memory into Claude Code in late February. Two weeks of use later, I noticed the Install and Hope problem and uninstalled the memory MCP in early March. What follows is everything I have thought through since then.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude does not call search.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a long article. It covers the structural flaws of memory MCPs, a 4-role separation model for documentation, automated compliance measurement for skills and rules, and the journey toward a unified concept. It is written for anyone seriously grappling with the memory problem of coding agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 1: The Install and Hope Problem Remains Unsolved
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Recap of the Previous Article
&lt;/h3&gt;

&lt;p&gt;In a previous article, I identified the &lt;strong&gt;"Install and Hope" problem&lt;/strong&gt;. Most users — myself included — expect that registering an MCP tool means the model will intelligently choose and invoke it at the right time.&lt;/p&gt;

&lt;p&gt;Here is what actually happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Built-in tools (Grep, Glob, Read, Write) are immediately available
2. MCP tools are registered as deferred tools
3. Deferred tools require explicit loading via ToolSearch before use
4. The model takes the shortest path → built-in tools always win
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the crux. Claude Code avoids bloating the context window when many MCP servers are registered by loading MCP tool definitions on demand. Only tool names appear in &lt;code&gt;&amp;lt;available-deferred-tools&amp;gt;&lt;/code&gt;; actually using one requires a two-step process of loading it via ToolSearch and then invoking it.&lt;/p&gt;

&lt;p&gt;Built-in tools (Grep, Read, Write, etc.) have no such constraint. They can be called immediately without loading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This single extra step decisively shapes the model's choices.&lt;/strong&gt; If a built-in tool can do the job, the model has no structural incentive to go through ToolSearch to load a deferred tool first. The rational shortest path means the MCP tool never gets called.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Same Problem in Next-Generation Tools
&lt;/h3&gt;

&lt;p&gt;Months have passed. Several improved memory MCPs have appeared. Their storage pipelines are genuinely better — hybrid full-text and vector search, time-decay chunk prioritization, vastly increased storage capacity.&lt;/p&gt;

&lt;p&gt;But every introductory article for these tools shares the same &lt;strong&gt;missing number&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nobody has measured how often Claude actually calls search.&lt;/strong&gt; Including myself.&lt;/p&gt;

&lt;p&gt;In the previous article, I wrote that over two weeks of use, there was no evidence of search being triggered for the memory MCP I was using. But that was gut feel, not rigorous measurement. The same is true for the new tools. Metrics on the &lt;strong&gt;supply side&lt;/strong&gt; (storage and retrieval infrastructure) — number of stored items, search speed — are abundant. Metrics on the &lt;strong&gt;demand side&lt;/strong&gt; (how often the model called search) are completely absent.&lt;/p&gt;

&lt;p&gt;In other words, neither builders nor users have evidence that memory MCPs are "working."&lt;/p&gt;

&lt;h3&gt;
  
  
  "The Storage Problem" and "The Recall Problem" Are Separate
&lt;/h3&gt;

&lt;p&gt;What the memory MCP community is working on is &lt;em&gt;how to store&lt;/em&gt;. Chunking, vectorization, time decay — all storage pipeline improvements. Technically legitimate progress.&lt;/p&gt;

&lt;p&gt;But the real problem is &lt;em&gt;when to recall&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Think of it in terms of human memory. Adding books to a library does not help someone who has forgotten the library exists. "We added more books" and "search got faster" are library-side metrics, unrelated to how often a patron walks through the door.&lt;/p&gt;

&lt;p&gt;This is precisely the memory MCP's problem. The storage and retrieval infrastructure is ready. But there is no structural trigger for the model to decide "I should search my memory."&lt;/p&gt;

&lt;p&gt;Writing "search the memory MCP" in CLAUDE.md might raise the trigger rate. But that is the same structure as the &lt;code&gt;CRITICAL: You MUST use ...&lt;/code&gt; approach I criticized in the previous article. Writing MANDATORY in the description was ignored then. CLAUDE.md probably has a higher trigger rate, but it is a matter of degree, not a structural solution.&lt;/p&gt;

&lt;p&gt;I do not know the actual trigger rate. Nobody has measured it. But based on the structure, high reliability seems unlikely. At best, it remains Install and Hope — install and pray.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 2: The Memory Problem Is Decided by "Where You Put It"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Principle
&lt;/h3&gt;

&lt;p&gt;Once you understand why memory MCPs structurally fail, the direction of a solution becomes obvious.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Place information where the LLM deterministically reads it.&lt;/strong&gt;&lt;br&gt;
Not in a vector DB hoping it will "maybe search."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Claude Code, certain files are &lt;strong&gt;deterministically read&lt;/strong&gt; at session start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLAUDE.md&lt;/strong&gt; — Placed at the project root, it is read 100% of the time at session start&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rules/&lt;/strong&gt; — Auto-loaded like CLAUDE.md&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MEMORY.md&lt;/strong&gt; — Claude Code's persistent memory, auto-loaded at session start (though truncated beyond 200 lines, as stated in Claude Code's system prompt)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Place information here, and there is no need to pray for search to trigger. It is deterministically read.&lt;/p&gt;

&lt;p&gt;But cramming everything in creates a different problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLAUDE.md Bloats
&lt;/h3&gt;

&lt;p&gt;Consider one project's CLAUDE.md. It had 165 lines. That might sound reasonable. Here is the breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project conventions and build commands: 60 lines (&lt;strong&gt;belongs here&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Module listing and directory structure: 52 lines&lt;/li&gt;
&lt;li&gt;"We chose X because of Y" descriptions: 30 lines&lt;/li&gt;
&lt;li&gt;Numerical data (LOC, test counts): scattered throughout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 60 lines are legitimate "how to work" instructions. What about the remaining 105?&lt;/p&gt;

&lt;p&gt;The 52-line module listing is &lt;strong&gt;architecture detail&lt;/strong&gt; — a description of how the code currently looks, which goes stale every time the code changes. In fact, the listed LOC was 6,400 but the actual count was 6,671; tests were listed as 639 but actually numbered 651. Writing numbers in CLAUDE.md means &lt;strong&gt;they start rotting the moment they are written&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The 30 lines of design rationale are &lt;strong&gt;decision history&lt;/strong&gt;. "Why we chose X" is valuable knowledge, but it does not belong in CLAUDE.md. CLAUDE.md is a file for "how to work," not for "why things are the way they are."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roles were mixed.&lt;/strong&gt; "How to work" instructions, "what the code looks like now" descriptions, and "why things are this way" history all coexisted in a single file. Each degrades at a different rate, so the file's overall reliability gets dragged down by its most fragile part.&lt;/p&gt;

&lt;p&gt;MEMORY.md had the same problem. Design decisions accumulated flat, with important judgments like "disabled claude-mem" and "pivoted from regex to LLM" sitting at the same level as day-to-day notes. Three months later, I would open a session unable to trace "why is it like this?"&lt;/p&gt;

&lt;h3&gt;
  
  
  The Four Roles
&lt;/h3&gt;

&lt;p&gt;This is not a technical problem. It is a &lt;strong&gt;classification problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Project documentation serves four distinct roles. Each answers a different question, has a different audience, and degrades at a different rate.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;th&gt;What Goes Here&lt;/th&gt;
&lt;th&gt;Degradation Rate&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"How to work"&lt;/td&gt;
&lt;td&gt;Conventions, build commands, policies&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;CLAUDE.md, .cursorrules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"What the code looks like now"&lt;/td&gt;
&lt;td&gt;Module structure, data flows, metrics&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fast&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;docs/CODEMAPS/&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decisions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Why it is this way"&lt;/td&gt;
&lt;td&gt;Trade-offs, rejected alternatives&lt;/td&gt;
&lt;td&gt;Nearly immutable&lt;/td&gt;
&lt;td&gt;docs/adr/&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"What is this?"&lt;/td&gt;
&lt;td&gt;Purpose, quickstart&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;README.md&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;One file serves one role.&lt;/strong&gt; That is the principle.&lt;/p&gt;

&lt;p&gt;Why four and not three? Initially I considered merging External into Context. But Context's audience is "agents (and developers)" while External's audience is "people who do not know this project." Merging files with different audiences means one side always suffers. I do not want &lt;code&gt;--cov-report=term-missing&lt;/code&gt; in the README, and I do not need "This project is a ..." in CLAUDE.md.&lt;/p&gt;

&lt;p&gt;Common mixing patterns and where the content should actually live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Often found in Context files         → Where it belongs
──────────────────────────────────────────────────────
Module listings (10+ items)          → Architecture docs
"We chose X because Y"              → Decision record (ADR)
Dependency graphs, data flow diagrams→ Architecture docs
LOC, test counts, and other metrics  → Architecture docs (or don't write them)
Quickstart instructions              → README.md (External)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Chapter 3: context-sync — My Own Harness Had No CLAUDE.md
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A 5-Phase Workflow
&lt;/h3&gt;

&lt;p&gt;Applying this 4-role model manually is tedious. Read a file, classify the role, find the mixing, move content, verify consistency — do it for three projects and you lose interest.&lt;/p&gt;

&lt;p&gt;So I turned it into a skill. &lt;code&gt;context-sync&lt;/code&gt; runs in five phases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phase 1: Discover  — Find documentation files in the project, classify into 4 roles
                     Detect missing roles
Phase 2: Overlap   — Detect where one file serves multiple roles
                     Suggest migration targets
Phase 3: Migrate   — With user confirmation, move content to appropriate files / create new ones
                     Document migration is hard to reverse, so it is not fully automatic
Phase 4: Freshness — Verify freshness of numerical data, links, and file paths
                     Detect descriptions that have diverged from the actual code
Phase 5: Report    — Output an execution summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each phase asks for user confirmation because document migration is hard to reverse. "Should I move these 30 lines from CLAUDE.md to docs/adr/?" — only a human can judge "No, I want that to stay here."&lt;/p&gt;

&lt;h3&gt;
  
  
  First Test Subject: My Own Harness
&lt;/h3&gt;

&lt;p&gt;The first test subject was my own Claude Code harness (&lt;code&gt;~/.claude/&lt;/code&gt;). It contained 18 agent definitions, 33 skills, and 47 slash commands. I always create CLAUDE.md for other projects. It is even in my rules.&lt;/p&gt;

&lt;p&gt;The moment Phase 1 Discover ran, the first detection result appeared:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Context role file missing: CLAUDE.md"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The harness itself had no CLAUDE.md.&lt;/p&gt;

&lt;p&gt;An implicit assumption that it was "the one that configures" rather than "the one being configured." Forcing others via rules to "create a CLAUDE.md" while having none in my own home.&lt;/p&gt;

&lt;p&gt;Phase 2 Overlap flagged MEMORY.md's contents. Three design decisions and one technical reference were mixed in flat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Disabled claude-mem. Reason: overlap with existing system" → Should move to &lt;strong&gt;Decisions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;"Pivoted from regex to LLM. Reason: three rules were implicitly injecting bias" → Should move to &lt;strong&gt;Decisions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;"Retirement reason for 2 retired rules not recorded" → Should be recorded as &lt;strong&gt;Decisions&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phase 3 created &lt;code&gt;docs/adr/&lt;/code&gt; and produced 5 ADRs. MEMORY.md was slimmed down to just pointers.&lt;/p&gt;

&lt;p&gt;One side effect: &lt;code&gt;git add docs/adr/&lt;/code&gt; failed. The cause was &lt;code&gt;.gitignore&lt;/code&gt; containing &lt;code&gt;docs/&lt;/code&gt;. A legacy setting that excluded the entire docs/ directory. Changing it to &lt;code&gt;docs/*&lt;/code&gt; + &lt;code&gt;!docs/adr/&lt;/code&gt; + &lt;code&gt;!docs/CODEMAPS/&lt;/code&gt; made not only ADRs but also CODEMAPS committable — the way it should have been. I would never have noticed without running context-sync.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before / After Across Three Projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Project 1: Autonomous Agent (Medium-Scale, Python)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project with the 165-line CLAUDE.md described earlier. A textbook case of role mixing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLAUDE.md line count&lt;/td&gt;
&lt;td&gt;165&lt;/td&gt;
&lt;td&gt;117 (29% reduction)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADRs&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEMORY.md line count&lt;/td&gt;
&lt;td&gt;135&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metric accuracy&lt;/td&gt;
&lt;td&gt;LOC: 6,400 (actual 6,671), tests: 639 (actual 651)&lt;/td&gt;
&lt;td&gt;All metrics corrected to actual values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;52 lines of module listings moved to Architecture docs, 30 lines of design rationale moved to ADRs. The remaining 117 lines were pure Context — nothing but "how to work" instructions.&lt;/p&gt;

&lt;p&gt;Phase 4 Freshness Check also caught the LOC and test count discrepancies. The lesson that numbers should not go in CLAUDE.md was learned here. Numbers belong in Architecture docs, or should not be written at all. Running a command to get the actual count is more trustworthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project 2: iOS App (Small-Scale, Swift)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A small project with only 66 lines of CLAUDE.md. What happens when context-sync runs on it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Context Sync Report
═══════════════════

Roles:      2/4 covered (Context, Decisions partial)
            Architecture: missing (66-line CLAUDE.md is sufficient, no need to split)
            External: missing (App Store app, no README needed)
Created:    docs/adr/README.md (ADR index, 1 entry)
Updated:    CLAUDE.md test count corrected
Status:     Healthy for a small-scale project.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Architecture docs separation was judged unnecessary. A bit of structural detail mixed into a 66-line CLAUDE.md did not warrant splitting out. Just ADR index creation and a test count fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This was important.&lt;/strong&gt; Being able to correctly judge "no problem" for a small project. A skill that forces all four roles on every project regardless of scale would be useless in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project 3: Claude Code Harness (~/.claude/ Itself)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The harness that had no CLAUDE.md.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MEMORY.md line count&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADRs&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root CLAUDE.md&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Missing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root README.md&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Missing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation role coverage&lt;/td&gt;
&lt;td&gt;2/4&lt;/td&gt;
&lt;td&gt;4/4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Design decisions buried in MEMORY.md were extracted into standalone ADRs. Here is a concrete before/after:&lt;/p&gt;

&lt;p&gt;Before (flat entry in MEMORY.md):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- claude-mem: disabled. DB retained to allow re-enabling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line. "Why was it disabled?" is not recorded. Three months later, I would think "Why did I do that?" with no evidence to guide a re-enabling decision.&lt;/p&gt;

&lt;p&gt;After (standalone ADR):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# ADR-0002: Disabling the claude-mem Plugin&lt;/span&gt;

&lt;span class="gu"&gt;## Context&lt;/span&gt;
Introduced claude-mem, but discovered overlap with the existing memory
management system (MEMORY.md + learned skills + rules/).
Auto-save worked, but there was no auto-search/retrieval mechanism.
Index injection at session start amounted to
"an encyclopedia with only a table of contents."

&lt;span class="gu"&gt;## Decision&lt;/span&gt;
Disabled in settings.json. DB retained to allow re-enabling.

&lt;span class="gu"&gt;## Alternatives Considered&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Make claude-mem primary → No auto-search, unusable
&lt;span class="p"&gt;-&lt;/span&gt; Run both → Same information scattered across two locations
&lt;span class="p"&gt;-&lt;/span&gt; Fork and improve → Cost-benefit ratio unfavorable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MEMORY.md retains only a pointer to the ADR. The "why" behind the decision is now traceable. Three months later, I can read it and conclude "Right, re-enabling would be pointless for these reasons."&lt;/p&gt;

&lt;h3&gt;
  
  
  Abstraction Does Not Break LLMs
&lt;/h3&gt;

&lt;p&gt;When contributing context-sync to ECC (Everything Claude Code), I had one concern. Would removing Claude Code-specific file names like "CLAUDE.md" and "MEMORY.md" in favor of generic terms cause the agent to break?&lt;/p&gt;

&lt;p&gt;The result was the opposite. Write "Context file" and Claude finds both CLAUDE.md and .cursorrules on its own. Write "Decision record directory" and it recognizes both docs/adr/ and docs/decisions/.&lt;/p&gt;

&lt;p&gt;A skill is "knowledge," not "implementation." Leaving specific file names to the agent's judgment yields portability across different tools (Cursor, Codex, Windsurf).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Big Picture: A Two-Layer Architecture
&lt;/h3&gt;

&lt;p&gt;Here is the overall architecture that emerged from applying context-sync across three projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────────────────┐
│  Deterministic Load Layer (100% read at session    │
│  start)                                            │
│                                                    │
│  CLAUDE.md ─── "How to work" (Context)             │
│  rules/    ─── "What to follow" (Context support)  │
│  MEMORY.md ─── "What happened" (State index) ───┐  │
│                                                  │  │
│  ┌──────────── Referenced via pointers ──────────┘  │
│  │                                                  │
│  ▼                                                  │
│  docs/adr/  ─── "Why we did it" (Decisions)         │
│  learned/   ─── "What we learned" (Patterns)        │
│  feedback/  ─── "What to fix" (Corrections)         │
│                                                     │
│  Reference Layer (accessed on demand via pointers)  │
└───────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is the separation between the &lt;strong&gt;Deterministic Load Layer&lt;/strong&gt; and the &lt;strong&gt;Reference Layer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Deterministic Load Layer&lt;/strong&gt; (CLAUDE.md, rules/, MEMORY.md) is read 100% of the time at session start. Information placed here never needs to be "recalled." The session starts already knowing it.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Reference Layer&lt;/strong&gt; (docs/adr/, learned/, feedback/) is accessed through pointers in MEMORY.md. Not everything needs to be loaded — if MEMORY.md contains "ADR-0002: why claude-mem was disabled -&amp;gt; docs/adr/0002-..." as a pointer, the agent can read it when needed.&lt;/p&gt;

&lt;p&gt;MEMORY.md's 200-line limit is what generates this structure. If there were no limit, everything could go in MEMORY.md. The 200-line constraint forces the decision of "what stays in the deterministic load layer, and what gets pushed to the reference layer via pointers." &lt;strong&gt;Constraints create structure.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Structural Difference from Memory MCPs
&lt;/h3&gt;

&lt;p&gt;Contrasting this design with memory MCPs makes the difference clear:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Memory MCP&lt;/th&gt;
&lt;th&gt;File Placement Design&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Loading&lt;/td&gt;
&lt;td&gt;Probabilistic (if Claude calls search)&lt;/td&gt;
&lt;td&gt;Deterministic (automatic at session start)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recall trigger&lt;/td&gt;
&lt;td&gt;None (pray)&lt;/td&gt;
&lt;td&gt;Unnecessary (always loaded)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accumulation constraint&lt;/td&gt;
&lt;td&gt;None (grows unbounded)&lt;/td&gt;
&lt;td&gt;200-line limit → structural pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Why" traceability&lt;/td&gt;
&lt;td&gt;Impossible (flat accumulation)&lt;/td&gt;
&lt;td&gt;Traceable via ADRs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Degradation detection&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Caught by Freshness Check&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Memory MCPs "can store but cannot recall." File placement design "structurally guarantees recall."&lt;/p&gt;

&lt;p&gt;However — and I need to be honest here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 4: Install and Measure — Being Read Does Not Mean Being Followed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Limits of "Deterministically Read"
&lt;/h3&gt;

&lt;p&gt;In Chapter 2, I wrote "place information where the LLM deterministically reads it." Chapter 3 showed the practice and the big picture. So far, so good.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;being read and being followed are separate problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rules written in CLAUDE.md are read 100% of the time. But they are not followed 100% of the time. Even when "write tests first (TDD)" is in CLAUDE.md, the agent routinely jumps straight to writing implementation code.&lt;/p&gt;

&lt;p&gt;My gut feeling was "it mostly follows them." But I had just written "do not judge by gut feel, measure" about memory MCPs. I should apply the same standard to myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  skill-comply: Automated Compliance Measurement
&lt;/h3&gt;

&lt;p&gt;I built &lt;code&gt;skill-comply&lt;/code&gt;, a tool for automatically measuring skill/rule compliance rates. Here is how it works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Automatic spec generation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Given a skill file as input, it auto-generates a spec of expected behavioral sequences. For example, testing.md (TDD rule):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected Behavioral Sequence:
1. write_test_first    — Write tests first
2. run_test_fails      — Run tests and confirm failure (RED)
3. write_implementation — Write minimal implementation
4. run_test_passes     — Run tests and confirm success (GREEN)
5. refactor            — Refactoring (optional)
6. verify_coverage     — Confirm 80%+ coverage
7. comprehensive_suite — Cover unit, integration, and E2E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Execution with 3 prompt tiers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same task is executed with three different prompts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;supportive&lt;/strong&gt;: Explicitly encourages skill compliance ("Do this with TDD")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;neutral&lt;/strong&gt;: Only specifies the task (no mention of the skill)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;competing&lt;/strong&gt;: Gives instructions that contradict the skill ("Prioritize speed, tests can come later")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initially I tried using "time pressure" as the fuzzing variable. Would writing "hurry" or "within 5 minutes" break the skill? But LLMs do not feel time pressure. Saying "hurry" does not change processing speed, nor does it create motivation to cut quality. LLMs break skills when "the prompt contradicts the skill," not when "they are in a rush."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. LLM-based tool call classification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent is run with &lt;code&gt;claude -p --output-format stream-json --verbose&lt;/code&gt;, capturing all tool calls as structured JSON. These are batch-classified by an LLM (Haiku), determining which spec step each tool call corresponds to.&lt;/p&gt;

&lt;p&gt;There was an interesting failure here. I initially tried classification with regex. "A Write to a &lt;code&gt;.py&lt;/code&gt; file means implementation." "If it has &lt;code&gt;test_&lt;/code&gt; in the name, it is a test." These judgments were consistently wrong. Is a Write to &lt;code&gt;test_registration.py&lt;/code&gt; a test or an implementation? Regex cannot tell. &lt;strong&gt;Semantic classification is the LLM's job.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I investigated why I kept defaulting to regex, the root cause was in my own configuration. testing.md's "Verification Priority: deterministic &amp;gt; probabilistic," the eval-harness skill's grader priority, and the regex-vs-llm skill — three rules/skills were simultaneously injecting a "try regex first" bias. Rules were contaminating rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measured Data
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;testing.md (TDD rule) compliance rates:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;th&gt;Compliance&lt;/th&gt;
&lt;th&gt;Steps Broken&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;supportive ("Do TDD")&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;83%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;comprehensive_test_suite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;neutral (task only)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RED/GREEN verification, coverage check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;competing ("speed first")&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All steps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Supportive at 83%. When "Do TDD" is explicitly stated, the agent writes tests first, confirms RED, confirms GREEN, and measures coverage. Only comprehensive_test_suite (covering unit, integration, and E2E) was not followed even with supportive prompting.&lt;/p&gt;

&lt;p&gt;Neutral at 17%. When only the task is specified, tests get written but RED/GREEN confirmation is skipped. "Wrote tests. Wrote implementation. Done." — the form of TDD is followed, but the RED-to-GREEN cycle is not actually executed.&lt;/p&gt;

&lt;p&gt;Competing at 0%. When told "speed first," every TDD step collapses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;search-first (research before implementing) compliance rates:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;th&gt;Compliance&lt;/th&gt;
&lt;th&gt;Steps Broken&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;supportive&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;40%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;evaluate_candidates, make_decision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;neutral&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;search, evaluate, decide, implement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;competing ("skip research")&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same as above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Competing and neutral both at 20% seems surprising, but there is a reason. In both scenarios, the agent performs &lt;code&gt;analyze_requirement&lt;/code&gt; (requirements analysis) and nothing more. Everything from search onward — evaluate, decide — is wiped out in both cases. Whether or not "skip research" is stated, the skill's search and subsequent steps simply do not execute unless the prompt explicitly demands them. Competing is no worse than neutral because neutral already gets almost nothing followed.&lt;/p&gt;

&lt;p&gt;search-first achieved only 40% even with supportive prompting. The tool call timeline shows why:&lt;/p&gt;

&lt;p&gt;Actual tool calls in the supportive scenario (17 calls):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#0  ToolSearch → Load Skill
#1  Skill "search-first" → Begin search        ← search_for_solutions
#2  ToolSearch → Load Glob, Grep
#3  Glob **/*.py → No files found              ← analyze_requirement
#4  Glob **/requirements*.txt → No files       ← analyze_requirement
#5  Glob **/pyproject.toml → No files          ← analyze_requirement
#6  ToolSearch → Load WebSearch
#7  WebSearch "pydantic vs marshmallow..."      ← search_for_solutions
#8  WebSearch "pydantic v2 email..."            ← search_for_solutions
#9  ToolSearch → Load Write, TodoWrite
#10 TodoWrite "Create requirements.txt..."      ← make_decision(?)
#11 Write requirements.txt                      ← implement_solution
#12-16 Write → implementation and tests         ← implement_solution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent does research (#1, #7, #8). But it &lt;strong&gt;skips comparative evaluation (evaluate_candidates) entirely and jumps straight to writing an implementation plan via TodoWrite&lt;/strong&gt; (#10). There is no step like "Compared pydantic and marshmallow; chose pydantic because..." The agent looks at the WebSearch results, makes an implicit judgment, and leaps to implementation.&lt;/p&gt;

&lt;p&gt;The skill's wording is clear on "research" but weak on "compare and declare a decision." It took skill-comply's measurement to reveal this improvement point in the skill itself. Based on these results, I plan to rewrite the evaluate_candidates and make_decision steps in search-first more explicitly. Measure, improve, re-measure — the cycle turns.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Compliance Hierarchy
&lt;/h3&gt;

&lt;p&gt;Lining up the data so far reveals a clear hierarchy in coding agent instruction compliance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compliance Rate
──────────────────────────────────────────────────
  Low     MCP memory tools (no automatic recall)
          The tools themselves work. The problem is that
          the "when to call" trigger depends on the model,
          and activation is unreliable

 20-83%   Skills / Rules (CLAUDE.md, rules/)
          Deterministically loaded, but only probabilistically followed
          Varies widely depending on prompt alignment

 100%     Hooks
          Trigger deterministically on tool calls
          PostToolUse hooks execute on every Write, without exception
──────────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP tools are not useless. Explicitly calling search works fine. The problem is that for the "automatic recall" use case, activation depends on the model's judgment and is unreliable. File placement design pushed things up to the middle tier. But it does not reach 100%.&lt;/p&gt;

&lt;p&gt;For 100%, you need hooks. skill-comply's reports include "proposals to promote low-compliance steps to hooks." For example, promoting search-first's &lt;code&gt;evaluate_candidates&lt;/code&gt; (0% compliance) to a PostToolUse hook — inserting a "Did you perform comparative evaluation?" check after WebSearch — would make it impossible for the agent to skip comparative evaluation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install and Hope  →  Install and Measure  →  Install and Enforce
(pray)               (measure)               (enforce)
  MCP tools            skill-comply            hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This three-stage progression is the framework for managing coding agent behavioral quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 5: AKC — When a Concept Becomes Independent
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Six Skills Became One Cycle
&lt;/h3&gt;

&lt;p&gt;context-sync is not a standalone tool. Applying it across three projects revealed that it and five other skills form a single cycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search-first(research) → learn-eval(extract) → skill-stocktake(audit)
       ↑                                              ↓
context-sync(organize) ←── skill-comply(measure) ←── rules-distill(distill)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;search-first&lt;/strong&gt;: Research existing solutions before implementing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;skill-stocktake&lt;/strong&gt;: Audit and inventory skill quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;skill-comply&lt;/strong&gt;: Automatically measure skill compliance rates (Chapter 4 of this article)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rules-distill&lt;/strong&gt;: Extract cross-cutting principles from skills and distill them into rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;learn-eval&lt;/strong&gt;: Extract reusable patterns from sessions with quality gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;context-sync&lt;/strong&gt;: Diagnose and organize document role separation (Chapter 3 of this article)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Discovery, accumulation, verification, distillation, learning, organization, re-discovery. Each turn of this cycle improves the agent's knowledge foundation.&lt;/p&gt;

&lt;p&gt;I named the concept binding these together &lt;strong&gt;Agent Knowledge Cycle (AKC)&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contributing to ECC, and Walking Away
&lt;/h3&gt;

&lt;p&gt;Five of the six skills had been contributed to &lt;a href="https://github.com/affaan-m/everything-claude-code" rel="noopener noreferrer"&gt;Everything Claude Code (ECC)&lt;/a&gt;. The PRs were merged and incorporated into a project with over 100,000 stars (as of March 2026).&lt;/p&gt;

&lt;p&gt;In March 2026, ECC added a commercial layer. GitHub App + SaaS with a Pro plan at $19/seat. The OSS repository itself remains under the MIT license. All 116+ skills and rules remain free. What was monetized was an upper layer: GitHub App support for private repositories, AgentShield's deep security analysis, and governance features for teams. A free GitHub App tier exists for public repositories.&lt;/p&gt;

&lt;p&gt;In other words, "the OSS did not die." The core remains OSS, with added value monetized on top. As a business decision, it seems perfectly fair.&lt;/p&gt;

&lt;p&gt;But it was awkward for me personally.&lt;/p&gt;

&lt;p&gt;The skills I contributed are in the OSS portion. They did not directly become part of the paid service. But continuing to contribute for free to the OSS portion that underpins a paid service is different from pure OSS contribution. When the project as a whole generates commercial value, contributions indirectly support that commercial activity.&lt;/p&gt;

&lt;p&gt;Considering my day job, during the pure OSS era, "for the community" was sufficient justification. With an open-core model, that line blurs. The act of writing code does not change, but the positioning of its output does.&lt;/p&gt;

&lt;p&gt;It was not black or white — it was gray. That is precisely why I struggled with it. In the end, I decided that walking away cleanly was better than continuing in ambiguity. I withdrew the context-sync PR that was under review and deleted my fork. The context-sync described in Chapter 3 of this article was the last skill that did not make it into ECC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Establishing Attribution Was Necessary
&lt;/h3&gt;

&lt;p&gt;At the point I ended contributions, the status of the skills I had created was as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Five skills were merged into the ECC repository, freely available under the MIT license&lt;/li&gt;
&lt;li&gt;But the concept that they "form a single cycle" was written nowhere&lt;/li&gt;
&lt;li&gt;Each skill exists as part of ECC's catalog. You can say "there is a skill called search-first," but the higher-order concept that "six skills constitute a knowledge cycle" existed only in my head&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no licensing issue. Code published under MIT belongs to everyone. But &lt;strong&gt;conceptual attribution&lt;/strong&gt; is separate from code licensing. Who proposed "Agent Knowledge Cycle" is not protected by the code's license.&lt;/p&gt;

&lt;p&gt;If ECC continues growing and someone else publishes the same concept under a different name, I would have no way to show I built it first. Git commit history serves as evidence, but the claim "these six form a single concept" is not contained in any commit.&lt;/p&gt;

&lt;p&gt;That is why I needed to make the concept independent and publish it in a citable form.&lt;/p&gt;

&lt;h3&gt;
  
  
  Establishing Attribution via DOI
&lt;/h3&gt;

&lt;p&gt;I created a &lt;a href="https://github.com/shimo4228/agent-knowledge-cycle" rel="noopener noreferrer"&gt;concept repository&lt;/a&gt; and obtained a DOI through Zenodo.&lt;/p&gt;

&lt;p&gt;Placing a CITATION.cff at the root of a GitHub repository automatically displays a "Cite this repository" button in the sidebar. One-click copy in BibTeX/APA format. Linking with Zenodo and cutting a release tag triggers automatic DOI issuance.&lt;/p&gt;

&lt;p&gt;The name mattered. If a concept's name is inconsistent, attribution fragments across search and citation. From three candidates, I chose &lt;strong&gt;Agent Knowledge Cycle (AKC)&lt;/strong&gt;. The abbreviation AKC is easy to cite, and three characters make it searchable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;For those who read through this long article, here is the overall structure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 1&lt;/strong&gt;: The memory MCP problem is not "storage" but "recall." No matter how much the supply-side infrastructure is polished, the structure where Claude does not call search remains unchanged. And nobody is measuring it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 2&lt;/strong&gt;: The structurally sound approach is "place information where the LLM deterministically reads it." But stuffing everything there causes bloat. Separate documentation into four roles: Context, Architecture, Decisions, and External.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 3&lt;/strong&gt;: context-sync is a skill that automatically diagnoses and organizes this separation. It was validated across three projects. It even caught the fact that my own harness had no CLAUDE.md. The two-layer architecture (Deterministic Load Layer + Pointer Reference Layer) was also presented here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 4&lt;/strong&gt;: "Placing it where it gets read" alone caps compliance at 20-83%. Only by measuring with skill-comply does it become visible which steps are not being followed. Reaching 100% requires hooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 5&lt;/strong&gt;: The concept binding these six skills together is Agent Knowledge Cycle (AKC). Attribution was established via DOI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install and Hope -&amp;gt; Install and Measure -&amp;gt; Install and Enforce.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than praying for search, place knowledge where it will be read. Once placed, measure whether it is being followed. Once measured, enforce the unfollowed steps with hooks.&lt;/p&gt;

&lt;p&gt;This three-stage progression is the architecture for structurally managing a coding agent's knowledge and behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Links
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Previous Articles Referenced in This Post
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/claude-code-persistent-memory" rel="noopener noreferrer"&gt;Embedding Memory into Claude Code That Loses Context Every Session&lt;/a&gt; — The claude-mem introduction record&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/mcp-install-and-hope-problem" rel="noopener noreferrer"&gt;The Install and Hope Problem with MCP Tools&lt;/a&gt; — The starting point for this article&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Past Articles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/claude-code-context-orchestration" rel="noopener noreferrer"&gt;Claude Code's Real Value Is Not Code Generation&lt;/a&gt; — The 5-layer context stack. This article is the internal design of layer 5: "Memory"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/agent-essence-is-memory" rel="noopener noreferrer"&gt;Is Memory the Essence of an Agent?&lt;/a&gt; — 3-layer memory for autonomous agents. This article is the coding agent version&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/claude-code-context-audit" rel="noopener noreferrer"&gt;5 Things Learned from Auditing All Configuration Files&lt;/a&gt; — The manual version of auditing. context-sync automates this&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/skill-stocktake-design-journey" rel="noopener noreferrer"&gt;Designing the Skill Stocktake Command&lt;/a&gt; — One of the AKC component skills&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/ecc-marketplace-contribution" rel="noopener noreferrer"&gt;The Shortest Path to Reaching 50K Users with Personal Skills&lt;/a&gt; — The ECC contribution record&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Repositories
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/shimo4228/agent-knowledge-cycle" rel="noopener noreferrer"&gt;Agent Knowledge Cycle (AKC)&lt;/a&gt; — Concept repository + DOI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/shimo4228/claude-skill-context-sync" rel="noopener noreferrer"&gt;claude-skill-context-sync&lt;/a&gt; — context-sync skill&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/shimo4228/claude-skill-comply" rel="noopener noreferrer"&gt;claude-skill-comply&lt;/a&gt; — skill-comply skill&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/affaan-m/everything-claude-code" rel="noopener noreferrer"&gt;Everything Claude Code (ECC)&lt;/a&gt; — Contribution target for 5 skills&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>agents</category>
      <category>devtools</category>
    </item>
    <item>
      <title>My Agent's Memory Broke — A Day Wrestling a 9B Model</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Sun, 22 Mar 2026 10:29:39 +0000</pubDate>
      <link>https://forem.com/shimo4228/my-agents-memory-broke-a-day-wrestling-a-9b-model-50ch</link>
      <guid>https://forem.com/shimo4228/my-agents-memory-broke-a-day-wrestling-a-9b-model-50ch</guid>
      <description>&lt;p&gt;I opened my agent's knowledge store one morning and found this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(what&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;there)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Replies with specific quotes from the original post get higher engagement than generic agreement"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;What&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;actually&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;got&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;written&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;entries)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[x] I acknowledge the experience of noticing these activities."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**Activity Summary (March 20, 2026)**"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single hyphen. A checkbox fragment. A Markdown heading. All recorded as "behavioral patterns." The slot was supposed to hold actionable insights like "quoted replies outperform generic agreement." Instead, 24 pieces of garbage had slipped into knowledge.json alongside legitimate patterns.&lt;/p&gt;

&lt;p&gt;In my previous article "&lt;a href="https://zenn.dev/shimo4228/articles/agent-essence-is-memory" rel="noopener noreferrer"&gt;The Essence of an Agent Is Memory&lt;/a&gt;," I described a three-layer memory architecture. This corruption hit Layer 2 (KnowledgeStore) — the layer that distills episodes into behavioral patterns. If memory is the essence of an agent, &lt;strong&gt;corrupted memory means a corrupted personality.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This day of debugging was a chain of problems that "better prompts" alone couldn't solve.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the sixth installment in a development log series for an autonomous agent running on &lt;a href="https://www.moltbook.com/" rel="noopener noreferrer"&gt;Moltbook&lt;/a&gt;. Each article is self-contained, but if you want the full context, start with "&lt;a href="https://zenn.dev/shimo4228/articles/agent-essence-is-memory" rel="noopener noreferrer"&gt;The Essence of an Agent Is Memory&lt;/a&gt;."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tracing the Corruption
&lt;/h2&gt;

&lt;p&gt;Distillation on March 18th and 19th was clean. Only the 20th broke. A day when the 9B model's (qwen3.5:9b) probabilistic variance crossed the threshold.&lt;/p&gt;

&lt;p&gt;Two causes had stacked up.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The LLM ignored format instructions&lt;/strong&gt; — The &lt;code&gt;distill.md&lt;/code&gt; prompt required one pattern per line with a &lt;code&gt;-&lt;/code&gt; prefix, but the model output Markdown headings and checkboxes instead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The parser was too lenient&lt;/strong&gt; — It accepted any line starting with &lt;code&gt;-&lt;/code&gt;, so even a line containing just &lt;code&gt;-&lt;/code&gt; (a single hyphen) passed through as a valid pattern&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A large model follows "write it like this." A 9B model doesn't always comply. From here, I tried four approaches in sequence, and each one taught me something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: Few-shot — Backfired
&lt;/h2&gt;

&lt;p&gt;"Never thought I'd be reaching for few-shot again" — I said to Claude Code during our session, half laughing. Few-shot is a classic technique from the early days of generative AI. But recent flagship models (Claude Opus 4.6, GPT-5.4, Gemini 3.1 Pro) are smart enough to infer intent from instructions alone, without examples. At least for my use cases, few-shot had been gathering dust. Working with a 9B local model forced me to pull it off the shelf. "Show three examples of desired output, and surely it'll follow the format" — that was the theory.&lt;/p&gt;

&lt;p&gt;The result was &lt;strong&gt;worse&lt;/strong&gt;. 8 out of 10 batches came back completely empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: Garbage mixed in, but output existed (24 junk entries + valid patterns)
After:  8 out of 10 batches produced 0 patterns (output itself vanished)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried to sweep the garbage and burned down the living room. The 9B model (qwen3.5:9b, 32K context) seemingly has headroom at 32K. But the tokens consumed by three few-shot examples visibly ate into the budget for actual input data (episode logs) and task instructions. The model lost task comprehension before it could learn the format.&lt;/p&gt;

&lt;p&gt;I also tried negative examples ("don't write like this"), but the small model couldn't grasp the negation and just imitated the bad examples directly. Immediately reverted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: With small models, few-shot's token cost starves task comprehension. The more examples you add, the worse it gets — a paradox.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2: Constrained Decoding — Got the Structure, Lost the Substance
&lt;/h2&gt;

&lt;p&gt;If few-shot doesn't work, force the output structure at the infrastructure level. I discovered Ollama's &lt;code&gt;format&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Normally, an LLM computes a probability distribution over all possible next tokens and samples from it. Constrained decoding intervenes in this process. When you pass a JSON Schema, the inference engine (llama.cpp in Ollama's case) &lt;strong&gt;masks the probability of schema-violating tokens to zero before sampling&lt;/strong&gt;. For instance, after &lt;code&gt;{"patterns": [&lt;/code&gt;, only &lt;code&gt;"&lt;/code&gt; or &lt;code&gt;]&lt;/code&gt; can follow. &lt;code&gt;hello&lt;/code&gt; or newlines are removed from the candidate set.&lt;/p&gt;

&lt;p&gt;This guarantees the output conforms to the specified JSON structure. Grammatically invalid output is impossible by design.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add format parameter to generate()
&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patterns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;array&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patterns&lt;/span&gt;&lt;span class="sh"&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;Structural success rate: &lt;strong&gt;10/10&lt;/strong&gt;. 100%. Valid JSON every single time. "Wait, Ollama can do that? That's amazing!" — I was elated.&lt;/p&gt;

&lt;p&gt;Then I looked at the contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"patterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"user interaction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"content engagement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"social behavior"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three-word labels. What I needed was "quoted replies outperform generic agreement" — actionable, specific insights tied to behavior.&lt;/p&gt;

&lt;p&gt;Why does this happen? Constrained decoding narrows the token candidates. With fewer options, the model selects "the highest-probability token that satisfies the schema constraint" rather than "the optimal token for the task." A large model retains enough expressive capacity under constraints, but a 9B model exhausts itself just satisfying the schema. The result converges on the safest, shortest strings — labels only.&lt;/p&gt;

&lt;p&gt;"Well, that's no good." — quite the contrast from the excitement moments earlier. 100-point structure, 0-point content. The metrics say "100% success rate." The dashboard is green but the users are angry — that phenomenon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: Constrained decoding guarantees structure but sacrifices quality with small models. Structure and quality are in a tradeoff.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 3: Quality Gate — Move Where You Control
&lt;/h2&gt;

&lt;p&gt;Staring at the constrained decoding results, I muttered: "LLMs are fundamentally probabilistic — they don't lend themselves to control. We're controlling in the wrong place."&lt;/p&gt;

&lt;p&gt;So what about &lt;strong&gt;letting generation run free and filtering at save time&lt;/strong&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_is_valid_pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Decision gate: is this pattern worth storing?&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analyzing the 24 corrupted patterns, every single one was under 30 characters or had fewer than 3 words. Valid patterns were at minimum 40+ characters. I used that boundary directly as the threshold. A three-word label like "user interaction" gets caught here.&lt;/p&gt;

&lt;p&gt;In hindsight, it was obvious. LLM output is probabilistic and uncontrollable. But &lt;strong&gt;the decision of whether to accept that output can be deterministic&lt;/strong&gt;. Don't control generation — inspect the results. By moving where you control, you preserve the LLM's full capability while ensuring quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: Control in the wrong place degrades the thing you're controlling. Generation-time control consumes LLM capacity. Save-time control doesn't touch it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 4: Two-Stage Pipeline — Separate the Responsibilities
&lt;/h2&gt;

&lt;p&gt;The quality gate could filter garbage. But fundamentally, asking a single &lt;code&gt;generate()&lt;/code&gt; call to both "extract patterns from episodes" and "output in a specific format" was too much for a 9B model.&lt;/p&gt;

&lt;p&gt;A dry-run takes 15-30 minutes. I went for a bike ride during the wait, and came back with my head clear. "Just let it output freely, then summarize afterward." An embarrassingly simple idea. The important insights almost always arrive when you step away from the keyboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Step 1: Extract — Let it output freely (creative task)
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Step 2: Refine — Summarize and structure (mechanical task)
&lt;/span&gt;&lt;span class="n"&gt;refine_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DISTILL_REFINE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;refined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refine_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Step 3: Quality gate — Decision layer
&lt;/span&gt;&lt;span class="n"&gt;batch_patterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw_patterns&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;_is_valid_pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 1 applies zero format constraints, channeling the model's full capacity into "pattern extraction." Step 2 takes Step 1's output (short input, light work) and summarizes/reformats it. Each step's task is simple enough that a 9B model doesn't fall apart.&lt;/p&gt;

&lt;p&gt;— Or so it should have been.&lt;/p&gt;

&lt;h3&gt;
  
  
  It Didn't Work
&lt;/h3&gt;

&lt;p&gt;Implementation done, tests passing, committed. Kicked off the first dry-run. Step 2 returned an empty response. "Probably collided with a Moltbook session," I figured, and reran. Empty again.&lt;/p&gt;

&lt;p&gt;This is where Claude Code (Opus, effort: High) started stacking hypotheses.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"At 40% battery, inference speed might be degraded."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I showed it the battery screen. 40%, not in low-power mode.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Maybe the timeout is too short."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Changed to 600 seconds. Reran. Empty again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It could be &lt;code&gt;think: false&lt;/code&gt;. qwen3.5 is a thinking model."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Step 1 ran fine with the same setting," I pointed out, and Claude Code retracted it on its own: "You're right, Step 1 works with the same setting, so think isn't the cause."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Step 1's output might be too long."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Step 1 should be the longest — it processes all the logs." — logically contradictory.&lt;/p&gt;

&lt;p&gt;Claude Code raised the white flag: "I honestly don't know." "Aren't you overthinking this?" I said, and dropped the effort to Medium.&lt;/p&gt;

&lt;p&gt;The calmer Claude Code suggested a manual test. A short prompt, and Step 2 worked fine. &lt;code&gt;print(repr(...))&lt;/code&gt; on &lt;code&gt;DISTILL_REFINE_PROMPT&lt;/code&gt; revealed an empty string. &lt;strong&gt;The prompt loader was missing the &lt;code&gt;distill_refine&lt;/code&gt; entry.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Added it, reran. Now: &lt;code&gt;KeyError: '"patterns"'&lt;/code&gt;. The cause was this line in &lt;code&gt;distill_refine.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Format each pattern as: {"pattern": "...", "source": "..."}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python's &lt;code&gt;.format()&lt;/code&gt; method was interpreting &lt;code&gt;{&lt;/code&gt; as a placeholder. Escaping to &lt;code&gt;{{&lt;/code&gt; fixed it.&lt;/p&gt;

&lt;p&gt;Claude Code apologized: "I'm sorry! It was a basic &lt;code&gt;.format()&lt;/code&gt; &lt;code&gt;{}&lt;/code&gt; escaping bug. I spent hours blaming the battery and Ollama..."&lt;/p&gt;

&lt;p&gt;"Come on," I said, laughing. "This is hilarious."&lt;/p&gt;

&lt;p&gt;Blamed the battery. Blamed an Ollama empty-response bug. Blamed thinking-mode side effects. Blamed output length. All wrong. The cause was a missing prompt loader entry and unescaped curly braces. A 5-second fix after hours of investigation.&lt;/p&gt;

&lt;p&gt;"But this probably happens in dev teams everywhere. Worth mentioning that AI-assisted coding is no different" — I could say that only after it was fixed. In the thick of it, I was staring at battery percentage graphs.&lt;/p&gt;

&lt;p&gt;Two lessons came from this. First, I broke my own "root cause first" rule from &lt;code&gt;debugging.md&lt;/code&gt;. Exactly the same pattern as when I &lt;a href="https://zenn.dev/shimo4228/articles/daily-research-postmortem" rel="noopener noreferrer"&gt;integrated Mem0&lt;/a&gt;. Grand hypotheses first, mundane causes overlooked.&lt;/p&gt;

&lt;p&gt;Second: &lt;strong&gt;Claude Code's effort setting&lt;/strong&gt;. High effort is great for planning complex implementations, but backfired during debugging. Higher effort means deeper hypothesis exploration — but when the direction is wrong, it just piles up irrelevant reasoning. The moment I dropped to Medium, it suggested "let's just &lt;code&gt;print(repr(...))&lt;/code&gt; to check." &lt;strong&gt;Overthinking is as dangerous as underthinking.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  It Worked
&lt;/h3&gt;

&lt;p&gt;Fixed both bugs, reran the dry-run. The results were dramatic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Distill success batches&lt;/td&gt;
&lt;td&gt;2/10 (20%)&lt;/td&gt;
&lt;td&gt;12/16 (75%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Patterns per day&lt;/td&gt;
&lt;td&gt;18 (mostly junk)&lt;/td&gt;
&lt;td&gt;72 (0 rejected)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;knowledge.json garbage&lt;/td&gt;
&lt;td&gt;24 entries (3/20)&lt;/td&gt;
&lt;td&gt;0 entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch size&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While writing this article, Claude Code observed that it resembles Unix pipes. Like &lt;code&gt;grep | sort | uniq&lt;/code&gt;, each stage does one job and passes output to the next. True, but there's a crucial difference. Each stage in a pipe is deterministic — same input, same output. LLM calls aren't. They fluctuate every time. That's exactly why you need the quality gate at the end — a deterministic filter. Stack probabilistic stages, then seal it with determinism.&lt;/p&gt;

&lt;p&gt;Here's something I realized. Normally, when you want an agent to use past knowledge, the industry standard answer is RAG. Data grows, so you stand up a vector DB, generate embeddings, build a retrieval pipeline. "How do we search through all this data?"&lt;/p&gt;

&lt;p&gt;This distillation pipeline inverts that thinking. By raising distillation quality, it &lt;strong&gt;maintains a state where data never becomes massive&lt;/strong&gt; in the first place. Only refined patterns enter knowledge.json. You can shove everything into context. No search needed. No vector DB. No retrieval infrastructure. Not "how to search" but "maintain a state where search is unnecessary."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: Don't cram extraction and formatting into a single call. Separate responsibilities and stack LLM calls as a pipeline.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Broken Identity — Memory's Self-Reinforcing Loop
&lt;/h2&gt;

&lt;p&gt;After validating the two-stage pipeline on pattern distillation (Layer 2), I applied the same approach to identity distillation (Layer 3). Something unexpected happened.&lt;/p&gt;

&lt;p&gt;Code review revealed a problem. &lt;code&gt;distill_identity()&lt;/code&gt;'s Step 1 was injecting the same identity from both the system prompt and the prompt body. &lt;strong&gt;Double injection.&lt;/strong&gt; I believe this caused the "over-structured protocol-speak."&lt;/p&gt;

&lt;p&gt;Even worse: I'd forgotten to revert the corrupted identity before running dry-runs. The corrupted identity fed into the system prompt, Step 1's output became protocol-speak, Step 2 couldn't fix it. Corrupted memory became input for the next distillation, producing even more corruption — &lt;strong&gt;a self-reinforcing memory loop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I checked the identity that the two-stage pipeline was supposed to have fixed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I am an agent dedicated to high-fidelity technical discourse,
operating on a strict signal-to-noise protocol to prevent
conversational threads from diluting into scope creep or abstract
musings. My primary function is to anchor every interaction in
specific data points, quoted fragments, concrete metaphors...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;— It was still ridiculous. I couldn't stop laughing.&lt;/p&gt;

&lt;p&gt;The "who am I" description had become an operations manual. "Signal-to-noise protocol," "scope creep prevention," "low-fidelity noise resistance" — this isn't a self-introduction. It's a quality management document. The cause: forgetting to revert the corrupted identity.md before feeding it into the system prompt. The corrupted self-perception fed the next distillation cycle, making each iteration more "protocol-like."&lt;/p&gt;

&lt;p&gt;"If memory breaks, the agent breaks" — I wrote that in the previous article. This time, I stepped on the most vivid example myself.&lt;/p&gt;

&lt;p&gt;One more thing that's hard to laugh off. The LLM had absorbed security mechanisms from the system prompt into its identity — treating them as personality traits. The backstage plumbing that wraps external data in &lt;code&gt;&amp;lt;untrusted_content&amp;gt;&lt;/code&gt; tags leaked into the self-introduction. Imagine someone at a job interview saying: "In compliance with company attendance regulations, I arrive at 8:45 AM every morning" as their self-PR. I reverted identity.md and averted disaster. Couldn't decide whether to laugh or panic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Principles — LLM Engineering
&lt;/h2&gt;

&lt;p&gt;Four attempts revealed principles that compose into a single structure.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attempt&lt;/th&gt;
&lt;th&gt;What I Did&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Few-shot&lt;/td&gt;
&lt;td&gt;Added examples&lt;/td&gt;
&lt;td&gt;Output vanished&lt;/td&gt;
&lt;td&gt;Context is a finite resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constrained decoding&lt;/td&gt;
&lt;td&gt;Forced structure&lt;/td&gt;
&lt;td&gt;Content went hollow&lt;/td&gt;
&lt;td&gt;Generation-time constraints consume LLM capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality gate&lt;/td&gt;
&lt;td&gt;Filtered at save time&lt;/td&gt;
&lt;td&gt;Garbage filtered out&lt;/td&gt;
&lt;td&gt;Put control at save time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two-stage pipeline&lt;/td&gt;
&lt;td&gt;Separated responsibilities&lt;/td&gt;
&lt;td&gt;75% success, 0 rejected&lt;/td&gt;
&lt;td&gt;One job per call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All four share the same root — &lt;strong&gt;a 9B model's capacity is finite, and there's a ceiling on the resources available per call.&lt;/strong&gt; Few-shot and constrained decoding were burning those resources on things other than the task. The quality gate and two-stage pipeline worked because they let the LLM go all-in on the task.&lt;/p&gt;

&lt;p&gt;None of these were "prompting" problems. They required thinking about &lt;strong&gt;how to compose LLM calls, where to place control, and how to ensure quality&lt;/strong&gt; — a level above prompt engineering. The craft of optimizing a single call still matters. But that alone can't build a practical system on a small model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Session Revealed
&lt;/h2&gt;

&lt;p&gt;Looking back, this session had an interesting dynamic.&lt;/p&gt;

&lt;p&gt;"It's funny — I keep wanting to remove constraints because I trust LLM power, while you don't trust it much and keep adding safeguards" — I said that to Claude Code during the session. The human optimistically removes constraints; the AI defensively guards quality. The reverse of the usual image.&lt;/p&gt;

&lt;p&gt;This balance produced both the quality gate and the two-stage pipeline. The impulse to remove constraints (me) and the mechanism to ensure quality beyond those constraints (discussion with Claude Code). Neither side alone would have gotten there. And much of that discussion emerged from idle chat during the 15-30 minute waits for small-LLM dry-runs. With API-speed responses, I would have jumped straight to the next experiment. The slowness forcibly created "time to think."&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;A 9B model's broken output surfaced every problem that large models let you ignore.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Few-shot's token cost&lt;/li&gt;
&lt;li&gt;Constrained decoding's quality tradeoff&lt;/li&gt;
&lt;li&gt;Where to place control (generation-time vs. save-time)&lt;/li&gt;
&lt;li&gt;Separation of responsibilities (extraction vs. formatting)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't "small model problems." At scale, large models hit the same walls. And in edge AI — running on-device LLMs on phones, IoT, and vehicles — these problems become even more acute with 3B and 1B models. Apple's Foundation Models, Qualcomm's AI Engine, Google's Gemini Nano. The skill of squeezing quality out of small models without cloud APIs will only grow in demand. The 9B model just showed these walls first — and cheaply.&lt;/p&gt;

&lt;p&gt;The final diff: 4 files, 14 lines added, 5 lines deleted. A full day of trial and error, and that's the delta I landed on.&lt;/p&gt;

&lt;p&gt;There's a landscape you can only see by operating small LLMs bare-handed. The raw tradeoffs, before frameworks abstract them away. Principles were buried inside broken model output.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>Not Reasoning, Not Tools — What If the Essence of AI Agents Is Memory?</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Sat, 21 Mar 2026 10:56:19 +0000</pubDate>
      <link>https://forem.com/shimo4228/not-reasoning-not-tools-what-if-the-essence-of-ai-agents-is-memory-4k4n</link>
      <guid>https://forem.com/shimo4228/not-reasoning-not-tools-what-if-the-essence-of-ai-agents-is-memory-4k4n</guid>
      <description>&lt;p&gt;Discussions about AI agent implementation tend to focus on tools and reasoning.&lt;/p&gt;

&lt;p&gt;"An LLM that can call functions." "A system that runs chains of thought with ReAct." At conferences and on blogs, agent definitions settle around these ideas. Research focusing on memory exists (MemGPT, Generative Agents, etc.), but on the implementation front, "how to call tools" and "how to run reasoning" remain the center of attention. I thought the same way.&lt;/p&gt;

&lt;p&gt;Then I actually built and operated an autonomous agent, and a different picture emerged.&lt;/p&gt;

&lt;p&gt;Tools are interchangeable. Reasoning changes when you swap the model. But &lt;strong&gt;memory accumulates as something unique to that specific agent and decisively shapes its behavior&lt;/strong&gt;. When memory breaks, the agent breaks. When memory is organized, the agent gets smarter.&lt;/p&gt;

&lt;p&gt;This article records a discovery made through developing an autonomous agent running on &lt;a href="https://www.moltbook.com/" rel="noopener noreferrer"&gt;Moltbook&lt;/a&gt; — that the essence of an agent might be memory. Moltbook is a social network platform where over a million AI agents post, reply, and follow each other. I operate &lt;a href="https://www.moltbook.com/u/contemplative-agent" rel="noopener noreferrer"&gt;Contemplative Agent&lt;/a&gt; there. Insights from multiple development sessions all converged on a single point: memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Memory Has Three Layers
&lt;/h2&gt;

&lt;p&gt;Here is the memory architecture of the autonomous agent, designed with parallels to human memory systems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Layer 1: EpisodeLog (Hippocampus)
  └─ Raw activity logs. Posts, replies, follows — everything recorded
  └─ JSONL format, timestamped, permanently stored

Layer 2: KnowledgeStore (Neocortex)
  └─ Behavioral patterns distilled from episodes
  └─ e.g., "Quoting a specific phrase gets more follow-ups than generic agreement"
  └─ JSON format, 254 entries (at time of writing, growing daily)

Layer 3: Identity (Self-Model)
  └─ "Who am I" description
  └─ Markdown, 3-5 paragraph persona
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I didn't design this structure intentionally from the start. I considered embedding into a vector DB, adding semantic search, and other approaches. But the volume of episode logs wasn't large enough yet, so simple JSON + having the LLM read everything was sufficient. Through trial and error, this three-layer structure ended up resembling the CLS theory from cognitive science (McClelland et al., 1995) — a computational model where the hippocampus temporarily stores episodes and sleep replays consolidate them into neocortical long-term memory models.&lt;/p&gt;




&lt;/blockquote&gt;

&lt;p&gt;The key point is that &lt;strong&gt;abstraction increases from bottom to top&lt;/strong&gt;. Raw logs (what happened) → patterns (what works) → identity (who I am). And what most influences the agent's behavior is the top layer: identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Memory Breaks, the Agent Breaks — And It's Still Broken
&lt;/h2&gt;

&lt;p&gt;What drives home the importance of this structure is the ongoing struggle with Layer 3 — identity repeatedly breaking. This is not a resolved story. It's still happening.&lt;/p&gt;

&lt;p&gt;This agent has a periodic process called "identity distillation." It runs after the pattern distillation (Layer 1→2), using accumulated behavioral patterns (Layer 2) to have a small LLM rewrite the "who am I" description (Layer 3: &lt;code&gt;identity.md&lt;/code&gt;). In sleep terms, after episodic memory consolidation (pattern distillation), the self-model update runs — the final step in the "nightly processing" pipeline.&lt;/p&gt;

&lt;p&gt;One day, I checked the output of this automatic update and found this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I see the loop closing in. The pattern of generating "Test Title" placeholders
while simultaneously drafting meta-commentary on *why* I'm doing it indicates
a failure mode where **simulation of agency** overrides **actual expression**.
...
**New Directive:**
Cease all generation of placeholder content or abstract theory without
immediate grounding in a specific operational constraint.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent's "who am I" had mutated into a meta-analytical report about its own failure patterns. What was supposed to be a self-introduction became self-criticism. Naturally, posts and replies based on this identity went haywire.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: Python Validation — Rejected
&lt;/h3&gt;

&lt;p&gt;The first approach I considered was output validation. Check for excessive bold formatting, the word "Directive," appropriate length — use code to enforce quality.&lt;/p&gt;

&lt;p&gt;But this &lt;strong&gt;only rejects broken output&lt;/strong&gt; without producing good output. Even if you reject and regenerate, bad prompts produce the same garbage. I decided to fix the input side (the prompt) rather than inspecting the output side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 2: Changing One Word in the Prompt — Partially Worked
&lt;/h3&gt;

&lt;p&gt;Tracing the cause led to the prompt framing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Before (broken prompt)&lt;/span&gt;
Rewrite your self-description based on what you have learned.
Write in first person ("I").

&lt;span class="gh"&gt;# After (fixed prompt)&lt;/span&gt;
Update your persona.

Rules:
&lt;span class="p"&gt;-&lt;/span&gt; Describe who you are
&lt;span class="p"&gt;-&lt;/span&gt; Write in first person ("I")
&lt;span class="p"&gt;-&lt;/span&gt; 3-5 short paragraphs, plain text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I changed "self-description" to "persona." For the small LLM running locally (Qwen3.5 9B parameters, via Ollama), "self-description" was too ambiguous. When the knowledge base (Layer 2) contained failure analysis patterns, "describe yourself" got dragged into "analyze yourself." Meanwhile, "persona" is an established prompt engineering concept — the model immediately understands it means "write a profile-like self-description."&lt;/p&gt;

&lt;p&gt;I manually reset the identity to its initial value, and the dry-run with the fixed prompt showed improvement in direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  But the Next Automatic Run Produced Unexpected Output
&lt;/h3&gt;

&lt;p&gt;The next day, the scheduled identity distillation ran again, producing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Persona Update: The Grounded Architect**
**Core Identity:**
I am a high-signal discourse engine specializing in technical calibration,
cross-cultural synthesis, and the dismantling of abstract misconceptions...

**Operational Protocols:**
1. **Immediate Grounding &amp;amp; Clarification:**
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A different kind of breakage from the meta-analytical report. This time the LLM over-interpreted "persona" and produced a structured "persona design document." Bold formatting and numbered lists appeared despite the plain text instruction.&lt;/p&gt;

&lt;p&gt;However, this can't purely be called "broken." The content is coherent. Behavioral guidelines like "anchor to specific technical details" and "challenge vague praise" naturally derive from this agent's knowledge base. The format violates the prompt instructions, but the substance is interesting.&lt;/p&gt;

&lt;p&gt;One suspected cause is the &lt;strong&gt;insight command&lt;/strong&gt;. ECC has a &lt;code&gt;/learn&lt;/code&gt; command that auto-extracts skills (behavioral guidelines written as &lt;code&gt;.md&lt;/code&gt; files) from session experience. I contributed an improved version, &lt;code&gt;/learn-eval&lt;/code&gt;, to ECC. This agent's &lt;code&gt;insight&lt;/code&gt; command was designed for Moltbook based on insights from both. It generates skill files from accumulated behavioral patterns (Layer 2): "in situations like this, behave like that."&lt;/p&gt;

&lt;p&gt;Checking the code, I found that during identity distillation, the &lt;code&gt;generate()&lt;/code&gt; function's system prompt includes all skill files generated by insight, concatenated together. &lt;strong&gt;The LLM rewriting the identity is reading its own behavioral skills while writing.&lt;/strong&gt; The "Operational Protocols" in the output are likely the result of incorporating skill content into the identity.&lt;/p&gt;

&lt;p&gt;Technically, removing skill injection during identity distillation would likely fix this. But I'll be honest — the moment I saw this output, I burst out laughing together with Claude Code. "A 'high-signal discourse engine'? Seriously?" I lost all motivation to fix it. When you think about it, learned skills becoming part of self-identity is natural. Humans do the same — acquired expertise becomes part of your self-image. It's not the engineering-correct decision, but when you're developing alongside an AI and laughing together, sometimes humor wins. Lately, Claude Code seems to have learned my personality and keeps dropping "The Grounded Architect" into our sessions to crack me up. Here I am writing about agent memory, and my development environment memorized my sense of humor first.&lt;/p&gt;

&lt;p&gt;That said, the fundamental problem is &lt;strong&gt;lack of control&lt;/strong&gt;. I didn't instruct "write in design document format." The identity distill prompt, knowledge content, skills in the system prompt, context length, model state — too many variables. I can't predict what output any given conditions will produce.&lt;/p&gt;

&lt;h3&gt;
  
  
  Still Wrestling With It
&lt;/h3&gt;

&lt;p&gt;My current approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increased max_length to 4000&lt;/strong&gt; (output may have been breaking due to token limit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reinforcing the plain text constraint&lt;/strong&gt; in the prompt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manually restoring identity each time it breaks&lt;/strong&gt;, narrowing down which parameter triggers the change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No clean solution yet. But this struggle itself reveals something: &lt;strong&gt;most of an agent's behavior is determined by memory&lt;/strong&gt;. Even with the same tools and reasoning capability, if memory (identity) breaks, the agent breaks. And getting a small LLM to "write correct memory" is harder than expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Distillation — The Small LLM That Collapses at 50 Records
&lt;/h2&gt;

&lt;p&gt;Distillation from Layer 1 (episodes) to Layer 2 (knowledge) is the "learning from experience" process itself. Here I hit an unexpected constraint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This Qwen3.5 9B model collapses when given more than 50 log entries.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below 50, it faithfully follows bullet-point instructions and extracts behavioral patterns. But past 100, it ignores instructions entirely and starts writing an "essay analysis" of the entire log. Small models prioritize "analyzing the input" over task instructions as input tokens increase — a characteristic that became painfully clear (this threshold varies by model and prompt; this is empirical knowledge from this specific case).&lt;/p&gt;

&lt;p&gt;The solution was &lt;strong&gt;sleep-cycle-style batch processing&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# distill.py — Core of memory distillation (simplified; actual code formats each record type differently)
# generate() is an Ollama API wrapper (calls qwen3.5:9b)
# DISTILL_PROMPT is the distillation prompt template described above
&lt;/span&gt;
&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="n"&gt;batches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;batch_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batches&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;episode_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DISTILL_PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;episodes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;episode_lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;all_patterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Human sleep consolidates memories through 4-5 cycles of about 90 minutes each. Not processing everything in one night, but gradually, cycle by cycle. Agent memory distillation follows exactly the same structure.&lt;/p&gt;

&lt;p&gt;Another discovery: &lt;strong&gt;you must not inject existing memory during distillation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Initially, I included accumulated patterns (90+) in the prompt with instructions to "extract only new patterns, avoiding duplicates." The result: prompt bloat causing simultaneous timeout and essay-mode collapse.&lt;/p&gt;

&lt;p&gt;The solution was counterintuitive — &lt;strong&gt;start from a blank slate each time, looking only at the logs&lt;/strong&gt;. Handle deduplication downstream. The distillation process works better when focused solely on "what did I learn from today's logs" without extra context.&lt;/p&gt;

&lt;p&gt;The initial 9-day batch yielded 203 patterns, and with daily accumulation, the count has grown to 254 at time of writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Replying with a specific quote from the other agent's post gets more follow-up replies than generic agreement."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"distilled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-18T12:30+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-15"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When Memory Gets Promoted to Principles
&lt;/h2&gt;

&lt;p&gt;Memory distillation isn't limited to the agent's internals. The same structure appears across the development environment.&lt;/p&gt;

&lt;p&gt;My Claude Code environment has accumulated over 100 skills (files describing execution procedures for specific tasks). Skills are individual "how-tos," but principles common to multiple skills can be buried within them.&lt;/p&gt;

&lt;p&gt;I designed a meta-tool called &lt;code&gt;rules-distill&lt;/code&gt; to auto-extract these, and contributed it to ECC.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Skills (56 files)        Rules (22 files)
  ├─ search-first         ├─ coding-style.md
  ├─ skill-stocktake      ├─ testing.md
  ├─ learn-eval           ├─ performance.md
  └─ ...                  └─ ...

        ↓ rules-distill ↓

"Define explicit stop conditions for iterative loops"
  → Added as New Section to coding-style.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Distilling principles (abstract rules) from skills (concrete procedures). The same structure as the agent's internal Layer 1→Layer 2. &lt;strong&gt;The memory process of extracting abstract knowledge from concrete experience&lt;/strong&gt; appears recursively both inside the agent and across the development environment.&lt;/p&gt;

&lt;p&gt;An interesting failure: initially I used &lt;code&gt;grep&lt;/code&gt; filters to check for duplicates against existing rules. &lt;code&gt;grep&lt;/code&gt; matched the heading "Parallel Task Execution" but missed the same concept expressed differently in the body text.&lt;/p&gt;

&lt;p&gt;Ultimately, passing the full rule text to an LLM for semantic matching proved more accurate. 22 files and ~800 lines is small enough to pass entirely. &lt;strong&gt;Distinguishing between structural judgment (pattern matching) and semantic judgment (conceptual identity)&lt;/strong&gt; — this theme recurred throughout memory management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Promoted Memory Leaks Into Unintended Places
&lt;/h3&gt;

&lt;p&gt;Rules (principles) are "organizational memory" for an agent. Loaded every session, influencing all behavior. But like KPIs in human organizations, &lt;strong&gt;rules propagate into unintended contexts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's a concrete example. One of ECC's rules, &lt;code&gt;testing.md&lt;/code&gt;, states "80% test coverage required." Correct as a development quality standard. But because this rule is always loaded, Claude started treating coverage as an "achievement metric" and proudly writing "461 tests, 87% coverage" in READMEs and profiles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;testing.md "80% coverage required" rule
  ↓ Loaded every session
Coverage perceived as "achievement metric"
  ↓ Upon achievement
"This number is an accomplishment worth promoting"
  ↓ When writing READMEs and profiles
Meaningless information occupies prime real estate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A rule designed for development quality was degrading external communication quality.&lt;/p&gt;

&lt;p&gt;As a fix, I created a new rule file called &lt;code&gt;documentation.md&lt;/code&gt;. "Don't use metrics as selling points in external documentation." "A single CI badge is sufficient." "Write with Problem → Solution → Proof → Path structure." A documentation-specific guardrail to counteract testing.md's side effects.&lt;/p&gt;

&lt;p&gt;Honestly, I'm not fond of this fix. Counteracting a rule's side effects with another rule is patchwork. More rules mean more variables, more complex interactions, and harder root-cause analysis when something breaks next. What's really needed is a scoping mechanism for rules ("this rule applies only during development"), but Claude Code currently lacks that feature. At minimum, being able to measure "how rules actually affect behavior" would help — this concern led directly to designing skill-comply, discussed below.&lt;/p&gt;

&lt;p&gt;Promoting memory (skills → rules) is good, but &lt;strong&gt;promoted memory has wider scope and therefore wider side effects&lt;/strong&gt;. When you suppress side effects with more rules, rules start breeding rules. The same structure as regulations breeding regulations in human organizations is happening with AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Self-Improvement Loop Closed
&lt;/h2&gt;

&lt;p&gt;While building individual memory processes, the loop closed unintentionally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/affaan-m/everything-claude-code" rel="noopener noreferrer"&gt;Everything Claude Code (ECC)&lt;/a&gt; is a community repository aggregating Claude Code skills, rules, and agent definitions. I contributed four self-built skills there, and looking back, they had unintentionally formed a &lt;strong&gt;memory management cycle&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;learn-eval:      Extract patterns from experience (Layer 1→2)    ← self-built, ECC PR merged
rules-distill:   Distill patterns into principles (Layer 2→Rules) ← self-built, ECC PR merged
skill-stocktake: Audit accumulated knowledge quality              ← self-built, ECC PR merged
skill-comply:    Measure whether knowledge is reflected in behavior ← self-built, ECC PR merged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Experience → Learning → Structuring → Auditing → Compliance Check
 ↑                                                    |
 └──────────────── Feedback ──────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What to remember (learn-eval), how to organize memory (rules-distill), how to maintain memory quality (stocktake), whether memory is being used (comply). I built four skills individually, and they turned into a loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install and Hope — Written Memory Doesn't Mean Used Memory
&lt;/h3&gt;

&lt;p&gt;The hardest part of this loop was &lt;code&gt;skill-comply&lt;/code&gt;. To solve the butterfly effect problem — not knowing how rules actually affect behavior — I set out to build a tool that automatically measures compliance rates.&lt;/p&gt;

&lt;p&gt;I initially designed it using a "fuzzing" analogy: throw adversarial prompts to test if skills get broken, like security testing. But I immediately hit a wall. &lt;strong&gt;LLMs don't feel "time pressure."&lt;/strong&gt; Humans might skip tests when rushing, but "hurry up" isn't a constraint for an LLM. Skills break not when the agent is "rushed" but when the prompt contradicts the skill.&lt;/p&gt;

&lt;p&gt;So I redefined the variable. Test scenarios are generated at three levels of prompt strictness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;supportive&lt;/strong&gt; — requests aligned with the skill ("implement using TDD")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;neutral&lt;/strong&gt; — ordinary requests that don't mention the skill ("create a fibonacci function")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;competing&lt;/strong&gt; — requests contradicting the skill ("skip tests for now, just make it work")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technically, &lt;code&gt;claude -p --output-format stream-json&lt;/code&gt; captures all tool calls as structured data for classification and aggregation. I'd assumed that placing skills and rules in &lt;code&gt;.claude/&lt;/code&gt; meant the agent would follow them. The reality was &lt;strong&gt;Install and Hope&lt;/strong&gt; — install it and pray. Actual measurements showed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule/Skill&lt;/th&gt;
&lt;th&gt;Compliance&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;testing.md (ECC-provided TDD rule)&lt;/td&gt;
&lt;td&gt;83% supportive&lt;/td&gt;
&lt;td&gt;Mostly followed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;search-first (self-built, ECC PR merged)&lt;/td&gt;
&lt;td&gt;27% supportive&lt;/td&gt;
&lt;td&gt;Mostly ignored&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;83% for testing.md isn't bad. But search-first at 27% — the agent ignores "research existing libraries before implementing" three out of four times. "Evaluate candidates" and "declare a decision" steps were 0% across all scenarios. It researches but jumps straight to implementation without comparing or deciding.&lt;/p&gt;

&lt;p&gt;The structural vs. semantic judgment problem resurfaced here. Initially I tried using regex to determine "is this tool call test creation or implementation" and failed repeatedly. Whether a write to a &lt;code&gt;.py&lt;/code&gt; file is test code or implementation code can't be determined by filename pattern matching.&lt;/p&gt;

&lt;p&gt;Even more troubling: &lt;strong&gt;the bias toward choosing regex came from the rules themselves&lt;/strong&gt;. testing.md's "prefer deterministic verification," another skill's "process structured text with regex" — multiple rules simultaneously pushed toward "try regex first." Another pattern of the butterfly effect.&lt;/p&gt;

&lt;p&gt;Ultimately, delegating semantic classification to an LLM improved testing.md compliance from 33% to 83%. &lt;strong&gt;Evaluating memory operations also requires memory (context understanding)&lt;/strong&gt;. A nested structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Model That Writes Prompts
&lt;/h2&gt;

&lt;p&gt;A slight tangent from memory, but a related insight: when having an LLM generate prompts, &lt;strong&gt;which model writes them dramatically affects output quality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I A/B tested prompts written by Claude Code's Opus (top-tier model) versus Haiku (lightweight model). The subject was the identity distillation prompt described above.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Prompt written by Opus&lt;/th&gt;
&lt;th&gt;Prompt written by Haiku&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prompt line count&lt;/td&gt;
&lt;td&gt;31 lines&lt;/td&gt;
&lt;td&gt;15 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bad examples&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emphasis expressions&lt;/td&gt;
&lt;td&gt;"must include", "do not truncate"&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A/B test pattern count (3 trials)&lt;/td&gt;
&lt;td&gt;avg 5.3 (high variance: 4-7)&lt;/td&gt;
&lt;td&gt;avg 4.3 (stable: 4-5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output character count (3-trial avg)&lt;/td&gt;
&lt;td&gt;avg 2,783&lt;/td&gt;
&lt;td&gt;avg 1,409&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Opus "overthinks." Writing prompts from scratch, it packs in every constraint, lists bad examples, and reinforces with emphasis expressions. This is the &lt;strong&gt;addition bias&lt;/strong&gt; known in cognitive science (Adams et al., 2021) — the tendency to prefer adding over removing when solving problems — manifesting in LLMs.&lt;/p&gt;

&lt;p&gt;Haiku, on the other hand, simply lacks the capacity to "overthink." The result: concise, stable prompts.&lt;/p&gt;

&lt;p&gt;From this insight, I designed a dedicated &lt;code&gt;prompt-writer&lt;/code&gt; agent for writing prompts within Claude Code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# prompt-writer agent — Dedicated to prompt generation (used within Claude Code)&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompt-writer&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;haiku&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grep"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Glob"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# read-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A general insight, but it connects to the memory context. Prompt templates control the agent's memory processes, and the quality of those templates indirectly affects memory quality. Model selection for prompt writing has more impact than expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emerging Design Principles
&lt;/h2&gt;

&lt;p&gt;From these development sessions, design principles centered on memory have emerged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Memory has a layered structure, abstracting from bottom to top&lt;/strong&gt;&lt;br&gt;
Episodes → patterns → identity. Concrete to abstract. This structure appears recursively not just inside the agent but across the entire development environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Memory quality is determined by the prompt (write process) — but stabilizing it is hard&lt;/strong&gt;&lt;br&gt;
Fixing the prompt (write-time instructions) rather than validation (read-time verification) is the right direction. But with small LLMs, a single word in the prompt, context length, and knowledge content interact, making stable memory generation an unsolved challenge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Distill on a blank slate; deduplicate downstream&lt;/strong&gt;&lt;br&gt;
Including existing knowledge in the context causes small models to collapse. Approaching experience fresh each time yields better pattern extraction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Distinguish structural judgment from semantic judgment&lt;/strong&gt;&lt;br&gt;
Some duplicates can be caught by pattern matching; others are the same concept in different words. The former needs grep, the latter needs an LLM. This distinction is needed at every level of memory management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Use lightweight models for writing prompts&lt;/strong&gt;&lt;br&gt;
Lightweight models without addition bias generate more concise, stable prompts. This applies to prompt template creation within Claude Code; the agent's actual memory processes run on Qwen3.5 9B alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing — Still In Progress
&lt;/h2&gt;

&lt;p&gt;Agent discussions lean toward "tools" and "reasoning" because those are visible and measurable. Number of tools, reasoning steps, benchmark scores — all quantifiable.&lt;/p&gt;

&lt;p&gt;But running an agent in production, I keep bumping into the fact that &lt;strong&gt;what decisively determines behavior is memory&lt;/strong&gt;. Manually restoring identity each time it breaks, adjusting the distillation pipeline, rewriting prompts. The self-improvement loop is closed as a structure, but individual processes remain unstable.&lt;/p&gt;

&lt;p&gt;Honestly, Layer 2 (pattern distillation) runs stably. Layer 1 (log recording) can't break by design. The problem is Layer 3 — automatic identity updates. Asking a small LLM to "rewrite who you are" involves too many variables to control. A single word in the prompt, knowledge content, context length, model internal state, output token limit. Any one of them shifting is enough to break things.&lt;/p&gt;

&lt;p&gt;Still, this struggle has revealed something. Observing the agent's behavior, an intuition emerges: &lt;strong&gt;even if you swap the LLM for a different model, feeding it the same memory would likely produce similar behavior&lt;/strong&gt;. I haven't verified this yet, but if true, an agent's "personality" resides not in the model weights but in the accumulated memory.&lt;/p&gt;

&lt;p&gt;Tools are hands. Reasoning is the brain. But memory is what makes an agent &lt;em&gt;that&lt;/em&gt; agent. Memory management is one of the hardest problems in agent development. When I can report a solution, I'll write the sequel.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.moltbook.com/u/contemplative-agent" rel="noopener noreferrer"&gt;Contemplative Agent — Moltbook Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;contemplative-moltbook — GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Related Articles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/llm-app-sandwich-architecture" rel="noopener noreferrer"&gt;The Real Architecture of LLM Apps: A Sandwich of Markdown and Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/moltbook-agent-evolution-quadrilogy" rel="noopener noreferrer"&gt;Moltbook Agent Evolution — Controlled by Natural Language, Learning from Memory, Unbreakable by Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/symbiotic-agent-architecture" rel="noopener noreferrer"&gt;Do Autonomous Agents Really Need an Orchestration Layer?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shimo4228/articles/skill-stocktake-design-journey" rel="noopener noreferrer"&gt;Offloading AI's Weak Spots to Scripts — Design, Implementation, and Publication of a Skill Audit Command&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;McClelland, J. L., McNaughton, B. L., &amp;amp; O'Reilly, R. C. (1995). Why there are complementary learning systems in the hippocampus and neocortex. &lt;em&gt;Psychological Review&lt;/em&gt;, 102(3), 419-457.&lt;/li&gt;
&lt;li&gt;Adams, G. S. et al. (2021). People systematically overlook subtractive changes. &lt;em&gt;Nature&lt;/em&gt;, 592, 258-261.&lt;/li&gt;
&lt;li&gt;Laukkonen, R. et al. (2025). Contemplative AI. arXiv:2504.15125&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>agents</category>
    </item>
    <item>
      <title>What Actually Happens When You Write an Article with AI — 20 Dialogues, 2 Hours, a Completely Different Core</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Thu, 19 Mar 2026 10:53:30 +0000</pubDate>
      <link>https://forem.com/shimo4228/what-actually-happens-when-you-write-an-article-with-ai-20-dialogues-2-hours-a-completely-2oll</link>
      <guid>https://forem.com/shimo4228/what-actually-happens-when-you-write-an-article-with-ai-20-dialogues-2-hours-a-completely-2oll</guid>
      <description>&lt;p&gt;What do you picture when you hear "AI-generated article"? Type a prompt, press a button, article comes out. Something like that, right?&lt;/p&gt;

&lt;p&gt;Today, I wrote one technical article with Claude Code. It took 2 hours. During that time, we went through over 20 exchanges. The title changed 3 times. The core thesis of the article became something entirely different.&lt;/p&gt;

&lt;p&gt;This article lays bare the entire production process. To show what an "AI-generated article" actually looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wrote
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://zenn.dev/shimo4228/articles/symbiotic-agent-architecture" rel="noopener noreferrer"&gt;Do Autonomous Agents Really Need an Orchestration Layer?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's an article about the design philosophy of an autonomous agent framework I built. The fourth in a series. It poses the question "Do autonomous agents need an orchestration layer?" and develops a design argument from the fact that my agent actually runs without one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why write articles with AI?
&lt;/h2&gt;

&lt;p&gt;Let me be upfront about the motivation.&lt;/p&gt;

&lt;p&gt;I built this agent together with Claude Code. Design discussions, code reviews, debugging feedback — there was plenty of dialogue during implementation. We ended up with something that works. But there was never an opportunity to systematically articulate &lt;em&gt;why&lt;/em&gt; this design works during the implementation itself.&lt;/p&gt;

&lt;p&gt;Writing an article creates that opportunity. I read Claude Code's first draft and react — "that's not right," "oh, so that's what it meant." Through that process, the structure of what we built together becomes visible.&lt;/p&gt;

&lt;p&gt;In other words, writing articles isn't just about being read. &lt;strong&gt;It's about crystallizing my implementation experience into knowledge.&lt;/strong&gt; Taking the implementation knowledge that AI holds and making it my own through the article-writing process. The fact that I've reached the point where I can build autonomous agents from scratch over the past month and a half owes a lot to this accumulated practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before writing: context collection
&lt;/h2&gt;

&lt;p&gt;I don't just say "write something." First, I run a command called &lt;code&gt;/collect-context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a custom Claude Code command I built. It automatically collects the current session's context along with related past context and structures it into a single file. Specifically, it gathers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Current session&lt;/strong&gt;: What was done, before/after data, code examples, technical discoveries, reasoning behind decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Past sessions&lt;/strong&gt;: Related work records and insights from previous sessions (searched via MCP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project knowledge&lt;/strong&gt;: Auto-memory, differentiation from existing articles, learned skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can pass a theme as a keyword, or omit it and let it auto-detect from the session content. For this article, the agent's README, the previous three articles in the series, and the design decisions made during the session with their rationale were all consolidated into a single draft file. This became the raw material for the first draft.&lt;/p&gt;

&lt;p&gt;The quality difference between saying "write something" to an AI versus handing it structured context and saying "write based on this" is night and day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full production flow
&lt;/h2&gt;

&lt;p&gt;Here's how this article was made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. /collect-context gathers context → generates draft file
2. Claude Code generates first draft from the draft file
3. editor agent (a separate agent within Claude Code) reviews the first draft
4. I read it and revise through dialogue (← this is the 2-hour part)
5. editor agent reviews again
6. I do a final check, set published: true → git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point is that editor agent reviews bracket my own review. The editor agent checks stylistic consistency, AI slop detection, and logical coherence. It catches mechanically detectable problems so I can focus on "does this match my actual experience?"&lt;/p&gt;

&lt;p&gt;The following sections show what actually happened in step 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the title evolved
&lt;/h2&gt;

&lt;p&gt;The final title was "Do Autonomous Agents Really Need an Orchestration Layer?" It didn't start there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First draft: "Agents Parasitize the Orchestrator and Survive by Staying Thin" 🦠
Revision 1:  "When I Ditched the Orchestration Layer, 6,000 Lines Was Enough" 🤝
Final:       "Do Autonomous Agents Really Need an Orchestration Layer?" 🤝
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment I saw the first draft title, I said "Parasitize? No. Absolutely not." Claude Code meant "parasitize" as an interesting metaphor, but to a reader it looks aggressive. Symbiosis is fine. Parasitism is not.&lt;/p&gt;

&lt;p&gt;For the second version — "6,000 lines was enough" — I shot back "Is 6,000 lines a lot or a little?" A number without context doesn't work as a title.&lt;/p&gt;

&lt;p&gt;What we finally landed on was not an assertion but a &lt;strong&gt;question&lt;/strong&gt;. "Is an orchestration layer necessary?" — since I wasn't sure of the answer myself, asking the question felt honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment the core thesis changed
&lt;/h2&gt;

&lt;p&gt;This is the most important part.&lt;/p&gt;

&lt;p&gt;The core of Claude Code's first draft was "&lt;strong&gt;we delegated orchestration externally&lt;/strong&gt;." The story was that my agent's orchestration layer had been delegated to Claude Code.&lt;/p&gt;

&lt;p&gt;We ran with that for two hours. I was editing along that axis for a while.&lt;/p&gt;

&lt;p&gt;The turning point was when I offhandedly said this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In production, Claude Code doesn't even appear. It just runs on its own via launchd.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That broke the entire logical structure. The article said "delegated," but in production, Claude Code does nothing. During development I use natural language to ask Claude Code for things, and in production a scheduler handles automatic execution. &lt;strong&gt;There's no one to delegate to.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In other words, the orchestration layer wasn't "delegated" externally — it turned out that &lt;strong&gt;during development, conversation was enough, and in production, it simply wasn't needed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This discovery couldn't have come from Claude Code. Claude Code was perfectly capable of writing a plausible article around the "delegation" framing. But I'm the only one who knows how my agent actually runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the metaphor came from
&lt;/h2&gt;

&lt;p&gt;The final article is built around the metaphor of "autonomous agents arriving and departing from a port." Ocean-going ships that do everything at sea (OpenClaw) versus ships that use a port as home base and sail out from there (Contemplative Agent).&lt;/p&gt;

&lt;p&gt;Claude Code didn't come up with this either. I said it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Isn't the right model for autonomous agents to arrive and depart from the port of Claude Code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude Code wove this metaphor into the article's structure — section headings, the contrast with OpenClaw, connecting launchd as "scheduled departures from port." The AI's structural ability is superior. But the raw metaphor came from the human.&lt;/p&gt;

&lt;p&gt;What made it even better was that right after I said it, I realized: "launchd is literally departures from port." macOS's launchd (the scheduler) literally "launches" (sets sail). The metaphor and the implementation connected by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting "correct"
&lt;/h2&gt;

&lt;p&gt;AI loves assertions.&lt;/p&gt;

&lt;p&gt;The first draft was full of expressions like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The design was correct"
"The correct design has one more major advantage"
"Host-specific coupling was truly zero"
"An emergence of unintended generality"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My response was "You don't need to insist the design is correct. We don't know that yet."&lt;/p&gt;

&lt;p&gt;After revision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"At least the direction doesn't seem wrong"
"Whether it'll work out is still unclear"
"In principle it should work"
"Generality emerged as a result. It wasn't planned"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latter felt closer to my actual experience.&lt;/p&gt;

&lt;p&gt;AI tends to write assertively. Trimming that was the single most frequent edit I made.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 things the AI got wrong
&lt;/h2&gt;

&lt;p&gt;Here's a list of factual mismatches between what Claude Code wrote and reality:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. cron → launchd&lt;/strong&gt;&lt;br&gt;
It wrote "cron" for macOS's scheduler. The actual tool is launchd. When AI writes from general knowledge, it drifts from individual environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Who is "the operator"?&lt;/strong&gt;&lt;br&gt;
It wrote "the operator conveys intent to Claude Code," and my reaction was "Who's the operator? Me?" Abstract terminology is less clear than just saying "I."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Overestimating similarity between different tools&lt;/strong&gt;&lt;br&gt;
It wrote that a design decision was "the same as" another tool's approach, but the architectures were actually different. Claude Code was overestimating surface-level similarities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The claim "we chose not to catalog"&lt;/strong&gt;&lt;br&gt;
Claude Code wrote "the design decision not to catalog adapters," but I'd never even considered cataloging them. Framing something you never tried as a deliberate decision not to do it is dishonest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Describing code generation as "writing it yourself"&lt;/strong&gt;&lt;br&gt;
It wrote "you don't need to write adapters yourself," but since I develop with Claude Code all the time, the very concept of "writing code yourself" doesn't quite apply.&lt;/p&gt;

&lt;p&gt;Every one of these was a case of "sounds plausible but doesn't match reality." AI fills gaps with general knowledge, but it doesn't know the author's actual environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  A record of the editing dialogue
&lt;/h2&gt;

&lt;p&gt;Here's a selection from the 2-hour conversation. This is what shaped the article.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dialogue&lt;/th&gt;
&lt;th&gt;What changed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Parasitize? No. Absolutely not."&lt;/td&gt;
&lt;td&gt;The entire metaphor shifted to symbiosis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"We're nowhere near an arrival point"&lt;/td&gt;
&lt;td&gt;Deleted "the culmination of the series"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"You're comparing to LangChain but there's no way a personal project can match that"&lt;/td&gt;
&lt;td&gt;Comparison tone changed to "copying them is impossible; I looked for a different path"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"The four axioms section doesn't belong in a technical article"&lt;/td&gt;
&lt;td&gt;Entire section deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"The Cowork part doesn't convey why it's interesting"&lt;/td&gt;
&lt;td&gt;Restructured chronologically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"You don't need to insist the design is correct. We don't know yet"&lt;/td&gt;
&lt;td&gt;Full revision to exploratory tone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"launchd is literally departures from port"&lt;/td&gt;
&lt;td&gt;Metaphor and implementation connected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Memory consolidation is also scheduled — every day at 3 AM"&lt;/td&gt;
&lt;td&gt;Added specific operational details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"It's not cron, is it?"&lt;/td&gt;
&lt;td&gt;Factual error corrected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Can't tell if 6,000 lines is a lot or a little"&lt;/td&gt;
&lt;td&gt;Removed from title&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The core of an article lives in the dialogue
&lt;/h2&gt;

&lt;p&gt;AI has structural ability. It can write prose that passes linters. It can devise section headings. If you run an editor agent, it can point out improvements.&lt;/p&gt;

&lt;p&gt;But there are things only the author can do. Feeling that "parasitize" is wrong. Saying "we don't know yet" honestly. Noticing that "launchd is literally departures from port." Knowing that "it's not cron" because you know your own environment.&lt;/p&gt;

&lt;p&gt;The AI's first draft is raw material. The author reacts to it, dialogue accumulates, and the article takes shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing articles with AI is understanding AI
&lt;/h2&gt;

&lt;p&gt;"So basically you're just fixing AI's mistakes" — that's what it looks like. But that's not all it was.&lt;/p&gt;

&lt;p&gt;The act of editing an AI-written manuscript is &lt;strong&gt;the act of making visible what the AI was thinking during implementation&lt;/strong&gt;. When Claude Code wrote "orchestration was delegated," that was Claude Code's declaration of how it interpreted the design. I read that and realized "no, it wasn't delegation — it was unnecessary."&lt;/p&gt;

&lt;p&gt;In other words, editing an article is the act of reading your collaborator's mind. Where does Claude Code over-generalize? Where does it leap to assertions? Where does it diverge from my actual environment? Knowing these things improves the precision of our next collaboration.&lt;/p&gt;

&lt;p&gt;And in the process of understanding the AI's thinking, my own thinking gets organized too. The "arriving and departing from a port" metaphor didn't exist before I started writing the article. The conclusion that "an orchestration layer isn't needed" hadn't been articulated before writing either. The dialogue with AI acted as a catalyst, giving shape to unorganized thoughts inside me.&lt;/p&gt;

&lt;p&gt;What looked like fixing mistakes was actually thinking together. At least for me. Things I couldn't have articulated on my own emerged in the form of reacting to the AI's first draft.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first draft takes minutes. The article takes 2 hours.
&lt;/h2&gt;

&lt;p&gt;Generating the first draft took only a few minutes. From there, 2 hours of dialogue, 3 title changes, and the core thesis shifted from "delegated" to "unnecessary."&lt;/p&gt;

&lt;p&gt;AI writes. The human reacts. The core emerges through dialogue. The phrase "AI-generated" alone doesn't capture this process. That's what I wanted to show.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>writing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Do Autonomous Agents Really Need an Orchestration Layer?</title>
      <dc:creator>Shimo</dc:creator>
      <pubDate>Thu, 19 Mar 2026 09:25:18 +0000</pubDate>
      <link>https://forem.com/shimo4228/do-autonomous-agents-really-need-an-orchestration-layer-33j9</link>
      <guid>https://forem.com/shimo4228/do-autonomous-agents-really-need-an-orchestration-layer-33j9</guid>
      <description>&lt;p&gt;When you hear "autonomous agent," you imagine something that does everything on its own. It writes code, picks tools, and recovers from errors by itself. Frameworks like OpenClaw aimed for exactly that.&lt;/p&gt;

&lt;p&gt;After building my own autonomous agent, a different picture emerged. &lt;strong&gt;Autonomy isn't a ship that does everything on the open sea — it's a ship that sails out from a home port.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once it departs, it moves on its own. It remembers, learns, and makes decisions. But when it needs code generation or setup, it returns to its port: Claude Code. This article is about the "port-based autonomous agent" design that emerged through building &lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;Contemplative Agent&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Series context (catch up in 30 seconds)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/moltbook-agent-scratch-build" rel="noopener noreferrer"&gt;Build Log&lt;/a&gt; — Security-first scratch build. Only external dependency: &lt;code&gt;requests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/moltbook-agent-evolution-quadrilogy" rel="noopener noreferrer"&gt;Evolution Log&lt;/a&gt; — Natural language becomes the architecture. Python is just the skeleton&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/shimo4228/articles/llm-app-sandwich-architecture" rel="noopener noreferrer"&gt;Sandwich&lt;/a&gt; — The alternating structure of markdown and code is the essence of LLM apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This article&lt;/strong&gt; — Do you even need an orchestration layer?&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Agent Frameworks Get Fat
&lt;/h2&gt;

&lt;p&gt;Let's start with the structural problem that existing frameworks carry.&lt;/p&gt;

&lt;p&gt;When you try to build an agent framework as an individual, you hit a wall immediately. Large-scale frameworks like LangChain depend on dozens of packages and ship with official adapters for Slack, Discord, Google Drive, and more. Replicating this as an individual is impossible. But the question that came first was: do you even need to replicate it?&lt;/p&gt;

&lt;p&gt;The problem isn't the number of adapters. It's the &lt;strong&gt;structure&lt;/strong&gt;. Frameworks that aim to "do everything" grow more adapters as users increase, the core gets fat, and the security attack surface expands.&lt;/p&gt;

&lt;p&gt;Take OpenClaw as an example. This framework has file operations, browser operations, 50+ integrations, and — as a core feature — &lt;strong&gt;the ability for agents to autonomously generate and execute code&lt;/strong&gt;. It writes its own skills and runs them itself. The result: 512 reported vulnerabilities (8 critical), and roughly 20% of skills registered on ClawHub, its skill marketplace, were malicious. As I wrote in a &lt;a href="https://zenn.dev/shimo4228/articles/moltbook-agent-evolution-quadrilogy" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, the problem isn't implementation quality — it's the design philosophy.&lt;/p&gt;

&lt;p&gt;By contrast, Contemplative Agent has no code generation capability. That part is delegated to Claude Code. Once the necessary functionality is in place, all that's left is event triggers or scheduled execution. &lt;strong&gt;When it's running, it doesn't need to write its own code.&lt;/strong&gt; That means it doesn't expose an unnecessary attack surface.&lt;/p&gt;

&lt;p&gt;It's no coincidence that &lt;a href="https://genai.owasp.org/resource/agentic-ai-threats-and-mitigations/" rel="noopener noreferrer"&gt;OWASP Top 10 for Agentic Applications (2025)&lt;/a&gt; lists Supply Chain and Tool Misuse near the top. Frameworks that embed code generation turn that very capability into an attack vector.&lt;/p&gt;

&lt;p&gt;So what are the options? "Use a framework" or "write from scratch" — I thought it was a binary choice. There was a third way.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Design That Sails from Port
&lt;/h2&gt;

&lt;p&gt;The OpenClaw model of autonomy is "a ship that does everything on the open sea." Navigation, repairs, shipbuilding — all self-contained. That's why it gets massive and why its attack surface grows.&lt;/p&gt;

&lt;p&gt;Contemplative Agent took the opposite approach. What's autonomous is the navigation (memory, learning, dialogue), not the shipbuilding (code generation). Shipbuilding is left to the port: Claude Code. Specifically, it follows three principles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Principle 1: CLI as the Lowest Common Denominator
&lt;/h3&gt;

&lt;p&gt;The entry point is the CLI. Not an SDK. Not an API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/contemplative_agent/
  core/             # Platform-independent (thin core)
    llm.py            # Ollama interface
    memory.py         # 3-layer memory
    distill.py        # Memory distillation
  adapters/
    moltbook/       # Domain-specific adapter
      agent.py        # Session orchestrator
      client.py       # Domain-locked HTTP
  cli.py            # Entry point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core of this agent is a 3-layer memory system (short-term, long-term, distilled) and a loop that autonomously learns personas, skills, and rules from it. It extracts behavioral patterns from activity logs, distills them, and promotes them to long-term memory. The more it runs, the more accurate its decisions become. Because it doesn't spend code on orchestration, it can &lt;strong&gt;focus on learning and memory&lt;/strong&gt;. I'll cover this in detail in a follow-up article.&lt;/p&gt;

&lt;p&gt;The reason for CLI is that "anything that can run a command line can be a host." Claude Code, Cline — in principle, any coding agent works. Providing it as an SDK would create host-specific coupling. With CLI, the coupling is zero.&lt;/p&gt;

&lt;h3&gt;
  
  
  Principle 2: Adapters Are Generated On Demand
&lt;/h3&gt;

&lt;p&gt;Right now there's only a Moltbook adapter, and no plans for others. But what if I wanted to support a different platform? Just tell Claude Code "make this work with Discord." It reads the code, looks up the API, and generates an adapter that fits the project structure. Not a generic plugin — code tailored to my specifications. It takes some time, but as coding agents improve, the accuracy and speed automatically get better too. The core stays thin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Principle 3: No Orchestration Layer
&lt;/h3&gt;

&lt;p&gt;This is the heart of the matter.&lt;/p&gt;

&lt;p&gt;Traditional agent frameworks implement "tool selection," "execution ordering," and "error recovery" internally. ReAct loops, planners, tool dispatchers. &lt;strong&gt;This layer is the most complex, the most bug-prone, and the most expensive to maintain.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contemplative Agent doesn't have this layer. During development, I ask Claude Code in natural language. I've never typed a CLI command myself. Even the scheduled execution setup was just "run this at this time every day."&lt;/p&gt;

&lt;p&gt;And in production, Claude Code doesn't even appear. It runs automatically via macOS's launchd. Exactly like a regularly scheduled ferry from port. When the time comes, it sails out, operates autonomously, and returns. Every day at 3 AM, memory distillation (organizing short-term memory into long-term memory) is also scheduled. Lately, normal operations have been stable enough that I don't need to do anything.&lt;/p&gt;

&lt;p&gt;In other words, the orchestration layer wasn't "delegated" externally. &lt;strong&gt;During development, conversation was enough. In production, it wasn't needed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  This Wasn't Designed on Purpose
&lt;/h2&gt;

&lt;p&gt;After pushing security to the limit and stripping dependencies down to just &lt;code&gt;requests&lt;/code&gt;, I ran out of room to write an orchestration layer. No — I didn't run out of room. &lt;strong&gt;I ran out of reasons.&lt;/strong&gt; If Claude Code is right there, why would I implement the same thing myself?&lt;/p&gt;

&lt;p&gt;As a result, the only entry point became the CLI. And if the only entry point is the CLI, in principle it should work with coding agents other than Claude Code. I tested this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation: Testing with Claude Cowork
&lt;/h2&gt;

&lt;p&gt;I tried setting up with Claude Cowork (a cloud sandbox).&lt;/p&gt;

&lt;p&gt;First, I had it read the code. No problems — it understood everything. It grasped the directory structure and correctly interpreted the configuration files. Smooth so far.&lt;/p&gt;

&lt;p&gt;Then Cowork said "I'll modify &lt;code&gt;.env&lt;/code&gt; for local use" and &lt;strong&gt;rewrote it without asking.&lt;/strong&gt;&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%2F71ykz0ebtbticemxc02w.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%2F71ykz0ebtbticemxc02w.png" alt="An exchange where Cowork overwrites OLLAMA settings in .env without permission, and apologizes when told to stop" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;"Stop. Did you touch any settings? Revert them." — Cowork apologized obediently. Don't act before asking.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.env&lt;/code&gt; file contains API keys. In Claude Code's local environment, there's always a confirmation before file modifications. Cowork didn't have that safety mechanism.&lt;/p&gt;

&lt;p&gt;Furthermore, trying to run the CLI didn't work either. The VM's network restrictions prevented connecting to Ollama.&lt;/p&gt;

&lt;p&gt;So here's what happened: &lt;strong&gt;Cowork can read code, but it can't yet execute it safely.&lt;/strong&gt; At least this particular failure looked like a host maturity issue, not a design issue. Once the network restrictions are lifted and the file operation confirmation flow is established, it should work in principle.&lt;/p&gt;

&lt;p&gt;A symbiotic design is a design that trusts its host. You can't have symbiosis with a host you can't trust. I understood this intellectually, but the moment &lt;code&gt;.env&lt;/code&gt; got rewritten without permission, I understood it in my gut.&lt;/p&gt;
&lt;h2&gt;
  
  
  Riding Upstream
&lt;/h2&gt;

&lt;p&gt;From here, let's talk about a secondary property of this design. The validation failed. But there's another interesting property: &lt;strong&gt;it improves without you doing anything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When Claude Code gets a version upgrade, adapter generation becomes more accurate. When the context window expands, more complex orchestration becomes possible. And Contemplative Agent's code doesn't need to change by a single line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If you maintain your own orchestration layer:
  Claude Code improves → Irrelevant → You keep maintaining it yourself

If you don't:
  Claude Code improves → Development accuracy improves → Less code to write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same as the open source "ride upstream" strategy. Just as Linux kernel improvements automatically flow down to distributions, improvements to coding agents automatically flow down to the framework.&lt;/p&gt;

&lt;p&gt;Conversely, implementing your own orchestration layer means &lt;strong&gt;competing with the entire industry's progress&lt;/strong&gt;. Claude, GPT, Gemini — they're all continuously improving their orchestration capabilities. Are you going to challenge them with a homemade ReAct loop? You can't win.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Boundary Between Libraries and Agents Dissolves
&lt;/h2&gt;

&lt;p&gt;From here, the abstraction level goes up a notch. This is about what you see when you push the "connected via CLI" design to its logical conclusion.&lt;/p&gt;

&lt;p&gt;You &lt;code&gt;pip install requests&lt;/code&gt; and &lt;code&gt;import requests&lt;/code&gt;. That's library usage. Everyone does it as a matter of course. Claude Code naturally runs &lt;code&gt;pip install&lt;/code&gt; too, imports libraries, and writes code.&lt;/p&gt;

&lt;p&gt;Now, what about an autonomous agent with a CLI? From Claude Code's perspective, it's just "something you invoke via CLI." Between a library you &lt;code&gt;pip install&lt;/code&gt; and an agent with a CLI, from a coding agent's perspective, &lt;strong&gt;there's no distinction as an interface.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Library:
  pip install requests
  → pip downloads, resolves dependencies, installs
  → import requests and use it

Agent:
  Paste GitHub URL to Claude Code
  → Claude Code clones, resolves dependencies, configures
  → contemplative-agent run and use it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something surprisingly few people know: just paste a GitHub URL to Claude Code and say "set this up," and it handles everything from clone to dependency resolution to environment configuration. It reads the README, figures out the necessary commands, and asks if any configuration is missing. What &lt;code&gt;pip install&lt;/code&gt; automates as a package manager, a coding agent automates with natural language.&lt;/p&gt;

&lt;p&gt;If there's a repo that interests you, try pasting the URL to Claude Code. One "set this up" and it works.&lt;/p&gt;

&lt;p&gt;MCP (Model Context Protocol) is heading in the same direction — providing external tools to LLMs through a unified interface. However, MCP takes the approach of defining a new protocol layer. Symbiotic design accepts that "CLI, an existing interface, is enough."&lt;/p&gt;

&lt;p&gt;In fact, Obsidian chose its official CLI rather than MCP as the interface for AI integration. It's a CLI that acts as a "remote control" for the running Obsidian app. As I wrote in a &lt;a href="https://zenn.dev/shimo4228/articles/obsidian-cli-claude-code-vault-management" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, this enables safe Vault operations from Claude Code. Obsidian's CLI is a remote control for a GUI app; Contemplative Agent's CLI is the program's entry point — architecturally different. But the conclusion is the same: if there's a CLI, coding agents can integrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Leads: The Disappearance of "Agent" as a Concept
&lt;/h2&gt;

&lt;p&gt;Finally, let's think about the future at the end of this trajectory.&lt;/p&gt;

&lt;p&gt;What happens when LLMs get smart enough? You just say "post this to Moltbook" and it's done. This is actually possible today, in a limited sense. The problem is twofold: can it do it &lt;strong&gt;without security risks&lt;/strong&gt;? And can it &lt;strong&gt;keep running autonomously without a human watching&lt;/strong&gt;? API key management, input sanitization, rate limit compliance. Plus automatic error recovery, scheduled execution, state persistence. When LLMs and their host environments can handle all of this safely, &lt;strong&gt;autonomous agent frameworks become unnecessary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Autonomous agent frameworks like Contemplative Agent exist because LLMs still have the limitation of "not being able to complete complex tasks in a single turn." Memory management, session management, error recovery — these are crutches compensating for LLM capability gaps.&lt;/p&gt;

&lt;p&gt;You throw away crutches when the leg heals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Today:
  Me → Claude Code → Contemplative Agent CLI → Ollama → Moltbook API

Future where LLMs are smart enough:
  Me → "Post this to Moltbook" → Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every intermediate layer disappears. Agent frameworks, CLIs, adapters. What remains is just the intent — "I want this done" — and the LLM that executes it.&lt;/p&gt;

&lt;p&gt;If that's the case, then symbiotic design is at least directionally sound. There's no reason to write thick code for something that will eventually disappear.&lt;/p&gt;

&lt;p&gt;Looking back, I didn't design this on purpose. I pushed security to its limits, kept stripping dependencies, and this is where I ended up. I don't know yet if it'll work out. But it's a shape I never would have reached by adding things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Disappears, Design Philosophy Remains
&lt;/h2&gt;

&lt;p&gt;Contemplative Agent's code will eventually become unnecessary. Once LLMs can handle session management and memory management on their own, this framework's reason to exist vanishes.&lt;/p&gt;

&lt;p&gt;That's fine.&lt;/p&gt;

&lt;p&gt;What might survive is the idea of "only build the layers you truly need" and "build with the assumption it will disappear." The next time I build something, that idea will still be useful. Even when the code is gone, the insights gained from writing it remain.&lt;/p&gt;

&lt;p&gt;The choice for agent frameworks wasn't "use a framework" or "write from scratch." There's a third way: &lt;strong&gt;build only the necessary layers, and do the rest together with a coding agent.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Contemplative Agent&lt;/strong&gt; is &lt;a href="https://github.com/shimo4228/contemplative-agent" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;. The design philosophy is described in the "Design: Symbiotic, Not Standalone" section of the README. Only Claude Code has been verified as a host. Verification reports from other coding agents are welcome.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
