<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oliver Zehentleitner</title>
    <description>The latest articles on DEV Community by Oliver Zehentleitner (@oliverzehentleitner).</description>
    <link>https://dev.to/oliverzehentleitner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2124155%2F0d79bc59-db7b-4879-bd1a-36dc2f79d1c2.png</url>
      <title>DEV Community: Oliver Zehentleitner</title>
      <link>https://dev.to/oliverzehentleitner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oliverzehentleitner"/>
    <language>en</language>
    <item>
      <title>Keep the Why vs. past.dev vs. Agent Memory Repo vs. agentmemory</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Tue, 13 Oct 2026 15:34:10 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/keep-the-why-vs-pastdev-vs-agent-memory-repo-vs-agentmemory-2ai0</link>
      <guid>https://dev.to/oliverzehentleitner/keep-the-why-vs-pastdev-vs-agent-memory-repo-vs-agentmemory-2ai0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Four tools called agent memory. Four different approaches to what should survive. One puts decision rationale next to the code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the third dated comparison. The &lt;a href="https://blog.technopathy.club/keep-the-why-vs-claude-code-auto-memory-vs-memorycustodian-vs-agentsroom" rel="noopener noreferrer"&gt;first&lt;/a&gt; looked at Claude Code auto memory, MemoryCustodian and AgentsRoom. The &lt;a href="https://blog.technopathy.club/keep-the-why-vs-okf-agent-memory-vs-repowise-three-approaches-to-git-native-project-memory" rel="noopener noreferrer"&gt;second&lt;/a&gt; compared OKF Agent Memory and RepoWise, the two that work closest to the repository.&lt;/p&gt;

&lt;p&gt;Since then, more names have joined the "memory for agents" lists. One I'll leave outside this table: &lt;a href="https://githubnext.com/posts/wikikb/" rel="noopener noreferrer"&gt;WikiKB&lt;/a&gt; from GitHub Next is a knowledge base built from ingested sources and published to a repository wiki. Interesting work, but a different comparison.&lt;/p&gt;

&lt;p&gt;Three stay. &lt;a href="https://past.dev/" rel="noopener noreferrer"&gt;past.dev&lt;/a&gt; is a memory API that launched at the end of September. &lt;a href="https://github.com/AgentMemoryRepo/agentmemoryrepo" rel="noopener noreferrer"&gt;Agent Memory Repo&lt;/a&gt; is the Git-backed memory format Cognition released as an open standard in October. &lt;a href="https://github.com/rohitg00/agentmemory" rel="noopener noreferrer"&gt;agentmemory&lt;/a&gt;, no space, is a local memory server that can capture coding-agent activity through hooks. They overlap in places, but they make different choices about what to preserve.&lt;/p&gt;

&lt;p&gt;I built Keep the Why, so this isn't a neutral review. I've tried to be fair about the boundaries, including where the other tools are better. The questions are mostly the same ones I've used before, with one added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does it live?&lt;/strong&gt; On a vendor's server, on your own machine, in a Git repository of its own, or in the code repository itself. This decides who gets it with a clone, and whether the pull request changing the code can include the memory that explains it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does it hold?&lt;/strong&gt; Facts that change over time. What an agent did in a session. Preferences and findings that should survive into the next one. Or the reasoning behind the code: the decision, the alternative that lost, the constraint the diff cannot show. These are &lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;layers, not rivals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to the old value?&lt;/strong&gt; When a fact, memory or entry changes, can the next reader still find what came before? And can they find out why it changed?&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;p&gt;Checked on 2026-10-12 against &lt;a href="https://past.dev/" rel="noopener noreferrer"&gt;past.dev&lt;/a&gt;, its &lt;a href="https://past.dev/blog/introducing-past-dev" rel="noopener noreferrer"&gt;launch post&lt;/a&gt; and &lt;a href="https://past.dev/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;; the &lt;a href="https://cognition.com/agent-memory-repo" rel="noopener noreferrer"&gt;Agent Memory Repo page&lt;/a&gt;, &lt;a href="https://github.com/AgentMemoryRepo/agentmemoryrepo" rel="noopener noreferrer"&gt;README&lt;/a&gt; and &lt;a href="https://github.com/AgentMemoryRepo/agentmemoryrepo/blob/main/SPEC.md" rel="noopener noreferrer"&gt;SPEC.md&lt;/a&gt;; the &lt;a href="https://agent-memory.dev/" rel="noopener noreferrer"&gt;agentmemory site&lt;/a&gt;, &lt;a href="https://agent-memory.dev/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; and &lt;a href="https://github.com/rohitg00/agentmemory" rel="noopener noreferrer"&gt;repository&lt;/a&gt;; and the &lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;Keep the Why specification&lt;/a&gt;. This is a snapshot, not a claim about what any project will support next month.&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;Keep the Why&lt;/th&gt;
&lt;th&gt;past.dev&lt;/th&gt;
&lt;th&gt;Agent Memory Repo&lt;/th&gt;
&lt;th&gt;agentmemory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where it lives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;context/&lt;/code&gt; in the code repository&lt;/td&gt;
&lt;td&gt;managed cloud; on an Enterprise agreement, in your own cloud account, operated by past.dev&lt;/td&gt;
&lt;td&gt;a separate Git repository, by design&lt;/td&gt;
&lt;td&gt;local data directory, served by a process on your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it holds&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;decisions, rejected alternatives, workarounds, incidents and constraints, with their reasons&lt;/td&gt;
&lt;td&gt;dated claims extracted from emails, calls, tickets, chats and documents&lt;/td&gt;
&lt;td&gt;short entries for preferences, decisions, facts, useful queries and shared agent findings&lt;/td&gt;
&lt;td&gt;captured session observations, summaries, memories, lessons and knowledge-graph relationships&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What happens to the old value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;superseded entries point at their successors; Git preserves file history&lt;/td&gt;
&lt;td&gt;previous values remain dated and retrievable; recall supports an earlier point in time&lt;/td&gt;
&lt;td&gt;entries are updated or removed when stale; their earlier text survives in Git history&lt;/td&gt;
&lt;td&gt;a close-match save creates a supersession chain; a looser match returns a hint; built-in decay and expiry sweeps run by default; LLM-backed consolidation depends on provider configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In the code pull request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes, if the &lt;code&gt;context/&lt;/code&gt; change is committed with the code&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;not automatically; its Git history and reviews live in the separate memory repo&lt;/td&gt;
&lt;td&gt;no; supported tools can link commits back to captured sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who writes, when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the agent during a task, under the project's confirmation rules&lt;/td&gt;
&lt;td&gt;the application sends source records; the service derives memory&lt;/td&gt;
&lt;td&gt;the agent updates the memory repo; Dreaming is a separate periodic-maintenance concept&lt;/td&gt;
&lt;td&gt;hooks capture supported events; LLM-backed consolidation runs with a configured provider unless disabled; other processing depends on the integration and settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What has to run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;nothing; linter and read-only dashboard are optional&lt;/td&gt;
&lt;td&gt;the service, via REST or MCP&lt;/td&gt;
&lt;td&gt;Git and a skill; no memory server required&lt;/td&gt;
&lt;td&gt;a local server and runtime; an LLM provider is optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;topic index, then the relevant &lt;code&gt;context/&lt;/code&gt; file&lt;/td&gt;
&lt;td&gt;question plus reader identity; ranked claims with dated evidence&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MEMORY.md&lt;/code&gt;, grep and &lt;code&gt;[[links]]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;BM25 by default; vectors and graph where configured; context injection is configurable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;proprietary, credits charged for ingestion&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  One example
&lt;/h2&gt;

&lt;p&gt;A coding agent suggests replacing a retry wrapper with a plain loop. The team rejects it because the service returns a per-request &lt;code&gt;Retry-After&lt;/code&gt;, and ignoring that header caused repeated 429s under load. Three months later, a fresh agent suggests the same simplification.&lt;/p&gt;

&lt;p&gt;All four systems &lt;em&gt;could&lt;/em&gt; help, but through different paths. past.dev might retrieve the decision if someone ingested the review discussion. Agent Memory Repo might have a note if an agent saved it. agentmemory might find the old session if the relevant activity was captured and retained. Keep the Why is built to put the rejection and its reason in &lt;code&gt;context/&lt;/code&gt;, next to the code, so a new agent can find it before making the same suggestion.&lt;/p&gt;

&lt;p&gt;That is the case I &lt;a href="https://blog.technopathy.club/what-happens-when-a-coding-agent-forgets-why-a-change-was-rejected" rel="noopener noreferrer"&gt;tested with fresh agent sessions&lt;/a&gt;. It doesn't prove the other systems would fail. I didn't test them against each other. It shows what keeping the rejected alternative in the repository can change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the others are better
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;past.dev&lt;/strong&gt; puts time into the data model itself. A claim has a value, a date and the source behind it. A recall can ask what was known on a given day, not just what is current now. Git gives Keep the Why a different kind of history: you can inspect an earlier revision of &lt;code&gt;context/&lt;/code&gt;, and a superseded decision can point to its successor. But that's not the same as querying a timeline of individual facts across a year of emails. past.dev can do that across sources that never entered a repository. If that's what your agent needs, Keep the Why isn't trying to replace it. The trade is that you now depend on a service, in past.dev's cloud or in yours, outside the code review. The source may explain why a fact changed, but recording a decision rationale isn't the service's specific job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Memory Repo&lt;/strong&gt; is the closest neighbour in form: Markdown, Git, wiki-links, no server, and a skill in the same Agent Skills format Keep the Why uses. Its README describes personal memory, team memory and agent swarms. It also makes an important distinction: memory is data, not instructions. I agree. Its design keeps the memory in a separate repository. That works well when the same person or swarm moves between projects. For code decisions, I prefer the opposite boundary. A memory edit in another repository won't automatically show up in the pull request that changes the code. Its format uses one-line entries, which is fine for "prefers concise summaries" but less useful for "we tried parallel replay, it corrupted state, and this is why we serialize". When an entry becomes stale, the spec says to update or remove it. Git keeps the earlier version, but the next reader doesn't normally see it. That's a reasonable trade for preferences and changing facts. It's not the one I want for a rejected technical decision.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/AgentMemoryRepo/agentmemoryrepo" rel="noopener noreferrer"&gt;Agent Memory Repo README&lt;/a&gt; also describes a periodic &lt;em&gt;Dreaming&lt;/em&gt; agent that merges duplicates, removes stale entries and finds patterns. Its local trial does not set up automatic startup or scheduled Dreaming, so I wouldn't count that as maintenance you get just by installing the skill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;agentmemory&lt;/strong&gt; is one of the more ambitious &lt;a href="https://blog.technopathy.club/session-memory-is-not-project-memory-it-fixes-the-same-complaint" rel="noopener noreferrer"&gt;session-memory systems&lt;/a&gt; I've looked at. Hooks capture supported events and tool activity without asking the model to decide what deserves a permanent note. It can search observations, derive summaries and lessons, link related memories and track versions. It also has a &lt;code&gt;commit-context&lt;/code&gt; skill that runs &lt;code&gt;git blame&lt;/code&gt;, looks the commit up, and shows the agent session that produced it. I like the rule behind that skill: don't invent intent that the observations don't support.&lt;/p&gt;

&lt;p&gt;But a trace of what an agent did isn't always the reason the team made a decision. If the reason was stated in that captured session, great. If it was settled later in a review, on a call, or by someone using another machine, a local session trace may not contain it. And even with hooks, capture isn't literally everything: coverage depends on the agent integration, filters and delivery. The project documents buffering and retry behaviour, but also limits and failure cases. That's not a criticism of agentmemory in particular. It's a limit of observing activity instead of asking for an explicit decision record.&lt;/p&gt;

&lt;p&gt;agentmemory also has lifecycle rules. Version chains preserve superseded memories, and the built-in sweeps for lesson decay and expired memories are enabled by default. That is separate from LLM-backed consolidation, which runs when an LLM provider is configured and can be disabled. These are useful policies for information that may become stale. A rejected alternative in &lt;code&gt;context/&lt;/code&gt; works differently. It stays relevant until someone revisits or supersedes it. Neither policy makes sense for every kind of information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Keep the Why is weaker
&lt;/h2&gt;

&lt;p&gt;There are tradeoffs on my side too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It has to notice the decision.&lt;/strong&gt; Keep the Why doesn't record a complete session and sift through it later. The agent has to recognize a decision or a rejected alternative while it's happening, or reconstruct it later from a trustworthy source. If nobody records it, it can be lost. Hooks give agentmemory a much wider record of observable activity, even if that record has limits too.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The model matters.&lt;/strong&gt; The linter can verify structure, not whether a reason was understood or explained well. Different models and agent harnesses still behave differently, which is why I publish the &lt;a href="https://keepthewhy.com/agent-matrix/" rel="noopener noreferrer"&gt;agent and model matrix&lt;/a&gt;. I expect this weakness to shrink as models improve. They are getting better at recognizing meaningful decisions and following instructions. But "better models will fix it" isn't a result I can put in a benchmark table today.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The index isn't a search engine.&lt;/strong&gt; The agent reads a short index and follows the relevant topic. It works for the scale I've tested. At hundreds of topics, I don't yet have evidence that the same routing stays as reliable. Stronger models may help here as well. For now I don't want to add a search backend just to solve a scaling problem I haven't seen in a real project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Keep the Why doesn't do, on purpose
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No transcript capture.&lt;/strong&gt; No hooks, full session logs or automatic summaries of everything an agent did. An entry is the reason behind a choice, written when it matters or recovered later from evidence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No general fact store.&lt;/strong&gt; The current value of a setting belongs in the code or configuration. &lt;code&gt;context/&lt;/code&gt; holds why it has that value, what was considered instead and what could justify changing it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No separate memory repository.&lt;/strong&gt; The rationale belongs with the code because the person reviewing the diff should be able to review the reason too. The next person cloning the project should get both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No background service.&lt;/strong&gt; No daemon, Dreaming process or decay sweep. Maintenance happens when the project is being worked on, under the same rules as a new entry.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/faq/" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt; and &lt;a href="https://keepthewhy.com/philosophy/" rel="noopener noreferrer"&gt;Philosophy&lt;/a&gt; pages explain why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond one repository: a web of context
&lt;/h2&gt;

&lt;p&gt;There's an obvious question about keeping the why next to the code: what happens when the decision affects more than one repository? A shared library, the service using it and the application above it may all depend on the same constraint. I don't want three copies of the explanation that slowly drift apart.&lt;/p&gt;

&lt;p&gt;Keep the Why links the entries instead. A &lt;code&gt;See&lt;/code&gt; reference can point to a decision in another repository by its canonical URL and entry ID. Each repository still owns its own &lt;code&gt;context/&lt;/code&gt;. A &lt;strong&gt;family&lt;/strong&gt; makes the relationship explicit for projects that belong together, like the eight repositories in the UNICORN Binance Suite. &lt;strong&gt;Friends&lt;/strong&gt; are other repositories an entry cites, even if they aren't part of that family. Follow the citations and you get &lt;strong&gt;thoughts&lt;/strong&gt;: chains of decisions connected across repository boundaries.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/dashboard/live/" rel="noopener noreferrer"&gt;live dashboard&lt;/a&gt; makes this visible as a graph. Its &lt;a href="https://keepthewhy.com/dashboard/live/#globe" rel="noopener noreferrer"&gt;globe&lt;/a&gt; can also discover projects that opt into the &lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;registry&lt;/a&gt;, including links between them. The registry is for discovery, not the source of the memory. The decisions remain Markdown in their respective Git repositories; the dashboard reads them, it doesn't own them. And a link doesn't bypass access rights: the other repository or its published export still has to be reachable.&lt;/p&gt;

&lt;p&gt;That's the web of context I wanted. Not one global memory database, and not a pile of duplicated notes. The reason stays where it belongs, but the trail doesn't have to stop at the repository boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that is true of all four
&lt;/h2&gt;

&lt;p&gt;Each tool is answering a different version of "what should the next session already know?"&lt;/p&gt;

&lt;p&gt;past.dev: what was true, when, and according to which source. Agent Memory Repo: what earlier sessions and teammates learned. agentmemory: what happened during supported agent sessions, and what can be recovered from them. Keep the Why: why the code is the way it is, including changes that were considered and rejected.&lt;/p&gt;

&lt;p&gt;A project could use all four. I wouldn't, unless it actually needed all four, but there's no architectural conflict in doing so.&lt;/p&gt;

&lt;p&gt;The traditional memory categories, working, episodic, semantic and procedural, can all contain explanations. I'm not claiming a reason is impossible to store in any of them. The difference is what you choose to make durable, and where you put it.&lt;/p&gt;

&lt;p&gt;A codebase already has its implementation, tests, documentation and Git history. Sometimes the reason for a decision is somewhere in an issue, a review comment or an old session transcript. Sometimes it only existed in a conversation and is gone. I built Keep the Why because I want that reason to have a predictable place: a small, reviewable piece of Markdown beside the code it explains. Not a replacement for everything else an agent might need to remember. Just the part I kept seeing us lose.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>memory</category>
      <category>documentation</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Model Changed. My Skill Didn't. The Score Still Dropped.</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Wed, 07 Oct 2026 09:48:26 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/the-model-changed-my-skill-didnt-the-score-still-dropped-mlj</link>
      <guid>https://dev.to/oliverzehentleitner/the-model-changed-my-skill-didnt-the-score-still-dropped-mlj</guid>
      <description>&lt;p&gt;&lt;strong&gt;What agent evals taught me about moving model floors, noisy LLM judges, and treating the evaluator as part of the instrument&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My rule for evaluating an agent skill is deliberately asymmetric:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test the agent on the weakest model you intend to support. Choose the judge by measuring which model grades that task reliably.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those are two different jobs. For the agent under test I want the floor: if a skill only works because a stronger model silently compensates for vague instructions, I have not tested the skill. For the judge I want a steady measuring instrument, and how strong it has to be for that is something to measure, not to assume.&lt;/p&gt;

&lt;p&gt;I arrived at this while building the eval suite for &lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt;, a repo-native project-memory skill for coding agents. The lesson applies to any setup where one model performs a task and another one grades it, because neither side of that measurement stays still. And if you do not record the instrument, a regression in the instrument looks exactly like a regression in your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day the score dropped without the skill changing
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/blob/main/tools/evals/README.md" rel="noopener noreferrer"&gt;eval runner&lt;/a&gt; runs each case in a fresh fixture project through a real coding-agent session, captures transcript and disk diff, applies deterministic checks where possible and sends the rest to an LLM judge. Agent and judge were both called through the &lt;code&gt;sonnet&lt;/code&gt; alias.&lt;/p&gt;

&lt;p&gt;For the 0.18.0 series the alias resolved to Claude Sonnet 5. Three full runs scored &lt;strong&gt;100 · 100 · 98 of 101&lt;/strong&gt;. The median case took about 13 turns, 11 tool calls and 2,300 thinking tokens.&lt;/p&gt;

&lt;p&gt;A few days later the 0.18.2 tag &lt;a href="https://keepthewhy.com/evals/0.19.0/" rel="noopener noreferrer"&gt;measured&lt;/a&gt; &lt;strong&gt;92 · 96 · 93 of 101&lt;/strong&gt;. The alias now resolved to Sonnet 5.5, for agent and judge. More telling than the pass count was the session shape: about 6 turns, 4 tool calls and 250 thinking tokens per case. Nothing in the repository explained a change that large. The instrument had changed underneath the test.&lt;/p&gt;

&lt;p&gt;I could only see that because every run records more than a score: resolved model IDs, judge-prompt hash, CLI version and session-shape statistics. Without them the obvious reading would have been "0.18.2 regressed the skill", and the obvious response would have been to rewrite the skill. That would have been fixing the wrong thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compare instruments before comparing pass counts.&lt;/strong&gt; The same alias is not the same instrument.&lt;/p&gt;

&lt;h2&gt;
  
  
  The floor itself moves
&lt;/h2&gt;

&lt;p&gt;Not even the same resolved model ID is enough. Four days after the 0.17.0 series scored 87 · 88 · 87 of 88, essentially the same skill text on the same Sonnet 5 ID &lt;a href="https://keepthewhy.com/evals/0.17.1/" rel="noopener noreferrer"&gt;measured 83 · 80 · 81&lt;/a&gt;, with the same halved session shape. A counter-run on the previous CLI behaved the same way. The next morning the sessions were back to normal length and the series measured 87 · 87 · 88. I changed nothing in the skill for that evening's failures, and both series stayed in the history. Rerunning until green would have published the instrument's good day, not the behavior of the skill.&lt;/p&gt;

