<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Artur]]></title><description><![CDATA[Artur]]></description><link>https://arturciocanu.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!tXiA!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdde14823-3d45-477e-a332-87081f913974_144x144.png</url><title>Artur</title><link>https://arturciocanu.substack.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 14 Jun 2026 11:15:31 GMT</lastBuildDate><atom:link href="https://arturciocanu.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Artur]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[arturciocanu@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[arturciocanu@substack.com]]></itunes:email><itunes:name><![CDATA[Artur]]></itunes:name></itunes:owner><itunes:author><![CDATA[Artur]]></itunes:author><googleplay:owner><![CDATA[arturciocanu@substack.com]]></googleplay:owner><googleplay:email><![CDATA[arturciocanu@substack.com]]></googleplay:email><googleplay:author><![CDATA[Artur]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Your agent’s memory might be discriminating. Would you even know?]]></title><description><![CDATA[Your agent&#8217;s memory classifies people, and classifying people is how discrimination hides. Librarians learned that fifty years ago &#8212; and how to stop it.]]></description><link>https://arturciocanu.substack.com/p/your-agents-memory-might-be-discriminating</link><guid isPermaLink="false">https://arturciocanu.substack.com/p/your-agents-memory-might-be-discriminating</guid><dc:creator><![CDATA[Artur]]></dc:creator><pubDate>Sat, 13 Jun 2026 23:20:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LHuv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In 1971 a cataloger named Sanford Berman published a book about a list of words, and the list turned out to have a conscience.</p><p>The list was the Library of Congress Subject Headings, the controlled vocabulary that sat under American librarianship the way wiring sits inside a wall. Nobody looked at it; that was rather the point. Berman looked. His book, <em>Prejudices and Antipathies</em>, made one specific charge: the headings encoded racism, sexism, and xenophobia, and they did it while wearing the uniform of neutral technical classification. Things filed under &#8220;PRIMITIVE&#8221; that were nothing of the sort. A subject heading felt like a fact about where a book belonged. It was a value judgment in disguise.</p><p>I build agents, and more recently I have been working on how they remember. The thing Berman caught the card catalog doing in 1971 is the thing I watch personalization systems do now. Same mechanism. Newer disguise.</p><p>This is the second half of an argument. The first half: persistent agent memory is, underneath the embeddings, a cataloging problem, and a competent team rebuilds library science without noticing it. That piece laid out a real architecture &#8212; a write path that normalizes signals into an authority-controlled vocabulary, a read path that queries it, a maintenance path that weeds it &#8212; and framed the payoff as data-protection compliance, the boring patterns turning out to be legal machinery. That was the easy half. This is the half about what happens when the cataloged thing is a person who can be discriminated against, where an older library problem switches on: not how to organize knowledge, but how classification harms the people being classified.</p><p>Keep that architecture in view, because nothing here is a fairness module bolted onto the side. The proxy lives in the same controlled vocabulary your agent already reads from and writes to. The protective tag is a field on the same record. The outcome monitor is one more job on the same maintenance path. This is not a second system sitting next to the memory. It is the memory system, asked to account for the one kind of record that can take you to court: a person.</p><p>So the answer comes in three parts, ranked by how much each one saves you. A reframe: proxy discrimination is an undeclared related-term edge. A mechanism: tag by what the system may do, not by what it inferred. A boundary: the catalog prevents, a monitor catches, and you need both.</p><h2><strong>When it switches on</strong></h2><p>Start with scope, because a lot of readers will otherwise decide this is someone else&#8217;s problem.</p><p>Discrimination is comparative. It cannot exist at n=1, because one person cannot be treated unfairly relative to a group that has no other members. So the inheritance switches on under exactly one condition: when a single operator&#8217;s classifications act on a population that can be sorted into protected classes. The trigger is the population. It is not &#8220;enterprise versus personal,&#8221; which is the axis everyone reaches for and the wrong one.</p><p>That matters, because &#8220;personal assistant&#8221; means two different things. Self-hosted, one user, one beneficiary: genuinely n=1, and the discrimination harm cannot form, though the privacy reasons to be careful all survive. A personal-assistant product, one vendor serving millions of &#8220;personal&#8221; instances, is the opposite animal: not n=1 at all, because the operator makes correlated decisions across a whole population while every session feels private from the inside. The per-user framing is camouflage, not exemption. Per-agent memory banks, the kind the last piece described, make the population invisible, not absent. And the case that flips the count entirely is the single-user assistant you point at other people, the one screening r&#233;sum&#233;s or ranking tenants: one user, and a protected population living inside its data. The risk attaches to whoever is being classified, not to how many people are typing.</p><h2><strong>A proxy is an undeclared related-term edge</strong></h2><p>This is the load-bearing idea. Take it if you take nothing else.</p><p>The agent is not dangerous because it stores protected attributes. Nobody competent stores race, health, or sexuality, and provenance keeps the inferences honest. It is dangerous because it acts on proxies. ZIP code stands in for race. A run of maternity-adjacent queries stands in for pregnancy. Browsing cadence stands in for disability. The system never names the attribute. It learns the correlation and acts on it anyway.</p><p>The law, where it has teeth, does not care that you never named it. US employment law under disparate impact is the cleanest case, and it is older than most of the engineers reading this. In 1971, the same year Berman published, the Supreme Court decided <em>Griggs v. Duke Power</em> and held that an employment practice &#8220;fair in form but discriminatory in operation&#8221; is unlawful under Title VII, intent or no intent. Effect over intent is not a modern AI-ethics talking point. It has been black-letter law since the year of the card-catalog reckoning. So &#8220;we apply the same model to every user&#8221; is not a defense; a neutral rule that lands differently on a protected class is the definition of disparate impact, not an escape from it. Engineers hear &#8220;same model for everyone&#8221; as fairness. The law hears it as the mechanism.</p><p>Structurally, a proxy is a relationship between two concepts, and relationships between concepts are the oldest furniture in library science. A controlled vocabulary relates its terms through explicit links: a broader term, a narrower term, and the dangerous one, the related term, the &#8220;see also&#8221; edge asserting that two concepts travel together. &#8220;ZIP code is associated with race&#8221; is a related-term edge. Your whole map of proxy risk is a set of those edges, sitting in a thesaurus nobody wrote down. Embedding similarity is a weak, implicit version of that thesaurus, and the gap between implicit and declared is where the liability lives.</p><p>That thesaurus is not hypothetical, and this is the part that connects back to the first piece. It is the authority-controlled vocabulary the memory system already maintains &#8212; the one the write path normalizes into and the read path queries. The proxy edge belongs there, a first-class relation sitting beside the concepts the agent already stores, governed the same way every other term is governed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LHuv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LHuv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 424w, https://substackcdn.com/image/fetch/$s_!LHuv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 848w, https://substackcdn.com/image/fetch/$s_!LHuv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 1272w, https://substackcdn.com/image/fetch/$s_!LHuv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LHuv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png" width="920" height="560" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/530da687-5745-4cbe-9e01-5988e95f495e_920x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:560,&quot;width&quot;:920,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The link your agent&#8217;s memory never declared&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The link your agent&#8217;s memory never declared" title="The link your agent&#8217;s memory never declared" srcset="https://substackcdn.com/image/fetch/$s_!LHuv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 424w, https://substackcdn.com/image/fetch/$s_!LHuv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 848w, https://substackcdn.com/image/fetch/$s_!LHuv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 1272w, https://substackcdn.com/image/fetch/$s_!LHuv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F530da687-5745-4cbe-9e01-5988e95f495e_920x560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>The link your agent&#8217;s memory never declared</em></figcaption></figure></div><p>The fix follows directly. Declare the edges. Make each dangerous association a first-class object with a provenance and an admitting authority, the way Berman&#8217;s successors eventually forced the Library of Congress to make its headings explicit and contestable. One instinct says detect the correlation after the fact. The other says declare and govern it up front. Only the second produces a record you can put in front of a regulator, because a correlation buried in an embedding is bias hiding behind technical neutrality. That is the whole problem, stated once.</p><h2><strong>Tag by use, not by attribute</strong></h2><p>The reflex is to tag sensitive memories by content: <code>inferred: pregnant</code>. That manufactures the liability. Under data-protection law, classifying a record as health-related is the act that creates regulated health data. The tag you added to be careful becomes the regulated artifact, and it is also a stored guess about a person, wrong some honest fraction of the time. You reached for the safety equipment and picked up the live wire.</p><p>Warrant is the library concept for why a term earns its place in a vocabulary. Literary warrant admits a term because the material is about it. Use warrant admits it because a use has to be supported or governed. Flip from literary to use warrant and the liability inverts. Tag the memory not by the attribute it reveals, which is a fragile claim about a person, but by what the system may do with it, which is a policy verb about behavior. Not <code>pregnant</code>, but <code>no_price_influence</code>. Mechanically this is the normalize-and-appraise step of the write path doing one extra job: before a memory is committed, decide not just what it is but what may be done with it. The protected-class reasoning still happens, once, transiently, in the layer that sets the restriction, and then it is discarded. What persists is the permission, not the assertion.</p><p>A permission is accuracy-tolerant, and that is the point. You do not have to be right that she is pregnant. You only have to be right that this evidence is sensitive-adjacent and must not move the price. For proxies, where the guess is usually wrong, that tolerance is the whole value. For the categories that touch money or access, go further and do not form the belief at all: never-form there, form-and-restrict elsewhere, cut by consequence. One tension survives, and I would rather name it than paper over it. A regulator asking &#8220;why was this restricted?&#8221; needs a real answer, so you cannot fully minimize. You keep a deliberately coarse reason, <code>restricted_reason: sensitive_adjacent</code>, never <code>pregnant</code>: fine enough to answer the question, too blunt to reconstruct the person from it.</p><h2><strong>The temporal part, and why it isn&#8217;t decay</strong></h2><p>A reader from the cognitive-science tradition has probably been waiting for memory decay by now &#8212; a forgetting curve, an exponential half-life, weights that fade so old facts quietly lose their grip. The first piece argued that decay is the wrong primitive for agent knowledge: human memory is lossy by design, and agent knowledge should be versioned instead. For discrimination the argument gets sharper, because three different things change on three different clocks, and a single decay rate models none of them.</p><p>Proxy edges drift. A correlation in your traffic is not stationary; ZIP-to-race can hold this quarter and weaken the next, so the edge carries temporal validity, a <code>valid_from</code> and a <code>valid_to</code>, and the monitor re-runs on a schedule to retire edges that stop holding. Decay would only lower a weight. Versioning lets you say when the edge was true, and prove it.</p><p>Sensitive restrictions expire; they do not fade. A pregnancy-adjacent restriction has a window, and when the window closes you deaccession the inference deliberately, on a clock, with an audit trail. That is the weeding move from the first piece pointed at sensitive data. A decayed weight that quietly stops restricting is the worst of both worlds: still stored, no longer protecting.</p><p>Retention is a legal clock, not a curve. Right-to-erasure and retention schedules demand provable deletion on a date, not a probability that a memory is unlikely to resurface. The versioned <code>valid_to</code> from the first piece is what makes that enforceable; a forgetting curve is not. So the temporal model here is a calendar, not a curve. You version, you schedule, you deaccession, and every one of those is an event you can show someone &#8212; which is the entire reason you are doing this instead of letting an embedding forget on its own.</p><h2><strong>Why it is a legal inheritance</strong></h2><p>You could read all of this as borrowed vocabulary, and a sceptic would be right to try. The strong claim is about law, and it rests on the book I opened with. Berman was not a fringe complaint that went nowhere. He became a reform movement that ran for decades, and in 2002 Hope Olson&#8217;s <em>The Power to Name</em> turned the grievance into a theory: classification marginalizes through the architecture of how categories get formed. The finding that should stop an engineer cold is this. A subject heading feels like a fact about the world. It is actually an exercise of power wearing the costume of a fact.</p><p>A learned proxy is the same maneuver in a newer medium. It looks like math, it feels inevitable, and it hands out different treatment along protected lines while insisting it is only optimizing. Which is why the controls regulators now demand for AI map nearly line for line onto the controls the library world already built. Authority control with an admitting authority is documented ownership of who may decide a category. The subject-heading change process, where altering a heading requires a written case that the current term is wrong or harmful, is the mechanism for contesting an automated decision that the law now obliges you to provide. The descriptive-versus-subject split is the evidence-versus-inference boundary behind explainability. None of that overlap is coincidence. Both fields are trying to stop the same wrong.</p><h2><strong>The catalog can&#8217;t watch its own outcomes</strong></h2><p>Everything above is preventive, and preventive controls catch only what you declared. The proxy that gets you is the one nobody enumerated. Worse, the model mints fresh proxies turn by turn, faster than any vocabulary committee could meet. Berman&#8217;s collection arrived through the front door at the pace of acquisitions and did not fight back. Yours does. No cataloger ever had to defend a collection that grows new biased categories while everyone sleeps.</p><p>So you add an empirical monitor, another job on the maintenance path beside the weeding. Measure decisions across cohorts. Watch for outcomes diverging along protected lines. Feed what you find back into the vocabulary as a newly declared edge. The catalog tells you what you know. The monitor tells you what you missed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gzgl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gzgl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 424w, https://substackcdn.com/image/fetch/$s_!gzgl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 848w, https://substackcdn.com/image/fetch/$s_!gzgl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 1272w, https://substackcdn.com/image/fetch/$s_!gzgl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gzgl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png" width="920" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:920,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two layers stop your agent&#8217;s memory from discriminating&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two layers stop your agent&#8217;s memory from discriminating" title="Two layers stop your agent&#8217;s memory from discriminating" srcset="https://substackcdn.com/image/fetch/$s_!gzgl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 424w, https://substackcdn.com/image/fetch/$s_!gzgl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 848w, https://substackcdn.com/image/fetch/$s_!gzgl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 1272w, https://substackcdn.com/image/fetch/$s_!gzgl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7c3dc6-0c54-4a0b-9868-73cf0630df2d_920x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Two layers stop your agent&#8217;s memory from discriminating</em></figcaption></figure></div><p>Two things make it hard, and neither is in any library textbook. The first: it is a statistics problem wearing a compliance costume. The four-fifths rule everyone reaches for is a screening heuristic, not a test, unstable at small samples and blind to significance, and there is a 2024 paper out of the fairness community titled, almost too neatly, &#8220;The four-fifths rule is not disparate impact.&#8221; Treat the heuristic as the test and you hand yourself confident wrong answers in both directions. The second is nastier: measuring discrimination needs the very attribute you refused to store. That forces a firewall, where the cohort signal lives transiently, in a separate enclave, aggregate-only, never joined back to a profile. Get it wrong and the monitor you built to reduce liability becomes a fresh pile of special-category data. The tail eats itself, and this smallest box on the diagram carries the most risk.</p><p>Here the scope note pays off. The monitor is the layer that needs a population. A true n=1 assistant can drop it and keep everything before it, running the catalog for privacy and accuracy alone. The personal-assistant product, with its millions of &#8220;personal&#8221; sessions, needs all of it.</p><h2><strong>The actual thesis</strong></h2><p>A capable team rebuilds the bones of library science whether or not it has heard of them, because persistent memory is a cataloging problem and cataloging problems come pre-shaped. The expensive knowledge is not the data structure. It is the knowledge of how classification harms the classified while disguising itself as fact, and it cost the library world fifty public years to buy.</p><p>When you find a new proxy, you do not quietly patch a model. You admit a term, version the vocabulary, and re-evaluate what was filed under the old one &#8212; the same write, the same maintenance path, the same memory system from the first piece, now doing the one job that keeps it out of court. The library world calls it authority work. You will call it compliance. Berman would recognize it on sight, because it is the work he gave his life to. You do not have to repeat the fifty years. That is the entire point of a discipline. The cataloger in the machine has been waiting for you, and she already knows where the bodies are buried, because she is the one who dug them up.</p><div><hr></div><p><em>Notes on scope: this essay treats anti-discrimination and data-protection concepts as design drivers and historical parallels, not as legal advice. Which regimes bite in which domains, the current state of AI-specific obligations, and the US state-versus-federal picture are genuinely in flux and should be checked against current sources and counsel before you rely on any of it. The structural argument is stable. The regulatory particulars are not.</em></p><div><hr></div><h2><strong>References</strong></h2><ol><li><p><strong>Artur Ciocanu</strong> &#8212; <em><a href="https://arturciocanu.substack.com/p/your-agents-memory-problem-is-an">Your agent&#8217;s memory problem is an information architecture problem</a></em> &#8212; the first half of this argument: the write/read/maintenance architecture, authority control, appraisal, and weeding.</p></li><li><p><strong>Sanford Berman</strong> (1971), <em>Prejudices and Antipathies: A Tract on the LC Subject Heads Concerning People</em>. Scarecrow Press. <a href="https://archive.org/details/prejudicesantipa0000berm">Internet Archive</a>.</p></li><li><p><strong>Hope A. Olson</strong> (2002), <em>The Power to Name: Locating the Limits of Subject Representation in Libraries</em>. Kluwer Academic. <a href="https://books.google.com/books/about/The_Power_to_Name.html?id=Af05TXLoUo4C">Overview</a>.</p></li><li><p><em>Griggs v. Duke Power Co.</em>, 401 U.S. 424 (1971) &#8212; the origin of disparate-impact doctrine under Title VII; &#8220;fair in form but discriminatory in operation.&#8221; <a href="https://supreme.justia.com/cases/federal/us/401/424/">Justia</a>.</p></li><li><p><strong>EEOC</strong> (1978), <em><a href="https://www.ecfr.gov/current/title-29/subtitle-B/chapter-XIV/part-1607">Uniform Guidelines on Employee Selection Procedures</a></em> (29 CFR Part 1607) &#8212; the source of the four-fifths / 80% rule.</p></li><li><p><strong>Watkins, E. A., et al.</strong> (2024), <em><a href="https://dl.acm.org/doi/10.1145/3630106.3658938">The four-fifths rule is not disparate impact: a woeful tale of epistemic trespassing in algorithmic fairness</a></em>, ACM FAccT &#8217;24 (<a href="https://arxiv.org/abs/2202.09519">arXiv:2202.09519</a>).</p></li><li><p><strong>Warrant theory</strong> &#8212; Mario Barit&#233;, <em><a href="https://www.isko.org/cyclo/literary_warrant">Literary warrant</a></em>, ISKO Encyclopedia of Knowledge Organization; the concept originates with E. Wyndham Hulme (1911), and the literary-vs-use distinction is what this essay leans on.</p></li><li><p><strong>T. R. Schellenberg</strong> (1956), <em><a href="https://www.archives.gov/research/alic/reference/archives-resources/appraisal-informational-values.html">The Appraisal of Modern Public Records</a></em> &#8212; archival appraisal, the basis for the &#8220;never-form&#8221; decision.</p></li><li><p><strong>Texas State Library</strong>, <em><a href="https://www.tsl.texas.gov/ld/pubs/crew/">CREW: A Weeding Manual for Modern Libraries</a></em> &#8212; the MUSTIE weeding criteria behind deliberate, scheduled deaccession.</p></li></ol>]]></content:encoded></item><item><title><![CDATA[Your agent’s memory problem is an information architecture problem]]></title><description><![CDATA[Why the persistent knowledge layer your agents need comes from librarians, ontologists, and database engineers &#8212; not from cognitive science or computer science alone]]></description><link>https://arturciocanu.substack.com/p/your-agents-memory-problem-is-an</link><guid isPermaLink="false">https://arturciocanu.substack.com/p/your-agents-memory-problem-is-an</guid><dc:creator><![CDATA[Artur]]></dc:creator><pubDate>Tue, 26 May 2026 20:00:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dMOO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been building agent systems for a while now, and I have been thinking about memory wrong. Not because I didn&#8217;t care about it &#8212; I did &#8212; but because I was reaching for the wrong mental models. I suspect most of the industry is making the same mistake, and the purpose of this essay is to trace how I arrived at that suspicion and what I found when I followed it.</p><p>The starting point was a simple observation: every agent framework ships a memory module, and almost all of them are thin wrappers around vector stores. Embed, index, retrieve by similarity. The consensus is that RAG solved retrieval, and retrieval solved memory. For demos, this holds. For anything that needs to persist knowledge across sessions &#8212; actually <em>know</em> things, detect contradictions, decide what to keep and what to discard &#8212; the consensus falls apart quickly.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://arturciocanu.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Here is one way to see the crack. &#8220;Hybrid search&#8221; is now standard practice across the vector database ecosystem. Pinecone, Weaviate, Qdrant &#8212; they all combine semantic similarity with BM25 keyword matching. That combination gets marketed as innovation, but think about what the admission actually means: pure similarity wasn&#8217;t enough, so they bolted on a technique from the 1990s. If your cutting-edge retrieval system needs a thirty-year-old algorithm as a crutch, maybe similarity was never the right primitive for knowledge in the first place.</p><p>Retrieval is not memory. Similarity is not meaning. Cosine distance is not knowledge.</p><p>That observation sent me down a path I didn&#8217;t expect. The path led through computer science and cognitive science &#8212; the two disciplines the industry reaches for when thinking about agent memory &#8212; and then, surprisingly, out the other side into Library Science, Information Science, and Knowledge Engineering. This essay traces that path.</p><p>I should say upfront: this thesis was shaped by the work of two people I want to credit explicitly. <a href="https://jessicatalisman.substack.com">Jessica Talisman</a> has been arguing from the Library Science side that enterprises outsourced knowledge work and now lack the infrastructure AI needs. <a href="https://ontologist.substack.com">Kurt Cagle</a> has been making the case from ontology engineering that agents maintaining state are making ontological commitments, and most do it accidentally. Both were saying this before it was fashionable.</p><h2><strong>What computer science actually gives us</strong></h2><p>Let me start with what CS gets right, because the critique only works if the credit is honest.</p><p>Computer science gave us B-trees, hash maps, LSM trees, vector indices, transaction isolation, query optimization. These are real contributions &#8212; load-bearing infrastructure that nobody is building agent memory without. The question is not whether CS matters. It does. The question is whether the primitives CS provides are sufficient for the problem of persistent agent knowledge. I think the answer is no.</p><p>The most interesting recent work from the CS camp borrows from operating systems. <a href="https://www.letta.com">Letta</a> (formerly MemGPT &#8212; the paper is literally titled <em><a href="https://arxiv.org/abs/2310.08560">&#8220;MemGPT: Towards LLMs as Operating Systems&#8221;</a></em>) treats the context window as virtual memory with two tiers: core memory that the agent can edit during conversations, and archival memory that is searchable but out of context. The agent pages between them using tools like <code>memory_insert</code>, <code>memory_replace</code>, and <code>archival_memory_search</code>.</p><p>Letta&#8217;s self-editing memory is genuinely clever &#8212; it gives agents agency over their own context. The agent decides what to remember, what to update, what to search for. That is real innovation.</p><p>But here is what kept nagging me. Virtual memory manages <em>space</em>. It answers &#8220;what fits in the window right now?&#8221; using access recency and frequency. It does not answer &#8220;is this fact still true?&#8221; or &#8220;does this contradict something else I know?&#8221; or &#8220;where did this come from, and how much should I trust it?&#8221;</p><p>LRU eviction doesn&#8217;t know that a user-stated budget constraint is more important than an agent-inferred style preference. Both are pages. One is load-bearing. The other is speculative. An eviction policy that treats them identically will eventually evict the wrong one.</p><p>There is no controlled vocabulary to normalize concepts &#8212; &#8220;dark mode&#8221; and &#8220;night mode&#8221; may live as two separate entries. There is no provenance hierarchy to distinguish user-stated facts from agent inferences. There is no appraisal system to evaluate whether a fact is worth keeping based on uniqueness, actionability, or sensitivity. If contradictory facts end up in archival memory, there is no mechanism to detect the contradiction. There is no principled strategy for what should be discarded and why.</p><p>These OS primitives are excellent low-level building blocks. They&#8217;d work <em>better</em> with a proper vocabulary layer, provenance hierarchy, and appraisal system feeding the agent&#8217;s decisions about what to keep in core memory. The problem is not that Letta exists. The problem is treating context window management as the entire memory architecture when it is one layer of a larger system.</p><p>Someone might reasonably object: &#8220;But databases have schemas, and schemas impose structure.&#8221; Fair point. A schema describes the <em>shape</em> of data, not its <em>meaning</em>. A <code>memories</code> table with <code>content</code>, <code>embedding</code>, and <code>timestamp</code> tells you nothing about whether those memories are facts, preferences, constraints, or contradictions. That distinction matters, and CS doesn&#8217;t make it. Shape without semantics is a filing cabinet without labels.</p><p>The more I sat with this, the clearer it became: CS provides containers. It tells you how to store and retrieve data efficiently. It doesn&#8217;t tell you what the data means, how it relates to other data, or how someone will need to find it in a context you can&#8217;t predict at design time. These are different problems. The first is engineering. The second is something I didn&#8217;t have a name for yet.</p><h2><strong>What cognitive science offered (and where it went wrong)</strong></h2><p>The second discipline the industry reaches for is cognitive science. Endel Tulving&#8217;s <a href="https://alicekim.ca/EMSM72.pdf">1972 taxonomy</a> &#8212; episodic versus semantic memory &#8212; was a genuine breakthrough in understanding human cognition. The AI community borrowed it wholesale: agents need &#8220;episodic memory&#8221; for experiences, &#8220;semantic memory&#8221; for facts, &#8220;procedural memory&#8221; for skills. The taxonomy gave teams a vocabulary and the vocabulary felt like a design.</p><p><a href="https://mem0.ai">Mem0</a> is the most prominent example. Its documentation explicitly uses the CogSci taxonomy &#8212; &#8220;semantic (facts), episodic (interactions), and procedural (styles) memory.&#8221; Under the hood, an LLM extracts &#8220;memories&#8221; from conversations, stores them as text with vector embeddings, and retrieves by semantic similarity.</p><p>What is instructive is how Mem0 has evolved. V1 gave the LLM four operations &#8212; ADD, UPDATE, DELETE, NOOP &#8212; so it could, in theory, detect conflicts and update existing memories. In practice, this was entirely LLM-mediated: it worked when the model noticed a contradiction, and silently failed when it didn&#8217;t.</p><p>Mem0 v3, released in 2026, made a deliberate architectural choice: drop UPDATE and DELETE entirely. ADD-only. Store everything, resolve contradictions at retrieval through ranking. From their migration docs: <em>&#8220;When information changes (e.g., a user moves from New York to San Francisco), both facts are preserved with temporal context.&#8221;</em> The community pushed back. GitHub issue <a href="https://github.com/mem0ai/mem0/issues/4896">#4896</a> documented the failure (&#8220;my name is Alice&#8221; followed by &#8220;my name is Bob&#8221; yields two stored facts, both retrieved with similar scores). Issue <a href="https://github.com/mem0ai/mem0/issues/4904">#4904</a> proposed a concrete fix with a full TDD plan to reintroduce the UPDATE path via cosine similarity. Both were declined. The resolution pressure didn&#8217;t disappear &#8212; it migrated to the skills layer, where <code>memory_update</code> now handles in-place edits &#8212; but at the core extraction level, ADD-only stands.</p><p>To be fair about what v3 improved: entity linking across memories, hybrid retrieval combining semantic, keyword, and entity signals, temporal reasoning for time-aware queries, and strong benchmark results (91.6 on LoCoMo, 93.4 on LongMemEval). These are real improvements, and the engineering is solid.</p><p>But the core philosophical bet is now explicit: <strong>store everything, resolve at retrieval.</strong> I will get to why I think this is backwards shortly. For now, note the trajectory: v1 delegated conflict resolution to the LLM (probabilistic), v3 abandoned write-time resolution entirely. The direction is toward <em>less</em> structure at write time, not more.</p><p>I kept turning this over. The deeper problem is that the mapping from human memory to agent memory is structurally wrong because the design requirements are opposite. Human memory is reconstructive &#8212; we rebuild narratives from fragments. Agent knowledge should be authoritative &#8212; the stored fact should be the fact. Human memory is lossy by design &#8212; forgetting enables generalization. Agent knowledge should be versioned &#8212; old values archived, not lost. Human memory is subjective &#8212; the same event is remembered differently by different people. Agent knowledge should be consistent &#8212; the same query should return the same fact. Human memory tolerates contradiction. Agent knowledge must detect and resolve conflicts.</p><p>Borrowing a taxonomy designed to describe a lossy reconstructive system and using it as a blueprint for a system that needs to be precise and reliable &#8212; that is not interdisciplinary thinking. That is anthropomorphization dressed up as architecture.</p><p>The CogSci labels gave teams a way to <em>name</em> their modules (&#8220;let&#8217;s build the episodic memory component&#8221;) without giving them a methodology for deciding what knowledge to persist, how to structure it, how to maintain it over time, or how to handle when two facts contradict. The labels created the illusion of having a design when what they had was a metaphor. Mem0&#8217;s evolution illustrates this: v1 delegated conflict resolution to the LLM (probabilistic), v3 abandoned write-time resolution entirely &#8212; a trajectory that moves further <em>away</em> from principled knowledge management, not toward it.</p><p>This diagnosis didn&#8217;t originate with me. Jessica Talisman has been arguing from the Library Science side &#8212; that enterprises underinvested in the knowledge infrastructure that AI needs to function reliably. Her core concept of <em>intentional arrangement</em> &#8212; deliberately deciding how knowledge should be classified, related, and retrieved &#8212; stands in direct contrast to the &#8220;embed everything and search&#8221; approach. Kurt Cagle has been making the case from Ontology Engineering &#8212; that every agent maintaining state is making ontological commitments, and most do it accidentally, in JSON blobs.</p><h2><strong>The turn I didn&#8217;t expect</strong></h2><p>If CS gives you containers without content architecture, and CogSci gives you labels without methodology, where do you find both?</p><p>The answer, once I found it, felt almost embarrassingly obvious. The discipline that has been solving the problem of &#8220;how do you classify, organize, relate, store, and retrieve knowledge so that someone can find what they need in a context you can&#8217;t predict&#8221; &#8212; for over a century &#8212; is Library Science. And its adjacent fields: Information Science, Knowledge Engineering, Ontology Engineering.</p><p>But first, a reframe that changes everything.</p><p>An agentic memory system is not a brain simulator. It is a <strong>Customer Data Platform where the channels are agents and the signals include natural language.</strong> The agent doesn&#8217;t have &#8220;a memory.&#8221; The <em>user</em> has a profile. Agents are channels that read from and write signals to it. This replaces cognitive metaphors with data engineering patterns that have been battle-tested for decades: identity resolution, signal hierarchies, golden records, traits versus events, computed attributes.</p><p>One clarification worth making explicit: this article addresses one specific layer &#8212; the <strong>persistent knowledge profile for users</strong>. What the system knows about the user across sessions, how it&#8217;s structured, how it&#8217;s maintained, how it&#8217;s retrieved. There is a separate and genuinely interesting question about agent identity &#8212; giving agents a consistent reasoning style, evolving beliefs, and disposition parameters that shape how they interpret facts. Hindsight&#8217;s CARA component tackles this with configurable skepticism, literalism, and empathy dimensions. For multi-tenant agent systems where different agents need different reasoning personalities over the same user knowledge, that&#8217;s a real problem worth solving. But these are complementary layers. This article is about the first one.</p><h2><strong>The disciplines we should have been reading</strong></h2><h3>Library Science &#8212; intentional arrangement</h3><p>Talisman&#8217;s core concept. Library and Information Science organizes knowledge through <em>intentional arrangement</em> &#8212; deliberately deciding how knowledge should be classified, related, and retrieved. Not metadata-as-afterthought. Metadata-as-architecture.</p><p>What it contributes to agent memory:</p><p><strong>Archival appraisal</strong> (Schellenberg, 1956) is value judgment at write time. Not &#8220;store everything and search later&#8221; &#8212; decide at ingestion whether something is worth keeping, based on uniqueness, evidential value, and actionability. A fact like &#8220;I have a severe peanut allergy&#8221; scores differently from &#8220;show me the blue one.&#8221; The system should know that at write time, not discover it during retrieval.</p><p><strong>CREW/MUSTIE weeding</strong> provides systematic criteria for what to discard &#8212; Misleading, Ugly, Superseded, Trivial, Irrelevant, Elsewhere. Agents need to forget deliberately, not through cache eviction. LRU is not a knowledge management strategy.</p><p><strong>Faceted classification</strong> (Ranganathan, 1933) offers multi-dimensional classification composed from independent facets, not pre-enumerated categories. Domain concepts multiplied by value types multiplied by provenance levels &#8212; composable, not combinatorial. An agent&#8217;s working vocabulary about one user is small (30 to 300 concepts per domain), not the 400K headings of the Library of Congress.</p><p><strong>Authority control</strong> ensures concept normalization through controlled vocabulary. Without it, &#8220;dark mode,&#8221; &#8220;night mode,&#8221; and &#8220;dark theme&#8221; are three different memories instead of one concept with three surface forms. With it, they all resolve to a single canonical concept, and deduplication is exact, not probabilistic.</p><p><strong>The Reference Interview</strong> (Taylor, 1968) models the gap between the stated question and the actual need. When an agent asks &#8220;what do I know about this user?&#8221; it needs a structured retrieval spec, not a vector similarity search. Taylor identified four levels of need &#8212; visceral, conscious, formalized, compromised &#8212; and the formalization step is exactly what a read path should perform.</p><h3>Knowledge and ontology engineering</h3><p>This is Cagle&#8217;s territory. Every agent that maintains state makes <em>ontological commitments</em> &#8212; what exists in its domain, what properties those things have, what relationships connect them. Most agents do this accidentally, in ad-hoc key-value pairs and JSON blobs. What happens when you do it intentionally: you get a vocabulary layer with hierarchical concepts, scope notes, synonym mappings, and lifecycle management.</p><p>Cagle&#8217;s persistent point: knowledge graphs are mature infrastructure, not hype. They are one of the older data structures in computing. And they are what LLMs actually need underneath &#8212; not as a replacement for the LLM, but as the structured knowledge layer the LLM reads from and writes to.</p><h3>Data management &#8212; the boring brilliance</h3><p>The patterns that make persistent knowledge reliable:</p><p><strong>SCD Type 2 temporal versioning</strong> preserves full history with zero information loss. When a user&#8217;s budget changes from $300 to $500, the old value is not deleted &#8212; it gets a <code>valid_to</code> timestamp. Any previous state is recoverable.</p><p><strong>Cascade invalidation via foreign keys</strong> means when a parent fact changes, derived facts are marked for re-evaluation automatically. If a computed trait (&#8220;prefers minimalist style&#8221;) was derived from three rejection events and those events are reassessed, the derived trait gets flagged.</p><p><strong>Provenance-weighted retrieval</strong> ensures user-stated facts at 1.0 always outrank agent-inferred facts at 0.6. The signal hierarchy &#8212; user_declared, agent_observed, tool_returned, agent_inferred, computed &#8212; determines trust, not recency.</p><p><strong>UPSERT semantics</strong> combined with controlled vocabulary make deduplication exact. No near-duplicate detection, no probabilistic matching.</p><p><strong>Constraints and conflict detection at write time</strong> catch two contradictory facts on the same concept at the database layer, not during the agent&#8217;s mid-conversation reasoning.</p><p>Agent memory systems have all the data management problems that databases solved decades ago &#8212; and ignore all the solutions because &#8220;we&#8217;re doing AI, not database work.&#8221;</p><p><em>The write path, read path, and maintenance path. Most steps are deterministic. The LLM classifies within a framework &#8212; it doesn&#8217;t architect freeform memories.</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2E_W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2E_W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 424w, https://substackcdn.com/image/fetch/$s_!2E_W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 848w, https://substackcdn.com/image/fetch/$s_!2E_W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!2E_W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2E_W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png" width="1456" height="950" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:950,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The Three Flows of Persistent Agent Knowledge&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The Three Flows of Persistent Agent Knowledge" title="The Three Flows of Persistent Agent Knowledge" srcset="https://substackcdn.com/image/fetch/$s_!2E_W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 424w, https://substackcdn.com/image/fetch/$s_!2E_W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 848w, https://substackcdn.com/image/fetch/$s_!2E_W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!2E_W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf83792b-e673-4f5e-94c3-53a2e5086931_1840x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>The three flows</strong></h2><p>How these principles translate into architecture. I want to stay at the principle level &#8212; not a specific database, but the general direction.</p><h3>The write path</h3><p>Three steps. First, <strong>detect</strong> candidate signals &#8212; rule-based, no LLM, cheap. Pattern matching identifies preference statements, corrections, constraints, goals. High recall, low precision &#8212; it is cheap to over-detect because the next step filters.</p><p>Second, <strong>normalize, appraise, and conflict-check</strong> &#8212; one structured LLM call acting as a librarian. Normalize the input to a controlled vocabulary (authority control). Extract a canonical value while preserving the original utterance &#8212; the user said &#8220;nothing over five hundred,&#8221; the system stores &#8220;Maximum budget: $500,&#8221; and both are preserved. Appraise on five dimensions: uniqueness, replaceability, actionability, stability, sensitivity. Check for conflicts with existing facts on the same concept.</p><p>Third, <strong>deterministic write</strong> &#8212; UPSERT for traits, APPEND for events. The schema enforces structure. No LLM in the write step.</p><p>The UPSERT/APPEND distinction deserves a closer look, because it&#8217;s where the thesis becomes concrete. When a new value arrives for the same concept as an existing value, is the old value now <em>false</em> or now <em>historical</em>? &#8220;My name is Alice&#8221; followed by &#8220;my name is Bob&#8221; &#8212; the old value is false. A person has one current name. Both stored means retrieval poisoning. &#8220;I live in New York&#8221; followed by &#8220;I moved to San Francisco&#8221; &#8212; the old value is historical. Both are true, time-scoped. Both stored means correct temporal reasoning.</p><p>Mem0&#8217;s ADD-only approach doesn&#8217;t model this distinction. It appends always. It&#8217;s right by luck on the location case and wrong by luck on the name case &#8212; and they market the case where the bug looks like a feature (&#8220;both facts preserved with temporal context&#8221;). An architecture with a vocabulary layer decides on purpose, per concept, at write time: supersede-with-history (new value current, old gets <code>valid_to</code>, both recoverable) or append-only (every value permanently true). The LLM never makes the resolution decision. It makes a classification (which concept), and resolution is a deterministic property of where the fact was filed.</p><p>The vocabulary carries this temporal semantics per concept. At least three classes: mutate-in-place (typo corrections, scratch values), supersede-with-history (name, budget, address &#8212; SCD Type 2), and append-only event stream (purchases, interactions, rejections). The vocabulary isn&#8217;t frozen at design time either. It grows through a governed lifecycle: LLM proposes candidate concepts, a review process admits or maps them, and a baking period accumulates evidence (frequency, observed cardinality, synonym collapse) before promotion. This is how library authority files have always worked &#8212; LC&#8217;s SACO program is exactly a propose-review-admit pipeline for new headings. The governance is the part libraries spent a century building.</p><p>The LLM&#8217;s role here is classifier and cataloger, not reasoner. Classification-grade, not reasoning-grade. You don&#8217;t need a frontier model for the memory subsystem. You need reliable structured output and a good rubric.</p><p>A careful reader will notice I just criticized Mem0 for relying on LLM-mediated conflict detection &#8212; and then proposed an architecture that also relies on an LLM during ingestion. That tension deserves to be named, not hidden.</p><p>The difference, I would argue, is structural. The LLM operates as a classifier within an explicit framework &#8212; a bounded vocabulary of 50 to 200 concepts, an appraisal rubric with defined dimensions, existing facts injected as comparison context. The framework constrains the LLM&#8217;s judgment; database constraints enforce the output after. The LLM proposes; the schema enforces.</p><p>But the weakness is real. The LLM can mis-classify, mis-appraise, or miss conflicts. The difference from Mem0 isn&#8217;t &#8220;LLM versus no LLM&#8221; &#8212; it&#8217;s &#8220;uncertainty observable versus uncertainty invisible.&#8221; When the LLM-as-librarian can&#8217;t confidently classify a signal, that failure is typed: a low-confidence classification (plausible concept exists, LLM isn&#8217;t sure) routes to adjudication against the existing vocabulary. An out-of-vocabulary signal (no concept exists) routes to the promotion pipeline as evidence the vocabulary is incomplete. Both go to a dead-letter queue where they&#8217;re visible, measurable, and drainable. Mem0&#8217;s Alice/Bob contradiction doesn&#8217;t error, flag, or queue &#8212; it succeeds wrongly. ADD-only with MD5 dedup has no place to admit something didn&#8217;t classify cleanly, so it doesn&#8217;t. A DLQ that nobody drains is slow data loss, not zero data loss. The true claim is visibility, not perfection &#8212; but visibility is the precondition for fixing.</p><p>In Library Science, this curation was done by trained professionals who understood classification theory, authority control, and their specific domain. No modern library has a human catalog every item from scratch &#8212; they use automated classification, vendor-supplied records, copy cataloging &#8212; but always within a framework of authority files and classification schemes. The LLM-as-librarian is the next step in that trajectory. It is a bet, and it should be named as such.</p><h3>The read path</h3><p>Four steps. <strong>Query formulation</strong> translates the agent&#8217;s raw need into a structured retrieval spec by domain, provenance level, and concept type. This is Taylor&#8217;s reference interview formalized: translate &#8220;help the user pick a thing&#8221; into a precise retrieval specification. Not &#8220;embed the query and find nearest neighbors.&#8221;</p><p><strong>Retrieval</strong> is a parameterized query against the fact store, filtered by domain, provenance, and appraisal value. No LLM. <strong>Deterministic ranking</strong> scores by appraisal value multiplied by provenance weight. Tunable configuration, not a learned parameter. <strong>Frame composition</strong> groups facts by provenance so the consuming agent can see trust levels &#8212; confirmed facts (user-stated, high confidence) separated from observed patterns (behavioral, moderate confidence) separated from tool-provided context. No summarization. No &#8220;the LLM condensed your memories into a paragraph.&#8221; A view, not a lossy compression. No information is lost.</p><p>Vector search is the fallback, not the primary path. When the vocabulary doesn&#8217;t cover a topic or the agent can&#8217;t formulate a structured query, semantic similarity helps find the nearest concept. Otherwise, structured retrieval wins because it is interpretable, auditable, and composable.</p><h3>The maintenance path</h3><p>Weeding is not hygiene. It is compliance.</p><p>A store-everything-forever architecture is not GDPR or CCPA compliant by construction. Right-to-erasure is not satisfiable by &#8220;we ranked it lower&#8221; &#8212; the fact must be provably gone, with an audit trail. Retention schedules require deaccessioning on a clock. A regulator does not accept &#8220;the embedding makes it unlikely to surface.&#8221; This is where the &#8220;boring&#8221; data management patterns stop being elegance and become compliance machinery: provenance tells you <em>what</em> to cascade-invalidate during an erasure request. SCD Type 2 <code>valid_to</code> timestamps enforce a retention clock. You literally cannot be compliant without these. Table stakes, not taste.</p><p>Beyond compliance, there is the correctness argument. Ranking-only conflict resolution assumes the ranker can always detect that two facts are about the same concept and in conflict. That detection is exactly the write-time step the store-everything camp deleted. &#8220;Just rank better&#8221; is circular &#8212; it smuggles back the conflict resolution it claimed to avoid, now at query time under latency pressure with less context. Mem0&#8217;s own issue tracker provides the proof: #4896 reports that &#8220;search returns both with similar scores, degrading retrieval quality.&#8221; That is the poisoning mechanism, stated by the reporter, confirmed by code.</p><p>MUSTIE criteria, applied as a background job, provide the principled alternative. <strong>Misleading</strong> facts that contradict a newer, higher-provenance fact &#8212; archive them. <strong>Ugly</strong> records that are malformed, partial, or corrupted &#8212; quarantine them. <strong>Superseded</strong> facts where a newer version exists &#8212; version them with SCD Type 2. <strong>Trivial</strong> facts with low value and zero access &#8212; remove them. <strong>Irrelevant</strong> facts where the user&#8217;s context has shifted &#8212; this one requires LLM judgment: &#8220;the user was planning a wedding; the wedding happened; wedding preferences are now irrelevant.&#8221; <strong>Elsewhere</strong> &#8212; facts redundant with an authoritative external source &#8212; replace with a pointer.</p><p>This is the part nobody builds. It is also the part that determines whether your memory system can be deployed on real user data in a regulated environment.</p><h2><strong>What the field is getting right, and what&#8217;s still missing</strong></h2><p><em>CS and CogSci ask the wrong questions. Library Science, Ontology Engineering, and Data Management ask the ones that directly address persistent agent knowledge.</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dMOO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dMOO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 424w, https://substackcdn.com/image/fetch/$s_!dMOO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 848w, https://substackcdn.com/image/fetch/$s_!dMOO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 1272w, https://substackcdn.com/image/fetch/$s_!dMOO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dMOO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png" width="1456" height="679" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:679,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Each Discipline Asks About Agent Memory&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Each Discipline Asks About Agent Memory" title="What Each Discipline Asks About Agent Memory" srcset="https://substackcdn.com/image/fetch/$s_!dMOO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 424w, https://substackcdn.com/image/fetch/$s_!dMOO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 848w, https://substackcdn.com/image/fetch/$s_!dMOO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 1272w, https://substackcdn.com/image/fetch/$s_!dMOO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F91aab50d-3918-43d8-b291-7e9be78aa429_1800x840.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The right questions produce the right systems. CS asks &#8220;how do I store and retrieve this efficiently?&#8221; &#8212; necessary but not sufficient. CogSci asks &#8220;how does a human remember this?&#8221; &#8212; interesting but misleading. Library Science asks &#8220;what is this, how does it relate to other things, and how will someone need to find it?&#8221; Ontology Engineering asks &#8220;what commitments am I making about the structure of this domain?&#8221; Data Management asks &#8220;how do I keep this consistent and reliable as it changes?&#8221; The last three directly address the problem of persistent, structured, reliable agent knowledge.</p><p>Not every existing system ignores these questions. <a href="https://arxiv.org/html/2512.12818v1">Hindsight</a>, from Vectorize.io (paper co-authored with Virginia Tech and The Washington Post), is the strongest existing system relative to this thesis &#8212; and it&#8217;s more than a counterpoint. It&#8217;s convergent evidence.</p><p>Hindsight organizes memory into four networks &#8212; World, Experience, Opinion, and Observation &#8212; that distinguish types of knowledge structurally. It performs entity resolution to canonicalize mentions. It runs four-way parallel retrieval &#8212; semantic, BM25, graph traversal, and temporal &#8212; fused with Reciprocal Rank Fusion and neural reranking. Its observation consolidation is functionally similar to materialized views. Its opinion evolution with confidence scores is a form of re-appraisal. The benchmark results are strong: 91.4% on LongMemEval with a frontier backbone (83.6% with the open-source 20B model), outperforming full-context GPT-4o.</p><p>Here is what matters for this argument: Hindsight is <strong>write-heavy by design.</strong> Its <code>retain()</code> pipeline does LLM fact extraction, network classification, entity resolution to canonical entities, and four-way link construction &#8212; all at write time. Their docs state it directly: <em>&#8220;Writes are heavier but designed for background ingestion.&#8221;</em> The field&#8217;s best-performing system does NOT defer structure to retrieval. Mem0 defers. They diverge. The one with heavier write-time structuring posts the SOTA number.</p><p>Hindsight independently arrived at write-time structuring, epistemic separation, entity canonicalization &#8212; without citing a single library scientist. That&#8217;s the strongest possible evidence the principles are real, not borrowed. The field is rediscovering authority control, appraisal-by-type, and structured ingestion under benchmark pressure.</p><p>But the gap remains. Hindsight offers an <em>optional</em> controlled vocabulary (their docs describe user-defined concept sets normalized at retain time), but it&#8217;s a tuning knob, not the architectural spine. Authority control isn&#8217;t the load-bearing primitive &#8212; cardinality and temporal semantics don&#8217;t flow from it. There is no archival appraisal at write time (no Schellenberg-style value judgment on ingestion). The world/experience/opinion split is a type classification, not a trust hierarchy &#8212; a world fact from a tool API has the same standing as one the user explicitly stated. There is no MUSTIE-style principled weeding, and no machinery to make it lawful (more on this below). Memory banks are per-agent, not per-user-across-agents &#8212; the CDP reframing isn&#8217;t present.</p><p>What Library Science adds isn&#8217;t decoration over work that good engineers already derived. It names the things that are <em>still missing from the best system in the field</em> &#8212; and names them as a coherent discipline rather than a list of patches.</p><p>Could Hindsight be extended? I think so. It is already two separable components: TEMPR (the retain/recall memory infrastructure) and CARA (the reasoning/belief/personality layer). For the persistent user knowledge problem, TEMPR is the relevant foundation &#8212; you would add a vocabulary layer, appraisal, provenance, and weeding to its retain pipeline. CARA addresses the complementary problem of agent reasoning identity and could remain an optional layer for use cases where agent personality matters.</p><p>We have been here before. Every technology eventually discovers it needs information architecture. The web did &#8212; information architecture became a discipline in the early 2000s because websites built without it were unusable. Enterprise data did &#8212; master data management exists because decades of unmanaged data created expensive chaos. AI agents are next. The only question is whether we learn from those cycles or rediscover the same lessons from scratch.</p><h2><strong>Where to start</strong></h2><p>This is harder than spinning up a vector store. Ontology development takes time. Building controlled vocabularies requires domain expertise. Schema design requires upfront thought. The payoff is a knowledge layer that is interpretable, maintainable, auditable, and composable &#8212; instead of a high-dimensional prayer.</p><p>Read Jessica Talisman&#8217;s <em><a href="https://jessicatalisman.substack.com">Intentional Arrangement</a></em> newsletter and her Ontology Pipeline framework. Her <a href="https://senzing.com/library-science-ai-systems-thesaurus/">Graph Power Hour episode on Library Science for AI systems</a> is a direct on-ramp. Read Kurt Cagle&#8217;s <em><a href="https://ontologist.substack.com">The Ontologist</a></em> newsletter and his work on <a href="https://ontologist.substack.com/p/the-future-of-knowledge-graphs">knowledge graph architecture</a> and ontology-driven agents. Learn the Library Science concepts that transfer directly: archival appraisal, faceted classification, authority control, MUSTIE weeding, the reference interview.</p><p>You don&#8217;t need a specialized &#8220;AI memory database.&#8221; You need structured knowledge on top of a reliable data platform &#8212; PostgreSQL, MongoDB, whatever you already run. The principles are database-agnostic.</p><p>Agents don&#8217;t need better recall. They need better librarianship. The oldest information profession has more to teach the newest than either is comfortable admitting. The tools exist. The theory exists. The practitioners exist. What&#8217;s missing is the willingness to look outside the disciplines that got us here and learn from the ones that have been organizing human knowledge since before computers existed.</p><div><hr></div><h2><strong>References</strong></h2><ol><li><p><strong>Jessica Talisman</strong> &#8212; Semantic Engineer, Information Architect, knowledge infrastructure strategist. <em><a href="https://jessicatalisman.substack.com">Intentional Arrangement</a></em><a href="https://jessicatalisman.substack.com"> (Substack)</a> &#183; <a href="https://graphrag.info/2026/03/12/jessica-talisman-a-library-science-approach-to-enterprise-ai/">A Library Science Approach to Enterprise AI</a> &#183; <a href="https://senzing.com/library-science-ai-systems-thesaurus/">Graph Power Hour Ep. 9</a> &#183; <a href="https://moderndata101.substack.com/p/the-ontology-pipeline-refresh">The Ontology Pipeline Refresh</a></p></li><li><p><strong>Kurt Cagle</strong> &#8212; Ontologist, knowledge graph architect, author of The Cagle Report. <em><a href="https://ontologist.substack.com">The Ontologist</a></em><a href="https://ontologist.substack.com"> (Substack)</a> &#183; <a href="https://ontologist.substack.com/p/the-future-of-knowledge-graphs">The Future of Knowledge Graphs</a> &#183; <a href="https://ontologist.substack.com/p/knowledge-graphs-and-ais">Knowledge Graphs and AIs</a></p></li><li><p>Packer et al. (2023), <em><a href="https://arxiv.org/abs/2310.08560">MemGPT: Towards LLMs as Operating Systems</a></em> &#8212; the paper behind Letta&#8217;s virtual memory architecture.</p></li><li><p>Chhikara et al. (2025), <em><a href="https://arxiv.org/abs/2504.19413">Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory</a></em> &#8212; published at ECAI 2025.</p></li><li><p><a href="https://docs.mem0.ai/migration/platform-v2-to-v3">Mem0 v3 migration guide</a> &#8212; documents the ADD-only architectural shift.</p></li><li><p>Latimer, Boschi et al. (2025), <em><a href="https://arxiv.org/abs/2512.12818">Hindsight is 20/20: Building Agent Memory that Retains, Recalls, and Reflects</a></em> &#8212; co-authored with Virginia Tech and The Washington Post. <a href="https://github.com/vectorize-io/hindsight">GitHub</a>.</p></li><li><p>Tulving, E. (1972), <a href="https://alicekim.ca/EMSM72.pdf">Episodic and Semantic Memory</a> &#8212; in <em>Organization of Memory</em> (pp. 381-403). Academic Press.</p></li><li><p>Schellenberg, T.R. (1956), <em>The Appraisal of Modern Public Records</em> &#8212; <a href="https://www.archives.gov/research/alic/reference/archives-resources/appraisal-informational-values.html">National Archives Bulletin 8</a>.</p></li><li><p>Texas State Library, <em><a href="https://shortgrass.ca/sites/corp/media/files/managers_manual_v2/5.3_CREW_Method.pdf">CREW: A Weeding Manual for Modern Libraries</a></em> &#8212; the source of the MUSTIE framework.</p></li><li><p>Ranganathan, S.R. (1931), <em><a href="https://en.wikipedia.org/wiki/Five_laws_of_library_science">Five Laws of Library Science</a></em> &#8212; the foundation of faceted classification.</p></li><li><p>Taylor, R.S. (1968), Question-negotiation and information-seeking in libraries &#8212; <em>College &amp; Research Libraries</em>, 29(3), 178-194.</p></li><li><p>IFLA, <em><a href="https://www.loc.gov/catdir/cpso/frbreng.pdf">Functional Requirements for Bibliographic Records (FRBR)</a></em> &#8212; the Work / Expression / Manifestation / Item abstraction.</p></li><li><p>DCMI, <em><a href="https://www.dublincore.org/resources/metadata-basics/">Dublin Core Metadata Basics</a></em> &#8212; common metadata envelope principles.</p></li></ol><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://arturciocanu.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Build the Loop Once]]></title><description><![CDATA[What building an agent from scratch with bash, curl, and jq taught me about the abstractions we trust]]></description><link>https://arturciocanu.substack.com/p/build-the-loop-once</link><guid isPermaLink="false">https://arturciocanu.substack.com/p/build-the-loop-once</guid><dc:creator><![CDATA[Artur]]></dc:creator><pubDate>Fri, 24 Apr 2026 12:39:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Dcs6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Name a thing and you half-forget it. Engineers who can explain every layer of the TCP/IP stack talk about &#8220;the cloud&#8221; as though it were weather. People who spent years debugging distributed systems deploy &#8220;serverless&#8221; functions without asking what server they&#8217;re running on.</p><p>The abstractions are often good. The managed infrastructure saves time. But the cost doesn&#8217;t appear until something breaks, and the cost is this: you can&#8217;t debug what you never understood.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://arturciocanu.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p>AWS Lambda landed around 2014. The pitch was clean: pay per invocation, no servers, infinite scale. By 2018 the word &#8220;serverless&#8221; had its own conference, ServerlessNYC.</p><p>Kelsey Hightower keynoted. He didn&#8217;t amplify the hype. He traced Lambda back to <code>xinetd</code>, a Unix superdaemon from the 1970s. Sits on a port. Waits for a connection. Spawns a process. Cleans up on exit. That is, in its essentials, what Lambda does. The billing model is new. The managed infrastructure matters. The architectural idea has been around longer than most engineers in that room had been alive.</p><p>His point wasn&#8217;t that Lambda was wrong. It was that when mythology outruns understanding, you lose the ability to reason about what the system is actually doing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!002N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!002N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 424w, https://substackcdn.com/image/fetch/$s_!002N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 848w, https://substackcdn.com/image/fetch/$s_!002N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 1272w, https://substackcdn.com/image/fetch/$s_!002N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!002N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png" width="1456" height="613" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:613,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:133160,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://arturciocanu.substack.com/i/195343156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!002N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 424w, https://substackcdn.com/image/fetch/$s_!002N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 848w, https://substackcdn.com/image/fetch/$s_!002N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 1272w, https://substackcdn.com/image/fetch/$s_!002N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1800c1b-ccc9-4d93-b27f-dd3712091932_1600x674.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I think about that talk more than I probably should.</p><div><hr></div><p>Agent hype is following the same arc. We&#8217;re roughly in the middle of it. Every week brings new frameworks, new platforms, new orchestration models. The vocabulary is elaborate: multi-agent systems, tool-calling, agentic loops, RAG pipelines, memory subsystems.</p><p>Most engineers building with this vocabulary can&#8217;t describe the mechanical loop that drives an agent. They know the behavior (give it a goal, it figures out the steps) but they haven&#8217;t seen the code. The framework became the mental model.</p><p>The problems this creates are invisible until they aren&#8217;t. Agents looping forever because the termination condition is buried in framework config. Tool calls failing silently because error handling is abstracted away. Conversation state corrupting in ways you can&#8217;t reproduce because you never saw the JSON.</p><div><hr></div><p>Last year Thorsten Ball wrote &#8220;How to Build an Agent.&#8221; A functional coding agent: ~400 lines of Go. A loop, a few tools, an API call. Geoffrey Huntley turned this into a free workshop: build your own agentic loop before you touch a framework, because you need to see what the framework is hiding.</p><p>I ran the experiment in <code>bash</code>. Deliberately. Not Go, not Python, not TypeScript with a maintained SDK. <code>bash</code>, <code>curl</code>, and <code>jq</code>.</p><p>Why? I wanted to see the JSON. An SDK hides the HTTP request behind a method call. A framework hides conversation history behind a state object. When you write <code>curl</code> by hand and watch the terminal, you see exactly what crosses the wire. There is nothing to look through.</p><p>The question I wanted to answer: how much of agent complexity is intrinsic, and how much is framework?</p><div><hr></div><p>The API call is just this:</p><pre><code><code>jq -n \
  --arg model "$ANTHROPIC_MODEL" \
  --argjson messages "$(cat "$messages_file")" \
  --argjson tools "$tools_json" \
  '{model: $model, max_tokens: 2048, messages: $messages, tools: $tools}' \