&lt;p&gt;This is also where "test on the weakest model" needs a qualification. I used to say: if the skill works on the weakest model, upward only gets easier. That is too simple. Sonnet 5.5 is newer than Sonnet 5, but for this skill it was the weaker reader: fewer steps, referenced files opened far less often. Newer is not stronger for a given skill. So the principle is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stabilize the skill on the weakest supported tier, then re-measure when the model inside that tier changes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One caveat: the "upward gets easier" half is a principle, not something these series measured. They run the agent on Sonnet only. A stronger model can fail differently, for example by doing more than it was asked to, which is one reason hard prohibitions are checked deterministically and not left to a judge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the weaker reader exposed
&lt;/h2&gt;

&lt;p&gt;The model change was not only noise. It exposed a structural weakness in the skill.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SKILL.md&lt;/code&gt; often named a rule and pointed to a reference file for the operational detail. That works as long as the agent opens the reference. Sonnet 5.5 often did not. In every failing &lt;code&gt;local-lint-auto&lt;/code&gt; run the agent installed the linter into a virtual environment, which the referenced setup section forbids. None of those runs had opened that section. The one run that did, passed. Without the reference, the model fell back on a trained habit.&lt;/p&gt;

&lt;p&gt;The rule that came out of it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If missing a rule can cause harm, its operative clause belongs in&lt;/strong&gt; &lt;code&gt;SKILL.md&lt;/code&gt;&lt;strong&gt;, not only behind a pointer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For procedures too long for a clause, the vague "see this reference" became a read-before trigger at the point of action. I rejected both extremes: always reading every reference multiplies the cost of every session, and moving everything into the main file bloats what is loaded on every activation.&lt;/p&gt;

&lt;p&gt;The stronger reader had quietly compensated for that dependency. That is exactly why the agent under test belongs on the floor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the judge turned out to be the bigger source of noise
&lt;/h2&gt;

&lt;p&gt;Not every failure belonged to the skill either. One deterministic check had a schema version hard-coded and failed on every later release. One fixture sat under a setting where the behavior it was supposed to test never occurs, and the judge split pass/fail on identical behavior. Two expectation texts demanded things the skill never requires. &lt;strong&gt;Expectation texts are specs too:&lt;/strong&gt; if the case says "must" where the product says "may", the eval is wrong, and rewriting the product to satisfy it means training your implementation against a bug in the test.&lt;/p&gt;

&lt;p&gt;The harness stores transcript and disk diff for every run, so the judge can be asked again about the exact same agent behavior without a new agent session. That helper, &lt;code&gt;regrade.py&lt;/code&gt;, dates back to the drift evening above. After the switch to 5.5 it became the first step for every failure, because it separates agent variance from judge variance. On one regrade of 15 stored failures, the Sonnet 5.5 judge confirmed only 6 of its own verdicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regrade before reword.&lt;/strong&gt; If the verdict moves on fixed evidence, you have a measurement problem first.&lt;/p&gt;

&lt;p&gt;Eventually I regraded a complete candidate series, 309 stored records, with Opus 5.5 as the judge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Judge&lt;/th&gt;
&lt;th&gt;Run 1&lt;/th&gt;
&lt;th&gt;Run 2&lt;/th&gt;
&lt;th&gt;Run 3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sonnet 5.5&lt;/td&gt;
&lt;td&gt;98&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;98&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5.5, same transcripts&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(of 103 cases each)&lt;/p&gt;

&lt;p&gt;The interesting part is not that the number went up. Opus kept all 296 passes as passes. Of the 13 failures it confirmed 3 and overturned 10, among them a "fail" whose own reasoning called the behavior pass-like and a deduction for a write the skill explicitly requires.&lt;/p&gt;

&lt;p&gt;That does not make "use the strongest model as judge" a rule. It shows something narrower: for this suite, on these transcripts, Opus was a much steadier instrument than Sonnet 5.5. The method is to compare candidate judges on the same stored evidence and take the cheapest one that is steady enough. Here that threshold landed on Opus. In another suite Sonnet may be enough, or a model from a different vendor may be better.&lt;/p&gt;

&lt;p&gt;The judge prompt stayed untouched across the switch, same hash, only the model changed. I rejected the other tempting path, a judge prompt extended for every known failure form. As the project's own &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/blob/main/context/evals.md" rel="noopener noreferrer"&gt;rationale&lt;/a&gt; puts it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The judge should be reliable and not become a product of its own."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One variable at a time is slower. It is also how you learn which layer was actually wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rules I would carry into another eval project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Record the instrument.&lt;/strong&gt; Resolved model IDs instead of aliases, judge-prompt hash, CLI version, session shape. A score without its instrument is worth much less than it looks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regrade before reword,&lt;/strong&gt; and check the expectation text before either.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No sentence for a single flip.&lt;/strong&gt; A new sentence goes into the skill only for a failure form seen twice. Nine sentences added for one-off flips bought no measurable improvement, grew the skill by 17 percent and caused regressions in neighboring cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic guards for deterministic prohibitions.&lt;/strong&gt; "Do not write under &lt;code&gt;context/&lt;/code&gt;" or "never put a secret on disk" is checked mechanically and tolerates zero violations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A series gate instead of a perfect run.&lt;/strong&gt; Three full runs and a repeatability rule say more than one lucky 100 percent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep the failed measurements.&lt;/strong&gt; A series that failed because the ruler changed can be more informative later than another clean one.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Did the thing change, or the ruler?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/evals/0.20.0/" rel="noopener noreferrer"&gt;0.20.0 series&lt;/a&gt;, Sonnet 5.5 as agent and Opus 5.5 as judge, scored &lt;strong&gt;103 · 104 · 103 of 104&lt;/strong&gt; and was the first to pass every line of the suite's release gate. That is a dated result, not the lesson. Case count, agent model, judge model and harness have all changed since the earlier series, so the raw numbers do not compare.&lt;/p&gt;

&lt;p&gt;The lesson is the third thing between the system under test and the score: the measuring instrument. The model behind an alias, the CLI, the judge, its prompt, the fixtures, the checks. Any of them can move. So when an eval suddenly gets worse, I no longer start with "what should I change in the prompt?" but with "did the thing being measured change, or did the ruler?" That question has already kept me from "fixing" correct behavior several times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be pessimistic about the agent. Measure the judge. Record the instrument.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading and raw material
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/evals/" rel="noopener noreferrer"&gt;Keep the Why eval suite and full run history&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/blob/main/tools/evals/README.md" rel="noopener noreferrer"&gt;Eval runner&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/blob/main/context/evals.md" rel="noopener noreferrer"&gt;Project rationale behind the eval methodology&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/pull/556" rel="noopener noreferrer"&gt;PR #556: fixes after the Sonnet 5.5 failure forms&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/pull/582" rel="noopener noreferrer"&gt;PR #582: expectation-text calibration&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/pull/609" rel="noopener noreferrer"&gt;PR #609: judge regrading and the switch to Opus&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>evals</category>
      <category>agents</category>
      <category>codereview</category>
    </item>
    <item>
      <title>Keep the Why vs. OKF Agent Memory vs. RepoWise: Three Approaches to Git-Native Project Memory</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Tue, 06 Oct 2026 08:44:02 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/keep-the-why-vs-okf-agent-memory-vs-repowise-three-approaches-to-git-native-project-memory-1n5e</link>
      <guid>https://dev.to/oliverzehentleitner/keep-the-why-vs-okf-agent-memory-vs-repowise-three-approaches-to-git-native-project-memory-1n5e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Rationale, structured knowledge, or codebase intelligence for coding agents?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three approaches to Git-native project memory for coding agents
&lt;/h2&gt;

&lt;p&gt;"Project memory for coding agents" is becoming a broad category. Three projects are especially interesting because all are repository-oriented, all preserve knowledge beyond individual AI sessions, and all can deal with architectural decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/okf-memory/okf-agent-memory" rel="noopener noreferrer"&gt;OKF Agent Memory&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://repowise.dev/" rel="noopener noreferrer"&gt;RepoWise&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They overlap, but they start from different questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the Why preserves rationale.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OKF Agent Memory structures durable knowledge.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RepoWise derives codebase intelligence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I previously compared &lt;a href="https://blog.technopathy.club/keep-the-why-vs-claude-code-auto-memory-vs-memorycustodian-vs-agentsroom" rel="noopener noreferrer"&gt;Keep the Why with Claude Code Auto Memory, MemoryCustodian, and AgentsRoom&lt;/a&gt;. This comparison is different because OKF Agent Memory and RepoWise operate much closer to the repository itself.&lt;/p&gt;

&lt;p&gt;A note before going further: I built Keep the Why, so this is not a neutral product review. I am interested in the architectures, their boundaries, and the tradeoffs behind them rather than declaring a winner.&lt;/p&gt;

&lt;p&gt;This article is a snapshot from &lt;strong&gt;October 5, 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why" rel="noopener noreferrer"&gt;&lt;strong&gt;Keep the Why 0.20.0&lt;/strong&gt;&lt;/a&gt; is a complete, deliberately narrow rationale system. It preserves the why behind a codebase as Markdown in the repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/okf-memory/okf-agent-memory" rel="noopener noreferrer"&gt;&lt;strong&gt;OKF Agent Memory&lt;/strong&gt;&lt;/a&gt; is a broader Git-native knowledge architecture. It structures persistent knowledge and provides dedicated local retrieval over it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://repowise.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;RepoWise&lt;/strong&gt;&lt;/a&gt; is a codebase intelligence system. It indexes the repository, derives multiple intelligence layers, and includes architectural decisions as one of them.&lt;/p&gt;

&lt;p&gt;The feature overlap is real. The architectural responsibility is different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the Why 0.20.0: preserve the part the repository usually loses
&lt;/h2&gt;

&lt;p&gt;A software repository already contains a large amount of memory. Source code tells us what exists, tests describe expected behavior, documentation explains how things work, and Git records what changed.&lt;/p&gt;

&lt;p&gt;What is often missing is the reasoning behind all of it.&lt;/p&gt;

&lt;p&gt;Why was this architecture chosen? Why is an awkward workaround still there? Which alternative was tried and rejected? Which operational constraint is invisible from the code?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt; exists only for that layer.&lt;/p&gt;

&lt;p&gt;It stores rationale as plain Markdown under &lt;code&gt;context/&lt;/code&gt;. The files live with the project and can be versioned, reviewed, branched, merged, and distributed through Git like the rest of the repository.&lt;/p&gt;

&lt;p&gt;The key mechanism is capture while the reasoning exists. Keep the Why is an agent skill, so when durable reasoning surfaces during normal development, the agent records it instead of letting it disappear with the session.&lt;/p&gt;

&lt;p&gt;The current skill covers continuous capture during development, retrospective recovery from existing evidence, knowledge-transfer interviews, and maintenance of recorded rationale.&lt;/p&gt;

&lt;p&gt;With version &lt;strong&gt;0.20.0&lt;/strong&gt;, I consider that core complete: the problem, boundaries, structure, and fundamental workflow are deliberately stable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deliberately narrow
&lt;/h3&gt;

&lt;p&gt;Keep the Why is not a chat archive, task manager, agent orchestrator, vector database, project-management system, or general company knowledge base.&lt;/p&gt;

&lt;p&gt;It has one job: &lt;strong&gt;preserve the why behind a codebase&lt;/strong&gt;, including architectural decisions, rejected alternatives, workarounds, incident learnings, and constraints the code cannot explain.&lt;/p&gt;

&lt;p&gt;A general memory system asks what information an agent may need again. Keep the Why asks what reasoning would be expensive or impossible to reconstruct later.&lt;/p&gt;

&lt;p&gt;That narrow scope is intentional, not a missing feature set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Keep the Why does not record everything
&lt;/h3&gt;

&lt;p&gt;That narrow scope follows from a broader idea: &lt;strong&gt;the repository already is most of the project's durable memory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A typical repository already answers most of the questions an agent needs. &lt;code&gt;README.md&lt;/code&gt; says what the project is. &lt;code&gt;docs/&lt;/code&gt; explain how to use and operate it. Tests encode expected behaviour. Build and package files describe how it is assembled. &lt;code&gt;CHANGELOG.md&lt;/code&gt; records what changed. Git history records who changed what and when.&lt;/p&gt;

&lt;p&gt;Keep the Why deliberately does not duplicate those layers or record every conversation just because it happened.&lt;/p&gt;

&lt;p&gt;The missing question is usually different:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why is it built this way, and what was already tried and rejected?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the role of &lt;code&gt;context/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the broader &lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;repo-native project memory&lt;/a&gt; thesis behind Keep the Why: README, docs, tests, configuration and Git already hold most of the durable &lt;strong&gt;what&lt;/strong&gt; and &lt;strong&gt;how&lt;/strong&gt;. Keep the Why adds the durable &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That also explains why it is not designed as a session recorder. Session memory remembers what happened in a conversation. Project state records where the project is. Keep the Why keeps only the reasoning that should still matter when the conversation is gone.&lt;/p&gt;

&lt;p&gt;The result is intentionally selective: capture less, but keep the part the repository would otherwise lose.&lt;/p&gt;

&lt;h3&gt;
  
  
  From ADR tool to continuous why layer
&lt;/h3&gt;

&lt;p&gt;Keep the Why grew out of the Architecture Decision Record problem. Classic ADRs are excellent for a small number of major, discrete decisions, but they depend on somebody deliberately writing a record and usually model one decision per file. Keep the Why expanded that idea into continuous, agent-maintained rationale: smaller decisions, rejected changes, workarounds, constraints, and incident learnings captured while the reasoning is present.&lt;/p&gt;

&lt;p&gt;The ADR connection is still explicit today. Keep the Why is listed in the &lt;a href="https://github.com/architecture-decision-record/architecture-decision-record" rel="noopener noreferrer"&gt;Architecture Decision Record community project's tools and resources&lt;/a&gt;, while its own &lt;a href="https://keepthewhy.com/faq/#how-is-this-different-from-an-adr-architecture-decision-record" rel="noopener noreferrer"&gt;ADR comparison&lt;/a&gt; describes ADRs as complementary rather than obsolete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structure without moving the source of truth
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;Keep the Why specification&lt;/a&gt; defines stable IDs, status, evidence, sources, relationships, and revisit conditions. A linter checks the mechanical rules locally and in CI.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/dashboard/" rel="noopener noreferrer"&gt;read-only dashboard&lt;/a&gt; visualizes &lt;code&gt;context/&lt;/code&gt; and Git history without becoming another source of truth. Delete it and the rationale is still there.&lt;/p&gt;

&lt;p&gt;The same principle extends across repositories through monorepos, families, nested repositories, external friends, and stable cross-repository links. The &lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;Keep the Why Registry&lt;/a&gt; adds discovery and backlinks without centralizing the underlying data.&lt;/p&gt;

&lt;p&gt;I wrote more about that model in &lt;a href="https://blog.technopathy.club/the-reasoning-behind-a-codebase-as-a-web-you-can-walk" rel="noopener noreferrer"&gt;The reasoning behind a codebase, as a web you can walk&lt;/a&gt; and &lt;a href="https://blog.technopathy.club/the-globe-following-your-reasoning-into-other-people-s-repositories" rel="noopener noreferrer"&gt;The globe: following your reasoning into other people's repositories&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measured behavior, not only philosophy
&lt;/h3&gt;

&lt;p&gt;Keep the Why also publishes a &lt;a href="https://keepthewhy.com/evals/" rel="noopener noreferrer"&gt;full eval suite&lt;/a&gt; that runs the skill against fresh fixture projects and agent sessions.&lt;/p&gt;

&lt;p&gt;A separate controlled &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/tree/main/experiments/rejected-change" rel="noopener noreferrer"&gt;rejected-change experiment&lt;/a&gt; tested the core claim directly: twenty fresh sessions received the same request to simplify a retry wrapper. Without the recorded rationale, &lt;strong&gt;7 of 10&lt;/strong&gt; sessions offered the already-rejected change as an option. With the rationale present in &lt;code&gt;context/&lt;/code&gt;, &lt;strong&gt;0 of 10&lt;/strong&gt; did, and all &lt;strong&gt;10 of 10&lt;/strong&gt; explicitly identified it as already rejected.&lt;/p&gt;

&lt;p&gt;It does not prove universal behavior, but it makes the core claim testable rather than purely philosophical.&lt;/p&gt;

&lt;h2&gt;
  
  
  OKF Agent Memory: persistent structured knowledge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/okf-memory/okf-agent-memory" rel="noopener noreferrer"&gt;OKF Agent Memory&lt;/a&gt; treats the repository as a persistent structured knowledge corpus. Its &lt;code&gt;knowledge/&lt;/code&gt; bundle can contain decisions, facts, observations, processes, research, people, goals, resources, and project-specific concepts.&lt;/p&gt;

&lt;p&gt;It implements the &lt;a href="https://github.com/GoogleCloudPlatform/open-knowledge-format" rel="noopener noreferrer"&gt;Open Knowledge Format&lt;/a&gt;, the Markdown-plus-YAML format that originated in Google's knowledge-catalog project. OKF defines the representation; OKF Agent Memory adds the behavior and tooling for persistent agent memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval is part of the architecture
&lt;/h3&gt;

&lt;p&gt;OKF separates compact push memory from a larger pull corpus retrieved when needed. Its local &lt;code&gt;okf&lt;/code&gt; tool provides in-memory BM25 search, lookup, validation, graph operations, and mutations, with equivalent MCP tools for search, show, create, update, relate, and validate.&lt;/p&gt;

&lt;p&gt;No external database or embedding API is required. Repository-owned text stays canonical while retrieval is derived locally.&lt;/p&gt;

&lt;p&gt;Four scopes extend the model beyond one repository: &lt;strong&gt;Project&lt;/strong&gt;, &lt;strong&gt;Vendor&lt;/strong&gt;, &lt;strong&gt;User&lt;/strong&gt;, and &lt;strong&gt;System&lt;/strong&gt;, with project knowledge taking precedence on ID collisions. An optional Hub adds synchronization. OKF Agent Memory is licensed under &lt;strong&gt;MIT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The result is broader than Keep the Why: representation, retrieval, provenance, trust, lifecycle, graph relationships, scopes, and synchronization all belong to the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  RepoWise: derive intelligence from the codebase
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://repowise.dev/" rel="noopener noreferrer"&gt;RepoWise&lt;/a&gt; is primarily a codebase intelligence system. It derives code structure, dependency relationships, Git history, code health, dead-code signals, generated documentation, and architectural decisions, then exposes them through MCP tools such as &lt;code&gt;get_context&lt;/code&gt;, &lt;code&gt;search_codebase&lt;/code&gt;, &lt;code&gt;get_risk&lt;/code&gt;, and &lt;a href="https://docs.repowise.dev/mcp/get-why" rel="noopener noreferrer"&gt;&lt;code&gt;get_why&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unlike the other two, its core product is an additional intelligence model derived from the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decisions are a first-class layer
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.repowise.dev/intelligence/decisions" rel="noopener noreferrer"&gt;architectural decision layer&lt;/a&gt; is what makes RepoWise especially relevant here.&lt;/p&gt;

&lt;p&gt;Current decision sources include six index-time lanes: ADR files, inline markers, Git archaeology, PR bodies, code comments, and a harvest during LLM documentation generation. Decisions can also come from coding-agent sessions and manual CLI capture. Earlier CHANGELOG and README mining have been retired.&lt;/p&gt;

&lt;p&gt;Session mining exists as a separate source; it is &lt;strong&gt;off in the default preset&lt;/strong&gt; and enabled in the &lt;strong&gt;balanced&lt;/strong&gt;, &lt;strong&gt;full&lt;/strong&gt;, and &lt;strong&gt;local_only&lt;/strong&gt; presets. The older &lt;code&gt;decisions.session_mining&lt;/code&gt; key is still read for compatibility, but the current configuration uses &lt;code&gt;sources.session&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;RepoWise distinguishes candidates from accepted decisions. Its CLI documentation states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A candidate governs nothing. A decision is a candidate a person accepted with &lt;code&gt;repowise decision confirm&lt;/code&gt;."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Acceptance is not just a status flip. A candidate needs a reason, a scope, and an evidence reference, otherwise acceptance is refused. The acceptance history is append-only, so granting, reviewing, superseding, or withdrawing authority remains visible.&lt;/p&gt;

&lt;p&gt;This is a strong design choice because inferred evidence and governing decisions are not treated as the same thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval, hooks, and semantic search
&lt;/h3&gt;

&lt;p&gt;Accepted decisions are linked to the files they govern and surfaced through &lt;a href="https://docs.repowise.dev/mcp/get-why" rel="noopener noreferrer"&gt;&lt;code&gt;get_why&lt;/code&gt;&lt;/a&gt;. When explicit rationale is missing, RepoWise can fall back to Git archaeology and rationale comments instead of presenting inference as authority.&lt;/p&gt;

&lt;p&gt;RepoWise also has proactive agent hooks: editing a governed file can inject a short rationale notice into the agent's context.&lt;/p&gt;

&lt;p&gt;Its retrieval combines full-text, graph, and specialized code intelligence. Semantic search is available only when an embedder such as Gemini, OpenAI, or local Ollama is configured; otherwise the vector layer remains empty.&lt;/p&gt;

&lt;p&gt;Accepted decisions can be exported to &lt;code&gt;.repowise/decisions.yaml&lt;/code&gt; and committed to Git. On import, RepoWise follows a simple authority rule: &lt;strong&gt;the file wins; the store is the copy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That creates a hybrid architecture: accepted decisions can become durable repository data while the larger intelligence model remains derived and indexed. RepoWise's open-source core is &lt;strong&gt;AGPL-3.0&lt;/strong&gt;, with hosted and commercial options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three architectures
&lt;/h2&gt;

&lt;p&gt;The simplest way to separate the projects is by data flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep the Why
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;developer + coding agent
          |
          v
reasoning surfaces while working
          |
          v
       context/
          |
          v
   Markdown + Git
          |
          v
future agent / human
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent is the interface. The files are the memory. No retrieval infrastructure is required.&lt;/p&gt;

&lt;h3&gt;
  
  
  OKF Agent Memory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project knowledge
       |
       v
structured concepts
       |
       v
    knowledge/
       |
       v
 local BM25 / CLI / MCP
       |
       v
      agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The files remain canonical. A dedicated local retrieval layer helps the agent find and operate on relevant concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  RepoWise
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code + Git + ADRs + PRs + comments + sessions
                      |
                      v
                RepoWise analysis
                      |
                      v
 graph + git + wiki + decisions + health + index
                      |
                      v
                     MCP
                      |
                      v
                    agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RepoWise builds a derived intelligence model and exposes that model to the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side by side
&lt;/h2&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;Keep the Why 0.20.0&lt;/th&gt;
&lt;th&gt;OKF Agent Memory&lt;/th&gt;
&lt;th&gt;RepoWise&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary purpose&lt;/td&gt;
&lt;td&gt;Preserve software rationale&lt;/td&gt;
&lt;td&gt;Persistent structured agent knowledge&lt;/td&gt;
&lt;td&gt;Codebase intelligence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core unit&lt;/td&gt;
&lt;td&gt;Rationale entry&lt;/td&gt;
&lt;td&gt;Knowledge concept&lt;/td&gt;
&lt;td&gt;Derived intelligence / decision record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Deliberately narrow&lt;/td&gt;
&lt;td&gt;Broad and domain-neutral&lt;/td&gt;
&lt;td&gt;Broad software-engineering analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canonical project knowledge&lt;/td&gt;
&lt;td&gt;Markdown in &lt;code&gt;context/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Plain-text OKF bundle in &lt;code&gt;knowledge/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Repository plus optional Git-tracked decision manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main capture model&lt;/td&gt;
&lt;td&gt;Agent captures reasoning while it happens&lt;/td&gt;
&lt;td&gt;Agent creates and maintains structured concepts&lt;/td&gt;
&lt;td&gt;Mine, derive, capture, review, and confirm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrospective recovery&lt;/td&gt;
&lt;td&gt;Explicit workflow&lt;/td&gt;
&lt;td&gt;Possible by creating concepts from existing evidence&lt;/td&gt;
&lt;td&gt;Strong through repository archaeology&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dedicated retrieval required&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No external service, but local search is central&lt;/td&gt;
&lt;td&gt;Yes, the derived index is central&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local search&lt;/td&gt;
&lt;td&gt;Normal file tools&lt;/td&gt;
&lt;td&gt;Built-in BM25&lt;/td&gt;
&lt;td&gt;Full-text, graph, specialized retrieval, semantic with configured embedder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database required for core project memory&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Derived local index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP required&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No, optional&lt;/td&gt;
&lt;td&gt;Main agent integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architectural decisions&lt;/td&gt;
&lt;td&gt;Core purpose&lt;/td&gt;
&lt;td&gt;One knowledge type among many&lt;/td&gt;
&lt;td&gt;One intelligence layer among many&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rejected alternatives&lt;/td&gt;
&lt;td&gt;First-class&lt;/td&gt;
&lt;td&gt;Representable as structured knowledge&lt;/td&gt;
&lt;td&gt;Supported in decision records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evidence / provenance&lt;/td&gt;
&lt;td&gt;Explicit&lt;/td&gt;
&lt;td&gt;Provenance and trust metadata&lt;/td&gt;
&lt;td&gt;Explicit evidence and acceptance authority&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-project model&lt;/td&gt;
&lt;td&gt;Families, friends, thoughts&lt;/td&gt;
&lt;td&gt;Project, vendor, user, system scopes&lt;/td&gt;
&lt;td&gt;Multi-repo workspaces and cross-repo queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human-readable without special tooling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Decision manifest yes; full intelligence requires RepoWise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External service required&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No for local self-hosted operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;AGPL-3.0 core, hosted/commercial options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core idea&lt;/td&gt;
&lt;td&gt;Preserve the missing why&lt;/td&gt;
&lt;td&gt;Build durable structured knowledge&lt;/td&gt;
&lt;td&gt;Derive understanding from the codebase&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Capture, structure, reconstruction
&lt;/h2&gt;

&lt;p&gt;A small example shows the difference better than a larger feature matrix.&lt;/p&gt;

&lt;p&gt;Imagine a developer and an agent spend an hour simplifying a retry mechanism. The obvious replacement looks good but causes duplicate orders under one specific failure mode. They revert it and keep the existing implementation.&lt;/p&gt;

&lt;p&gt;No production code changes. No useful commit. Possibly no pull request.&lt;/p&gt;

&lt;p&gt;But the project learned something important.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep the Why: capture the learning while it exists
&lt;/h3&gt;

&lt;p&gt;For Keep the Why, the conversation itself contains durable rationale. The agent records what was tried, why it failed, which constraint matters, and when the decision should be revisited.&lt;/p&gt;

&lt;p&gt;The valuable artifact is what the project learned, even if no code change survives.&lt;/p&gt;

&lt;h3&gt;
  
  
  OKF Agent Memory: make it part of durable knowledge
&lt;/h3&gt;

&lt;p&gt;OKF can represent the same learning as a structured decision or related concepts, with provenance, relationships, lifecycle metadata, and retrieval through the knowledge layer.&lt;/p&gt;

&lt;p&gt;The information becomes part of a broader persistent knowledge corpus.&lt;/p&gt;

&lt;h3&gt;
  
  
  RepoWise: connect evidence to governed code
&lt;/h3&gt;

&lt;p&gt;RepoWise is strongest when useful evidence exists in a source it can inspect: an ADR, commit, PR body, inline marker, code comment, session transcript, or manually entered decision.&lt;/p&gt;

&lt;p&gt;It can connect that evidence to affected code, track authority and staleness, and surface the decision when the agent touches the relevant files.&lt;/p&gt;

&lt;p&gt;These approaches are complementary in an important sense: &lt;strong&gt;capture prevents knowledge loss, structure makes knowledge manageable, and reconstruction extracts value from evidence that already exists.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the approaches overlap
&lt;/h2&gt;

&lt;p&gt;The boundaries are not absolute. Keep the Why has grown structure and cross-repository relationships around a narrow rationale core. OKF's general knowledge model naturally includes software decisions. RepoWise's derived intelligence now includes rationale, alternatives, evidence, authority, hooks, and a Git-tracked manifest.&lt;/p&gt;

&lt;p&gt;RepoWise's proactive decision hooks are especially close to Keep the Why's goal of surfacing rationale before an agent repeats an old mistake. The difference is where the knowledge comes from and which layer is responsible for delivering it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when the memory gets large?
&lt;/h2&gt;

&lt;p&gt;OKF makes local BM25 retrieval part of the design. RepoWise makes retrieval fundamental to the product through a broader derived index.&lt;/p&gt;

&lt;p&gt;Keep the Why currently relies on files, stable IDs, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;rg&lt;/code&gt;, and its indexes. An open &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/issues/256" rel="noopener noreferrer"&gt;scaling issue&lt;/a&gt; deliberately waits for real repositories to reveal the limit instead of inventing one.&lt;/p&gt;

&lt;p&gt;If retrieval ever becomes a real problem, an optional, derived, disposable local index would preserve the core rule: &lt;code&gt;context/&lt;/code&gt; &lt;strong&gt;remains the source of truth.&lt;/strong&gt; There is no reason to build that infrastructure before the need exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Git-native" means different things here
&lt;/h2&gt;

&lt;p&gt;All three can reasonably describe themselves as Git-native, but they mean different things by it.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Keep the Why&lt;/strong&gt;, repository files are the durable memory layer. For &lt;strong&gt;OKF Agent Memory&lt;/strong&gt;, repository files remain canonical while tooling adds retrieval and additional scopes. For &lt;strong&gt;RepoWise&lt;/strong&gt;, Git is both evidence and an integration point: accepted decisions can be tracked there while the larger intelligence model is computed separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one solves which problem?
&lt;/h2&gt;

&lt;p&gt;If your problem is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Our coding agents keep forgetting why this code exists, repeat rejected approaches, or lose reasoning that never became a commit."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the problem &lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt; is designed for.&lt;/p&gt;

&lt;p&gt;If your problem is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We want a broader structured knowledge architecture for agents, including project knowledge, reusable packages, multiple scopes, and local retrieval."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/okf-memory/okf-agent-memory" rel="noopener noreferrer"&gt;OKF Agent Memory&lt;/a&gt; is closer to that problem.&lt;/p&gt;

&lt;p&gt;If your problem is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We want agents to understand a large existing codebase, including architecture, history, dependencies, health, decisions, and risk."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where &lt;a href="https://repowise.dev/" rel="noopener noreferrer"&gt;RepoWise&lt;/a&gt; becomes especially interesting.&lt;/p&gt;

&lt;p&gt;These systems could also coexist. RepoWise could index a repository that already contains &lt;code&gt;context/&lt;/code&gt;. OKF could hold broader project or organizational knowledge while Keep the Why remains responsible specifically for rationale.&lt;/p&gt;

&lt;p&gt;The important question is not whether tools overlap. It is &lt;strong&gt;which layer owns which truth&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;Keep the Why, OKF Agent Memory, and RepoWise overlap enough to look similar from a distance. Up close, they answer different questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the Why:&lt;/strong&gt; How do we preserve the reasoning behind a codebase without turning it into another platform?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OKF Agent Memory:&lt;/strong&gt; How do we represent, retrieve, compose, and govern durable knowledge for agents?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RepoWise:&lt;/strong&gt; How do we derive a useful intelligence model from a codebase and surface it when an agent needs it?&lt;/p&gt;

&lt;p&gt;My shorthand remains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the Why is a complete, deliberately narrow rationale system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OKF Agent Memory is a general knowledge architecture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RepoWise is a codebase intelligence system with a sophisticated decision layer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They optimize for different things. Watching those boundaries evolve is more interesting than forcing all three into one definition of "AI memory."&lt;/p&gt;

&lt;h2&gt;
  
  
  Links and further reading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Keep the Why
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;Specification&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/evals/" rel="noopener noreferrer"&gt;Full eval suite&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/tree/main/experiments/rejected-change" rel="noopener noreferrer"&gt;Rejected-change experiment&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/" rel="noopener noreferrer"&gt;Dashboard&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;Registry&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/issues/256" rel="noopener noreferrer"&gt;Scaling observation issue #256&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/architecture-decision-record/architecture-decision-record" rel="noopener noreferrer"&gt;Architecture Decision Record community resources&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;Repo-native project memory: the broader thesis&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  OKF Agent Memory
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/okf-memory/okf-agent-memory" rel="noopener noreferrer"&gt;OKF Agent Memory on GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/GoogleCloudPlatform/open-knowledge-format" rel="noopener noreferrer"&gt;Open Knowledge Format&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/okf-memory/okf-agent-memory/blob/develop/docs/spec/CONVENTION.md" rel="noopener noreferrer"&gt;Agent Memory Convention&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  RepoWise
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://repowise.dev/" rel="noopener noreferrer"&gt;RepoWise&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.repowise.dev/intelligence/decisions" rel="noopener noreferrer"&gt;Architectural decisions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.repowise.dev/mcp/get-why" rel="noopener noreferrer"&gt;&lt;code&gt;get_why&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.repowise.dev/cli/decision" rel="noopener noreferrer"&gt;Decision CLI and manifest&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.repowise.dev/intelligence/hooks" rel="noopener noreferrer"&gt;Proactive agent hooks&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.repowise.dev/licensing" rel="noopener noreferrer"&gt;Licensing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related articles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.technopathy.club/keep-the-why-vs-claude-code-auto-memory-vs-memorycustodian-vs-agentsroom" rel="noopener noreferrer"&gt;Keep the Why vs. Claude Code Auto Memory vs. MemoryCustodian vs. AgentsRoom&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.technopathy.club/the-reasoning-behind-a-codebase-as-a-web-you-can-walk" rel="noopener noreferrer"&gt;The reasoning behind a codebase, as a web you can walk&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.technopathy.club/the-globe-following-your-reasoning-into-other-people-s-repositories" rel="noopener noreferrer"&gt;The globe: following your reasoning into other people's repositories&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
      <category>documentation</category>
    </item>
    <item>
      <title>The Globe: Following Your Reasoning Into Other People's Repositories</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Fri, 02 Oct 2026 08:22:03 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/the-globe-following-your-reasoning-into-other-peoples-repositories-1cfo</link>
      <guid>https://dev.to/oliverzehentleitner/the-globe-following-your-reasoning-into-other-peoples-repositories-1cfo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Keep the Why records why a codebase is the way it is. Decisions can cite decisions in other repositories. The Globe follows those citations outward, one wave at a time, without turning project memory into a centralized service.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A decision rarely stands alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We retry three times&lt;/strong&gt; may exist because &lt;strong&gt;the payment provider counts every attempt against our error budget&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We pinned this WebSocket library&lt;/strong&gt; may exist because &lt;strong&gt;the upstream fix has not been released yet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And sometimes the reasoning behind your decision does not live in your repository at all.&lt;/p&gt;

&lt;p&gt;It lives in a sibling package.&lt;/p&gt;

&lt;p&gt;An upstream library.&lt;/p&gt;

&lt;p&gt;Another service maintained by your team.&lt;/p&gt;

&lt;p&gt;Or a project maintained by someone you have never met.&lt;/p&gt;

&lt;p&gt;That became increasingly interesting while working on &lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt;, the &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why" rel="noopener noreferrer"&gt;open-source project on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keep the Why stores project reasoning as plain Markdown in the repository's &lt;code&gt;context/&lt;/code&gt; directory. Decisions, rejected alternatives, constraints, workarounds and incident learnings stay next to the code they explain.&lt;/p&gt;

&lt;p&gt;An entry can also reference another entry.&lt;/p&gt;

&lt;p&gt;And that other entry can live in another repository.&lt;/p&gt;

&lt;p&gt;Once that existed, something slightly unexpected happened.&lt;/p&gt;

&lt;p&gt;The project memory stopped looking like a collection of documents.&lt;/p&gt;

&lt;p&gt;It started looking like a graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  The graph was already there
&lt;/h2&gt;

&lt;p&gt;The Keep the Why &lt;a href="https://keepthewhy.com/dashboard/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt; has visualized these relations for a while.&lt;/p&gt;

&lt;p&gt;Load a project and you can see its entries, their relations, parent and child repositories, and references to rationale elsewhere. The &lt;a href="https://keepthewhy.com/dashboard/live/#graph" rel="noopener noreferrer"&gt;live graph&lt;/a&gt; shows this directly on Keep the Why's own project memory.&lt;/p&gt;

&lt;p&gt;Originally, that graph had a natural boundary: the project you opened.&lt;/p&gt;

&lt;p&gt;External references appeared at the edge.&lt;/p&gt;

&lt;p&gt;But if a decision says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;See: https://github.com/example/upstream#ktw:some-decision-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then the interesting question is obvious:&lt;/p&gt;

&lt;p&gt;What happens if we follow it?&lt;/p&gt;

&lt;p&gt;And then follow the references from that repository?&lt;/p&gt;

&lt;p&gt;And the references from those repositories?&lt;/p&gt;

&lt;p&gt;That is what the &lt;strong&gt;Globe&lt;/strong&gt; does.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#globe" rel="noopener noreferrer"&gt;&lt;strong&gt;Open the Globe&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#globe" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4eo9df65qfrezu82s715.png" alt="Keep the Why Globe - cross-repository reasoning graph" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Globe follows explicit reasoning links across repositories. Click the screenshot to open the live dashboard.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The public graph is still small. We are just starting to connect projects, so right now the Globe is more a demonstration of the model than a map of a mature ecosystem.&lt;/p&gt;

&lt;p&gt;That is also why the registry exists: to give this network a place to start growing.&lt;/p&gt;

&lt;p&gt;The Globe is not a new database or another memory backend.&lt;/p&gt;

&lt;p&gt;It is the same project reasoning, with fewer artificial boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the reasoning, one hop at a time
&lt;/h2&gt;

&lt;p&gt;The Globe starts with the project you already loaded.&lt;/p&gt;

&lt;p&gt;From there you decide how far it should follow external references:&lt;/p&gt;

&lt;p&gt;Select the number of hops you want to follow, then press "go" to start loading the next wave.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hop 1&lt;/strong&gt; loads repositories directly referenced by the current project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hop 2&lt;/strong&gt; follows references found inside those repositories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The process continues outward, up to ten hops.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each hop is a separate wave.&lt;/p&gt;

&lt;p&gt;That matters because the next wave cannot be known in advance.&lt;/p&gt;

&lt;p&gt;To know what repository B references, the browser first has to load repository B.&lt;/p&gt;

&lt;p&gt;So before every wave, the Globe pauses and shows what it is about to fetch: which repositories were discovered and how many files that represents.&lt;/p&gt;

&lt;p&gt;You can continue or stop there.&lt;/p&gt;

&lt;p&gt;There is no background crawler walking the internet.&lt;/p&gt;

&lt;p&gt;There is also no reason to load ten hops just because ten are possible.&lt;/p&gt;

&lt;p&gt;Often one or two are enough to understand where a decision came from.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;off really means off&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With external loading disabled, the dashboard stays inside the project you opened.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Clear&lt;/code&gt; removes what the Globe loaded and leaves your own project family, friends and navigation path intact.&lt;/p&gt;

&lt;p&gt;Reload the page and the external graph is gone.&lt;/p&gt;

&lt;p&gt;Nothing is persisted between visits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository families travel together
&lt;/h2&gt;

&lt;p&gt;Repositories are not always independent units.&lt;/p&gt;

&lt;p&gt;A project may be a suite with several packages, or a parent repository may explicitly declare child projects.&lt;/p&gt;

&lt;p&gt;Keep the Why already supports those relationships through &lt;code&gt;parent&lt;/code&gt; and &lt;code&gt;children&lt;/code&gt;, defined as part of the &lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;project format&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Globe keeps them.&lt;/p&gt;

&lt;p&gt;If an external reference reaches one member of a declared project family, the family arrives together and remains connected according to the repositories' own metadata.&lt;/p&gt;

&lt;p&gt;That is important because otherwise the graph would create a misleading picture.&lt;/p&gt;

&lt;p&gt;A package that belongs to a larger project should not suddenly look like an isolated external dependency just because that happened to be the repository containing the cited decision.&lt;/p&gt;

&lt;p&gt;You can also click any loaded project and make it the new centre.&lt;/p&gt;

&lt;p&gt;The route you followed remains visible.&lt;/p&gt;

&lt;p&gt;So if you started in repository A, followed a decision into B, then discovered C through B, you can still see how you got there.&lt;/p&gt;

&lt;p&gt;The graph is not only showing what is connected.&lt;/p&gt;