| curl -sS "$ANTHROPIC_API_URL" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: $ANTHROPIC_VERSION" \
    -H "content-type: application/json" \
    --data @-</code></code></pre><p><code>jq</code> builds the payload. <code>curl</code> sends it. The response is JSON you can read in a terminal. No magic.</p><p>The loop is maybe eight lines:</p><pre><code><code>while (( turns &lt; MAX_TURNS )); do
  api_call "$messages_file" &gt; "$response_file"
  stop_reason="$(jq -r '.stop_reason' "$response_file")"

  if [[ "$stop_reason" == "tool_use" ]]; then
    tool_results="$(build_tool_results "$response_file")"
    append_user_tool_results_message "$messages_file" "$tool_results"
    ((turns += 1))
    continue
  fi

  extract_text_response "$response_file"
  return 0
done</code></code></pre><p><code>stop_reason == "tool_use"</code> means the model wants to act. Extract tool name, run it, append result, loop. <code>end_turn</code> means the model is done. Print and exit. That&#8217;s the whole agent.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dcs6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dcs6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 424w, https://substackcdn.com/image/fetch/$s_!Dcs6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 848w, https://substackcdn.com/image/fetch/$s_!Dcs6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 1272w, https://substackcdn.com/image/fetch/$s_!Dcs6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dcs6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png" width="1456" height="894" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/feadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:894,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:130150,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://arturciocanu.substack.com/i/195343156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dcs6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 424w, https://substackcdn.com/image/fetch/$s_!Dcs6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 848w, https://substackcdn.com/image/fetch/$s_!Dcs6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 1272w, https://substackcdn.com/image/fetch/$s_!Dcs6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffeadec6e-8c31-470c-a9f1-7f0e6bbf2f15_1600x982.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Tool dispatch is a case statement:</p><pre><code><code>case "$tool_name" in
  read_file)  run_read_file  "$input_json" "$workspace_root" ;;
  list_files) run_list_files "$input_json" "$workspace_root" ;;
  edit_file)  run_edit_file  "$input_json" "$workspace_root" ;;
  *)          printf 'ERROR: unknown tool: %s\n' "$tool_name" ;;