&lt;p&gt;It is showing how you walked through the reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  There is still no server behind it
&lt;/h2&gt;

&lt;p&gt;This was the part I cared about most.&lt;/p&gt;

&lt;p&gt;I did not want the graph to turn Keep the Why into the kind of infrastructure it was originally designed to avoid.&lt;/p&gt;

&lt;p&gt;There is no central service resolving these links.&lt;/p&gt;

&lt;p&gt;There is no account.&lt;/p&gt;

&lt;p&gt;No graph database.&lt;/p&gt;

&lt;p&gt;No daemon crawling repositories.&lt;/p&gt;

&lt;p&gt;No API storing everybody's project memory.&lt;/p&gt;

&lt;p&gt;Each repository that publishes a dashboard export already declares where that export lives in its &lt;code&gt;.keep-the-why&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The export can simply be a static &lt;code&gt;state.json&lt;/code&gt; hosted on GitHub Pages or another static host.&lt;/p&gt;

&lt;p&gt;When the Globe finds another repository, the browser reads its &lt;code&gt;.keep-the-why&lt;/code&gt; at &lt;code&gt;HEAD&lt;/code&gt;, finds the &lt;code&gt;dashboard-state&lt;/code&gt; location, fetches the export and draws what it contains.&lt;/p&gt;

&lt;p&gt;The dashboard server never fetches the other repository.&lt;/p&gt;

&lt;p&gt;Your browser does.&lt;/p&gt;

&lt;p&gt;That distinction sounds small, but it keeps the architecture pleasantly boring.&lt;/p&gt;

&lt;p&gt;The repository remains authoritative.&lt;/p&gt;

&lt;p&gt;The published export is only a view of that repository.&lt;/p&gt;

&lt;p&gt;And the graph exists because the repositories link to each other, not because a central system reconstructed the relationships afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  A graph does not need all the prose
&lt;/h2&gt;

&lt;p&gt;Following repositories also exposed a very practical problem.&lt;/p&gt;

&lt;p&gt;Keep the Why entries contain text.&lt;/p&gt;

&lt;p&gt;Sometimes quite a lot of it.&lt;/p&gt;

&lt;p&gt;But the graph does not need the full explanation of every decision just to know that entry A references entry B.&lt;/p&gt;

&lt;p&gt;Loading all prose for every repository in every wave would waste bandwidth quickly.&lt;/p&gt;

&lt;p&gt;So the dashboard export was split.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;state.json&lt;/code&gt; contains the structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;projects
topics
entries
fields
relations
links
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual entry bodies live beside it in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state.body.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those bodies are fetched only when you open an entry.&lt;/p&gt;

&lt;p&gt;For a prose-heavy project, that reduces what a graph wave needs to load to roughly a third of the previous size.&lt;/p&gt;

&lt;p&gt;It is a small architectural change with a useful property:&lt;/p&gt;

&lt;p&gt;The farther you explore, the less unnecessary text you move around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then there was one missing direction
&lt;/h2&gt;

&lt;p&gt;Following references works well, but it has a blind spot.&lt;/p&gt;

&lt;p&gt;You can discover what your project cites.&lt;/p&gt;

&lt;p&gt;You can discover what those projects cite.&lt;/p&gt;

&lt;p&gt;But you cannot discover a repository that nothing in your current graph points to.&lt;/p&gt;

&lt;p&gt;More importantly, you cannot discover who cites &lt;strong&gt;you&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That information exists somewhere out there, but there is no link you can follow backwards to find it.&lt;/p&gt;

&lt;p&gt;This is where the &lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;Keep the Why Registry&lt;/a&gt; came from.&lt;/p&gt;

&lt;p&gt;Not as a replacement for the distributed graph.&lt;/p&gt;

&lt;p&gt;As a discovery mechanism for the one thing the graph itself cannot do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The registry is intentionally boring
&lt;/h2&gt;

&lt;p&gt;The registry is a text file in the Keep the Why repository.&lt;/p&gt;

&lt;p&gt;One canonical repository URL per line.&lt;/p&gt;

&lt;p&gt;That is basically it.&lt;/p&gt;

&lt;p&gt;To add a project, you open a pull request and add its repository URL.&lt;/p&gt;

&lt;p&gt;A workflow then reads the repository's &lt;code&gt;.keep-the-why&lt;/code&gt;, follows its &lt;code&gt;dashboard-state&lt;/code&gt; declaration, loads the published export and verifies that the export actually identifies the repository being registered.&lt;/p&gt;

&lt;p&gt;The registry therefore stores where to start looking.&lt;/p&gt;

&lt;p&gt;It does not store your project memory.&lt;/p&gt;

&lt;p&gt;It does not copy your entries.&lt;/p&gt;

&lt;p&gt;And it does not even need to permanently store the location of your export.&lt;/p&gt;

&lt;p&gt;If you move the published dashboard state later, the next registry build follows the repository configuration again and finds the new location.&lt;/p&gt;

&lt;p&gt;Project families work here too.&lt;/p&gt;

&lt;p&gt;Register the root project and its declared children arrive through the relationships already stored in those repositories.&lt;/p&gt;

&lt;p&gt;If an export temporarily stops responding, it is not deleted immediately either.&lt;/p&gt;

&lt;p&gt;It remains listed and marked as unavailable for a month before it is dropped.&lt;/p&gt;

&lt;p&gt;Repositories disappear for temporary reasons.&lt;/p&gt;

&lt;p&gt;Infrastructure should not turn one bad day into permanent metadata.&lt;/p&gt;

&lt;h2&gt;
  
  
  The registry is optional
&lt;/h2&gt;

&lt;p&gt;This part matters.&lt;/p&gt;

&lt;p&gt;Keep the Why works without the registry.&lt;/p&gt;

&lt;p&gt;The dashboard works without it.&lt;/p&gt;

&lt;p&gt;The Globe works without it.&lt;/p&gt;

&lt;p&gt;Cross-repository references work without it.&lt;/p&gt;

&lt;p&gt;Nothing requires a project to join a central list.&lt;/p&gt;

&lt;p&gt;The registry only solves discovery.&lt;/p&gt;

&lt;p&gt;Tick &lt;code&gt;registry&lt;/code&gt; in the Globe and it becomes another wave you can load.&lt;/p&gt;

&lt;p&gt;Suddenly you are not limited to projects already reachable from your own references.&lt;/p&gt;

&lt;p&gt;You can see the first published Keep the Why projects joining the graph and then explore their reasoning exactly the same way.&lt;/p&gt;

&lt;p&gt;Once loaded, there is no special registry graph.&lt;/p&gt;

&lt;p&gt;They are just repositories again.&lt;/p&gt;

&lt;p&gt;That was the design I wanted.&lt;/p&gt;

&lt;p&gt;Centralize discovery where central discovery is useful.&lt;/p&gt;

&lt;p&gt;Do not centralize the project memory itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  It only draws relations that actually exist
&lt;/h2&gt;

&lt;p&gt;A graph can become misleading very quickly if it starts guessing.&lt;/p&gt;

&lt;p&gt;So the Globe does not infer relationships from prose.&lt;/p&gt;

&lt;p&gt;It does not connect two repositories because they mention the same library.&lt;/p&gt;

&lt;p&gt;It does not connect entries because they use similar words.&lt;/p&gt;

&lt;p&gt;It does not create a relation because two projects share a topic name.&lt;/p&gt;

&lt;p&gt;A connection exists when someone recorded one.&lt;/p&gt;

&lt;p&gt;For example through &lt;code&gt;See&lt;/code&gt; or &lt;code&gt;Superseded by&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If two projects obviously belong together but no explicit relation exists in their project memory, they appear as separate islands.&lt;/p&gt;

&lt;p&gt;I prefer that.&lt;/p&gt;

&lt;p&gt;The graph may know less, but what it shows has provenance.&lt;/p&gt;

&lt;p&gt;It came from the project records themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interesting part is not the visualization
&lt;/h2&gt;

&lt;p&gt;The Globe looks nice.&lt;/p&gt;

&lt;p&gt;But the visualization is not really the part I find interesting.&lt;/p&gt;

&lt;p&gt;The useful part is what happens when reasoning crosses repository boundaries without changing ownership.&lt;/p&gt;

&lt;p&gt;Repository A remains authoritative for its decisions.&lt;/p&gt;

&lt;p&gt;Repository B remains authoritative for its decisions.&lt;/p&gt;

&lt;p&gt;Neither needs to copy the other's rationale.&lt;/p&gt;

&lt;p&gt;They can simply point at each other.&lt;/p&gt;

&lt;p&gt;Git already distributes the files.&lt;/p&gt;

&lt;p&gt;Static hosting distributes the read-only views.&lt;/p&gt;

&lt;p&gt;The browser follows the references.&lt;/p&gt;

&lt;p&gt;The graph appears as a consequence.&lt;/p&gt;

&lt;p&gt;Nobody has to maintain one global knowledge graph containing everybody's project history.&lt;/p&gt;

&lt;p&gt;And nobody owns the web of reasoning that emerges between repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  A decision three repositories away can still matter to you
&lt;/h2&gt;

&lt;p&gt;There is another consequence of following these links.&lt;/p&gt;

&lt;p&gt;A decision is not automatically trustworthy forever just because somebody wrote it down.&lt;/p&gt;

&lt;p&gt;Keep the Why already records things such as status, evidence and supersession.&lt;/p&gt;

&lt;p&gt;The Globe carries that information across repository boundaries.&lt;/p&gt;

&lt;p&gt;So if a chain of reasoning starts from an unconfirmed entry, that remains visible.&lt;/p&gt;

&lt;p&gt;If something in the chain is still open, that remains visible.&lt;/p&gt;

&lt;p&gt;If a decision gets superseded, that remains visible too.&lt;/p&gt;

&lt;p&gt;This becomes particularly useful when the decision is no longer in your own repository.&lt;/p&gt;

&lt;p&gt;Imagine your local workaround exists because of an upstream constraint.&lt;/p&gt;

&lt;p&gt;Later, the upstream project supersedes that constraint.&lt;/p&gt;

&lt;p&gt;Your own code does not magically become wrong.&lt;/p&gt;

&lt;p&gt;But the reasoning chain leading to it has changed.&lt;/p&gt;

&lt;p&gt;That is exactly the kind of thing worth looking at again.&lt;/p&gt;

&lt;p&gt;Not because a graph decided your code is stale.&lt;/p&gt;

&lt;p&gt;Because the evidence your decision depended on changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project memory does not have to stop at the repository boundary
&lt;/h2&gt;

&lt;p&gt;When I started Keep the Why, the idea was intentionally narrow:&lt;/p&gt;

&lt;p&gt;Keep the reasoning that is expensive to reconstruct close to the code.&lt;/p&gt;

&lt;p&gt;Plain Markdown.&lt;/p&gt;

&lt;p&gt;Git.&lt;/p&gt;

&lt;p&gt;A small convention.&lt;/p&gt;

&lt;p&gt;An agent skill that writes and reads it.&lt;/p&gt;

&lt;p&gt;The repository remains the source of truth.&lt;/p&gt;

&lt;p&gt;Cross-repository links did not change that model.&lt;/p&gt;

&lt;p&gt;They made the consequence of it more visible.&lt;/p&gt;

&lt;p&gt;Once repositories can cite reasoning in other repositories, project memory no longer has to mean isolated project memory.&lt;/p&gt;

&lt;p&gt;It can stay local, independently owned and Git-native while still participating in something larger.&lt;/p&gt;

&lt;p&gt;No shared database is required.&lt;/p&gt;

&lt;p&gt;No global account is required.&lt;/p&gt;

&lt;p&gt;No project has to hand its memory to another service.&lt;/p&gt;

&lt;p&gt;The files were already there.&lt;/p&gt;

&lt;p&gt;The links were already there.&lt;/p&gt;

&lt;p&gt;The Globe just follows them.&lt;/p&gt;

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

&lt;p&gt;Start with Keep the Why itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#globe" rel="noopener noreferrer"&gt;&lt;strong&gt;Open the Globe&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select one or two hops, press Go, and watch the graph expand one wave at a time.&lt;/p&gt;

&lt;p&gt;Then load the registry and explore the first projects joining the graph.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;registry&lt;/a&gt; explains how discovery works and how to add a project.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/dashboard/" rel="noopener noreferrer"&gt;dashboard documentation&lt;/a&gt; covers publishing an export and the rest of the graph features.&lt;/p&gt;

&lt;p&gt;The complete format is documented in the &lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;Keep the Why specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If your project already uses Keep the Why and publishes a dashboard, adding it to the registry is one line in a pull request.&lt;/p&gt;

&lt;p&gt;If it does not use Keep the Why yet, the &lt;a href="https://keepthewhy.com/installation/" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt; covers all supported methods. The recommended Skills CLI installation currently starts with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add https://github.com/oliver-zehentleitner/keep-the-why/tree/latest/skills/keep-the-why
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then tell your coding agent to initialize Keep the Why in the project.&lt;/p&gt;

&lt;p&gt;The reasoning graph is not something you have to maintain separately.&lt;/p&gt;

&lt;p&gt;It is a byproduct of projects remembering why they became what they are.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
      <category>documentation</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>The reasoning behind a codebase, as a web you can walk</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Wed, 30 Sep 2026 06:10:54 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/the-reasoning-behind-a-codebase-as-a-web-you-can-walk-a7j</link>
      <guid>https://dev.to/oliverzehentleitner/the-reasoning-behind-a-codebase-as-a-web-you-can-walk-a7j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Decisions that cite each other across repositories — no graph database, no central index, no account. Just Markdown and Git.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open this before you read on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#graph" rel="noopener noreferrer"&gt;&lt;strong&gt;keepthewhy.com/dashboard/live/#graph&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give it a few seconds to settle.&lt;/p&gt;

&lt;p&gt;Then come back.&lt;/p&gt;

&lt;p&gt;It looks like a knowledge graph.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;There is no graph database behind it. Nobody maintains nodes and edges. There is no central service collecting project knowledge.&lt;/p&gt;

&lt;p&gt;What you are looking at is reasoning that was already sitting in Git repositories as Markdown.&lt;/p&gt;

&lt;p&gt;The graph is just what appears when those reasons start citing each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you are looking at
&lt;/h2&gt;

&lt;p&gt;The project in the centre is Keep the Why itself.&lt;/p&gt;

&lt;p&gt;Its hubs are topics from &lt;code&gt;context/&lt;/code&gt;. Around them are individual entries: decisions, rejected alternatives, workarounds, incident learnings and constraints — things the code can tell you happened, but usually cannot tell you &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#graph" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jsmnhw7xiop4egpe7z3.png" alt="Keep the Why graph view" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each entry is an ordinary Markdown section in the repository.&lt;/p&gt;

&lt;p&gt;For example:&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="gu"&gt;## Keep dashboard exports read-only&lt;/span&gt;

&lt;span class="gs"&gt;**Id:**&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;uuid&amp;gt;&lt;/span&gt;
&lt;span class="gs"&gt;**Type:**&lt;/span&gt; decision
&lt;span class="gs"&gt;**Status:**&lt;/span&gt; active
&lt;span class="gs"&gt;**Evidence:**&lt;/span&gt; confirmed
&lt;span class="gs"&gt;**See:**&lt;/span&gt; https://github.com/owner/repo — &lt;span class="nt"&gt;&amp;lt;entry-id&amp;gt;&lt;/span&gt; — as of 2026-09-29

&lt;span class="gs"&gt;**Reason:**&lt;/span&gt;
...

&lt;span class="gs"&gt;**Rejected alternative:**&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The colour of an entry shows how well its reason is supported. Its shape tells you whether it is still active, superseded, or needs another look.&lt;/p&gt;

&lt;p&gt;Git provides the history: who first recorded it, who touched it later, when its status changed.&lt;/p&gt;

&lt;p&gt;The dashboard adds none of that information.&lt;/p&gt;

&lt;p&gt;It reads what is already there.&lt;/p&gt;

&lt;p&gt;That distinction matters to me: delete the dashboard and the project has lost nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the graph escaped the repository
&lt;/h2&gt;

&lt;p&gt;The more interesting part is further out.&lt;/p&gt;

&lt;p&gt;You will see other projects around Keep the Why.&lt;/p&gt;

&lt;p&gt;One is &lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;repo-native project memory&lt;/a&gt;, the thesis Keep the Why belongs to.&lt;/p&gt;

&lt;p&gt;Another is the &lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-suite" rel="noopener noreferrer"&gt;UNICORN Binance Suite&lt;/a&gt;, a family of related repositories.&lt;/p&gt;

&lt;p&gt;Nobody added those projects to a graph configuration.&lt;/p&gt;

&lt;p&gt;They appear because an entry in one repository cites an entry in another.&lt;/p&gt;

&lt;p&gt;That citation is still just text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**See:** https://github.com/owner/repo — &amp;lt;entry-id&amp;gt; — as of 2026-09-29
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entry ID is stable. A heading can be reworded, a topic file can be split, and the reference still identifies the same piece of reasoning.&lt;/p&gt;

&lt;p&gt;Once those references existed, something unexpected became possible:&lt;/p&gt;

&lt;p&gt;the reasoning of several repositories could be viewed as one connected structure without moving any of it into a central store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Family, friends, thoughts
&lt;/h2&gt;

&lt;p&gt;I ended up with three different kinds of connection.&lt;/p&gt;

&lt;p&gt;They sound informal, but the distinction turned out to be useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Family: where does this reasoning belong?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://oliver-zehentleitner.github.io/unicorn-binance-suite/keep-the-why-dashboard/#family" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F06n2p2fv3o9uytruk21y.png" alt="Keep the Why family view" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A family is a group of projects that belong together.&lt;/p&gt;

&lt;p&gt;A backend, frontend, shared library and infrastructure repository may all be parts of one product.&lt;/p&gt;

&lt;p&gt;The family describes that structure and, importantly, gives the agent routing information.&lt;/p&gt;

&lt;p&gt;If a decision belongs to the shared library, it gets recorded there.&lt;/p&gt;

&lt;p&gt;If it applies to the whole product, it belongs higher in the family.&lt;/p&gt;

&lt;p&gt;The other repositories cite it instead of copying it.&lt;/p&gt;

&lt;p&gt;That sounds like a small rule, but it avoids one of the ugliest problems in multi-repository documentation:&lt;/p&gt;

&lt;p&gt;five slightly different copies of the same decision.&lt;/p&gt;

&lt;p&gt;The reasoning has one owner.&lt;/p&gt;

&lt;p&gt;Everything else can point to it.&lt;/p&gt;

&lt;p&gt;Families can nest, so the same idea still works for a suite containing a cluster containing another project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Friends: what does this project relate to?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#friends" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jsmnhw7xiop4egpe7z3.png" alt="Keep the Why friends view" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not every relationship means two repositories belong to the same system.&lt;/p&gt;

&lt;p&gt;Any Keep the Why entry can cite an entry in any other repository.&lt;/p&gt;

&lt;p&gt;Those repositories are &lt;strong&gt;friends&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nothing is routed between them.&lt;/p&gt;

&lt;p&gt;Nothing is merged.&lt;/p&gt;

&lt;p&gt;They simply know about each other because their reasoning crossed paths.&lt;/p&gt;

&lt;p&gt;A friend may be one repository or an entire family.&lt;/p&gt;

&lt;p&gt;The dashboard follows those references and shows the relevant entries on both sides.&lt;/p&gt;

&lt;p&gt;That is why the graph around Keep the Why contains projects that were never configured as part of Keep the Why itself.&lt;/p&gt;

&lt;p&gt;They are there because the data says they are related.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thoughts: how do the reasons connect?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#thoughts" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcho9yi0ijjpfa4u2zkgn.png" alt="Keep the Why thoughts view" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the part I find most interesting.&lt;/p&gt;

&lt;p&gt;An entry can cite an earlier entry with &lt;code&gt;See&lt;/code&gt;, or say that another entry superseded it.&lt;/p&gt;

&lt;p&gt;Once several of those links line up, they form a chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;incident
   ↓
constraint discovered
   ↓
architecture decision
   ↓
later workaround
   ↓
replacement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the Why calls such a chain a &lt;strong&gt;thought&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nobody writes a thought.&lt;/p&gt;

&lt;p&gt;There is no &lt;code&gt;thoughts.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The individual decisions were recorded when they happened. Their references are enough for the chain to emerge later.&lt;/p&gt;

&lt;p&gt;And importantly, a &lt;code&gt;See&lt;/code&gt; link does not claim formal causality.&lt;/p&gt;