esac</code></code></pre><p>Three tools. Enough to do meaningful file editing. The workspace sandboxing, a function called <code>resolve_in_workspace()</code>, is about fifteen lines of path resolution that rejects anything escaping the working directory. Worth reading if you&#8217;d rather your agent not do unexpected things to your filesystem.</p><p>I did use <code>jq</code>. The alternative, parsing the Claude API response with <code>awk</code> and <code>grep</code>, was technically possible and practically inadvisable. JSON and <code>awk</code> have a complicated relationship. <code>jq</code> was the responsible choice: close enough to the metal to be educational, not so close that the exercise becomes an extended meditation on shell quoting.</p><p>The answer to my question: 410 lines. <code>agent.sh</code> at 263, <code>tools.sh</code> at 148. Most of the complexity is framework.</p><div><hr></div><p>Building it, the mystical feeling evaporated fast. Before I wrote it, &#8220;agent&#8221; felt like a substantial technical concept. After, it felt like a while loop. Both are true. The loop is the agent. The model is the intelligence inside the loop. The framework is scaffolding around the loop, scaffolding that hides the loop from you.</p><p>Some scaffolding is valuable. Production systems need retry logic, proper error handling, monitoring, cost controls. A shell script gives you none of that. I&#8217;m not suggesting you ship <code>bash</code> to production.</p><p>I&#8217;m suggesting you build the loop once, in something low-level enough to see it. After that, every framework is something you understand rather than something you trust. You know which knobs control which behavior. You know which abstractions are leaking. You know what the JSON looks like when things go wrong.</p><p>Event-driven compute goes back to the 1970s. This specific loop (call model, execute tool, feed result back) has existed since someone figured out that language models could use function call results. Neither idea requires a framework to understand.</p><p>The code is at <a href="https://github.com/artur-ciocanu/coding-agent-bash">github.com/artur-ciocanu/coding-agent-bash</a> if you want to read it instead of writing it. Though I&#8217;d argue writing it is the point.</p><p>Build the loop once. The rest is configuration.</p><div><hr></div><h2><strong>References</strong></h2><ol><li><p>Kelsey Hightower, <strong>ServerlessNYC 2018 Keynote</strong> &#8212; traced Lambda back to <code>xinetd</code>. <a href="https://www.youtube.com/watch?v=J5OFI0umJ6w">YouTube</a></p></li><li><p>Thorsten Ball, <strong>&#8220;How to Build an Agent&#8221;</strong> (April 2025) &#8212; a coding agent in ~400 lines of Go. <a href="https://ampcode.com/how-to-build-an-agent">ampcode.com/how-to-build-an-agent</a></p></li><li><p>Geoffrey Huntley, <strong>&#8220;How to Build a Coding Agent: Free Workshop&#8221;</strong> &#8212; the pedagogical case for building your own loop first. <a href="https://ghuntley.com/agent">ghuntley.com/agent</a> | <a href="https://github.com/ghuntley/how-to-build-a-coding-agent">GitHub repo</a></p></li><li><p>The Bash agent described in this post: <a href="https://github.com/artur-ciocanu/coding-agent-bash">github.com/artur-ciocanu/coding-agent-bash</a></p></li></ol><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://arturciocanu.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The Hierarchy of Agentic Needs]]></title><description><![CDATA[You&#8217;re Stuck Lower Than You Think]]></description><link>https://arturciocanu.substack.com/p/the-hierarchy-of-agentic-needs</link><guid isPermaLink="false">https://arturciocanu.substack.com/p/the-hierarchy-of-agentic-needs</guid><pubDate>Thu, 16 Apr 2026 11:41:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!slrX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most teams building with agents right now are solving the wrong problem. Not because they&#8217;re incompetent, but because the interesting problems are at the top and the load-bearing problems are at the bottom, and human nature reliably picks interesting over load-bearing.</p><p>This is, I should point out, a well-documented pattern. Abraham Maslow proposed his hierarchy of human needs in 1943, and the core insight was almost embarrassingly simple: you have to meet physiological needs before psychological ones matter, and those before self-actualization becomes possible. The hierarchy turned out to be a flawed model of human psychology (people pursue meaning while hungry all the time), but what&#8217;s interesting is that the flaw in psychology becomes a feature in engineering. A broken foundation genuinely does guarantee a broken system. No exceptions. We have a tendency to anthropomorphize agents, which is to say, we assume they have the same flexibility as humans. They don&#8217;t. An agent isn&#8217;t a person pursuing self-actualization despite unmet needs. It&#8217;s a system where the upper layers literally cannot function correctly if the lower ones are broken. The rigidity that made Maslow&#8217;s model too simplistic for human motivation makes it an excellent model for engineering dependencies.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://arturciocanu.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Monica Rogati mapped this same structural insight to data science in 2017 with her &#8220;AI Hierarchy of Needs.&#8221; Her argument was precise and, as it turned out, prophetic: the industry was obsessing over deep learning at the top of the pyramid while neglecting the data collection, storage, cleaning, and transformation layers underneath. Most organizations, she argued, should be investing in data infrastructure, not model architecture. She was right. The organizations that succeeded in the ML era weren&#8217;t the ones with the most sophisticated models. They were the ones with the cleanest data pipelines.</p><p><em>Adapted from Rogati&#8217;s &#8220;The AI Hierarchy of Needs&#8221; (2017). Used with attribution.</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Fcrx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Fcrx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 424w, https://substackcdn.com/image/fetch/$s_!Fcrx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 848w, https://substackcdn.com/image/fetch/$s_!Fcrx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 1272w, https://substackcdn.com/image/fetch/$s_!Fcrx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Fcrx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png" width="1456" height="927" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:927,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The AI Hierarchy of Needs, adapted from Monica Rogati&#8217;s original (2017). Data collection and infrastructure at the base, AI and deep learning at the top. Most teams invest at the top while stuck at the bottom.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The AI Hierarchy of Needs, adapted from Monica Rogati&#8217;s original (2017). Data collection and infrastructure at the base, AI and deep learning at the top. Most teams invest at the top while stuck at the bottom." title="The AI Hierarchy of Needs, adapted from Monica Rogati&#8217;s original (2017). Data collection and infrastructure at the base, AI and deep learning at the top. Most teams invest at the top while stuck at the bottom." srcset="https://substackcdn.com/image/fetch/$s_!Fcrx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 424w, https://substackcdn.com/image/fetch/$s_!Fcrx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 848w, https://substackcdn.com/image/fetch/$s_!Fcrx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 1272w, https://substackcdn.com/image/fetch/$s_!Fcrx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6241b084-060d-4b9c-b787-cb4993d0cdb0_1600x1019.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve watched the same pattern play out with agents. A team invests weeks into an autonomous agent workflow (self-improving, memory-enabled, the works) and then spends a Thursday afternoon debugging why the underlying API returns inconsistent pagination tokens. The agent wasn&#8217;t broken. The foundation was. Everything above it just made the failure more expensive to diagnose. Different technology, different decade, same structural error.</p><p>So this is the claim I want to make, and I want to make it directly: there is a hierarchy of needs for agentic systems, and most teams are investing at the top while the bottom is shaky. You can&#8217;t skip layers. The industry has been told this before, in different contexts, by people who saw it more clearly and earlier. We didn&#8217;t listen then. I&#8217;m not optimistic we&#8217;ll listen now, but the argument is worth making anyway.</p><p>Here is the test. Pick the agent system you&#8217;re most proud of. Now ask yourself: if you removed every piece of autonomy, every skill, every composed workflow, if you stripped it down to raw API calls, would the foundation hold? Would the APIs return consistent data? Would auth work reliably? Would rate limits be handled gracefully rather than with retry loops and prayer? Would the data schemas be stable enough that a consumer could depend on them without defensive parsing?</p><p>If the answer is &#8220;mostly, but&#8230;&#8221; then you&#8217;ve found your actual problem. Everything above a shaky foundation isn&#8217;t a capability. It&#8217;s a liability. You&#8217;re compounding fragility, not composing intelligence. I cannot emphasize this enough: the hierarchy isn&#8217;t a taxonomy for organizing your architecture deck. It&#8217;s a diagnostic tool. You use it by looking down, not up. The question isn&#8217;t &#8220;what layer am I building at?&#8221; It&#8217;s &#8220;what layer is actually broken?&#8221;</p><h2>The Foundation Layers</h2><p>The hierarchy has five layers, and I&#8217;ll walk them from the bottom up, but the weight of this piece sits on the diagnostic for each layer, not the description. You probably already know what APIs and CLIs are. The interesting question is whether you&#8217;ve honestly assessed where your system actually stands.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!slrX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!slrX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 424w, https://substackcdn.com/image/fetch/$s_!slrX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 848w, https://substackcdn.com/image/fetch/$s_!slrX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 1272w, https://substackcdn.com/image/fetch/$s_!slrX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!slrX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png" width="1456" height="889" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:889,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The Hierarchy of Agentic Needs. APIs at the base, Self-Improvement at the top. Each layer depends on the ones below. Most teams invest at the top while stuck at the bottom.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The Hierarchy of Agentic Needs. APIs at the base, Self-Improvement at the top. Each layer depends on the ones below. Most teams invest at the top while stuck at the bottom." title="The Hierarchy of Agentic Needs. APIs at the base, Self-Improvement at the top. Each layer depends on the ones below. Most teams invest at the top while stuck at the bottom." srcset="https://substackcdn.com/image/fetch/$s_!slrX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 424w, https://substackcdn.com/image/fetch/$s_!slrX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 848w, https://substackcdn.com/image/fetch/$s_!slrX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 1272w, https://substackcdn.com/image/fetch/$s_!slrX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff744073c-a983-427c-8ce8-86e2d6fbbdae_1600x977.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The Hierarchy of Agentic Needs. APIs at the base, Self-Improvement at the top. Each layer depends on the ones below. Most teams invest at the top while stuck at the bottom.</figcaption></figure></div><p>At the very bottom sits raw access. REST, GraphQL, gRPC: the low-level building blocks. Getting a list of entities, CRUD on resources, auth tokens, API keys. This is the layer nobody wants to talk about at conferences, and it&#8217;s the layer that determines whether everything above it works or just appears to work. The diagnostic is simple: if the underlying API is flaky, rate-limited, or returns inconsistent data, no CLI wrapper or MCP will save you. You&#8217;re putting a nice interface on a broken foundation.</p><p>I&#8217;ve seen teams build elaborate agent toolchains on top of APIs that have undocumented rate limits, inconsistent error formats across endpoints, and pagination that silently drops records under load. The agent performs beautifully in demos. In production, it hallucinates confidence over unreliable data, which is considerably worse than failing outright.</p><p>The fix is boring. Audit your API layer. Test it under the conditions your agent will actually encounter: concurrent requests, token refresh during long operations, malformed responses from upstream services. If you find problems, fix them before you build anything else. I know this sounds obvious. I also know it&#8217;s consistently ignored. The last few agents I&#8217;ve seen in production were dealing with API inconsistencies that no model, however capable, can reason away: GET to retrieve a single item, POST with filtering criteria to list them. The same resource, two completely different interaction patterns. All of that API weirdness is a tax you pay in wasted tokens and confused reasoning, while the real fix is to go back and make the API consistent. Nobody wants to do that. Everybody wants to add another layer on top.</p><p>One layer up, you reach composed access: CLIs and MCPs. This is where you combine one or more API calls into something useful. Auth plus operate on a resource. Caching. Convenience wrappers. Both CLIs and MCPs sit at this level, since they&#8217;re both composition layers over raw APIs.</p><p>But they&#8217;re not equivalent, and this is where I want to take a principled position. The case for CLIs over MCPs isn&#8217;t tribal; it&#8217;s structural. MCPs made sense before we had better models and skills. They provided a standardized way to give agents access to external systems. Fair enough. But coding agents are very good at Bash. Really, very good. And CLIs give you something MCPs don&#8217;t: full Unix composability. Piping. The existing ecosystem of tools that have been battle-tested for decades.</p><p>Which is to say, the CLI isn&#8217;t just a different interface to the same capability. It&#8217;s an interface that brings an entire universe of composable tooling along for the ride. When your agent can pipe the output of one CLI into another, filter it with <code>jq</code>, redirect it to a file, and chain the result into a third command, you get combinatorial power that no MCP protocol can match. Peter Steinberger arrived at the same conclusion building OpenClaw. So have many others. Skills plus CLI is a more powerful combination than MCP, because the CLI brings the entire Unix toolkit along for the ride, and skills encode the domain knowledge about how to combine them. Some MCPs claim to include workflows, but this is the exception rather than the common reality.</p><p>Someone might reasonably ask: why does this composition-layer distinction matter for the hierarchy? Because the composition layer is where agents spend most of their reasoning budget. If your agent is making raw HTTP calls and managing auth flows and handling pagination, that&#8217;s reasoning capacity that isn&#8217;t going to the actual task. The composition layer exists to make the foundation disappear, to give the agent pre-composed tools so it can think about the problem, not the plumbing. The diagnostic here: giving an agent raw API access without composed tooling is like giving someone a pile of lumber instead of a toolkit. The agent can make the HTTP calls, but it&#8217;ll spend most of its reasoning budget on undifferentiated heavy lifting.</p><h2>Where Domain Knowledge Lives</h2><p>This is the layer where things get genuinely interesting, and it&#8217;s the layer most teams skip. Not because they don&#8217;t understand it, but because encoding domain knowledge is harder than it sounds and less impressive than autonomy in a demo.</p><p>Skills are workflows, SOPs, procedures: domain knowledge made executable. They span across CLIs and MCPs, or combine them. A deployment procedure. A content creation workflow. The specific sequence of steps your team follows when onboarding a new service, including the weird step where you have to manually update that one config file because nobody ever automated it.</p><p>Before I go further, I should briefly define what an agent actually is in this context, since the term has become almost uselessly overloaded. An agent, for our purposes, is a system that uses a language model to decide which tools to call, in what order, and with what parameters, to accomplish a task. It&#8217;s the decision-making layer on top of the tools. That&#8217;s it. The mystique around the word obscures what is, structurally, a fairly straightforward loop: observe context, pick an action, execute, observe the result, repeat. If this sounds familiar, it should. John Boyd formalized the same cycle as the OODA loop (Observe, Orient, Decide, Act) in the context of military decision-making. Agents aren&#8217;t doing anything conceptually new. They&#8217;re running OODA loops with language models as the orientation and decision layer.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1tJ7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1tJ7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 424w, https://substackcdn.com/image/fetch/$s_!1tJ7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 848w, https://substackcdn.com/image/fetch/$s_!1tJ7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 1272w, https://substackcdn.com/image/fetch/$s_!1tJ7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1tJ7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png" width="1200" height="697" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f296d56d-479d-4053-baa5-c3c843865d69_1200x697.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:697,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The OODA loop: Observe, Orient, Decide, Act. The same decision cycle that agents run, formalized by John Boyd for military strategy.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The OODA loop: Observe, Orient, Decide, Act. The same decision cycle that agents run, formalized by John Boyd for military strategy." title="The OODA loop: Observe, Orient, Decide, Act. The same decision cycle that agents run, formalized by John Boyd for military strategy." srcset="https://substackcdn.com/image/fetch/$s_!1tJ7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 424w, https://substackcdn.com/image/fetch/$s_!1tJ7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 848w, https://substackcdn.com/image/fetch/$s_!1tJ7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 1272w, https://substackcdn.com/image/fetch/$s_!1tJ7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff296d56d-479d-4053-baa5-c3c843865d69_1200x697.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The OODA loop: Observe, Orient, Decide, Act. The same decision cycle that agents run, formalized by John Boyd for military strategy.</figcaption></figure></div><p>Now, why does the skills layer matter so much? Because it&#8217;s the precondition for autonomy. Without encoded domain knowledge, autonomy is just confident improvisation. The agent will chain tools together, impressively even, but without knowing your deployment procedure or your content creation workflow, it&#8217;s guessing at the &#8220;what,&#8221; not just the &#8220;how.&#8221;</p><p>This is worth sitting with for a moment. An autonomous agent without skills isn&#8217;t autonomous in any meaningful sense. It&#8217;s a very capable improviser. It can look at context clues, infer likely workflows, and produce something that looks right. But &#8220;looks right&#8221; and &#8220;follows the procedure that keeps production stable&#8221; are different things, and the gap between them is where incidents live.</p><p>Let me make this concrete. Consider a deployment skill for a typical microservice. The skill encodes a specific sequence:</p><ol><li><p>Check the CI status on the target branch</p></li><li><p>Verify that the staging environment passed its smoke tests</p></li><li><p>Pull the latest approved image tag from the registry</p></li><li><p>Run the canary deployment to 5% of traffic</p></li><li><p>Monitor error rates for ten minutes</p></li><li><p>If error rates stay below threshold, proceed to 25%, then 50%, then full rollout</p></li><li><p>If at any point error rates spike, automatically roll back to the previous image tag and notify the on-call channel</p></li></ol><p>Without this skill, an agent given a &#8220;deploy to production&#8221; instruction will improvise something reasonable. It might even get most of the steps right. But it will almost certainly miss something specific to your environment: perhaps the step where you have to drain the connection pool on the legacy service before cutting over, or the check against the feature-flag service to make sure no half-rolled-out experiments get caught in the deploy. Those specifics are exactly what make deployments safe, and they&#8217;re exactly what can&#8217;t be inferred from general knowledge.</p><p>There&#8217;s also a forcing-function insight here that I think is underappreciated. The process of encoding skills for an agent forces institutional clarity. When you sit down to write a deployment skill, you discover that your &#8220;deployment process&#8221; is actually three different processes that three different engineers follow, with subtle variations that have never been reconciled. The skill-encoding process is valuable even if you never give it to an agent, because it forces you to confront the gap between what you think your procedures are and what they actually are. Teams that get this layer right share a common trait: they&#8217;ve already done the hard work of documenting their procedures for humans. The leap from &#8220;written SOP&#8221; to &#8220;executable skill&#8221; is shorter than the leap from &#8220;tribal knowledge in someone&#8217;s head&#8221; to &#8220;executable skill.&#8221; If your team can&#8217;t describe the procedure to a new hire, it can&#8217;t encode it for an agent.</p><p>Claude Code is a useful reference point here, not because it&#8217;s perfect, but because it demonstrates what becomes possible when skills sit on well-composed tooling. It operates with skills layered on top of CLI access, which means the agent can encode complex workflows (project scaffolding, refactoring sequences, test-then-commit cycles) as skills that leverage the full power of the underlying Unix environment. The skills aren&#8217;t just prompt templates; they&#8217;re structured procedures that combine tool calls, conditional logic, and domain knowledge. That combination of skills plus CLI composability is what makes it genuinely useful rather than merely impressive.</p><p>The diagnostic for this layer: could a new team member follow a written version of what your agent does? If the procedure only exists as agent behavior, not as documented workflow, your skills layer is implicit. And implicit skills are fragile skills.</p><h2>Autonomy and What Comes After</h2><p>Up until the skills layer, everything is human-invoked. You ask the agent to run a skill. You tell it which CLI to use. You invoke the API call. At the autonomy layer, the agent starts deciding what to invoke based on context, environment, and user preferences.</p><p>There are two distinct flavors here, and the difference matters. Reactive autonomy is what you see in chat-based agents. The agent picks the right skill without being told which one, but it still waits for you to ask. You say &#8220;deploy the staging environment&#8221; and it selects the deployment skill, checks the prerequisites, and runs it. This is valuable, but it&#8217;s still fundamentally human-initiated.</p><p>Proactive autonomy is where things get genuinely different. Monitoring events, triggered by schedules, acting on triggers. The agent notices that a dependency has a security update, checks your update policy, runs the test suite, and opens a PR while you&#8217;re asleep. This is the &#8220;magic&#8221; angle, and it&#8217;s real, but only if the layers below are solid.</p><p>OpenClaw is a good example here. Steinberger built on solid foundations (CLIs over MCPs, well-composed tooling) and the result is an agent that can operate proactively because the layers below it are reliable. The proactive behavior isn&#8217;t the innovation. The reliable foundation that makes proactive behavior trustworthy is the innovation.</p><p>I should note: building multi-agent systems at scale amplifies this dynamic. When you have multiple agents coordinating, a shaky foundation doesn&#8217;t just produce errors; it produces cascading errors. Agent A makes a decision based on unreliable data from the API layer, passes that decision to Agent B, which compounds the error. Foundation problems can&#8217;t be papered over with better reasoning at the top. The trap at this layer is investing in autonomy before the layers below can support it. An agent that proactively does the wrong thing is worse than an agent that waits for you to ask.</p><p>At the very top sits self-improvement: knowledge extraction at the end of each session. Extracted knowledge becomes memory. Memory compounds over time. The agent internalizes user workflows, company procedures, and can anticipate what should be done or which skill to invoke.</p><p>This is the top of the pyramid, and the honest diagnostic is simple: almost nobody is here yet, and that&#8217;s fine. If your layers below are solid, you&#8217;ll get here. If they&#8217;re not, self-improvement just means your agent gets progressively better at doing the wrong things. Memory without judgment is just a growing pile of notes. Self-improvement compounds only when the agent already knows how to act well on its own.</p><p>What does compounding look like when it actually works, though? It&#8217;s worth being concrete. In session one, the agent learns that your team&#8217;s PR template requires a specific &#8220;Test Plan&#8221; section. In session five, it has internalized that your test plans follow a pattern: unit tests for logic changes, integration tests for API changes, manual QA steps for UI changes. By session twenty, it drafts the test plan section automatically, matched to the type of change, before you ask. Each piece of knowledge makes the next piece more useful. The PR template knowledge is mildly helpful on its own; combined with the testing pattern knowledge, it becomes genuinely predictive. That&#8217;s what compounding means in practice. It&#8217;s not just accumulation; it&#8217;s the interaction between accumulated pieces that creates something more useful than their sum.</p><p>Or consider a different axis: the agent learns your code review preferences. First, it learns that you prefer early returns over nested conditionals. Then it notices that you always flag functions longer than thirty lines. Eventually it connects these preferences to your broader principle (which you never explicitly stated) that readability matters more than cleverness, and starts applying that principle to novel situations you haven&#8217;t reviewed together. That kind of emergent understanding is what separates genuine self-improvement from a lookup table of past corrections.</p><h2>The Honest Self-Assessment</h2><p>Here&#8217;s a practical exercise. For each layer, answer honestly, and I mean with the kind of honesty that&#8217;s uncomfortable rather than the kind that confirms what you already believe.</p><ul><li><p><strong>APIs:</strong> Can your agent&#8217;s API calls run for 8 hours without hitting an unhandled error? Not a theoretical 8 hours; actually run it. If you hit an unhandled error at hour 3, that&#8217;s your answer. If you can&#8217;t run the test because &#8220;the environment isn&#8217;t set up for that,&#8221; that&#8217;s also your answer. A reasonable threshold: fewer than one unhandled error per thousand API calls under production-realistic load.</p></li><li><p><strong>CLIs/MCPs:</strong> Does your agent spend more than 20% of its reasoning on plumbing (auth, pagination, error handling) versus the actual task? If you&#8217;re not sure, that&#8217;s already an answer. Examine your agent&#8217;s trace logs and count the reasoning tokens spent on infrastructure versus domain logic. If the ratio surprises you, it will surprise you in the wrong direction.</p></li><li><p><strong>Skills:</strong> Could a new team member follow a written version of what your agent does? If the procedure only exists as agent behavior, not as documented workflow, your skills layer is implicit. Pick your agent&#8217;s three most common workflows. Write them out as step-by-step instructions. If you can do this in an afternoon, you&#8217;re close. If it takes a week of interviewing different engineers to piece together what actually happens, your skills gap is larger than you thought.</p></li><li><p><strong>Autonomy:</strong> When your agent acts without being asked, do you trust the result enough to not review it? If you review every proactive action, you have the overhead of autonomy without the benefit. Track the percentage of proactive agent actions that require human correction over a month. If it&#8217;s above 15%, the autonomy is costing more in oversight than it&#8217;s saving in automation.</p></li><li><p><strong>Self-Improvement:</strong> Is your agent measurably better at its job than it was a month ago? Not in a &#8220;the model improved&#8221; sense, but in a &#8220;it has internalized our specific workflows and preferences&#8221; sense.</p></li></ul><p>Most teams I talk to are genuinely solid at Layer 1 for the APIs they control, shaky at Layer 1 for third-party APIs, inconsistent at Layer 2, absent at Layer 3, and ambitious at Layer 4. The gap between Layer 2 and Layer 4, the missing skills layer, is where most agent projects go sideways.</p><h2>Where the Weight Falls</h2><p>If you&#8217;ve been reading carefully, you&#8217;ve noticed that the layer descriptions were in service of the diagnostics, not the other way around. That&#8217;s deliberate. The pyramid isn&#8217;t a taxonomy for organizing your thinking about agents; it&#8217;s a tool for finding the gap between where you think you are and where you actually are. And the gap is almost always further down than expected.</p><p>I think this happens for three predictable reasons.</p><p>First, the top layers are more fun. Autonomy is a better demo than API reliability. Self-improvement is a better conference talk than consistent pagination. The incentive structure of the industry (funding, attention, conference slots) rewards the top layers. Nobody gets venture capital for fixing their API layer.</p><p>Second, the bottom layers feel solved. APIs have been around forever. CLIs are old technology. The assumption is that these are commodity problems, already handled. But &#8220;commodity&#8221; and &#8220;reliable&#8221; aren&#8217;t synonyms. Your team&#8217;s specific API surface, with its specific quirks and rate limits and auth flows, is not a commodity. It&#8217;s a custom integration that needs the same engineering attention as anything else.</p><p>Third, the failures are invisible from the top. When an autonomous agent makes a bad decision, the natural instinct is to debug the autonomy logic. Was the prompt wrong? Did the agent misinterpret the context? Should we add more memory? But often the agent&#8217;s reasoning was fine; it was working with bad data from a broken API, or it was improvising because no skill encoded the correct procedure. The failure looks like an autonomy problem but is actually a foundation or skills problem.</p><p>This is the structural pattern worth naming: failures propagate upward, but attention flows to the layer where the symptom appears, not the layer where the cause lives. And since symptoms always appear at the top (that&#8217;s where the user-facing behavior is) attention gets systematically misallocated.</p><p>We&#8217;re making the same mistake Rogati identified in 2017, just with different technology. The pyramid I&#8217;ve described here (APIs, CLIs and MCPs, Skills, Autonomy, Self-Improvement) maps the same insight to agentic systems. And the diagnostic is the same: look down, not up. Find the lowest broken layer, fix it, and the layers above will improve without being touched.</p><p>The question isn&#8217;t whether this pattern will repeat itself. It already is. The question is whether you&#8217;ll be one of the teams that recognizes it early enough to invest correctly. Most won&#8217;t. The incentives are too strong, the demos are too compelling, and the bottom layers are too boring. But the teams that get the foundation right are the ones whose autonomous agents actually work in production, not just in demos. And they&#8217;re the ones who, two years from now, will look like they had some unfair advantage, when really, they just didn&#8217;t skip layers.</p><p>That&#8217;s the entire argument. Look down, not up. Fix the foundation. The rest follows, or it doesn&#8217;t follow at all.</p><div><hr></div><h2>References</h2><ol><li><p>Maslow, A. H. (1943). &#8220;A Theory of Human Motivation.&#8221; <em>Psychological Review</em>, 50(4), 370-396. <a href="https://psychclassics.yorku.ca/Maslow/motivation.htm">Full text at York University Classics in the History of Psychology</a></p></li><li><p>Rogati, M. (2017). &#8220;The AI Hierarchy of Needs.&#8221; <em>HackerNoon</em>. <a href="https://medium.com/hackernoon/the-ai-hierarchy-of-needs-18f111fcc007">https://medium.com/hackernoon/the-ai-hierarchy-of-needs-18f111fcc007</a></p></li><li><p>Boyd, J. R. (1996). &#8220;The Essence of Winning and Losing.&#8221; Unpublished briefing. A summary of the OODA loop concept is available at <a href="https://en.wikipedia.org/wiki/OODA_loop">https://en.wikipedia.org/wiki/OODA_loop</a></p></li></ol><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://arturciocanu.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>