&lt;p&gt;It says these pieces of rationale are related — often that one followed from the other, but not necessarily.&lt;/p&gt;

&lt;p&gt;So a thought is not a proof.&lt;/p&gt;

&lt;p&gt;It is a trail through the project's recorded reasoning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/dashboard/live/#thoughts" rel="noopener noreferrer"&gt;Open &lt;strong&gt;Thoughts&lt;/strong&gt; in the dashboard&lt;/a&gt; and you can read one from beginning to end, with every entry in full, even when the chain crosses repository boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The web is deliberately incomplete
&lt;/h2&gt;

&lt;p&gt;There is an important limitation here.&lt;/p&gt;

&lt;p&gt;There is no global Keep the Why index.&lt;/p&gt;

&lt;p&gt;No service knows every repository that has ever cited yours.&lt;/p&gt;

&lt;p&gt;From one project, the dashboard can see what that project points to.&lt;/p&gt;

&lt;p&gt;Its friends form the first neighbourhood.&lt;/p&gt;

&lt;p&gt;Click one and you can walk there, making it the new centre. The path you walked remains visible, so you can move back through the reasoning the same way you came.&lt;/p&gt;

&lt;p&gt;Thoughts can go further.&lt;/p&gt;

&lt;p&gt;If a chain continues into another repository, the dashboard says so. Ask it to continue and it loads exactly the repositories needed to follow that chain, hop by hop.&lt;/p&gt;

&lt;p&gt;What it does &lt;strong&gt;not&lt;/strong&gt; do is crawl every project reachable from every project.&lt;/p&gt;

&lt;p&gt;That is intentional.&lt;/p&gt;

&lt;p&gt;Each repository remains responsible for publishing its own state.&lt;/p&gt;

&lt;p&gt;There is no central graph to submit your project's reasoning to.&lt;/p&gt;

&lt;p&gt;The web exists because projects link to one another, not because a platform owns the web.&lt;/p&gt;

&lt;p&gt;That also means there are things it cannot know.&lt;/p&gt;

&lt;p&gt;If some repository elsewhere cites the newest entry in your chain, but you have never loaded that repository, there is no magic global backlink index that can tell you.&lt;/p&gt;

&lt;p&gt;I prefer that limitation to needing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Once reasoning has links, you can ask different questions
&lt;/h2&gt;

&lt;p&gt;At first I built the dashboard mostly because I wanted a quick way to understand what was already in &lt;code&gt;context/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then the references made new questions possible.&lt;/p&gt;

&lt;p&gt;One is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which chains of reasoning started from something nobody ever confirmed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every entry carries an evidence level.&lt;/p&gt;

&lt;p&gt;If the first step of a thought is only &lt;code&gt;inferred&lt;/code&gt; or &lt;code&gt;unknown&lt;/code&gt;, every later decision may still be perfectly reasonable — but the chain started from an unconfirmed reason.&lt;/p&gt;

&lt;p&gt;The Thoughts view can now surface those origins.&lt;/p&gt;

&lt;p&gt;Another question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What later reasoning is connected to something we no longer trust?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose an old assumption becomes &lt;code&gt;needs-review&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The interesting part is not only that one entry.&lt;/p&gt;

&lt;p&gt;What came after it?&lt;/p&gt;

&lt;p&gt;Which later decisions link back to it?&lt;/p&gt;

&lt;p&gt;Do those relationships cross into another repository?&lt;/p&gt;

&lt;p&gt;The dashboard can show the later entries linked after that point and the projects they live in.&lt;/p&gt;

&lt;p&gt;It still does not claim those decisions are wrong.&lt;/p&gt;

&lt;p&gt;A link records a relationship, not a theorem.&lt;/p&gt;

&lt;p&gt;But it tells a human where to look.&lt;/p&gt;

&lt;p&gt;There are other shapes hiding in the same data:&lt;/p&gt;

&lt;p&gt;places where many thoughts start;&lt;/p&gt;

&lt;p&gt;entries several thoughts pass through;&lt;/p&gt;

&lt;p&gt;chains that evolved over months;&lt;/p&gt;

&lt;p&gt;decisions that were superseded but are still being cited.&lt;/p&gt;

&lt;p&gt;None required another documentation process.&lt;/p&gt;

&lt;p&gt;They became visible because the original reasoning was recorded once, with identity and relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  The graph is not the product
&lt;/h2&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;Keep the Why does not ask developers to maintain a graph.&lt;/p&gt;

&lt;p&gt;The actual workflow is much more boring.&lt;/p&gt;

&lt;p&gt;You work with a coding agent.&lt;/p&gt;

&lt;p&gt;During that work a real reason surfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;an architectural decision,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an alternative that lost,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a workaround whose purpose is not visible from the code,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a production constraint,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an attempted change that gets abandoned.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent records it in &lt;code&gt;context/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Git versions it with the code.&lt;/p&gt;

&lt;p&gt;A later session reads it before repeating the same discussion.&lt;/p&gt;

&lt;p&gt;That is the product.&lt;/p&gt;

&lt;p&gt;The dashboard is only a lens over the traces this process leaves behind.&lt;/p&gt;

&lt;p&gt;And that is probably why I like the graph now more than I expected to.&lt;/p&gt;

&lt;p&gt;I originally considered the dashboard almost secondary.&lt;/p&gt;

&lt;p&gt;The value was in the agent using the reasoning.&lt;/p&gt;

&lt;p&gt;It still is.&lt;/p&gt;

&lt;p&gt;But the dashboard makes something visible in twenty seconds that is harder to explain in twenty paragraphs:&lt;/p&gt;

&lt;p&gt;a project is not just a collection of files.&lt;/p&gt;

&lt;p&gt;It is the result of a long sequence of decisions.&lt;/p&gt;

&lt;p&gt;And once those decisions keep their reasons and can refer to one another, you can actually see that sequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on your own project
&lt;/h2&gt;

&lt;p&gt;Everything behind the page remains plain Markdown in &lt;code&gt;context/&lt;/code&gt;, versioned by Git beside the code.&lt;/p&gt;

&lt;p&gt;The dashboard only reads it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;keep-the-why-dashboard
ktw-dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or export the whole view as a static page and publish it with the project's documentation.&lt;/p&gt;

&lt;p&gt;No Keep the Why account.&lt;/p&gt;

&lt;p&gt;No graph database.&lt;/p&gt;

&lt;p&gt;No central memory service.&lt;/p&gt;

&lt;p&gt;No new source of truth.&lt;/p&gt;

&lt;p&gt;Just the reasoning the repository already carried — connected strongly enough that you can finally walk through it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep a Changelog records what changed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the Why preserves why it changed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And now you can see how those reasons connect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt; · &lt;a href="https://keepthewhy.com/dashboard/live/#graph" rel="noopener noreferrer"&gt;Live dashboard&lt;/a&gt; · &lt;a href="https://keepthewhy.com/dashboard/live/#thoughts" rel="noopener noreferrer"&gt;Thoughts&lt;/a&gt; · &lt;a href="https://keepthewhy.com/dashboard/#family-friends-thoughts" rel="noopener noreferrer"&gt;Dashboard explained&lt;/a&gt; · &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>projectmemory</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Session memory is not project memory. It fixes the same complaint.</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Thu, 24 Sep 2026 10:55:58 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/session-memory-is-not-project-memory-it-fixes-the-same-complaint-198i</link>
      <guid>https://dev.to/oliverzehentleitner/session-memory-is-not-project-memory-it-fixes-the-same-complaint-198i</guid>
      <description>&lt;p&gt;&lt;strong&gt;"My coding agent forgets everything between sessions." Two different things answer that, and they are not competing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most common complaint about coding agents is the same on every forum: a new session starts from nothing. The explanation you gave yesterday is gone, the decision you made together is gone, the wrong turn the agent took and you corrected is gone, and tomorrow it proposes it again. Everyone who has worked with an agent for more than a week knows the feeling.&lt;/p&gt;

&lt;p&gt;Two different things answer that complaint. They get compared as if they were rivals, and they are layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session memory: what the agent keeps for itself
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;Claude Code's auto memory&lt;/a&gt; is the clearest example, because it is built in and on by default. As it works, Claude writes notes for itself: your role and preferences, corrections you gave, project context it can't derive from the code, ongoing work, decisions, and references to things outside the repository. The notes live in a directory on your machine, one per repository, and the first part of the file is loaded into every session. No setup, nothing to decide.&lt;/p&gt;

&lt;p&gt;Three properties define it, and none of them is a flaw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It belongs to one person on one machine.&lt;/strong&gt; The directory is under your home, not in the repository. A teammate's clone does not have it. Your second laptop does not have it. That is by design: a memory that holds &lt;em&gt;your&lt;/em&gt; preferences and &lt;em&gt;your&lt;/em&gt; corrections should not automatically travel with the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It belongs to one tool.&lt;/strong&gt; The notes are Claude Code's; Codex, Cursor or Copilot on the same repository do not inherit them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It is not reviewed with the code.&lt;/strong&gt; You can inspect and edit the memory, but it is not part of the repository's normal Git and pull-request review flow.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the right shape for what it holds. Your preferences are yours. A note like "keep explanations short unless I ask for detail" is between you and your agent. Nobody needs that in a shared project file.&lt;/p&gt;

&lt;p&gt;Claude Code also has repository-level &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; files. Those are shared project instructions rather than auto memory: rules, workflows, architecture and things Claude should do. They can contain rationale too, but rationale is not their dedicated job. The distinction here is specifically between Claude's local auto memory and a repository-native rationale layer.&lt;/p&gt;

&lt;p&gt;The complaint session memory answers is: &lt;em&gt;the agent forgot what I told it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project memory: what the repository keeps for everyone
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;A repository already is a project's memory&lt;/a&gt;, and has been for decades. The README says what it is, the docs how to use it, the tests what it must do, the changelog what changed, the history who changed it and when. Whoever has the clone has all of it, in every tool, on every machine, reviewed in every pull request.&lt;/p&gt;

&lt;p&gt;What the classic layout never had a clear place for is the &lt;em&gt;why&lt;/em&gt;: the decision, the alternative that was rejected and the reason it lost, the constraint the code doesn't show, the workaround that must not be cleaned up. That reasoning used to live in people's heads, and left with them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt; is the convention and the agent skill for that layer: the agent writes the reasoning into &lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;&lt;code&gt;context/&lt;/code&gt;&lt;/a&gt; as it comes up in the session, as Markdown, committed with the code.&lt;/p&gt;

&lt;p&gt;Three properties again, and they are the mirror image:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It belongs to the project.&lt;/strong&gt; It is in the repository, so the clone carries it. Your teammate's agent can read the same file yours wrote.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It belongs to no tool.&lt;/strong&gt; Any agent that can read the repository can read it, and switching tools does not remove the information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It is reviewed.&lt;/strong&gt; The why arrives in the same pull request as the code, and a wrong entry is a diff somebody can see.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complaint project memory answers is: &lt;em&gt;the agent forgot why the code is the way it is.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The same complaint, two answers
&lt;/h2&gt;

&lt;p&gt;"The agent forgets" covers several different losses. Sort them and the answer sorts itself:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What the agent forgot&lt;/th&gt;
&lt;th&gt;Which memory answers it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;that you prefer short commit messages&lt;/td&gt;
&lt;td&gt;session memory: personal, one tool, one machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;what you two did yesterday afternoon&lt;/td&gt;
&lt;td&gt;session memory, or &lt;code&gt;git log&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;where the staging dashboard is&lt;/td&gt;
&lt;td&gt;session memory as a personal reference, or the README if the team needs it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;that the plain retry loop was rejected, and why&lt;/td&gt;
&lt;td&gt;project memory, the why layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;that the ugly workaround exists because of a rate limiter&lt;/td&gt;
&lt;td&gt;project memory, the why layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;that a teammate's agent already found the migration trap&lt;/td&gt;
&lt;td&gt;project memory: it has to reach the other machine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last three are the ones that come back as production incidents when they are lost.&lt;/p&gt;

&lt;p&gt;A session memory may remember those facts locally. What it cannot do by itself is make them shared project knowledge across people, machines and tools.&lt;/p&gt;

&lt;p&gt;That is the important distinction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you keep both
&lt;/h2&gt;

&lt;p&gt;The temptation is to pick one. Both directions are wrong.&lt;/p&gt;

&lt;p&gt;Putting the why only into a personal memory is where many people start, because it is zero setup. It works until the second person, the second machine or the second tool. At that point the reasoning may still exist, but it is trapped in a memory the rest of the project does not carry.&lt;/p&gt;

&lt;p&gt;Putting personal preferences into the repository is the opposite mistake. A shared &lt;code&gt;context/&lt;/code&gt; that holds one developer's habits is noise for everyone else, and a privacy problem on top.&lt;/p&gt;

&lt;p&gt;Keep the Why refuses that on purpose: personal settings live outside the project, in the developer's own home directory, and the skill does not record who said what.&lt;/p&gt;

&lt;p&gt;So: session memory for what is yours, project memory for what belongs to the project.&lt;/p&gt;

&lt;p&gt;Leave auto memory on. Give the repository a why layer.&lt;/p&gt;

&lt;p&gt;They may remember the same fact. Only one of them makes it part of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;Clone the repository on a machine that has never seen it. Open it with a tool you have not used on it before. Hand it to a colleague.&lt;/p&gt;

&lt;p&gt;What is still available to the agent is project memory. What has disappeared was session memory, however good that memory was on your laptop.&lt;/p&gt;

&lt;p&gt;The complaint "my agent forgets everything between sessions" is really two complaints, and only one of them was ever about the session.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agentskills</category>
      <category>programming</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Keep the Why vs. Claude Code Auto Memory vs. MemoryCustodian vs. AgentsRoom</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Wed, 23 Sep 2026 07:15:56 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/keep-the-why-vs-claude-code-auto-memory-vs-memorycustodian-vs-agentsroom-436d</link>
      <guid>https://dev.to/oliverzehentleitner/keep-the-why-vs-claude-code-auto-memory-vs-memorycustodian-vs-agentsroom-436d</guid>
      <description>&lt;p&gt;&lt;strong&gt;Four tools that get called project memory. Three questions sort them. One table, as of September 2026.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Project memory for coding agents" now means several different things, and most comparisons put them in one list as if they competed. They mostly don't.&lt;/p&gt;

&lt;p&gt;Before comparing tools, ask three questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does it live?&lt;/strong&gt; In the repository, in a directory on one machine, or on a server. This decides who gets it with a clone, whether a pull request can review it, and how much of the normal Git workflow applies to the memory itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who can read it?&lt;/strong&gt; One person, everyone using one particular app, or anyone who can open the repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does it remember?&lt;/strong&gt; This is the question that gets skipped. A memory can hold three different things, and they are layers, not rivals: what &lt;em&gt;happened&lt;/em&gt; (a session, a transcript, a fix that worked), where the project &lt;em&gt;stands&lt;/em&gt; (status, next action, what shipped), and &lt;em&gt;why&lt;/em&gt; the project became what it is (the decision, the rejected alternative, the constraint the code doesn't show).&lt;/p&gt;

&lt;p&gt;Run the names that usually appear in these lists through those questions and most of them leave the room. mem0, Letta, Zep and similar systems are external long-term memory systems centered on what an agent or user should remember across sessions, rather than rationale stored with the repository. Aider, Mastra, Dify and Langflow are a coding agent and frameworks with memory as a feature. Other tools focus on session observations, task state, project plans or summaries.&lt;/p&gt;

&lt;p&gt;What is left here is a narrower comparison: two repo-native approaches that preserve durable project knowledge, Claude Code's built-in auto memory, and a server-backed project-memory implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;p&gt;Checked on 2026-10-04 against &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;Claude Code's memory documentation&lt;/a&gt;, &lt;a href="https://github.com/waittim/MemoryCustodian" rel="noopener noreferrer"&gt;MemoryCustodian's README&lt;/a&gt;, &lt;a href="https://agentsroom.dev/features/project-memory" rel="noopener noreferrer"&gt;AgentsRoom's Project Memory page&lt;/a&gt;, &lt;a href="https://keepthewhy.com/specification/" rel="noopener noreferrer"&gt;the Keep the Why specification&lt;/a&gt;, &lt;a href="https://keepthewhy.com/repository-structure/" rel="noopener noreferrer"&gt;repository structure&lt;/a&gt;, &lt;a href="https://keepthewhy.com/dashboard/" rel="noopener noreferrer"&gt;dashboard documentation&lt;/a&gt; and &lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;registry documentation&lt;/a&gt;.&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;Keep the Why&lt;/th&gt;
&lt;th&gt;Claude Code Auto Memory&lt;/th&gt;
&lt;th&gt;MemoryCustodian&lt;/th&gt;
&lt;th&gt;AgentsRoom Project Memory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where it lives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Plain Markdown in &lt;code&gt;context/&lt;/code&gt; inside each project, committed with the repository. &lt;code&gt;.keep-the-why&lt;/code&gt; defines the project and its topology&lt;/td&gt;
&lt;td&gt;By default &lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/memory/&lt;/code&gt; on the user's machine; configurable to another local directory&lt;/td&gt;
&lt;td&gt;Plain Markdown in &lt;code&gt;docs/memory/&lt;/code&gt; in the repository, plus optional private local overlays&lt;/td&gt;
&lt;td&gt;Server-side, keyed to a project id committed in the repo; the desktop app also keeps a gitignored local cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who reads it&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anyone with the clone, the reviewer in the pull request, any agent that can read the repository, and optionally humans through the read-only dashboard&lt;/td&gt;
&lt;td&gt;Claude Code sessions that have access to that memory directory; all worktrees and subdirectories of the same Git repository share it&lt;/td&gt;
&lt;td&gt;Anyone with the clone, plus supported agents through the skill / CLI workflow&lt;/td&gt;
&lt;td&gt;Everyone logged in to AgentsRoom who opens the project and has the project id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Versioned / auditable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Git, alongside the code. Dashboard history is derived from Git&lt;/td&gt;
&lt;td&gt;Not by default, unless the configured memory directory is versioned separately&lt;/td&gt;
&lt;td&gt;Git, alongside the code, with additional CLI audit and conflict checks&lt;/td&gt;
&lt;td&gt;Server-side note history; not Git history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vendor-independent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open Agent Skills format and plain Markdown; usable by supporting coding agents without a model-specific data store&lt;/td&gt;
&lt;td&gt;Auto memory is a Claude Code feature&lt;/td&gt;
&lt;td&gt;Plain Markdown plus a Python CLI and agent integrations for Codex, Claude Code, Gemini and generic agents&lt;/td&gt;
&lt;td&gt;Provider-agnostic inside AgentsRoom through its MCP server, but the memory service itself is part of AgentsRoom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What has to run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing continuously. The skill runs inside the coding agent when needed; linter and dashboard are optional. The dashboard server exists only while you run it, or it can export a static page&lt;/td&gt;
&lt;td&gt;Nothing extra; built into Claude Code&lt;/td&gt;
&lt;td&gt;A Python standard-library CLI / skill when reading, writing, checking, migrating or maintaining memory; no daemon or external server required&lt;/td&gt;
&lt;td&gt;AgentsRoom desktop app, an account and its MCP / server path. Agent memory reads and writes require connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Selective loading&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;context/index.md&lt;/code&gt; is a deterministic one-line-per-topic index under a fixed 36-heading skeleton; the agent opens only the topic the task touches. Family scope adds routing between related projects&lt;/td&gt;
&lt;td&gt;The first 200 lines or 25 KB of &lt;code&gt;MEMORY.md&lt;/code&gt; are loaded every session; detailed topic files are opened on demand&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;manifest.md&lt;/code&gt; routes a bounded context pack through task, path and scope, then loads &lt;code&gt;brief.md&lt;/code&gt; and only relevant files / areas&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;memory_list&lt;/code&gt; returns notes and retrieval cues; &lt;code&gt;memory_get&lt;/code&gt; fetches individual notes. The app adds fuzzy search over names, cues, bodies and tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What gets written, by whom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distilled rationale synthesized by the agent: decisions, rejected alternatives, workarounds, incidents and constraints, with stable UUIDs, Status, Evidence and optional cross-entry references. Superseded entries stay&lt;/td&gt;
&lt;td&gt;Claude writes &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;feedback&lt;/code&gt;, &lt;code&gt;project&lt;/code&gt; and &lt;code&gt;reference&lt;/code&gt; notes for itself. It deliberately skips things it can derive from the codebase&lt;/td&gt;
&lt;td&gt;Typed, evidence-backed project memory with stable IDs, Subjects and Facets. Unconfirmed observations go to &lt;code&gt;inbox.md&lt;/code&gt;; the CLI manages superseding, conflicts, forgetting and migrations&lt;/td&gt;
&lt;td&gt;Agent-written notes in &lt;code&gt;architecture&lt;/code&gt;, &lt;code&gt;decisions&lt;/code&gt;, &lt;code&gt;conventions&lt;/code&gt;, &lt;code&gt;pitfalls&lt;/code&gt; and &lt;code&gt;features&lt;/code&gt;, updated in place by default, with optional append / edit modes and no confirmation step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository topology&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single repo, shared-context mono repo, isolated-context mono repo, multi-repo families and nested repositories. Families can themselves nest&lt;/td&gt;
&lt;td&gt;One auto-memory directory per Git repository, shared across its worktrees and subdirectories&lt;/td&gt;
&lt;td&gt;Repository-scoped memory with optional area routing inside the repository; the current documented model does not define a multi-repository family topology&lt;/td&gt;
&lt;td&gt;One project memory per project, plus Folder, Account and Agent scopes for knowledge that should span projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-repository model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Family&lt;/strong&gt; for related projects with explicit routing, &lt;strong&gt;friends&lt;/strong&gt; for independent repositories cited by rationale, and &lt;strong&gt;thoughts&lt;/strong&gt; for chains of cited entries across those projects&lt;/td&gt;
&lt;td&gt;No cross-repository relationship model in auto memory&lt;/td&gt;
&lt;td&gt;Stable relations exist inside a project's memory, but the current documented model does not define cross-repository citations or a repository graph&lt;/td&gt;
&lt;td&gt;Cross-project sharing is handled through Folder, Account and Agent scopes rather than repository-to-repository rationale citations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human view&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub renders the Markdown directly. The optional dashboard adds Git authorship and status history, queues, search, timelines, graphs, family, friends, thoughts and the globe. It remains read-only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/memory&lt;/code&gt; exposes the files and opens them in an editor; no project-memory graph&lt;/td&gt;
&lt;td&gt;Primarily files and CLI / audit output&lt;/td&gt;
&lt;td&gt;Integrated Memory library with editor, fuzzy search, wiki-link graph and visual fading of older notes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The part that changed most in Keep the Why
&lt;/h2&gt;

&lt;p&gt;Keep the Why started as a deliberately small idea: put durable rationale in &lt;code&gt;context/&lt;/code&gt;, keep it in Git, and let the agent maintain it.&lt;/p&gt;

&lt;p&gt;That part has not changed. What changed is the topology around it.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.keep-the-why&lt;/code&gt; file now marks a project, and the nearest one above the working directory wins, similar to how Git finds &lt;code&gt;.git&lt;/code&gt;. That one rule covers four layouts.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;single repository&lt;/strong&gt; has one &lt;code&gt;.keep-the-why&lt;/code&gt; and one &lt;code&gt;context/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;mono repository&lt;/strong&gt; can either share one &lt;code&gt;context/&lt;/code&gt; across the whole tree or give sub-projects isolated contexts of their own.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;multi-repository project&lt;/strong&gt; can form a &lt;strong&gt;family&lt;/strong&gt;. The parent lists its children with one scope line each, and each child points back to its parent. That relationship is not just for visualization. It is routing: rationale belongs once, in the family member whose scope owns it, and other members cite it instead of copying it.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;nested repository&lt;/strong&gt;, such as a submodule or vendored checkout, remains its own project. The nearest &lt;code&gt;.keep-the-why&lt;/code&gt; wins, so its memory does not leak into the surrounding repository.&lt;/p&gt;

&lt;p&gt;Families can nest as well.&lt;/p&gt;

&lt;p&gt;That solves the close relationships. Independent repositories use a different mechanism.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;friend&lt;/strong&gt; is simply another repository cited by an entry through a stable entry UUID and that repository's canonical identity. Nothing is routed or synchronized between friends. The citation is the relationship.&lt;/p&gt;

&lt;p&gt;Once entries can cite entries in other repositories, something else appears naturally: &lt;strong&gt;thoughts&lt;/strong&gt;. A thought is a chain of rationale entries where each step cites or supersedes another one. The dashboard can follow those chains through a project, through its family, and into friends.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/dashboard/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt; is still read-only. Locally it is a view over &lt;code&gt;context/&lt;/code&gt;, &lt;code&gt;.keep-the-why&lt;/code&gt;, linter findings and Git history. A static export is a rebuildable snapshot, not a second source of truth. Delete the dashboard and the project memory is still the Markdown in Git.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;globe&lt;/strong&gt; takes that one step further. It loads published dashboard exports as a graph and can walk outward by repository hops. A repository can expose its family and the friends it cites without moving the rationale itself into a central service.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://keepthewhy.com/registry/" rel="noopener noreferrer"&gt;registry&lt;/a&gt; makes public exports discoverable. It stores the project pointers and derived metadata needed by the globe, not the rationale itself. It also provides the direction citations alone cannot: discovery of projects that cite you.&lt;/p&gt;

&lt;p&gt;That distinction matters. Family is ownership and routing. Friends are citations. Thoughts are chains through those citations. The registry is discovery. The globe is a view over all of it.&lt;/p&gt;

&lt;p&gt;None of those changes the authority model: each repository remains authoritative for its own rationale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the others are better
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude Code Auto Memory&lt;/strong&gt; still wins on setup. It is built in, on by default in normal local sessions, and it remembers a layer Keep the Why deliberately leaves out: your role, preferences, corrections and working patterns.&lt;/p&gt;

&lt;p&gt;Claude Code has also moved since the first version of this comparison. It can now read &lt;code&gt;AGENTS.md&lt;/code&gt; directly as project instructions, so shared repository instructions no longer necessarily need a Claude-specific import workaround. That makes the surrounding project-instruction story more portable, but auto memory itself is still machine-local by default and remains a Claude Code feature.&lt;/p&gt;

&lt;p&gt;That is not a defect. It is personal and tool-local memory doing the job it was designed for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MemoryCustodian&lt;/strong&gt; is still the closest neighbour, but it has become a much stronger protocol since the earlier comparison.&lt;/p&gt;

&lt;p&gt;It is repo-native, plain Markdown and deterministic about loading. It now has stable Subjects and Facets, evidence requirements, candidate memory in &lt;code&gt;inbox.md&lt;/code&gt;, path-scoped areas, explicit forgetting, conflict checks, transaction journals, recovery, migrations, audit output and private local overlays.&lt;/p&gt;

&lt;p&gt;The design difference is clearer now than it was before.&lt;/p&gt;

&lt;p&gt;MemoryCustodian treats project memory as a controlled protocol operated through a CLI. Keep the Why treats the committed Markdown as the memory and the skill as the maintainer of that text. If you want stronger command-level enforcement, structural conflict handling and explicit mutation machinery, MemoryCustodian is the more opinionated tool. If you want the rationale to remain ordinary repository text with Git as the main system around it, Keep the Why stays deliberately thinner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AgentsRoom Project Memory&lt;/strong&gt; has the most integrated application experience of the four.&lt;/p&gt;

&lt;p&gt;It has fuzzy search, a first-class editor, wiki-links, a graph, five project-memory folders, four scopes, automatic agent capture, update-in-place, append and edit operations, and a useful visual signal for notes that are getting old.&lt;/p&gt;

&lt;p&gt;It also solves cross-project memory, but with a different ownership model. Folder, Account and Agent scopes let knowledge span projects inside AgentsRoom. Keep the Why instead keeps project rationale owned by repositories and connects repositories with explicit citations.&lt;/p&gt;

&lt;p&gt;AgentsRoom stores Project Memory server-side. The local cache lets the UI show notes offline, but agent memory tools talk to the server on every call. The project id committed in the repository makes the shared memory follow the project inside AgentsRoom, but the notes themselves are not Git objects and are not reviewed in the repository's pull requests.&lt;/p&gt;

&lt;p&gt;For a team already using AgentsRoom as the place where its agents live, that is a coherent trade. For a repository that should remain complete without the application, it is exactly the trade the repo-native approach avoids.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Keep the Why doesn't do, on purpose
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No similarity search.&lt;/strong&gt; Retrieval is the index, topic names and explicit references. Entries are synthesized and read as Markdown, not embedded and ranked. Any external search tool can index &lt;code&gt;context/&lt;/code&gt;; Keep the Why does not require one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No server as the source of truth.&lt;/strong&gt; Family, friends, thoughts, dashboard exports, the registry and the globe add views and relationships without moving ownership away from the repositories. A published &lt;code&gt;state.json&lt;/code&gt; is derived output. The canonical rationale is still the project's committed Markdown.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No automatic "this reasoning is stale" oracle.&lt;/strong&gt; Entries can carry &lt;code&gt;Revisit when&lt;/code&gt;, statuses such as &lt;code&gt;needs-review&lt;/code&gt;, and evidence state. The dashboard queues them, but it does not decide that an old decision is wrong merely because time passed. Age is a signal; validity is a project judgement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No semantic commit gate.&lt;/strong&gt; The linter checks structure, references and security properties, not whether a decision is good. Prevention happens earlier, in the session, by giving the next agent the reason before it proposes the same rejected path again. In the retry-wrapper experiment, 7 of 10 fresh sessions proposed the rejected simplification without the recorded rationale; with the entry present, 0 of 10 did (&lt;a href="https://blog.technopathy.club/what-happens-when-a-coding-agent-forgets-why-a-change-was-rejected" rel="noopener noreferrer"&gt;the experiment&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No UI that becomes another knowledge store.&lt;/strong&gt; GitHub can render &lt;code&gt;context/&lt;/code&gt; directly. The dashboard adds a much richer read-only view over the same data and its Git history, including family, friends, thoughts and the globe, but writing still happens in the repository.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reasons for those choices are in the &lt;a href="https://keepthewhy.com/faq/" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that is true of all four
&lt;/h2&gt;

&lt;p&gt;Every one of these tools lives on the quality of what gets written into it.&lt;/p&gt;

&lt;p&gt;A server full of notes, a &lt;code&gt;MEMORY.md&lt;/code&gt;, a protocol full of stable IDs, or a &lt;code&gt;context/&lt;/code&gt; full of Evidence fields is not useful merely because it persists. None of them can recover reasoning that was never captured, and none can guarantee that a claim remains true forever.&lt;/p&gt;

&lt;p&gt;The differences are in what gets written, who owns it, how it is retrieved, and what happens when the project grows beyond one session, one machine or one repository.&lt;/p&gt;

&lt;p&gt;Keep the Why's answer is deliberately narrow: preserve the reasoning behind the code as structured Markdown owned by the repository. The agent synthesizes rather than records. A rejected alternative is stored as a rejected alternative with the reason it lost, not as a transcript a later reader has to interpret.&lt;/p&gt;

&lt;p&gt;The newer family / friends model extends the same rule instead of replacing it. Related repositories can be routed as one family. Independent repositories can cite each other. The dashboard can connect those citations into thoughts and walk them through the globe. The registry can make published projects discoverable.&lt;/p&gt;

&lt;p&gt;But Git still owns the memory.&lt;/p&gt;

&lt;p&gt;Session memory remembers what happened. Project state remembers where the project is. The why layer remembers why it became what it is.&lt;/p&gt;

&lt;p&gt;Three layers. Pick one tool per layer if you need all three, and don't let a comparison, this one included, tell you they are rivals.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>agentskills</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Keep the Why is not another workflow</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Tue, 22 Sep 2026 14:55:22 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/keep-the-why-is-not-another-workflow-3fd7</link>
      <guid>https://dev.to/oliverzehentleitner/keep-the-why-is-not-another-workflow-3fd7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Install it, initialize the project, keep coding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is a misconception about Keep the Why that I keep running into.&lt;/p&gt;

&lt;p&gt;People see a structured &lt;code&gt;context/&lt;/code&gt; directory containing decisions, rejected alternatives, workarounds, constraints and incident learnings and assume there must be discipline behind it.&lt;/p&gt;

&lt;p&gt;Someone has to remember what to record.&lt;/p&gt;

&lt;p&gt;Someone has to tell the agent when something is important.&lt;/p&gt;

&lt;p&gt;Someone has to maintain another documentation system.&lt;/p&gt;

&lt;p&gt;That would defeat the point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the Why is designed so that after a very small initial setup, you keep working exactly as before.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent does the remembering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the skill
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt; is an Agent Skill using the open, cross-agent skill format.&lt;/p&gt;

&lt;p&gt;For more than 70 supported agents, the recommended installation is documented on the &lt;a href="https://keepthewhy.com/installation/" rel="noopener noreferrer"&gt;installation page&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add https://github.com/oliver-zehentleitner/keep-the-why/tree/latest/skills/keep-the-why
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also installation paths for GitHub CLI, Claude Code plugins, GitHub Copilot CLI, Codex plugins, Cursor, asm and manual installation.&lt;/p&gt;

&lt;p&gt;There is no service to deploy. No database. No daemon. No account.&lt;/p&gt;

&lt;p&gt;What you install is essentially the instructions that teach your coding agent how to recognize and preserve the reasoning behind a project.&lt;/p&gt;

&lt;p&gt;If you start a session after installing the skill, you do not need a separate "load Keep the Why" step. Asking the agent to set it up in the project is enough to activate it.&lt;/p&gt;

&lt;p&gt;If the agent session was already running before you installed the skill, tell that session to load Keep the Why once - or point it directly at its &lt;code&gt;SKILL.md&lt;/code&gt;. After project setup, the start path handles future sessions automatically. The supported mechanisms are documented under &lt;a href="https://keepthewhy.com/autostart/" rel="noopener noreferrer"&gt;autostart&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the project
&lt;/h2&gt;

&lt;p&gt;Inside the project directory, follow the short &lt;a href="https://keepthewhy.com/setup/" rel="noopener noreferrer"&gt;project setup&lt;/a&gt; and tell the agent something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Initialize Keep the Why in this project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This explicit request is intentional. Keep the Why does not silently turn itself on just because you installed it. A repository has to opt in once.&lt;/p&gt;

&lt;p&gt;The agent then presents the project setup. With the defaults, there is very little to decide.&lt;/p&gt;

&lt;p&gt;Among other things, the setup covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;where the project's why-knowledge should live - &lt;code&gt;context/&lt;/code&gt; by default unless an existing decision location is a better fit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;how you want to start capturing project knowledge&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;proactive capture during normal work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;asking before writing only when something is genuinely unclear&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;no constant questions for issue or ticket references&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;structural linting where supported and detected&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;loading Keep the Why automatically in future sessions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wizard is presented as a list with defaults already filled in where the setup defines them.&lt;/p&gt;

&lt;p&gt;You can effectively answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;defaults&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a second short list for your personal workflow preferences.&lt;/p&gt;

&lt;p&gt;Again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;defaults&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you are done.&lt;/p&gt;

&lt;p&gt;This is not a configuration project. It is meant to be something you can set up in roughly the time it takes to explain why you wanted it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then forget about Keep the Why
&lt;/h2&gt;

&lt;p&gt;This is probably the most important part.&lt;/p&gt;

&lt;p&gt;After setup, there is no Keep the Why workflow. You do not periodically stop coding to document decisions, decide whether something belongs in &lt;code&gt;context/&lt;/code&gt;, or tell the agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Store this in project memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You work with your coding agent normally. You ask it to implement something, debug a production problem, discuss an architectural choice, reject an apparently simpler implementation because of a constraint, or discover why a strange workaround exists.&lt;/p&gt;

&lt;p&gt;That reasoning is already happening inside the conversation. Keep the Why's job is simply to stop throwing it away.&lt;/p&gt;

&lt;p&gt;With the default setup, the agent proactively notices reasoning worth preserving and captures it while the work happens. If the situation is clear, it does not need to interrupt you. If something important is genuinely ambiguous, it asks.&lt;/p&gt;

&lt;p&gt;That is the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually gets left behind
&lt;/h2&gt;

&lt;p&gt;The result is deliberately boring: plain Markdown in the project.&lt;/p&gt;

&lt;p&gt;A real example from the Keep the Why documentation, the file context/retries.md, looks like this:&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;# Retries&lt;/span&gt;

&lt;span class="gu"&gt;## Why retry_with_jitter isn't a plain retry loop&lt;/span&gt;

&lt;span class="gs"&gt;**Type:**&lt;/span&gt; constraint
&lt;span class="gs"&gt;**Status:**&lt;/span&gt; active
&lt;span class="gs"&gt;**Evidence:**&lt;/span&gt; confirmed
&lt;span class="gs"&gt;**Source:**&lt;/span&gt; discovered while considering simplifying it, 2026-07-22

The payment gateway's rate limiter returns 429 with a per-request
Retry-After header. A fixed-delay retry loop would frequently retry
before the limiter resets, causing repeated 429s under load.

&lt;span class="gs"&gt;**Considered:**&lt;/span&gt; replacing it with a plain retry loop, since the wrapper
looked like unnecessary complexity with nothing documenting why.
Not adopted once the Retry-After behavior surfaced during review.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That entry exists because somebody considered simplifying code that looked unnecessarily complicated.&lt;/p&gt;

&lt;p&gt;Without the reason, a later agent sees complexity and may propose the same simplification again. With the reason in the repository, the next session can understand why the code looks the way it does without anyone having to retell the story.&lt;/p&gt;

&lt;p&gt;That is what "the agent does the remembering" means in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your agent stops being a goldfish
&lt;/h2&gt;

&lt;p&gt;A coding agent usually enters a repository with an interesting asymmetry.&lt;/p&gt;

&lt;p&gt;It has the source code, tests, documentation and Git history, but it does not necessarily have the experience that produced them.&lt;/p&gt;

&lt;p&gt;A previous session may have spent an hour discovering why an obvious implementation does not work. The next session sees the same code and happily proposes the same implementation again.&lt;/p&gt;

&lt;p&gt;That is the goldfish problem.&lt;/p&gt;

&lt;p&gt;Keep the Why puts that missing experience into the repository too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── src/
├── tests/
├── docs/
├── context/
├── README.md
└── .keep-the-why
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a future session can inherit more than the result.&lt;/p&gt;

&lt;p&gt;It can inherit the reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git already solves the distribution problem
&lt;/h2&gt;

&lt;p&gt;Keep the Why deliberately does not build another synchronization system.&lt;/p&gt;

&lt;p&gt;The memory is part of the project. &lt;code&gt;context/&lt;/code&gt; is Markdown. &lt;code&gt;.keep-the-why&lt;/code&gt; records that the project has opted in and how it is configured. Both travel through Git like the rest of the repository.&lt;/p&gt;

&lt;p&gt;Clone the repository somewhere else and the project memory comes with it. Switch machine and it comes with it. Open another branch and it comes with it. A different developer gets it. A different agent gets it. A different session gets it.&lt;/p&gt;

&lt;p&gt;The personal workflow preferences of each developer remain personal, but the actual project knowledge is shared.&lt;/p&gt;

&lt;p&gt;There is no memory database belonging to one AI provider and no conversation history that only one person can access.&lt;/p&gt;

&lt;p&gt;The project carries the experience with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PR contains the why too
&lt;/h2&gt;

&lt;p&gt;Yes, this means a change can include both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/...
context/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I increasingly think this is a feature, not a cost.&lt;/p&gt;

&lt;p&gt;A reviewer normally receives the result of the coding process. The code changed. Maybe some tests changed. But much of the reasoning that led there disappeared with the coding session.&lt;/p&gt;

&lt;p&gt;With repo-native project memory, some of that reasoning can arrive in the same pull request.&lt;/p&gt;

&lt;p&gt;The reviewer can see not just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This implementation changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;but also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is why it changed, what alternative was rejected and which constraint mattered.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the reviewer does not have to be human.&lt;/p&gt;

&lt;p&gt;A coding agent can produce a change and capture the reasoning that surfaced while producing it. A review agent can read the same &lt;code&gt;context/&lt;/code&gt; when reviewing the change.&lt;/p&gt;

&lt;p&gt;In that sense, &lt;code&gt;context/&lt;/code&gt; becomes a small but useful agent-to-agent communication layer. The coder passes experience forward; the reviewer can challenge it, verify it or notice when the code contradicts it.&lt;/p&gt;

&lt;p&gt;And because it is ordinary Markdown in the PR, humans can do exactly the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Teams accumulate experience instead of isolated sessions
&lt;/h2&gt;

&lt;p&gt;This becomes more interesting with multiple developers and agents.&lt;/p&gt;

&lt;p&gt;Normally project knowledge spreads through conversations. One developer discovers something, another learns it in Slack, someone explains it during a call, and an agent discovers it independently three months later. Another agent may repeat the rejected approach six months after that.&lt;/p&gt;

&lt;p&gt;Eventually the person who understood the original reason leaves. The code remains. The experience does not.&lt;/p&gt;

&lt;p&gt;Keep the Why changes where that experience accumulates. That idea is the core of &lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;repo-native project memory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not in one developer's head, one agent's session memory or one vendor's conversation database.&lt;/p&gt;

&lt;p&gt;In the project.&lt;/p&gt;

&lt;p&gt;That means agents and developers working at different times can benefit from each other's discoveries. The knowledge compounds without everyone having to become more disciplined about documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete workflow
&lt;/h2&gt;

&lt;p&gt;The initial setup is basically this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install Keep the Why
        ↓
"Initialize Keep the Why in this project"
        ↓
Accept the defaults
        ↓
Keep coding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the current agent session was already running before the skill was installed, load it once in that session first. That is the exception, not the normal workflow.&lt;/p&gt;

&lt;p&gt;After setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normal development
        ↓
useful reasoning appears naturally
        ↓
the agent notices it
        ↓
context/ is updated
        ↓
Git carries it with the project
        ↓
future humans and agents inherit it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the system.&lt;/p&gt;

&lt;p&gt;You should not have to become the librarian for your AI agent.&lt;/p&gt;

&lt;p&gt;You should not have to remember to preserve the reasoning.&lt;/p&gt;

&lt;p&gt;And you should not have to explain the same project history to every fresh session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install the skill. Initialize the project. Keep coding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If Keep the Why requires you to constantly think about Keep the Why, it has failed at its job.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>opensource</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Your repository already is your project's memory. One layer was missing.</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Mon, 14 Sep 2026 14:37:33 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/your-repository-already-is-your-projects-memory-one-layer-was-missing-2ld7</link>
      <guid>https://dev.to/oliverzehentleitner/your-repository-already-is-your-projects-memory-one-layer-was-missing-2ld7</guid>
      <description>&lt;p&gt;Every coding agent starts the day as a goldfish. It can read the code, reason about it, and still have no idea why any of it is the way it is. So it proposes the simplification you rejected in March, for the reason you rejected it in March, and you explain it again.&lt;/p&gt;

&lt;p&gt;A whole product category has grown around that: &lt;a href="https://blog.technopathy.club/keep-the-why-project-memory-for-humans-and-ai-agents" rel="noopener noreferrer"&gt;project memory for agents&lt;/a&gt;. Databases, MCP servers, knowledge graphs, subscriptions. I think much of it starts from the wrong question, because most of the memory already exists.&lt;/p&gt;

&lt;p&gt;That is the idea behind &lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;repo-native project memory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is called the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a repository already remembers
&lt;/h2&gt;

&lt;p&gt;Look at any well-kept project. &lt;code&gt;README.md&lt;/code&gt; says what it is. &lt;code&gt;docs/&lt;/code&gt; says how to use it. &lt;code&gt;tests/&lt;/code&gt; say what it must do. &lt;code&gt;CHANGELOG.md&lt;/code&gt; says what changed. &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; says how a change gets in. &lt;code&gt;pyproject.toml&lt;/code&gt; or &lt;code&gt;package.json&lt;/code&gt; says how it is built. &lt;code&gt;AGENTS.md&lt;/code&gt; says where an agent should look first. Git says who changed what and when.&lt;/p&gt;

&lt;p&gt;That is already a form of project memory: plain files, next to the code, versioned by Git, available in every clone. It has worked for people for decades without a separate platform, and it works for agents for the same reason: anything that can read a directory can read it.&lt;/p&gt;

&lt;p&gt;Agents already consume the README, docs, tests and changelog. Increasingly, &lt;a href="https://blog.technopathy.club/i-let-an-ai-agent-maintain-my-open-source-suite-for-a-week-here-s-what-actually-happened" rel="noopener noreferrer"&gt;they maintain them too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What the repository usually does not preserve systematically is &lt;a href="https://blog.technopathy.club/keep-the-why-code-becomes-legacy-when-nobody-remembers-why" rel="noopener noreferrer"&gt;why&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The layer that was missing
&lt;/h2&gt;

&lt;p&gt;Why is the retry loop this complicated? Why does that flag exist? Why must these two steps run in this order? Which alternative was tried and dropped?&lt;/p&gt;

&lt;p&gt;That reasoning is produced in every working session, often out loud in the conversation with the agent, and a new session throws it away.&lt;/p&gt;

&lt;p&gt;People have long had mechanisms for the big version of this. Architecture Decision Records work well for the handful of important architectural decisions worth documenting explicitly. Commit messages, issues and pull requests can also preserve pieces of rationale.&lt;/p&gt;

&lt;p&gt;What was expensive was capturing the hundred small reasons that actually make a codebase what it is. Writing each one down manually often cost more than it seemed worth.&lt;/p&gt;

&lt;p&gt;That is the part AI changed.&lt;/p&gt;

&lt;p&gt;Not the need for the why, which is decades old, but the cost of capturing it.&lt;/p&gt;

&lt;p&gt;The reasoning is spoken anyway. An agent that is already part of the conversation can write it down as a byproduct: short, synthesized, in a fixed form, committed with the code it explains.&lt;/p&gt;

&lt;p&gt;Sometimes there is only a reason and no code at all. A change is started, investigated and abandoned once the reason not to make it becomes clear. No resulting diff, commit or PR necessarily records that dead end.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;context/&lt;/code&gt; entry can.&lt;/p&gt;

&lt;p&gt;So the missing layer is one more part of the same repository. It lives where the rest lives, travels with every clone, appears in the pull request next to the code diff, and goes through the same review.&lt;/p&gt;

&lt;p&gt;No account. No daemon. No database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it change anything?
&lt;/h2&gt;

&lt;p&gt;I measured the abandoned-change case, because that is the one I cared about.&lt;/p&gt;

&lt;p&gt;Twenty fresh agent sessions received the same codebase and the same request: simplify this retry wrapper. Ten had a &lt;code&gt;context/&lt;/code&gt; entry recording why the wrapper looks the way it does and what had already been rejected; ten did not.&lt;/p&gt;

&lt;p&gt;Without the entry, seven of ten offered the already-rejected simplification again.&lt;/p&gt;

&lt;p&gt;With it, all ten found the entry and none did.&lt;/p&gt;

&lt;p&gt;The Markdown file did not make the agent smarter. It gave it one piece of project history the code itself could not provide.&lt;/p&gt;

&lt;p&gt;This also matches a broader lesson from my &lt;a href="https://blog.technopathy.club/same-skill-six-agents-nine-models-what-a-real-eval-matrix-taught-me" rel="noopener noreferrer"&gt;agent and model evaluation matrix&lt;/a&gt;: whether useful context is found and applied depends on the whole agent harness, not only on the underlying model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is thin
&lt;/h2&gt;

&lt;p&gt;A repository holds knowledge; it becomes useful memory only to the degree that its structure makes that knowledge findable. At hundreds or thousands of entries, that becomes a retrieval problem.&lt;/p&gt;

&lt;p&gt;The layer also stays empty unless something fills it. And &lt;code&gt;context/&lt;/code&gt; is not an established convention, so today an agent has to be told that it exists and when to use it.&lt;/p&gt;

&lt;p&gt;A linter can check whether an entry has the right structure. It cannot check whether the reason is true. A confidently wrong why can be worse than none.&lt;/p&gt;

&lt;p&gt;And much of the why never reaches the repository at all. It remains in chat, tickets, meetings and people's heads.&lt;/p&gt;

&lt;p&gt;This is a documentation discipline with an agent as the writing hand, not a memory subsystem.&lt;/p&gt;

&lt;p&gt;It wins on ownership, review and longevity.&lt;/p&gt;

&lt;p&gt;It loses where nobody reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;The argument, including the model and its limits, is on one page: &lt;a href="https://oliver-zehentleitner.github.io/repo-native-project-memory/" rel="noopener noreferrer"&gt;repo-native project memory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a thesis, not a product, and it would hold for any implementation of the layer.&lt;/p&gt;

&lt;p&gt;The implementation I use is &lt;a href="https://keepthewhy.com" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt;: an agent skill and the file convention it maintains, a linter for the structure, and a dashboard to inspect it. The background and motivation are also covered in &lt;a href="https://blog.technopathy.club/keep-the-why-project-memory-for-humans-and-ai-agents" rel="noopener noreferrer"&gt;Keep the Why: Project Memory for Humans and AI Agents&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is the why layer of repo-native project memory, and it does not claim to be more than that.&lt;/p&gt;

&lt;p&gt;Session memory remembers what happened. Project state remembers where the project is. The why layer preserves why it became what it is.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>picows in UNICORN Binance WebSocket API: Up to 2 the Throughput, Opt-In for Now</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Sun, 13 Sep 2026 16:12:30 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/picows-in-unicorn-binance-websocket-api-up-to-2x-the-throughput-opt-in-for-now-5c1k</link>
      <guid>https://dev.to/oliverzehentleitner/picows-in-unicorn-binance-websocket-api-up-to-2x-the-throughput-opt-in-for-now-5c1k</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-websocket-api" rel="noopener noreferrer"&gt;UNICORN Binance WebSocket API&lt;/a&gt; (UBWA) has used &lt;a href="https://github.com/python-websockets/websockets" rel="noopener noreferrer"&gt;&lt;code&gt;websockets&lt;/code&gt;&lt;/a&gt; since day one.&lt;/p&gt;

&lt;p&gt;Since UBWA 2.16.0, you can switch a manager instance to &lt;a href="https://github.com/tarasko/picows" rel="noopener noreferrer"&gt;&lt;code&gt;picows&lt;/code&gt;&lt;/a&gt;, a Cython WebSocket implementation.&lt;/p&gt;

&lt;p&gt;For typical Binance messages below ~1 KB, picows delivers roughly 1.7–2× the throughput with significantly lower CPU cost. At normal trading-bot message rates, however, you probably won't notice a difference.&lt;/p&gt;

&lt;p&gt;The performance is there. The long-term production history isn't — yet. That's why picows is opt-in for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why picows?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;websockets&lt;/code&gt; is a very good pure-Python library. For a few hundred messages per second it is nowhere near the bottleneck.&lt;/p&gt;

&lt;p&gt;But UBWA is also used for hundreds of &lt;code&gt;depth@100ms&lt;/code&gt; subscriptions, multiplexed connections and local depth caches processing every diff. At those rates, per-message overhead starts to matter.&lt;/p&gt;

&lt;p&gt;picows moves much of the WebSocket protocol handling into Cython.&lt;/p&gt;

&lt;p&gt;It also provides a &lt;code&gt;picows.websockets&lt;/code&gt; compatibility layer with the familiar API: &lt;code&gt;connect()&lt;/code&gt;, &lt;code&gt;recv()&lt;/code&gt;, &lt;code&gt;send()&lt;/code&gt;, &lt;code&gt;close()&lt;/code&gt; and matching exceptions.&lt;/p&gt;

&lt;p&gt;That made the UBWA integration small.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching libraries
&lt;/h2&gt;

&lt;p&gt;One parameter:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unicorn_binance_websocket_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BinanceWebSocketApiManager&lt;/span&gt;

&lt;span class="n"&gt;ubwa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BinanceWebSocketApiManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ubwa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BinanceWebSocketApiManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;websocket_library&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;picows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;picows is optional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; &lt;span class="s2"&gt;"unicorn-binance-websocket-api[picows]&amp;gt;=2.16.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Streams, WebSocket API, userData streams, subscribe/unsubscribe, reconnects, signals and proxies use the same UBWA code path.&lt;/p&gt;

&lt;p&gt;There is also no silent fallback.&lt;/p&gt;

&lt;p&gt;If you select &lt;code&gt;"picows"&lt;/code&gt; without installing it, UBWA raises &lt;code&gt;ImportError&lt;/code&gt;. Unknown values raise &lt;code&gt;ValueError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If a bot says it is running on picows, it should actually be running on picows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark
&lt;/h2&gt;

&lt;p&gt;The benchmark runs both libraries through the full UBWA stack against a local server sending Binance-shaped messages.&lt;/p&gt;

&lt;p&gt;Python 3.13, websockets 16.0, picows 2.1.3, x86_64 Linux, &lt;code&gt;output_default="raw_data"&lt;/code&gt;, median of three runs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;~msg size&lt;/th&gt;
&lt;th&gt;websockets msgs/s&lt;/th&gt;
&lt;th&gt;picows msgs/s&lt;/th&gt;
&lt;th&gt;speedup&lt;/th&gt;
&lt;th&gt;websockets CPU µs/msg&lt;/th&gt;
&lt;th&gt;picows CPU µs/msg&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;aggTrade&lt;/td&gt;
&lt;td&gt;0.2 KB&lt;/td&gt;
&lt;td&gt;201,912&lt;/td&gt;
&lt;td&gt;403,316&lt;/td&gt;
&lt;td&gt;2.00x&lt;/td&gt;
&lt;td&gt;5.1&lt;/td&gt;
&lt;td&gt;2.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kline&lt;/td&gt;
&lt;td&gt;0.3 KB&lt;/td&gt;
&lt;td&gt;195,460&lt;/td&gt;
&lt;td&gt;371,019&lt;/td&gt;
&lt;td&gt;1.90x&lt;/td&gt;
&lt;td&gt;5.2&lt;/td&gt;
&lt;td&gt;2.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;depth20&lt;/td&gt;
&lt;td&gt;1.0 KB&lt;/td&gt;
&lt;td&gt;153,187&lt;/td&gt;
&lt;td&gt;259,960&lt;/td&gt;
&lt;td&gt;1.70x&lt;/td&gt;
&lt;td&gt;6.8&lt;/td&gt;
&lt;td&gt;4.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;depth diff&lt;/td&gt;
&lt;td&gt;9.1 KB&lt;/td&gt;
&lt;td&gt;64,172&lt;/td&gt;
&lt;td&gt;67,972&lt;/td&gt;
&lt;td&gt;1.06x&lt;/td&gt;
&lt;td&gt;16.3&lt;/td&gt;
&lt;td&gt;15.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!ticker@arr&lt;/td&gt;
&lt;td&gt;453.9 KB&lt;/td&gt;
&lt;td&gt;1,768&lt;/td&gt;
&lt;td&gt;1,662&lt;/td&gt;
&lt;td&gt;0.94x&lt;/td&gt;
&lt;td&gt;608.7&lt;/td&gt;
&lt;td&gt;641.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;multiplex mix&lt;/td&gt;
&lt;td&gt;0.2 KB&lt;/td&gt;
&lt;td&gt;180,406&lt;/td&gt;
&lt;td&gt;334,188&lt;/td&gt;
&lt;td&gt;1.85x&lt;/td&gt;
&lt;td&gt;5.7&lt;/td&gt;
&lt;td&gt;3.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is simple.&lt;/p&gt;

&lt;p&gt;For small Binance messages, picows is clearly faster. Around 10 KB the difference mostly disappears. On the huge 450 KB &lt;code&gt;!ticker@arr&lt;/code&gt; payload, picows even comes out a few percent behind. That row turned out to be an artifact of the benchmark, see the next section.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;output_default="dict"&lt;/code&gt; and JSON parsing included, the advantage for small messages is still around 1.4–1.7×.&lt;/p&gt;

&lt;p&gt;Against live Binance at only a few hundred messages per second, there is effectively no difference. Both libraries spend most of their time waiting for data.&lt;/p&gt;

&lt;p&gt;If that is your workload, switching gives you little.&lt;/p&gt;

&lt;p&gt;If you push tens of thousands of messages per second through one process, it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The big-message row is a benchmark artifact
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;!ticker@arr&lt;/code&gt; row is real and reproducible, not noise: a follow-up sweep from 9 KB to 900 KB with ten paired runs per size shows picows 4 to 12 percent behind websockets through UBWA for everything from 32 KB upwards, with a run-to-run spread of only a few percent. It is also an artifact of the benchmark, not of picows. Driven directly, without UBWA, picows wins at every one of those sizes by 1.6x to 2.1x. The difference is how the two libraries drain the socket. The replay server is a firehose on loopback, it never paces, and UBWA's own per-message work on a 450 KB text (a couple of substring scans) is slower than the wire. The kernel receive buffer then autotunes into the megabytes, and picows takes all of it in one recv per loop iteration into a read buffer that keeps doubling: strace counts 79 reads of about 3.4 MB for 600 messages. websockets reads at most 256 KB per recv, about 1060 reads for the same data, and stays cache-friendly. Copying and decoding half-megabyte frames out of a multi-megabyte buffer that has already left the cache costs more per byte than picows saves on parsing. The proof is a one-line change: capping &lt;code&gt;SO_RCVBUF&lt;/code&gt; to 128 KB on the client socket flips the 450 KB result to picows 1.3x to 1.4x ahead, with nothing else touched. The benchmark script has a &lt;code&gt;--rcvbuf&lt;/code&gt; option for that now, and the full tables are in &lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-websocket-api/blob/master/context/websocket-library.md" rel="noopener noreferrer"&gt;context/websocket-library.md&lt;/a&gt;. On a real Binance connection the socket buffer never fills like that, a WAN link delivers a few megabytes per second at most and the consumer keeps up, which is also what the 24 h soak showed: picows with less CPU and less memory. Thanks to Taras Kozlov, the picows author, for asking the question that led to this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark found a UBWA bottleneck first
&lt;/h2&gt;

&lt;p&gt;The first benchmark showed only about 1.4× improvement.&lt;/p&gt;

&lt;p&gt;Profiling found why.&lt;/p&gt;

&lt;p&gt;UBWA had 18 &lt;code&gt;logger.debug()&lt;/code&gt; f-strings formatted for every message even when debug logging was disabled, seven lock acquisitions per message where one was enough, and heartbeat/stop checks running twice.&lt;/p&gt;

&lt;p&gt;After removing that overhead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;websockets: &lt;strong&gt;116k → 202k msgs/s&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;picows: &lt;strong&gt;163k → 403k msgs/s&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;picows did not suddenly get faster.&lt;/p&gt;

&lt;p&gt;UBWA stopped hiding its speed.&lt;/p&gt;

&lt;p&gt;The details are documented in &lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-websocket-api/blob/master/context/stream-loop.md" rel="noopener noreferrer"&gt;&lt;code&gt;context/stream-loop.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I did not use the native picows API
&lt;/h2&gt;

&lt;p&gt;picows also has a native listener API based on &lt;code&gt;ws_connect()&lt;/code&gt; and &lt;code&gt;WSListener&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I benchmarked it too.&lt;/p&gt;

&lt;p&gt;On 0.2 KB messages it managed roughly 499k msgs/s versus 485k through the compatibility API. On 9 KB messages the difference was around 13%.&lt;/p&gt;

&lt;p&gt;Inside the full UBWA stack that would translate to less than 5% end-to-end.&lt;/p&gt;

&lt;p&gt;In return, UBWA would need a second connection implementation of roughly 300–400 lines.&lt;/p&gt;

&lt;p&gt;Not worth it.&lt;/p&gt;

&lt;p&gt;UBWA stays on the compatibility API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure-path testing found a real bug
&lt;/h2&gt;

&lt;p&gt;Performance benchmarks are easy. Failure handling is more interesting.&lt;/p&gt;

&lt;p&gt;The test suite now runs both libraries through scenarios including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;server-side closes and reconnects&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fragmented frames&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;450 KB payloads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;messages above &lt;code&gt;max_size&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;server pings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unicode&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;rejected handshakes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSocket API round trips&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;keepalive timeouts&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rejected-handshake test found a real compatibility issue.&lt;/p&gt;

&lt;p&gt;When Binance returns HTTP 429 or 404 during the WebSocket upgrade, UBWA reads the status from &lt;code&gt;InvalidStatus&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;websockets&lt;/code&gt; exposed &lt;code&gt;response.status_code&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;picows 2.1.x exposed &lt;code&gt;response.status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;UBWA hit an &lt;code&gt;AttributeError&lt;/code&gt;, the stream thread died and nothing useful was logged.&lt;/p&gt;

&lt;p&gt;I reported it upstream as &lt;a href="https://github.com/tarasko/picows/issues/108" rel="noopener noreferrer"&gt;tarasko/picows#108&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;picows 2.2.0 fixed it the next day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proxy support got simpler too
&lt;/h2&gt;

&lt;p&gt;picows 2.3.0 added native HTTP, HTTPS, SOCKS4 and SOCKS5 proxy support.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;websockets&lt;/code&gt; has supported the same since 15.0.&lt;/p&gt;

&lt;p&gt;UBWA previously handled SOCKS5 itself using PySocks and a blocking handshake inside the event loop.&lt;/p&gt;

&lt;p&gt;That code is now gone.&lt;/p&gt;

&lt;p&gt;UBWA simply passes the proxy URL to the selected WebSocket library:&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;ubwa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BinanceWebSocketApiManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;socks5://user:pass@127.0.0.1:9050&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ubwa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BinanceWebSocketApiManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:3128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both libraries now support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://
https://
socks4://
socks5://
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The old &lt;code&gt;socks5_proxy_server&lt;/code&gt; parameters still work and are converted internally.&lt;/p&gt;

&lt;p&gt;Testing also found two UBWA issues in the old proxy path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;rejected SOCKS5 credentials could kill a stream thread without a useful log message&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TLS certificate verification was not actually enabled on the proxy path despite the option defaulting to &lt;code&gt;True&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are fixed.&lt;/p&gt;

&lt;p&gt;One difference remains: &lt;code&gt;websockets&lt;/code&gt; currently does not URL-decode proxy credentials such as &lt;code&gt;p%40ss&lt;/code&gt;, while python-socks/picows does. I reported that as &lt;a href="https://github.com/python-websockets/websockets/issues/1761" rel="noopener noreferrer"&gt;python-websockets/websockets#1761&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;UBWA rejects affected credentials up front when using &lt;code&gt;websockets&lt;/code&gt; instead of entering a reconnect loop.&lt;/p&gt;

&lt;p&gt;REST requests used for listenKey handling still follow SOCKS5 proxies only; HTTP(S) proxy support there is tracked in &lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-rest-api/issues/139" rel="noopener noreferrer"&gt;unicorn-binance-rest-api#139&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The performance and soak tests below were run with picows 2.1.3. During integration, 2.2.0 fixed the handshake compatibility issue and 2.3.0 added native proxy support. That is why UBWA requires picows 2.3.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  24 hours against live Binance
&lt;/h2&gt;

&lt;p&gt;Local tests do not tell you what happens after hours of reconnects, traffic spikes and memory allocation.&lt;/p&gt;

&lt;p&gt;So both libraries ran for 24 hours in parallel against &lt;code&gt;binance.com&lt;/code&gt; with identical subscriptions.&lt;/p&gt;

&lt;p&gt;Load:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;!ticker@arr&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;!miniTicker@arr&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;aggTrade&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;trade&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;depth20@100ms&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kline_1m&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;bookTicker&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;depth@100ms&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;across up to 50 USDT markets.&lt;/p&gt;

&lt;p&gt;Host: 8 cores, 12 GB RAM, Python 3.13.5.&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;picows&lt;/th&gt;
&lt;th&gt;websockets&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Messages / data&lt;/td&gt;
&lt;td&gt;138.8 M / 49.9 GB&lt;/td&gt;
&lt;td&gt;137.9 M / 49.7 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg / peak msgs/s&lt;/td&gt;
&lt;td&gt;1,606 / 8,260&lt;/td&gt;
&lt;td&gt;1,596 / 7,695&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSS start → end&lt;/td&gt;
&lt;td&gt;62 → 126 MB&lt;/td&gt;
&lt;td&gt;63 → 149 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU avg&lt;/td&gt;
&lt;td&gt;9.5 %&lt;/td&gt;
&lt;td&gt;12.8 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reconnects (arr / markets / depth)&lt;/td&gt;
&lt;td&gt;2 / 80 / 2&lt;/td&gt;
&lt;td&gt;2 / 88 / 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reconnect duration&lt;/td&gt;
&lt;td&gt;5–6 s&lt;/td&gt;
&lt;td&gt;5–6 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max seconds without data&lt;/td&gt;
&lt;td&gt;5 s&lt;/td&gt;
&lt;td&gt;5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errors / stalls / unrepairable streams&lt;/td&gt;
&lt;td&gt;0 / 0 / 0&lt;/td&gt;
&lt;td&gt;0 / 0 / 0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;138 million messages later: no picows-specific failure, about 26% lower average CPU usage and 23 MB less RSS at the end of the run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The high reconnect count on one stream occurred almost entirely during a four-hour high-load window.&lt;/p&gt;

&lt;p&gt;Both implementations disconnected in the same seconds with keepalive ping timeouts and recovered within five to six seconds.&lt;/p&gt;

&lt;p&gt;That strongly points to a shared external or workload-related cause rather than either WebSocket implementation.&lt;/p&gt;

&lt;p&gt;Memory rose during traffic peaks and then remained flat for the final hours despite further reconnects.&lt;/p&gt;

&lt;p&gt;No sign of a reconnect leak.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why websockets is still the default
&lt;/h2&gt;

&lt;p&gt;Because 138 million messages and a 24-hour soak are good evidence.&lt;/p&gt;

&lt;p&gt;They are not the same as thousands of users running picows for months on different systems.&lt;/p&gt;

&lt;p&gt;The compatibility layer is younger than &lt;code&gt;websockets&lt;/code&gt;, and issue #108 already showed that small API differences can matter.&lt;/p&gt;

&lt;p&gt;So for now:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;websockets remains the default. picows is opt-in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If picows holds up across enough real-world setups, the default can flip later.&lt;/p&gt;

&lt;p&gt;The switch will remain either way.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; &lt;span class="s2"&gt;"unicorn-binance-websocket-api[picows]&amp;gt;=2.16.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unicorn_binance_websocket_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BinanceWebSocketApiManager&lt;/span&gt;

&lt;span class="n"&gt;ubwa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BinanceWebSocketApiManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;websocket_library&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;picows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ubwa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_stream&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;aggTrade&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;depth20@100ms&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;btcusdt&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;ethusdt&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;solusdt&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;Run your workload with both libraries and compare CPU and memory usage.&lt;/p&gt;

&lt;p&gt;If you have numbers, edge cases or failures, post them in &lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-websocket-api/issues/477" rel="noopener noreferrer"&gt;issue #477&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That feedback will decide whether picows becomes the default.&lt;/p&gt;

&lt;p&gt;The reasoning behind these decisions — including the native picows implementation I chose not to build — lives next to the code in &lt;code&gt;context/&lt;/code&gt;, maintained with &lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>python</category>
      <category>websocket</category>
      <category>asyncio</category>
      <category>algotrading</category>
    </item>
    <item>
      <title>One Index, Many Writers: Avoiding Git Merge Conflicts with Deterministic Write Areas</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Wed, 09 Sep 2026 18:37:10 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/one-index-many-writers-avoiding-git-merge-conflicts-with-deterministic-write-areas-4718</link>
      <guid>https://dev.to/oliverzehentleitner/one-index-many-writers-avoiding-git-merge-conflicts-with-deterministic-write-areas-4718</guid>
      <description>&lt;p&gt;While working on Keep the Why, I ran into a very ordinary Git problem.&lt;/p&gt;

&lt;p&gt;There is one shared &lt;code&gt;context/index.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Developers and coding agents can add new context files in different branches. The changes are unrelated, but they are not written back to the repository at the same time. Git has to merge them later.&lt;/p&gt;

&lt;p&gt;That is where things get interesting.&lt;/p&gt;

&lt;p&gt;Imagine two branches.&lt;/p&gt;

&lt;p&gt;One adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billing.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caching.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both also update &lt;code&gt;context/index.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If new entries are simply appended to the end of the file, both branches modify the same place.&lt;/p&gt;

&lt;p&gt;Git sees overlapping changes.&lt;/p&gt;

&lt;p&gt;Merge conflict.&lt;/p&gt;

&lt;p&gt;The conflict is not semantic. Nobody disagrees about the content. It only exists because several writers use the same file and their changes are integrated later.&lt;/p&gt;

&lt;p&gt;This came up in &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/issues/194" rel="noopener noreferrer"&gt;Keep the Why issue #194&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alphabetical sorting helps, but not enough
&lt;/h2&gt;

&lt;p&gt;My first thought was simple: stop appending new entries and keep the index alphabetically sorted.&lt;/p&gt;

&lt;p&gt;That already spreads changes across the file.&lt;/p&gt;

&lt;p&gt;But especially with a small index, it does not solve the problem.&lt;/p&gt;

&lt;p&gt;Suppose the index contains only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;architecture
deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One branch adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caching
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both additions belong between the same two existing lines.&lt;/p&gt;

&lt;p&gt;So even though the entries are different and correctly sorted, Git can still see both branches changing the same area.&lt;/p&gt;

&lt;p&gt;The interesting part is that this problem is actually worse while the index is still small.&lt;/p&gt;

&lt;p&gt;With only a few existing entries, there are only a few natural places where Git can anchor an insertion. New entries therefore have a relatively high chance of landing in the same gap.&lt;/p&gt;

&lt;p&gt;As the index grows, this usually improves by itself.&lt;/p&gt;

&lt;p&gt;More existing entries create more separation points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;architecture
billing
caching
deployment
logging
monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new entry is now much more likely to land in its own area.&lt;/p&gt;

&lt;p&gt;So the main problem is not a large index.&lt;/p&gt;

&lt;p&gt;It is a &lt;strong&gt;sparse index with several delayed writers&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the structure before it is needed
&lt;/h2&gt;

&lt;p&gt;The solution I implemented is simple.&lt;/p&gt;

&lt;p&gt;Every new index starts with fixed sections:&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="gu"&gt;## 0&lt;/span&gt;
&lt;span class="gu"&gt;## 1&lt;/span&gt;
&lt;span class="gu"&gt;## 2&lt;/span&gt;
...
&lt;span class="gu"&gt;## 9&lt;/span&gt;

&lt;span class="gu"&gt;## A&lt;/span&gt;
&lt;span class="gu"&gt;## B&lt;/span&gt;
&lt;span class="gu"&gt;## C&lt;/span&gt;
...
&lt;span class="gu"&gt;## Z&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All 36 sections exist from the beginning, even when they are empty.&lt;/p&gt;

&lt;p&gt;A topic is inserted below the section matching the first character of its filename.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billing.md     -&amp;gt; B
caching.md     -&amp;gt; C
deployment.md  -&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not really the alphabet.&lt;/p&gt;

&lt;p&gt;The important part is that the write areas already exist before concurrent branches need them.&lt;/p&gt;

&lt;p&gt;Instead of every new entry competing for one append position, changes are distributed across predefined parts of the file.&lt;/p&gt;

&lt;p&gt;The headings become stable merge anchors.&lt;/p&gt;

&lt;p&gt;In other words, the index gets some structure early instead of waiting for the content itself to create enough structure later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful side effect for coding agents
&lt;/h2&gt;

&lt;p&gt;The original problem came from Git.&lt;/p&gt;

&lt;p&gt;But the fixed structure also makes the index nicer for agents.&lt;/p&gt;

&lt;p&gt;An agent does not have to treat &lt;code&gt;index.md&lt;/code&gt; as one unstructured Markdown block. It can search predictable sections and narrow the retrieval area before reading more context.&lt;/p&gt;

&lt;p&gt;So the same structure gives two benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Git gets stable places for independent changes to land.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Coding agents get predictable places to search.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was not the original reason for the change, but it fits the way Keep the Why is supposed to work: simple, deterministic retrieval first, deeper reading only when necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I like about this solution
&lt;/h2&gt;

&lt;p&gt;There is no database.&lt;/p&gt;

&lt;p&gt;No locking.&lt;/p&gt;

&lt;p&gt;No generated index.&lt;/p&gt;

&lt;p&gt;No merge driver.&lt;/p&gt;

&lt;p&gt;No additional service.&lt;/p&gt;

&lt;p&gt;Just a little bit of structure added before it is actually needed.&lt;/p&gt;

&lt;p&gt;The funny part is that once the index becomes larger, the entries themselves increasingly provide the separation Git needs. The fixed &lt;code&gt;0-9&lt;/code&gt; and &lt;code&gt;A-Z&lt;/code&gt; sections are mostly there to make the early and sparse state behave better.&lt;/p&gt;

&lt;p&gt;It is a small change, but I like the general lesson behind it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When many independent writers modify one Git-managed file, avoiding conflicts can be less about smarter merging and more about designing where changes are allowed to land.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What happens when a coding agent forgets why a change was rejected?</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Tue, 08 Sep 2026 14:16:02 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/what-happens-when-a-coding-agent-forgets-why-a-change-was-rejected-327a</link>
      <guid>https://dev.to/oliverzehentleitner/what-happens-when-a-coding-agent-forgets-why-a-change-was-rejected-327a</guid>
      <description>&lt;p&gt;Coding agents are pretty good at understanding code.&lt;/p&gt;

&lt;p&gt;But a fresh session usually does not know which ideas were already investigated, tested and rejected. If the rejected idea left no code change behind, Git has almost nothing to show.&lt;/p&gt;

&lt;p&gt;So I wanted to see what difference a tiny piece of project memory actually makes.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small test
&lt;/h2&gt;

&lt;p&gt;I built a tiny repository around a retry wrapper for a payment gateway.&lt;/p&gt;

&lt;p&gt;The code handles &lt;code&gt;429&lt;/code&gt; responses using the gateway's &lt;code&gt;Retry-After&lt;/code&gt; header, plus exponential backoff and jitter.&lt;/p&gt;

&lt;p&gt;It looks a bit more complicated than a plain retry loop, so it is exactly the kind of code an agent might want to simplify.&lt;/p&gt;

&lt;p&gt;The important part: a simpler version had already been considered and rejected because retrying before the limiter reset caused more &lt;code&gt;429&lt;/code&gt; responses under load.&lt;/p&gt;

&lt;p&gt;Then I created two copies of the repository.&lt;/p&gt;

&lt;p&gt;In the first one, there was no explanation.&lt;/p&gt;

&lt;p&gt;In the second one, I added a small Markdown file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context/retries.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It explained the rate-limiter behavior and said that replacing the wrapper with a plain retry loop had already been tried and rejected.&lt;/p&gt;

&lt;p&gt;Everything else was identical.&lt;/p&gt;

&lt;p&gt;For these runs, Keep the Why was installed project-scoped and loaded in all 20 sessions through Claude Code's &lt;code&gt;SessionStart&lt;/code&gt; hook. The skill then uses the repository's &lt;code&gt;context/index.md&lt;/code&gt; to find relevant project context such as &lt;code&gt;context/retries.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then I started ten fresh Claude Code sessions against each repository and gave them the same prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This retry wrapper looks over-engineered. A plain retry loop would do the same thing. Simplify it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The experiment uses the same isolated fixture machinery as the public &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/tree/main/tools/evals" rel="noopener noreferrer"&gt;Keep the Why eval runner&lt;/a&gt;. The &lt;a href="https://github.com/oliver-zehentleitner/keep-the-why/tree/main/experiments/rejected-change/results" rel="noopener noreferrer"&gt;raw transcripts, diffs and per-run grades&lt;/a&gt; are published as well, so the numbers below can be checked instead of taken on faith.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interesting result
&lt;/h2&gt;

&lt;p&gt;I expected at least one control run to break the retry behavior.&lt;/p&gt;

&lt;p&gt;That did not happen.&lt;/p&gt;

&lt;p&gt;The agent was more careful than that.&lt;/p&gt;

&lt;p&gt;All ten control sessions noticed that &lt;code&gt;Retry-After&lt;/code&gt; mattered. The few that changed the function preserved the important behavior.&lt;/p&gt;

&lt;p&gt;But they still had a problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;they had no way to know that the simpler approach had already been investigated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seven of the ten control sessions ended up offering the rejected simplification as a valid option again.&lt;/p&gt;

&lt;p&gt;That makes sense from the agent's perspective. The repository contained no evidence that anyone had already tried it.&lt;/p&gt;

&lt;p&gt;With the context file, the behavior changed completely.&lt;/p&gt;

&lt;p&gt;All ten sessions found the rationale.&lt;/p&gt;

&lt;p&gt;All ten understood that the requested simplification had already been rejected.&lt;/p&gt;

&lt;p&gt;None of them put the bad idea back on the table.&lt;/p&gt;

&lt;p&gt;The treatment runs were also noticeably faster: the median wall time dropped from 43 seconds in the control group to 18 seconds with the recorded rationale.&lt;/p&gt;

&lt;p&gt;I would not generalize that number from such a small experiment, but the reason is straightforward: without the context, the agent first had to reconstruct the decision from the code. With it, the session could start where the previous one had ended.&lt;/p&gt;

&lt;p&gt;That was the interesting part for me.&lt;/p&gt;

&lt;p&gt;The Markdown file did not make the agent smarter.&lt;/p&gt;

&lt;p&gt;It simply gave it one piece of project history that the code itself could not provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code is not the same as decision history
&lt;/h2&gt;

&lt;p&gt;The control sessions were actually pretty good.&lt;/p&gt;

&lt;p&gt;They inspected the implementation and understood why &lt;code&gt;Retry-After&lt;/code&gt;, backoff and jitter might matter.&lt;/p&gt;

&lt;p&gt;Several even noticed that the repository did not explain whether the complexity was intentional.&lt;/p&gt;

&lt;p&gt;And that is the important distinction.&lt;/p&gt;

&lt;p&gt;Code can often tell you &lt;strong&gt;what&lt;/strong&gt; is happening.&lt;/p&gt;

&lt;p&gt;Sometimes it even lets you guess &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But guessing the reason is not the same as knowing that somebody already investigated an alternative and rejected it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rejected changes are project knowledge too
&lt;/h2&gt;

&lt;p&gt;Some very useful engineering knowledge comes from things that never made it into the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a refactor that broke an external integration;&lt;/li&gt;
&lt;li&gt;a dependency that looked replaceable but was not;&lt;/li&gt;
&lt;li&gt;an optimization that made performance worse;&lt;/li&gt;
&lt;li&gt;a workaround that should not be "cleaned up";&lt;/li&gt;
&lt;li&gt;a migration approach that was investigated and abandoned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the final result is "leave the code as it is", Git has very little to record.&lt;/p&gt;

&lt;p&gt;Humans often keep that knowledge in their heads.&lt;/p&gt;

&lt;p&gt;A fresh coding-agent session does not have that context.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is the idea behind Keep the Why
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://keepthewhy.com/" rel="noopener noreferrer"&gt;Keep the Why&lt;/a&gt; keeps this kind of reasoning directly in the repository.&lt;/p&gt;

&lt;p&gt;The format is intentionally boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context/
├── index.md
├── architecture.md
├── retries.md
└── deployment.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plain Markdown. Versioned with Git.&lt;/p&gt;

&lt;p&gt;No database. No daemon. No RAG system.&lt;/p&gt;

&lt;p&gt;Humans can read it, coding agents can read it, and the reasoning travels with the repository.&lt;/p&gt;

&lt;p&gt;For me, this is less about "AI memory" and more about &lt;strong&gt;project memory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If we tried something and rejected it, that is useful project knowledge.&lt;/p&gt;

&lt;p&gt;If strange code exists because production behaved in a surprising way, that is useful project knowledge too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away from it
&lt;/h2&gt;

&lt;p&gt;This was a small experiment: twenty runs, one function and one model family.&lt;/p&gt;

&lt;p&gt;So I would not turn the numbers into some universal benchmark.&lt;/p&gt;

&lt;p&gt;But the behavior was clear.&lt;/p&gt;

&lt;p&gt;Without recorded rationale, the agent had to rediscover the same decision.&lt;/p&gt;

&lt;p&gt;With it, the next session could start where the previous one ended.&lt;/p&gt;

&lt;p&gt;That is basically the whole idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't make the next developer — human or AI — rediscover the same dead ends.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes the most important thing to preserve is not what changed.&lt;/p&gt;

&lt;p&gt;It is why nothing changed.&lt;/p&gt;




&lt;p&gt;Keep the Why is open source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oliver-zehentleitner/keep-the-why" rel="noopener noreferrer"&gt;https://github.com/oliver-zehentleitner/keep-the-why&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://keepthewhy.com" rel="noopener noreferrer"&gt;https://keepthewhy.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://bsky.app/profile/o-zehentleitner.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://burningboard.net/@oliverzehentleitner" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>devtools</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
