<?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:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by George Violaris on Medium]]></title>
        <description><![CDATA[Stories by George Violaris on Medium]]></description>
        <link>https://medium.com/@violaris.org?source=rss-06a73c8d69eb------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*ApybHgmwr0bojE8vPsyLmw.jpeg</url>
            <title>Stories by George Violaris on Medium</title>
            <link>https://medium.com/@violaris.org?source=rss-06a73c8d69eb------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 05 Jun 2026 05:04:46 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@violaris.org/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The agentic development loop: how I ship features with Claude Code]]></title>
            <link>https://medium.com/@violaris.org/the-agentic-development-loop-how-i-ship-features-with-claude-code-a14db265379c?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/a14db265379c</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[ai-tools]]></category>
            <category><![CDATA[developer-productivity]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[claude-code]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Tue, 10 Mar 2026 08:44:14 GMT</pubDate>
            <atom:updated>2026-03-10T08:44:14.403Z</atom:updated>
            <content:encoded><![CDATA[<p><em>The value of agentic coding isn’t speed. It’s that it forces you to be explicit about what you’d normally leave implicit.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s_Vb5BTkGv309bmjDKrZTw.png" /></figure><p>Every engineering team has a dirty secret: the gap between “we know what we want to build” and “we have a plan detailed enough to build it” is where most projects quietly die. Not dramatically. Just a slow drift into scope creep, missed edge cases, and architectural decisions made by whoever happened to be writing code that afternoon.</p><p>I’ve been using Claude Code as the backbone of my development workflow, not just for writing code but for planning, review, testing, and deployment. The thing I didn’t expect: it doesn’t just speed things up. It changes the structure of the work. The process ends up more rigorous than what most teams pull off with purely human workflows, and I don’t think that’s an accident.</p><p>This isn’t a “look what AI can do” piece. It’s a working methodology I use to ship real products. Opinionated, specific, and it assumes you’re building things that actually need to work in production.</p><h3>The workflow at a glance</h3><p>Before diving in, here’s the full loop:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LkVlqGd-wvdEqlpNSnMqKQ.png" /></figure><p>Now let’s break each phase down.</p><h3>Phase 1: Brainstorming the feature set</h3><p>The first conversation with Claude Code isn’t about code at all. It’s about scope.</p><p>I describe the project, its purpose, its constraints, the user it serves, the infrastructure it runs on, and ask Claude to brainstorm the feature set. The instruction is to think in terms of deliverable capabilities, not implementation details. Features described as outcomes, not as code.</p><p>For a simple task list app, this might yield five features. For a production AI platform, it might yield thirty. The size doesn’t matter. The discipline is the same.</p><p>What makes this work is that Claude Code has context on your codebase, your infrastructure, your existing architecture. It’s not brainstorming in a vacuum. It’s brainstorming against the reality of what you already have. That’s a different conversation than what you get from a blank-page planning session.</p><p>I tell Claude to be exhaustive but honest. If a feature is a “nice to have,” label it that way. If something is a prerequisite for something else, surface that dependency. The brainstorm should be a complete map, not a wish list.</p><h3>Phase 2: Writing plan files</h3><p>Every feature gets its own plan file. This is non-negotiable.</p><p>A plan file isn’t a user story. It isn’t a ticket. It’s a contract. A document detailed enough that an agent (or a developer unfamiliar with the project) could pick it up and implement the feature without ambiguity. Each plan file includes:</p><ul><li><strong>Objective:</strong> What does “done” look like? Described in terms of observable behavior, not implementation.</li><li><strong>Constraints:</strong> What must this feature not do? What existing behavior must it preserve?</li><li><strong>Dependencies:</strong> What other features, services, or infrastructure does this touch?</li><li><strong>Acceptance criteria:</strong> Specific, testable conditions that must all pass.</li><li><strong>Open questions:</strong> Anything unresolved. These get resolved in refinement, not swept under the rug.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/738/1*6WYnigOHL7r8VjSDn5-lwA.png" /></figure><p>Why files and not tickets? Plan files live in your repo. They’re versioned. They’re diffable. When the plan changes, you can see what changed and when. This is infrastructure-as-code thinking applied to project planning, and it holds up better than whatever lives in your project management tool.</p><h3>Phase 3: Iterative refinement</h3><p>This is where most agentic workflows stop too early.</p><p>I take each plan file back to Claude Code and run a dedicated refinement pass. The instruction is: “Read this plan. Now think about everything that could go wrong, everything that’s underspecified, and everything that’s implicitly assumed but not stated. Rewrite the plan with that level of detail.”</p><p>This second pass typically doubles the length of each plan. That’s the point. The detail that comes out is exactly the detail that would otherwise become a bug report three sprints from now.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hvG7HWfvfBsf9kCzQqvMUg.png" /></figure><p>Notice the human review gate. The agent refines, but you approve. You’re the taste-maker and the context-holder. The agent surfaces the questions you should be asking. You decide the answers.</p><h3>Phase 4: Cross-plan analysis and master tracking</h3><p>This is the step that most people skip, and it’s the one that saves you the most pain later.</p><p>Once all plans are refined, I ask Claude Code to take all plans together and perform a cross-cutting analysis. Specifically:</p><ol><li>Are two features planning to modify the same component? Do they introduce competing abstractions?</li><li>Does Feature A’s constraint contradict Feature B’s requirement?</li><li>What’s the optimal implementation order given inter-feature dependencies?</li><li>What common utilities or patterns should be extracted rather than duplicated?</li></ol><p>This analysis feeds into a master tracking file, a single document that maps every plan’s status, its dependencies, known conflicts, and implementation order.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/949/1*9y4nlXaGMjPbncLm9pV61g.png" /></figure><p>Without this step, you’re basically hoping that feature-level isolation holds up when everything comes together. In my experience, it never does. The cross-plan analysis catches integration issues at the cheapest possible moment, before any code exists.</p><h3>Phase 5: Implementation, plan by plan</h3><p>With refined plans and a clear implementation order, the coding phase gets almost boring. That’s the goal.</p><p>Each plan gets implemented as a unit. Claude Code reads the plan, writes the implementation, and the code traces directly back to the spec. No interpretation, no creative license with requirements, no scope expansion.</p><p>The implementation contract is simple:</p><ul><li>Claude Code implements exactly what’s in the plan.</li><li>Any deviation requires updating the plan first, then implementing.</li><li>The plan file becomes the commit message’s source of truth.</li></ul><p>Claude Code’s awareness of your codebase matters here. It knows your patterns, your conventions, your existing abstractions. It doesn’t reinvent what already exists.</p><h3>Phase 6: Testing, four layers deep</h3><p>Each implemented plan goes through four testing layers before it’s considered complete. The order matters.</p><h3>Layer 1: Unit tests</h3><p>Claude Code writes the tests alongside (or immediately after) the implementation. The instruction is: test the contract, not the implementation. Tests should validate the acceptance criteria from the plan file, not the internal mechanics of how the code works.</p><p>I ask Claude to include:</p><ul><li>Happy path tests for every acceptance criterion</li><li>Boundary condition tests for every constraint</li><li>Negative tests, what should not happen</li><li>Regression anchors, tests that would break if someone later violates the plan’s constraints</li></ul><h3>Layer 2: Code review (for agentic code)</h3><p>This is where things get weird compared to traditional development, and worth going into detail on.</p><p>Traditional code review assumes a human wrote the code with intent, and you’re reviewing their reasoning. With agentic code, you’re not reviewing someone’s thought process. You’re reviewing whether an execution matched a specification. This changes what you look for.</p><h4>What agentic code review actually looks like</h4><p>Review against the plan, not against vibes. The plan file is your rubric. Every line of code should trace back to a requirement in the plan. If it doesn’t, that’s either scope creep (the agent added something unsolicited) or a plan gap (the plan was underspecified and the agent filled in the blank). Both need to be caught.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XBRsgHLOVPxGBjC8ZecyGg.png" /></figure><p><strong>Check for silent assumptions.</strong> Agents don’t flag uncertainty the way a junior developer might. They don’t say “I wasn’t sure about this, so I went with X.” They just go with X. Your job is to find the X’s and determine if they’re correct. Common places where agents make silent assumptions:</p><ul><li>Error handling strategies (retry? fail fast? degrade gracefully?)</li><li>Concurrency and ordering guarantees</li><li>Default values and fallback behaviors</li><li>Security boundaries and input validation depth</li><li>Resource cleanup and lifecycle management</li></ul><p><strong>Review the self-report.</strong> I ask Claude Code to generate a diff annotation alongside every implementation, a summary of what it did, why, and where it deviated from or interpreted the plan. This gives you a starting point for review instead of making you reverse-engineer intent from raw diffs.</p><p><strong>Look for pattern violations.</strong> The agent knows your codebase, but it doesn’t have the tribal knowledge of your team. It might introduce a pattern that’s technically correct but inconsistent with how your team does things. Watch for:</p><ul><li>Logging conventions and observability patterns</li><li>Error type hierarchies and exception handling strategies</li><li>Naming conventions that carry semantic meaning in your domain</li><li>Architectural boundaries (what’s allowed to call what)</li></ul><p><strong>Check for unintended coupling.</strong> Cross-reference the implementation against the master tracking file. Did this feature’s implementation touch something that belongs to another feature’s domain? The plan-level isolation should hold at the code level too.</p><h4>The meta-review: reviewing plans as code</h4><p>It took me a while to see this clearly: in an agentic workflow, plan files are code. They’re the source of truth that the agent compiles into implementation. A bug in the plan produces a bug in the code, and the code-level review might not catch it because the code faithfully implements the (flawed) plan.</p><p>This means plan review is a first-class activity. When you review a plan, you’re asking: if an agent implements this literally, will the result be correct? Will it be complete? Will it integrate cleanly with everything else?</p><h3>Layer 3: Functional testing</h3><p>After unit tests and code review, Claude Code runs functional tests that exercise the feature end-to-end. These aren’t unit tests with mocks. They’re tests against real (or near-real) environments that validate the feature works as a user would experience it.</p><p>The plan’s acceptance criteria are the test cases. Every criterion maps to at least one functional test.</p><h3>Layer 4: Pipeline and deployment tests</h3><p>Claude Code knows my CI/CD infrastructure. It can trigger pipeline builds and watch for failures, validate that deployment artifacts are correctly produced, run smoke tests against deployed environments, and verify that rollback procedures work.</p><p>This layer catches the class of bugs that only show up in the build/deploy process: dependency resolution issues, environment variable misconfigurations, containerization problems, and so on.</p><h3>Phase 7: The production boundary, remote debugging and guardrails</h3><p>This is probably the most polarizing part of this whole setup. Claude Code can SSH into preprod and production environments for debugging. Here’s how I do it without losing sleep.</p><h3>The guardrails framework</h3><p>I work with five categories of guardrails, and I don’t budge on any of them.</p><h4>1. Access scoping</h4><p>The agent operates with minimum privilege for the task at hand.</p><p><strong>Production</strong> is read-only by default. The agent can read logs, query metrics, inspect running state, and examine configuration. It cannot modify anything without explicit human approval for that specific action.</p><p><strong>Preprod</strong> has broader permissions, but still bounded. The agent can restart services, modify configuration, deploy new versions, but within a defined blast radius. It cannot touch data stores that mirror production data without approval.</p><p>The principle: treat the agent like a smart junior engineer who hasn’t built trust yet. You wouldn’t give them sudo on day one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P_JfWkJmjm6KwTYyr6fpqw.png" /></figure><h4>2. Session isolation and audit</h4><p>Every SSH session the agent opens is:</p><ul><li><strong>Fully logged.</strong> Every command, every output, every timestamp. The audit trail is immutable and stored outside the agent’s access.</li><li><strong>Time-bounded.</strong> Sessions have a hard timeout. If the agent hasn’t finished debugging in the allocated window, the session terminates and the agent reports what it found so far.</li><li><strong>Attributable.</strong> The agent connects with a dedicated service account, not a shared credential. You can always distinguish agent actions from human actions in your audit logs.</li><li><strong>Isolated where possible.</strong> Ideally the agent operates in a containerized or sandboxed session that limits its blast radius even within the allowed permission set.</li></ul><h4>3. Rollback contracts</h4><p>Before the agent takes any action in preprod or production, it must state:</p><ol><li>What it intends to do. The specific action, not a vague description.</li><li>Why it believes this will help, traced back to the diagnostic evidence.</li><li>What the rollback path is, how to undo the action if it makes things worse.</li><li>What it will check afterward, how it will verify the action had the intended effect.</li></ol><p>This is the plan file pattern applied to operational actions. The same discipline that prevents scope creep in development prevents reckless debugging in production.</p><h4>4. Kill switches</h4><p>Multiple layers of “stop everything”:</p><ul><li><strong>Command allowlists/denylists.</strong> The agent can run kubectl logs but not kubectl delete. It can run SELECT but not DROP. These are enforced at the session level, not by trusting the agent to self-restrict.</li><li><strong>Rate limiting.</strong> The agent can’t rapid-fire commands. There’s a deliberate cooldown that forces sequential, observable actions.</li><li><strong>Escalation triggers.</strong> Certain patterns in command output (error spikes, cascading failures, resource exhaustion) automatically pause the agent and alert the human operator.</li><li><strong>The big red button.</strong> A single command that terminates all agent sessions across all environments, immediately. This should exist and it should be tested regularly.</li></ul><h4>5. The observe-hypothesize-propose-execute pattern</h4><p>The agent doesn’t just SSH in and start running commands. It follows a structured debugging protocol:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U3d5oU8UBQVcUY7szq0Suw.png" /></figure><p>The framing I keep coming back to: these aren’t restrictions on the agent’s capability. They’re the same operational discipline you’d expect from any engineer with production access. The agent is fast and tireless, but it doesn’t have operational judgment yet. You provide that judgment structurally through guardrails rather than hoping the agent will self-moderate.</p><h3>Why this works</h3><p>Stepping back from the mechanics, here’s why I think this actually works better than most purely human workflows.</p><p><strong>Plans as forcing functions.</strong> Most teams skip the level of specification that plan files require. They rely on shared context, verbal agreements, and the assumption that experienced developers will “figure it out.” The agent can’t figure it out. It needs the spec. So you write the spec. And then you have it forever, as documentation, as test criteria, as onboarding material.</p><p><strong>Cross-plan analysis as a free architecture review.</strong> The step where Claude examines all plans together for conflicts and overlaps is something most teams only do informally, if at all. Making it an explicit step catches integration bugs before any code exists.</p><p><strong>The master tracking file as living documentation.</strong> Your project’s state is always visible, always current, always in version control. Nobody needs to ask “what’s the status of feature X?” The tracking file answers it.</p><p><strong>Structured debugging as operational maturity.</strong> The guardrails framework isn’t just about safety. It’s about building a repeatable, auditable debugging process that works whether the operator is an agent or a human at 3 AM during an incident.</p><p>The thing I keep coming back to: the agent doesn’t replace engineering discipline. It demands it. You can’t be lazy about specs when your implementer is a literal machine that will do exactly what you said, including the parts you got wrong.</p><p>The plans, the tests, the guardrails, the tracking file. None of that is overhead imposed by working with an agent. It’s what good engineering looks like when you actually do it instead of just talking about it. The agent just makes you write it all down.</p><p><em>George Violaris is CTO at HatchworksVC, where he builds AI products and reviews AI startup deals. He writes about the intersection of engineering practice and AI at </em><a href="https://mergeconf.substack.com"><em>Merge Conflict</em></a><em> and builds open-source tools including </em><a href="https://vio.violaris.org"><em>Vio Framework</em></a><em>. Find him on X </em><a href="https://x.com/atr0t0s"><em>@atr0t0s</em></a><em>.</em></p><p><a href="http://violaris.org">violaris.org</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a14db265379c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI existential dread and developer ego death]]></title>
            <link>https://medium.com/codetodeploy/ai-existential-dread-and-developer-ego-death-aef8bfc93214?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/aef8bfc93214</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[personal-growth]]></category>
            <category><![CDATA[future-of-work]]></category>
            <category><![CDATA[developer-experience]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Tue, 10 Mar 2026 05:17:44 GMT</pubDate>
            <atom:updated>2026-03-10T05:17:44.048Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TMLMcz8FLodaJ0ql97M65Q.png" /></figure><p>Last Tuesday, I reviewed fourteen pull requests before lunch. I didn’t write any of them. Three autonomous agents had been working overnight on a feature I’d specced out the evening before, and my job was to read their work, check their reasoning, and decide what to merge. I caught two subtle bugs and one architectural misstep. Solid morning.</p><p>On the drive home, I realized I hadn’t written a single line of code in over two weeks. Not one. And the strangest part wasn’t the realization itself. It was that I didn’t feel anything about it.</p><p>That numbness is what got me. Because three years ago, writing code wasn’t just what I did. It was who I was.</p><blockquote>🚀 Master AI, Cloud &amp; Code in 3 Months</blockquote><blockquote><a href="https://www.codetodeploy.tech/2026/02/master-ai-cloud-coding-in-3-monthsstart.html">Unlock Free Trial Today</a></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pkQFf0NUnzoFbVEVQCF5WQ.png" /></figure><h3>2023: The ego was bulletproof</h3><p>When ChatGPT became unavoidable in early 2023, I treated it the way most experienced developers did: as a slightly smarter Stack Overflow. Paste in an error message, scan the response, fix the obvious hallucinations, and move on. Sometimes it saved me a tab or two of Googling. Sometimes it was flat-out wrong, and I’d feel a small, satisfying thrill catching it.</p><p>Looking back, that thrill should have worried me.</p><p>I was measuring my self-worth by how often I was better than it. Every hallucination was proof that I still mattered. The machine was impressive but flawed, and I was the human who knew the difference.</p><p>My ego was completely intact. If anything, AI made it stronger. Look at me, the veteran developer, wielding this tool with the discernment that only fifteen years of production bugs and 3 am incidents can provide.</p><p>I had no idea what was coming.</p><h3>2024: The first crack</h3><p>The shift happened in a meeting. I was designing architecture for a new service. I’d spent two days sketching diagrams, thinking through failure modes, going back and forth with myself about event-driven vs. request-response. On a whim, I described the problem to Claude and asked it to propose something.</p><p>It came back with an approach I hadn’t considered. Not a wild one. A boring, pragmatic one that was better than mine. It had spotted a bottleneck I’d missed and suggested a pattern I knew about but hadn’t thought to apply here. The kind of insight you usually get from a senior colleague who’s seen this exact problem before.</p><p>I sat with that for a while.</p><p>This wasn’t autocomplete. This was judgment. Taste. The ability to hold a complex system in your head and make good trade-offs. The thing I’d always told myself was the part machines couldn’t do.</p><p>I used its suggestion. It worked well. I told no one where it came from.</p><p>That was the first crack. Not because the AI was right, but because I hid it. If I admitted an AI had out-architected me, I’d have to deal with what that meant, and I wasn’t ready.</p><h3>2025: The year I stopped typing</h3><p>By 2025, I was what people started calling a “vibe coder.” Describe what you want in natural language, iterate on the output, assemble systems from generated components. On a good day, I’d write maybe forty lines of code by hand. The rest was generated, reviewed, and refined.</p><p>Then one week, the forty lines became zero.</p><p>I was building a data pipeline. In the past, this was my happy place. Thoughtful code, clean abstractions, that particular satisfaction of something well-built. But I found myself describing the pipeline to an AI, getting back something functional, adjusting two prompts, and shipping it. The whole thing took an afternoon. It would have taken me three days by hand.</p><p>That evening, I sat in my home office and felt something I can only describe as grief. Not for a person. For an identity. The developer I’d spent fifteen years becoming, the one who took pride in elegant solutions, who could debug by instinct, who knew the right pattern for the right problem. That person was becoming unnecessary. Not unemployed. Unnecessary.</p><p>The code still needed to be correct. The systems still needed to work. But the act of writing them, the craft I’d built my entire professional identity around, was no longer the bottleneck. I was.</p><p>For a while, I told myself AI code was fine for prototypes, but production systems needed a human touch. Then I told myself I’d use AI for the boring parts and keep the interesting work for myself. Then I got angry about everyone shipping sloppy, generating garbage, and losing the fundamentals.</p><p>But mostly I was just sad. I missed building something line by line and knowing every decision was mine.</p><h3>2026: What’s on the other side</h3><p>I wish I could say there was a clean turning point. There wasn’t. It was more like slowly waking up and realizing the nightmare was actually just a different kind of morning.</p><p>Today, I run multi-agent workflows. I spec features, design systems, then dispatch AI agents to implement them in parallel. I review their code with the same rigor I once applied to junior developers’ pull requests, except there are more of them, they work faster, and they don’t get defensive when I request changes. I’m the one who decides what ships. The testing is largely automated, but I’m the one who decides when it’s enough.</p><p>It’s not writing code. But I’d argue it’s still engineering.</p><p>The ego death was real. “I am a person who writes excellent code” had to die. And the thing that replaced it is harder to romanticize. Nobody writes blog posts about the satisfaction of a well-crafted code review comment. There’s no flow state equivalent when you’re reading someone else’s output, even if that someone is a machine. The dopamine is quieter now. A system prompt that makes agents produce consistently good output. A testing workflow that catches a category of bugs before any human sees them. Shipping in a day what used to take a sprint.</p><p>I think about the younger version of me, the one who felt that thrill every time he caught a hallucination. He’d look at what I do now and think I’d given up. That I’d let the machines win.</p><p>He’d be wrong. But I get why he’d feel that way. What he was really afraid of wasn’t AI. It was irrelevant. And the only way through that fear was to let go of the version of myself that needed to be the one typing.</p><h3>What I’d tell you if you asked</h3><p>I still catch bugs. I still make architectural decisions. I still solve hard problems. They’re just different problems now, and honestly, some days I’m not sure if different is better or just different.</p><p>The old me would have hated who I’ve become. But the old me also mass-produced code that fed mass-produced bugs that required mass-produced fixes. It was a hamster wheel, and I’d convinced myself it was a craft.</p><p>Most days, I don’t miss it. Some days I do. But I had to grieve it before I could see what was actually in front of me.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aef8bfc93214" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codetodeploy/ai-existential-dread-and-developer-ego-death-aef8bfc93214">AI existential dread and developer ego death</a> was originally published in <a href="https://medium.com/codetodeploy">CodeToDeploy</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google’s MCP Toolbox for Databases Deserves Your Attention]]></title>
            <link>https://ai.plainenglish.io/googles-mcp-toolbox-for-databases-deserves-your-attention-2543bf910e30?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/2543bf910e30</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[mcp-server]]></category>
            <category><![CDATA[fintech]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Wed, 04 Mar 2026 08:26:57 GMT</pubDate>
            <atom:updated>2026-03-04T08:26:57.624Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9uWhKVLGngh2D1uBrnd5_g.png" /></figure><p>There’s a repo in the googleapis GitHub org that I think more people should know about. It’s called genai-toolbox, now officially MCP Toolbox for Databases. At first glance it looks like another Google cloud tool.</p><p>It’s more than that.</p><h3>What it actually does</h3><p>MCP Toolbox is an open-source MCP server written in Go. You point it at your databases (Postgres, MySQL, AlloyDB, Spanner, BigQuery, Firestore, and more), define your agent tools declaratively in a tools.yaml file, and it exposes those tools to any AI agent framework that speaks MCP. Which, at this point, is most of them.</p><p>A tool definition looks like this:</p><pre>tools:<br>  search-hotels-by-name:<br>    kind: postgres-sql<br>    source: my-pg-source<br>    description: Search for hotels based on name.<br>    parameters:<br>      - name: name<br>        type: string<br>        description: The name of the hotel.<br>    statement: SELECT * FROM hotels WHERE name ILIKE &#39;%&#39; || $1 || &#39;%&#39;;</pre><p>And consuming it from TypeScript takes about four lines:</p><pre>import { ToolboxClient } from &#39;@toolbox-sdk/core&#39;;</pre><pre>const client = new ToolboxClient(&#39;http://127.0.0.1:5000&#39;);<br>const tools = await client.loadToolset(&#39;my-toolset&#39;);</pre><p>Those tools work with LangChain, LlamaIndex, Genkit, Google’s ADK, or a raw API call. One definition, any framework.</p><h3>The real shift: decoupling tool logic from agent code</h3><p>Here’s what makes this actually interesting.</p><p>For the past two years, the standard way to build database-backed AI agents has been to shove tool logic directly into your agent code. You write a Python function that opens a connection, runs a query, handles errors, returns results — and that function lives right next to your agent. Schema changes? Redeploy your agent. Query needs tuning? Redeploy. Second agent needs the same data? Copy-paste or extract into a shared library.</p><p>MCP Toolbox inverts this. Tool definitions become a separately managed, separately deployed configuration layer. The Toolbox server runs on its own, hot-reloads by default (change tools.yaml, it picks it up, no restart), and your agent code just points at it.</p><p>We’ve done this before. We stopped embedding SQL in application code and moved to ORMs, then API layers, then service meshes. Same trajectory, new domain.</p><h3>Why MCP matters here</h3><p>MCP (Model Context Protocol), originally from Anthropic, is quickly becoming the standard wire protocol for agent-tool communication. Google took a project that predated MCP and retrofitted full compatibility onto it instead of building their own thing. That says something.</p><p>Claude speaks MCP natively. Cursor, Windsurf, and most modern AI IDEs support it. Google has committed to it. A tool definition you write today for Gemini will work with Claude or GPT-4o tomorrow, without changes. That kind of portability didn’t exist 18 months ago.</p><h3>What it handles that you don’t want to write yourself</h3><p>Beyond the declarations, Toolbox deals with a set of infrastructure problems that are genuinely annoying to get right.</p><p><strong>Connection pooling.</strong> Database connections are expensive. Managing a pool across concurrent agent invocations is non-trivial, especially when agents are async and unpredictable in when they call what.</p><p><strong>Authentication.</strong> Integrated auth support, so you’re not passing raw credentials through your orchestration layer.</p><p><strong>Observability.</strong> OpenTelemetry out of the box. Metrics and traces flow into whatever you’re already running.</p><p><strong>Tool reuse.</strong> Define a financial-data toolset once, load it into your research agent, portfolio agent, and alerting agent. No duplication.</p><p>Not flashy. But it’s the difference between a demo and something you’d actually run.</p><h3>The fintech angle</h3><p>I spend most of my time at the intersection of AI agents and financial data, so here’s why this caught my eye.</p><p>Financial data models are complex and always moving: market data, portfolio state, transaction history, news events, derived signals. Every new agent capability — a new analytical query, a new alert condition, a new join — normally means touching application code. Toolbox lets you treat database tools as a versioned API surface that agents consume, separate from the agent logic.</p><p>When you have multiple agents running concurrently against the same data, connection pooling and centralized tool management stop being optional. They’re structural.</p><p>The fit is even more natural for persistent memory. Memory retrieval is a parameterized query. Memory writing is an insert. Wrap those as Toolbox tools and any agent, in any framework, can talk to your memory layer through MCP without knowing your schema.</p><h3>What to watch out for</h3><p>Still v0.x. Google explicitly flags the API as unstable until v1.0, and minor versions can break things. I wouldn’t move production to it now, but I’d be building proofs of concept.</p><p>The config is SQL-centric. If your tools need complex business logic or multi-step operations, you’ll bump up against what YAML and SQL can express. Toolbox doesn’t replace your service layer. It speeds up the data access part.</p><p>And it’s Google’s take on what this should look like. As MCP matures, there will be competing implementations. Whether Toolbox becomes the default depends on v1.0 timing and community traction.</p><h3>Why I care</h3><p>What’s interesting to me isn’t the feature set per se. It’s what it signals about where agent architecture is going. We’re moving from AI bolted onto apps toward agents as real architectural components that consume managed tool interfaces. Same forces that gave us microservices and Infrastructure as Code, playing out again in a new domain.</p><p>MCP Toolbox is an early, concrete version of that.</p><p>The repo is at <a href="https://github.com/googleapis/genai-toolbox">github.com/googleapis/genai-toolbox</a>, Apache 2.0 licensed. Current version is v0.22.0.</p><p><em>If you’re building AI-powered financial platforms or working where databases meet LLMs, I’d like to hear how you’re handling tool architecture. Drop a comment or reach out.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2543bf910e30" width="1" height="1" alt=""><hr><p><a href="https://ai.plainenglish.io/googles-mcp-toolbox-for-databases-deserves-your-attention-2543bf910e30">Google’s MCP Toolbox for Databases Deserves Your Attention</a> was originally published in <a href="https://ai.plainenglish.io">Artificial Intelligence in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Localhost Ego Trip]]></title>
            <link>https://medium.com/@violaris.org/the-localhost-ego-trip-4a68c19ddf67?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/4a68c19ddf67</guid>
            <category><![CDATA[openai]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[anthropic-claude]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Fri, 27 Feb 2026 01:01:00 GMT</pubDate>
            <atom:updated>2026-02-27T01:01:00.728Z</atom:updated>
            <content:encoded><![CDATA[<p><em>I self-hosted an LLM to avoid depending on APIs. I was already depending on three of them.</em></p><p>This one’s embarrassing to admit, but it’s honest.</p><p>Early on, I ran a local LLM for the orchestration layer of my AI financial analysis platform. The part that decides how to approach each user request — what data to fetch, what to analyze, how to structure the response — ran on a model hosted on my own machine. The source file’s header literally said “Llama-orchestrated financial analysis agent.” I felt good about it. Self-hosted. No API dependency. Full control.</p><p>There was just one problem. I was already calling an API for vision analysis to read chart images. I was already calling APIs for market data. I was already calling cloud services for everything that required external information. The local model was handling one step — the planning and routing — and nothing else.</p><p>So I was paying the cost of running a local LLM (memory pressure, build complexity, finicky compilation dependencies) to avoid one more API call. Meanwhile, every other part of the pipeline was hitting the cloud anyway.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9vfhTOVaVmzKpDJYiiT2vA.png" /></figure><h4>The swap</h4><p>The commit that fixed this changed two files. Swap one dependency for another, point the orchestrator at a cloud API instead of localhost. 17 lines changed. The file header went from “Llama-orchestrated” to “Claude-orchestrated.”</p><p>The orchestration got faster and the responses got better. I stopped pretending that running one model locally while depending on cloud APIs for everything else was some kind of principled stance.</p><p>Self-hosting made sense as a starting point. It let me prototype without worrying about API costs or rate limits. But I held onto it too long because it felt like the right kind of engineering. The honest reason I kept it wasn’t technical. It was identity. I wanted to be the kind of developer who self-hosts things.</p><h4>The model per job thing</h4><p>Once I let go of the idea that the whole system needed to be independent from external APIs, a different kind of architecture fell into place.</p><p>Different models are good at different things. Some are fast and cheap and great at structured decisions. Some are strong at understanding images. Some are better at nuanced language and synthesis. Instead of picking one and forcing it to do everything, I started routing different parts of the pipeline to different models based on what each step actually required.</p><p>One model handles the planning — looking at a user’s question and deciding what data to fetch and how deep to go. A different model handles visual analysis when chart images are involved. Another handles the synthesis, pulling everything together into a coherent response. Each one was chosen for the job, not for brand loyalty or architectural purity.</p><p>This isn’t a novel idea. But it’s one I couldn’t have arrived at while I was committed to running everything on one local model. That commitment narrowed my thinking. Once I dropped it, the question changed from “how do I make this one model do everything?” to “what’s the best tool for each part of the job?”</p><h4>What the models can’t do</h4><p>The more I worked with this setup, the more I realized that the interesting engineering wasn’t in the models at all. It was in everything around them.</p><p>Making the system feel responsive even though it’s doing heavy work behind the scenes. Designing notifications that pull users back at the right moment without being annoying. Figuring out what data to show first and what to hold back. Getting the streaming right so words appear at a natural pace instead of in jarring chunks. These are UX and infrastructure problems. The models are an ingredient, not the product.</p><p>I think a lot of people building with LLMs right now are over-indexing on the model choice and under-investing in everything else. Which model you use matters less than how you orchestrate them, how you present their output, and how you handle the cases where they’re wrong. The engineering challenge has shifted from “build AI” to “build around AI.” The models are good enough. The question is whether the product around them is.</p><h4>Where this landed</h4><p>I’m about a month into building. 84 commits. The most important ones are the ones that removed things.</p><p>There’s a version of this story where I describe a clean, intentional process — where each pivot was a strategic decision made from a position of clarity. That’s not what happened. What happened was I built things, realized they weren’t working or weren’t worth their complexity, and deleted them. Sometimes it took longer to reach that conclusion than it should have.</p><p>The platform works. I use it every day. If you’re curious what financial analysis looks like when you stop trying to build AI from scratch and start giving capable models the right tools, you’ll hear about it soon enough.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4a68c19ddf67" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[1,500 Lines, One Commit]]></title>
            <link>https://medium.com/@violaris.org/1-500-lines-one-commit-fc7afc3fa6d5?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/fc7afc3fa6d5</guid>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[technical-analysis]]></category>
            <category><![CDATA[tradingview]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[charts]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Wed, 25 Feb 2026 14:01:01 GMT</pubDate>
            <atom:updated>2026-02-25T14:01:01.979Z</atom:updated>
            <content:encoded><![CDATA[<p><em>I built a custom charting engine from scratch. Then I deleted it all and accidentally created a better problem to solve.</em></p><p>I wrote a full custom charting system for my AI financial analysis platform. Drawing tools. Per-asset persistence so your annotations followed you between sessions. A keyboard shortcuts modal. Two context providers managing chart state and drawing state separately. I had a whole commit dedicated to getting the Bollinger Band colors right. Faded cyan accents. I was proud of those.</p><p>This took weeks. Multiple iterations on the drawing tools alone. I was building with LightweightCharts, which is a solid library, but turning it into a production charting experience meant writing a lot of glue code. Context providers, toolbar components, keyboard handling, state persistence.</p><p>Then I looked at TradingView’s embeddable widget.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KabR7MdNFh7x2rhZ6p9hpA.png" /></figure><h4>The commit</h4><p>It had everything I’d built. RSI, MACD, volume profiles, dozens of drawing tools, multi-pane layouts, and a UX that actual traders already knew how to navigate. It was free to embed.</p><p>The commit that replaced my charting system: 10 files changed, 155 insertions, 1,923 deletions. All my context providers, the toolbar, the keyboard shortcuts modal — gone. Weeks of work, replaced by a div and a script tag.</p><p>There’s a specific kind of deflation that comes from deleting something you worked hard on. You sit there looking at the diff and thinking about all the edge cases you solved, all the little details you got right. The cyan accents. The per-asset persistence. And then you look at the widget doing all of that plus more, and you know the decision is correct, and it still doesn’t feel great.</p><p>But I’d make it again without hesitation. Building a production-grade charting experience is a full-time job for a team. I was one person building a product, and the chart was one piece of it. Every hour I spent on drawing tool edge cases was an hour I didn’t spend on the part that actually differentiates the product — the AI analysis.</p><h4>The better problem</h4><p>Here’s the part I didn’t plan for. Deleting my custom charts created a more interesting problem than the one I’d been solving.</p><p>When I owned the charting library, the AI pipeline could render its own charts server-side. Same library, consistent output, full control over what the AI would see. When I switched to an embedded widget, that broke. The AI was generating its own charts that looked nothing like what users were actually staring at. Different rendering engine, different indicator styles, different timeframes visible. The AI and the user were literally looking at different pictures.</p><p>The fix required thinking about the problem differently. Instead of the AI generating its own view of the data, it needed to see what the user sees. The widget’s own API lets you capture exactly what’s on screen, so now the frontend grabs the user’s actual chart across multiple timeframes and sends those captures to a vision model. The AI analyzes the same image the user is looking at.</p><p>I couldn’t have designed this workflow while I was busy building my own charting library. I was too deep in the drawing tool weeds, solving problems that had nothing to do with the core product. The deletion forced me to step back and rethink what the AI actually needed, and the answer turned out to be simpler and better than what I’d been building toward.</p><h4>Build vs. use</h4><p>There’s a gravitational pull toward building things yourself, especially when you have the skills to do it. Custom code feels like control. Dependencies feel like risk. And sometimes that instinct is right — there are parts of my system that had to be built from scratch because nothing exists that does what I need.</p><p>But the charting wasn’t one of them. I was building a commodity component and treating it like a differentiator. The actual differentiator was what happened after the user looked at the chart — the analysis, the synthesis, the intelligence layer. Every hour I spent on chart rendering was borrowed from that.</p><p>I keep asking myself the same question now before I start building anything: is this the part that makes my product different? If the answer is no, I look for something that already exists. For me the differentiator is the AI pipeline and the analysis layer. The charting was never it. I just liked building it.</p><p>The 1,923-line deletion was the most productive commit I’ve made on this project. It stung for about a day. Then I started working on the part that actually matters, and I forgot about the cyan accents.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fc7afc3fa6d5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Model That Never Made a Trade]]></title>
            <link>https://medium.com/@violaris.org/the-model-that-never-made-a-trade-2d44289d116a?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/2d44289d116a</guid>
            <category><![CDATA[reinforcement-learning]]></category>
            <category><![CDATA[trading-analysis]]></category>
            <category><![CDATA[ai-trading-strategies]]></category>
            <category><![CDATA[ai-financial-tools]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Sun, 22 Feb 2026 21:49:32 GMT</pubDate>
            <atom:updated>2026-02-22T21:49:32.831Z</atom:updated>
            <content:encoded><![CDATA[<p><em>I built a reinforcement learning trading agent. Then I shelved it — not because it was wrong, but because it wasn’t fast enough to ship first.</em></p><p>The first thing I built for my AI financial analysis platform was a reinforcement learning agent. PPO algorithm, gymnasium environment, three actions: hold, buy, sell. I wrote the environment class, set up the observation space, imported PyTorch and stable-baselines3. A separate module ran a continuous loop pulling news articles, tweets, and price data, storing everything in Redis. The idea was that the agent would learn from this stream of real-time information and figure out when to enter and exit positions.</p><p>The `step()` and `reset()` methods ended up commented out. I got far enough to define the environment but never far enough to train anything useful.</p><p>Training an RL agent on financial data is a real project. You need historical simulation infrastructure. You need careful reward shaping so the model doesn’t learn degenerate strategies — like never trading, which technically minimizes losses. You need compute to iterate. I was trying to build a product and a research project at the same time, and the research project was winning the fight for my attention while the product sat there unshipped.</p><h4>The shift</h4><p>While I was deep in reward functions, large language models were getting good enough that they didn’t need to be trained on domain data. They just needed access to it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ckiXkcbMZnO3CM3cbprTkg.png" /></figure><p>My RL agent needed thousands of episodes of simulated trading to learn anything at all. An LLM needed a function call to a market data API and it could tell you what it saw. Not perfectly, not with the precision a trained model could eventually reach, but well enough to be useful right now.</p><p>That’s the tension I kept coming back to. The RL approach would probably produce better results eventually. More refined decision-making, less susceptible to the kind of confident-but-shallow analysis that LLMs sometimes produce. But “eventually” doesn’t ship. And in a market where new AI products are launching every week, getting something useful into people’s hands matters more than getting something perfect into a research paper.</p><h4>The decision</h4><p>So I shelved the RL pipeline. Moved the files to an archive folder. Replaced the whole thing with a tool-calling agent — give an LLM access to market data tools and let it plan its own analysis for each request.</p><p>The dependency cleanup alone told me I’d made the right call for now. Removing PyTorch, gymnasium, and stable-baselines3 cut about 2GB from the Docker image. The AI container went from sluggish multi-minute builds to something I could iterate on quickly. All that weight, and the code it supported never ran in production once.</p><p>The model that shipped has zero training data. It works by looking at the same data a human analyst would — price history, technical indicators, chart patterns — and synthesizing what it sees. It’s not going to catch subtle statistical edges that a trained model might find. But it can tell you that Bitcoin just broke below a key support level with declining volume, and here’s what that usually means, and here’s what to watch for next. That’s useful today, not in six months after training converges.</p><h4>The plan</h4><p>The RL work isn’t dead. It’s waiting.</p><p>The way I think about it now: the LLM-based pipeline is the fast path to market. It gets the product in front of users, generates real usage data, and proves that the core idea works. The RL layer is the refinement that comes later — trained on actual user interactions and real market outcomes, not simulated environments.</p><p>There’s an argument that this is the right order anyway. An RL model trained without real user data is guessing about what matters. An RL model trained on months of actual usage, actual market conditions, and actual outcomes from the LLM pipeline’s recommendations has something real to learn from. The LLM gets you to market. The RL makes you better once you’re there.</p><p>The commented-out `step()` function is still sitting in the archive. I’ll get back to it.</p><h4>Shortcuts that ship</h4><p>RL felt like the serious approach. Tool-calling felt like a shortcut. But the “shortcut” shipped and the “serious approach” is still commented out.</p><p>In a landscape where the models themselves improve every few months, waiting for the perfect architecture is its own kind of risk. You can spend six months training a model and find that a newer foundation model with tool access does the same thing out of the box. Or you can ship now, learn from real users, and build the RL layer on top of actual production data instead of simulated environments.</p><p>I’ll come back for it. But I’m glad I stopped waiting.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2d44289d116a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The EU Doesn’t Kill Innovation. The US Regulatory Mess Might.]]></title>
            <link>https://medium.com/@violaris.org/the-eu-doesnt-kill-innovation-the-us-regulatory-mess-might-8ba87d53d1c1?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/8ba87d53d1c1</guid>
            <category><![CDATA[usa]]></category>
            <category><![CDATA[innovation]]></category>
            <category><![CDATA[european-union]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[regulation]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Wed, 18 Feb 2026 11:18:10 GMT</pubDate>
            <atom:updated>2026-02-18T11:18:10.762Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Why the ‘Europe is bad for tech’ narrative is lazy, outdated, and costing builders real money</em></p><p>There’s a story Silicon Valley loves to tell. It goes like this: Europe is a graveyard for innovation. A continent drowning in red tape, regulatory overreach, and bureaucratic paralysis, where ambitious startups go to die and tech giants get fined into submission. The US is the land of move fast, break things. Where real builders thrive.</p><p>It’s a compelling story. It’s also mostly wrong.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rUP09lwT1GspvBfjIAl_mg.jpeg" /></figure><p>I’ve built and shipped products across AI, fintech, crypto, and data platforms. I’ve navigated regulatory environments on both sides of the Atlantic. What I’ve found is that the reality is almost the inverse of the myth: the EU’s so-called heavy regulation often provides clearer, more predictable ground to build on, while the US, for all its libertarian swagger, has created one of the most fragmented, litigation-heavy, enforcement-by-surprise regulatory environments on the planet.</p><p>This isn’t a piece defending bureaucracy for its own sake. It’s a reality check for anyone actually trying to build something serious.</p><h3>The ‘Regulatory Chaos’ Tax Is Real, and the US Charges It</h3><p>Let’s start with something builders care about deeply: certainty. When you’re designing a system, writing a compliance policy, or raising a round, you need to know what the rules are. The EU gives you that. The US frequently doesn’t.</p><p>Take crypto. The EU passed MiCA (the Markets in Crypto-Assets regulation): a comprehensive, coherent licensing framework that tells you exactly what a crypto exchange, a stablecoin issuer, or a token project needs to do to operate legally across all 27 member states. One framework. One license pathway. Clear definitions. You can build your compliance stack on top of it.</p><p>Now look at the US. For years, and this is not hyperbole, the SEC and CFTC have been locked in a jurisdictional cold war over who gets to regulate crypto. The result is a landscape where you genuinely cannot know if your token is a security or a commodity until someone sues you. Where enforcement actions replace regulation as the primary mechanism of governance. Where projects that spent years and millions trying to do the right thing still got obliterated by a regulator that moved the goalposts after the game started.</p><blockquote><strong><em>“Regulate by lawsuit” is not a feature of the US system. It’s a bug that has killed real companies and real jobs.</em></strong></blockquote><p>For builders, this isn’t an abstract problem. It’s an existential one. When regulatory risk is unknowable, you either over-lawyer everything (expensive), move offshore (costly and complex), or fly blind and hope for the best (dangerous). None of these are good for building.</p><p>The EU at least tells you the rules before it penalises you for breaking them.</p><h3>GDPR Was Painful. It Was Also the Best Thing to Happen to Data Infrastructure.</h3><p>The conventional take on GDPR is that it was a nightmare: a compliance bomb that cost millions, generated cookie banners nobody reads, and made lawyers rich. That part is true. But the conventional take misses what GDPR actually did to the industry.</p><p><strong>It forced data hygiene.</strong> Before GDPR, most tech companies had almost no idea what data they were collecting, where it was stored, who had access to it, or how long it was retained. GDPR forced teams to answer those questions. The companies that went through that exercise properly came out the other side with dramatically cleaner, more auditable, more trustworthy data infrastructure.</p><p><strong>It created a moat.</strong> If you’re building any kind of B2B data product, and especially if you’re selling into enterprise or financial services, GDPR compliance is now table stakes for European customers and increasingly a strong differentiator for US enterprise buyers who’ve watched American companies get torched in breaches and FTC actions. “We’re GDPR compliant” has real commercial value.</p><p><strong>It set a global standard.</strong> California CCPA, Brazil LGPD, India’s DPDP Act: every major data privacy framework that came after GDPR borrowed from it heavily. Love it or hate it, Europe wrote the playbook that the rest of the world is now following. Building to that standard from day one means you’re not scrambling to retrofit privacy into your architecture later.</p><blockquote><strong><em>Privacy by design isn’t a compliance cost. It’s what good engineering looks like in 2025.</em></strong></blockquote><p>The US, by contrast, still has no comprehensive federal data privacy law. It has a patchwork of state laws in California, Virginia, Colorado, Texas and elsewhere, each with different definitions, scopes, and enforcement mechanisms. If you’re operating at scale across US states, you’re not dealing with one GDPR. You’re dealing with 15 overlapping frameworks that are still being actively written. That is objectively more expensive to comply with than a single coherent standard.</p><h3>The EU AI Act: Scary on the Surface, Sensible Underneath</h3><p>Of all the EU’s recent regulatory moves, the AI Act generates the most fear among builders. The language is dense, the risk classifications seem arbitrary at first read, and the compliance timelines felt aggressive. But step back and look at what the Act actually does.</p><p>It creates a tiered system. Most AI applications, including recommendation engines, productivity tools, content generation, and analytics, fall into the minimal or limited risk categories. These face almost no regulatory requirements beyond some transparency obligations. The heavy requirements kick in for high-risk applications: AI systems making consequential decisions about people’s employment, creditworthiness, medical treatment, or law enforcement. That’s… reasonable? The idea that AI making credit decisions should face scrutiny is not exactly a radical proposition.</p><p>Contrast that with the US approach to AI regulation: a mix of executive orders, agency guidance letters, voluntary commitments from labs, and a patchwork of state-level proposals. This creates an environment that feels freer but is actually riskier. Without clear federal standards, you’re one viral incident or high-profile lawsuit away from emergency legislative action written in a panic and implemented badly. The AI Act was written slowly, with extensive industry consultation. That process is annoying. The output is more durable.</p><p>More practically: for anyone building AI into financial services, healthcare, or HR, the EU’s high-risk AI requirements largely overlap with what best practice looks like anyway. Model documentation, human oversight mechanisms, bias testing, audit trails. If you’re not doing those things, you should be, regardless of whether Brussels is watching.</p><h3>The Single Market Is Genuinely Underrated</h3><p>Here’s something that gets lost in the regulatory noise: the EU’s single market is one of the most powerful commercial facts on earth.</p><p>500 million consumers. A single legal and contractual framework for commercial activity. Financial services passporting that lets you acquire a license in one member state and operate across the entire bloc. A unified digital infrastructure actively harmonising everything from e-signatures to payment systems.</p><p>Compare this to the US, where operating across state lines in regulated industries like financial services, healthcare, and insurance means navigating 50 different licensing regimes, 50 different consumer protection frameworks, and 50 different attorneys general who may or may not decide to make an example of you. The interstate commerce headaches in the US are genuinely severe for fintech companies, and they rarely get talked about because the narrative is always focused on federal-level US vs. EU comparisons.</p><p>A fintech with an EMI license in Lithuania can serve customers in Germany, France, Spain, and Italy. A fintech with a money transmitter license in New York still needs separate licenses in California, Texas, Florida, and 45 other states. Which market is actually more open?</p><blockquote><strong><em>The EU single market is 500 million customers, one license pathway, and one legal framework. That’s not a burden. That’s a distribution advantage.</em></strong></blockquote><h3>Startups Are Actually Thriving in Europe</h3><p>If the EU kills innovation, someone forgot to tell European founders.</p><p>Fintech: Revolut, Klarna, Wise, Monzo, N26. Healthcare: Doctolib, Kry, Alan. AI and deep tech: Mistral, Aleph Alpha, Wayve, Helsing, Poolside. Crypto: several of the most sophisticated and compliant exchanges and infrastructure providers in the world are EU-based, precisely because MiCA gave them a framework to build within.</p><p>European tech VC hit record levels in recent years and the ecosystem maturation is real. The argument that EU regulation suppresses startup formation has not been borne out by the data. What it has done is push a certain kind of ‘move fast, ignore rules, figure it out later’ approach offshore. And honestly, given what that approach produced in US crypto, US social media, and US consumer data, maybe that’s not the worst outcome.</p><p>What the EU produces is startups that are built to last. That understand compliance as architecture rather than afterthought. That have institutional defensibility baked in from the start. This is what enterprise customers want. This is what acquirers value. This is what survives.</p><h3>The Real Innovation Risk Isn’t Regulation. It’s Regulatory Uncertainty.</h3><p>Here’s the actual thesis. The threat to innovation isn’t regulation. It’s unpredictability. Builders can handle rules. They cannot handle the absence of rules, or rules that change retroactively, or rules that exist but aren’t enforced until someone powerful decides to make an example of you.</p><p>The EU is slow and sometimes frustrating. But it is predictable. Consultations happen publicly. Timelines are published. Enforcement follows process. When the EU says <em>here is a framework</em>, you can build to that framework and have reasonable confidence that following it will protect you.</p><p>The US is fast and often exhilarating. But it is unpredictable in ways that materially harm builders. Regulatory agencies change priorities with administrations. Enforcement is selective and political. The gap between what is technically legal and what will get you sued or investigated is enormous and poorly mapped. For companies building in AI, crypto, or data, the three highest-velocity sectors of the current tech cycle, that uncertainty is a real tax on progress.</p><blockquote><strong><em>The scariest thing you can hear as a founder isn’t “here are the rules.” It’s “we’ll tell you the rules after we see what you built.”</em></strong></blockquote><p>You can plan around regulation. You cannot plan around regulatory ambiguity.</p><h3>What This Means If You’re Building Right Now</h3><p><strong>Build GDPR compliance into your architecture from day one.</strong> Not as a legal checkbox but as a genuine engineering discipline. Data minimisation, purpose limitation, user control: these are good product principles, not just compliance obligations. They also make you immediately commercially viable in the world’s second largest economy.</p><p><strong>Take the EU AI Act seriously but proportionately.</strong> Spend time on the risk classification for your specific use case. Most AI products are minimal or limited risk. For the ones that aren’t, the high-risk requirements are a quality bar you should be hitting anyway.</p><p><strong>If you’re building in fintech or crypto and you haven’t looked at MiCA, look at MiCA.</strong> The EU is building the most coherent regulated crypto market in the world. That is a commercial opportunity, not a threat.</p><p>And most importantly: don’t let the Silicon Valley narrative about European bureaucracy substitute for actual due diligence. The myth that Europe is closed for business is repeated most loudly by people who haven’t actually built there, or who built something that relied on regulatory arbitrage that was never going to last.</p><h3>The Bottom Line</h3><p>The EU has real problems. The AI Act’s implementation will have rough edges. MiCA has gaps. GDPR compliance is genuinely expensive to implement well. The single market is not as seamless as the theory suggests. These are all fair critiques.</p><p>But the claim that the EU kills innovation while the US enables it is a myth, and a costly one. The US’s regulatory chaos has destroyed companies, misallocated billions of venture capital into businesses built on sand, and produced outcomes like surveillance capitalism, crypto casino culture, and algorithmic harm at scale that are now generating a political backlash far more disruptive than any Brussels directive.</p><p>The EU asked builders to slow down, think carefully, and build things that could last. A lot of builders are realising that was actually good advice.</p><p>The myth is that regulation and innovation are in tension. The reality is that the right regulation, clear, predictable, and proportionate, is what makes serious, durable innovation possible.</p><p>Europe understood that. The US is still learning it the hard way.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8ba87d53d1c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Software Is Not Dead — It’s Evolving]]></title>
            <link>https://medium.com/@violaris.org/software-is-not-dead-its-evolving-e3dd683ded2f?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/e3dd683ded2f</guid>
            <category><![CDATA[tech-industry]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Mon, 16 Feb 2026 17:09:54 GMT</pubDate>
            <atom:updated>2026-02-22T22:13:55.217Z</atom:updated>
            <content:encoded><![CDATA[<h3>A response to Mark Cuban’s AI claim and what it really means for engineers</h3><p>A short clip of <strong>Mark Cuban</strong> has been circulating where he suggests that “software is dead” because AI will customize everything to individual businesses.</p><p>Here’s a longer discussion where he talks about AI reshaping work and business:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FFWDWFBcO3fs%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DFWDWFBcO3fs&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FFWDWFBcO3fs%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/8a9432fd1a4eedb4f0c5c6cc5c1b0a66/href">https://medium.com/media/8a9432fd1a4eedb4f0c5c6cc5c1b0a66/href</a></iframe><p>His broader thesis is this:</p><blockquote><em>The future isn’t traditional SaaS. It’s AI-driven customization for every company.</em></blockquote><p>That part is directionally correct.</p><p>But “software is dead” is the wrong conclusion.</p><p>As a CTO and working engineer, I want to explain why — in practical, architectural terms — software isn’t disappearing. It’s becoming more sophisticated.</p><h3>Why “Software Is Dead” Is Technically Wrong</h3><p>AI systems are not a replacement for software.</p><p>They are software.</p><p>Large language models run inside:</p><ul><li>Cloud infrastructure</li><li>Containers</li><li>API gateways</li><li>Application servers</li><li>Logging pipelines</li><li>Databases</li></ul><p>A typical AI-powered production system looks like this:</p><pre>User → Frontend → API Gateway → Application Layer → AI Service → Database<br>                               ↘ Logging / Monitoring ↙</pre><p>Every box in that diagram is traditional software engineering.</p><p>AI adds capability.</p><p>It does not remove architecture.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TJSoNXCTLS8bqF2Vj8GvwQ.png" /></figure><h3>What AI Actually Changes</h3><p>AI reduces friction in:</p><ul><li>Writing boilerplate code</li><li>Drafting documentation</li><li>Generating SQL queries</li><li>Producing UI scaffolding</li><li>Prototyping workflows</li></ul><p>That is meaningful.</p><p>But production systems are not prototypes.</p><p>Real systems require:</p><ul><li>Idempotency guarantees</li><li>Concurrency control</li><li>Security boundaries</li><li>Observability</li><li>Deployment pipelines</li><li>Performance optimization</li><li>Failure recovery</li></ul><p>AI doesn’t remove those constraints.</p><p>If anything, probabilistic outputs introduce new ones.</p><h3>The Real Shift: From Static SaaS to Adaptive Systems</h3><p>Cuban’s strongest insight is this:</p><p>Businesses want customization.</p><p>Traditional SaaS enforced standardized workflows. AI allows systems to adapt dynamically.</p><p>We are moving toward:</p><ul><li>AI-augmented internal tools</li><li>Intelligent workflow orchestration</li><li>Domain-specific copilots</li><li>Automated decision support</li></ul><p>That’s not the death of software.</p><p>It’s the next layer of it.</p><h3>Example: AI Still Needs Engineering</h3><p>An LLM can generate something like:</p><pre>export async function createInvoice(orderId: string) {<br>  const order = await getOrder(orderId);<br>  const total = order.items.reduce((sum, i) =&gt; sum + i.price, 0);<br>  return saveInvoice({ orderId, total });<br>}</pre><p>Looks fine.</p><p>In reality, production requires:</p><ul><li>Currency precision handling</li><li>Tax calculation logic</li><li>Transactional safety</li><li>Idempotent retries</li><li>Audit logging</li><li>Multi-tenant isolation</li><li>Error compensation flows</li></ul><p>The complexity doesn’t vanish.</p><p>It compounds.</p><h3>Where Software Is Expanding</h3><h3>1. AI Integration Engineering</h3><p>Companies now need:</p><ul><li>Secure LLM integrations</li><li>Prompt evaluation frameworks</li><li>Guardrails and validation layers</li><li>Rate limiting and cost controls</li><li>Vector databases</li><li>Retrieval pipelines</li></ul><p>That’s software engineering.</p><h3>2. Platform Engineering</h3><p>AI workloads increase demand for:</p><ul><li>Distributed systems expertise</li><li>Observability tooling</li><li>Infrastructure as code</li><li>GPU orchestration</li><li>Performance tuning</li></ul><p>Not less engineering. More.</p><h3>3. Regulated Systems</h3><p>In fintech, healthcare, and enterprise SaaS:</p><ul><li>Outputs must be traceable</li><li>Decisions must be auditable</li><li>Data must remain secure</li></ul><p>AI adds risk surfaces that engineers must manage.</p><h3>What Might Actually Be Dying</h3><p>If something is fading, it’s:</p><ul><li>Undifferentiated CRUD SaaS</li><li>Static dashboards with no intelligence</li><li>Tools that don’t automate meaningfully</li></ul><p>But every technological shift has eliminated weak products.</p><p>It has never eliminated engineering.</p><h3>The Strategic Reality for Developers</h3><p>If you’re early in your career, here’s the correct takeaway:</p><p>Don’t abandon software fundamentals.</p><p>Double down on them.</p><p>Learn:</p><ul><li>Systems design</li><li>Distributed systems</li><li>Data modeling</li><li>Security</li><li>Performance engineering</li></ul><p>Then layer AI on top.</p><p>The engineers who win in the next decade will not be prompt typists.</p><p>They will be system builders who understand how AI fits inside reliable architecture.</p><h3>Final Take</h3><p>“Software is dead” is a viral soundbite.</p><p>But software is not dying.</p><p>It’s becoming:</p><ul><li>More adaptive</li><li>More composable</li><li>More AI-integrated</li><li>More systemically complex</li></ul><p>AI is not the end of software engineering.</p><p>It’s the beginning of a harder, more interesting version of it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e3dd683ded2f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ObsiTUI: Why I Built a Terminal UI for Obsidian]]></title>
            <link>https://ai.plainenglish.io/obsitui-why-i-built-a-terminal-ui-for-obsidian-7f1ebc2fcd15?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/7f1ebc2fcd15</guid>
            <category><![CDATA[ai-enhanced-notes]]></category>
            <category><![CDATA[notes]]></category>
            <category><![CDATA[productivity]]></category>
            <category><![CDATA[generative-ai-tools]]></category>
            <category><![CDATA[obsidian]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Sun, 15 Feb 2026 16:00:54 GMT</pubDate>
            <atom:updated>2026-02-15T16:00:54.004Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How a CTO’s workflow frustrations led to building a vim-powered, AI-enhanced terminal client for Obsidian</em></p><p>As someone who spends most of my day in the terminal — managing infrastructure, writing code, debugging systems — there’s something deeply frustrating about having to break my flow to open a GUI just to check my notes.</p><p>I’m the CTO at Zeig.ai, a financial analysis platform, and group CTO at Hatchworks VC where we’re building everything from crypto exchanges to AI agent platforms. My work lives in the terminal: TypeScript services, Python scripts, blockchain tooling, Go microservices. But my knowledge base? That lived in Obsidian’s Electron app, an entirely different context that required alt-tabbing, mouse navigation, and a complete shift in muscle memory.</p><p>This context switching wasn’t just annoying — it was expensive. Every time I needed to reference a technical note, check a decision log, or look up an API pattern, I had to leave the mental model of the terminal and enter a completely different interaction paradigm.</p><p>So I built ObsiTUI: a terminal UI for Obsidian that brings your entire vault into the CLI, with vim keybindings, AI-powered search and chat, graph visualization, and zero compromises on functionality.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Bsf4IqWqiXcPVLFS" /><figcaption>A terminal UI for browsing and managing Obsidian vaults — featuring vim keybindings, rich markdown rendering, fuzzy search, graph view, and AI-powered chat/search/suggestions. Built with Ink (React for CLI) and TypeScript.</figcaption></figure><h3>The Pain Points</h3><h3>1. Context Switching Tax</h3><p>When you’re deep in a debugging session at 2 AM, hunting down a race condition in your Ethereum yield aggregator, the last thing you want is to:</p><ol><li>Alt-tab to Obsidian</li><li>Click the search bar</li><li>Type your query (in a different UI paradigm)</li><li>Navigate results with a mouse</li><li>Alt-tab back</li><li>Relocate your mental context</li></ol><p>This workflow interruption can easily add 30–60 seconds per note lookup. Over a day of deep work, that’s not just lost time — it’s dozens of broken flow states.</p><h3>2. Vim Users Exiled from Their Editor</h3><p>I live in vim. My hands know hjkl better than arrow keys. Operators and motions aren&#39;t shortcuts—they&#39;re how I <em>think</em> about text manipulation. But Obsidian&#39;s editor, while good, doesn&#39;t give me that modal editing power. There are vim plugins, but they&#39;re approximations at best.</p><p>With ObsiTUI, I wanted full vim modal editing: normal, insert, and visual modes; operators that compose with motions (d3w, ci(, yap); dot repeat; marks; and a command mode that actually feels like vim. Not a plugin that tries—a real implementation.</p><h3>3. AI Features That Required Integration Dance</h3><p>The rise of AI assistants is transforming how we work with knowledge bases. But integrating Claude or GPT into your note-taking workflow typically means:</p><ul><li>Copy note text</li><li>Switch to ChatGPT/Claude</li><li>Paste</li><li>Get response</li><li>Copy back</li><li>Format</li><li>Save</li></ul><p>Or you could use an Obsidian plugin, but then you’re locked into whatever LLM provider they chose, with whatever API they wrapped, and whatever features they thought to implement.</p><p>I wanted AI that worked <em>my</em> way: RAG chat with source citations, semantic search across my vault, intelligent summarization, and tag/link suggestions — all with the flexibility to use Anthropic’s Claude, OpenAI’s GPT, or run completely locally with Ollama.</p><h3>4. Speed and Performance</h3><p>Electron apps are… not fast. Obsidian does a remarkable job, but there’s no getting around the overhead of a full browser runtime. When you have thousands of notes and complex queries, you feel it.</p><p>Terminal UIs are fast. Ink (React for the CLI) gives me the component model I love from React, but renders directly to the terminal with minimal overhead. No Chromium, no GPU acceleration, just ANSI escape codes and raw speed.</p><h3>The Solution: ObsiTUI</h3><p>I built ObsiTUI with a few core principles:</p><h3>1. Terminal-Native, Not Terminal-Adapted</h3><p>This isn’t a web app that happens to run in the terminal. It’s built for the terminal from the ground up:</p><ul><li>Vim keybindings are first-class, not an afterthought</li><li>Fuzzy search uses fzf-style scoring</li><li>Graph view uses ASCII art with Bresenham line drawing</li><li>Everything is keyboard-driven with mnemonics that make sense</li></ul><h3>2. AI Without SDK Bloat</h3><p>I didn’t want to depend on heavyweight AI SDKs that abstract away control. Instead, ObsiTUI uses raw fetch() calls to LLM APIs with SSE streaming. This means:</p><ul><li>Support for multiple providers (Anthropic, OpenAI, Ollama) with the same codebase</li><li>Zero dependency hell</li><li>Full control over prompt engineering</li><li>Easy to add new providers</li></ul><p>The RAG (Retrieval-Augmented Generation) implementation searches your vault, builds context, and streams responses with [[wiki-link]] citations back to the source notes. Semantic search uses embeddings with cosine similarity, cached locally for speed.</p><h3>3. Vim Editor That Doesn’t Compromise</h3><p>The editor is a pure functional implementation with immutable state. Every operation (d, c, y, motion, etc.) returns a new EditorState object. This makes it:</p><ul><li>Easy to reason about</li><li>Trivial to implement undo/redo</li><li>Naturally supports dot repeat</li><li>Composable: 3dw works because the implementation composes naturally</li></ul><p>It supports:</p><ul><li>All standard motions: h/j/k/l, w/b/e, 0/$, gg/G, f/F, %</li><li>Operators with counts: 5j, 3dw, c2w</li><li>Visual mode with selection</li><li>Command mode: :w, :q, :wq, :q!, :&lt;line&gt;</li><li>Text objects would be next if there’s demand</li></ul><h3>4. Features That Scale</h3><p>ObsiTUI isn’t a minimal viewer — it’s a full vault manager:</p><p><strong>Navigation:</strong></p><ul><li>Tree sidebar with expand/collapse</li><li>Fuzzy finder with name/content/semantic search</li><li>Tag browser with drilldown</li><li>Backlinks with context</li><li>Graph view (global and local)</li><li>Navigation history (vim-style jumplist)</li></ul><p><strong>Organization:</strong></p><ul><li>Bookmarks with reorder</li><li>Note pinning (always visible)</li><li>Daily notes with auto-creation</li><li>Templates with variable replacement</li><li>Quick capture to Inbox</li></ul><p><strong>Productivity:</strong></p><ul><li>Kanban board (scans checkboxes across notes)</li><li>Pomodoro timer</li><li>Outline/TOC sidebar</li><li>Content search with context lines</li><li>File watcher with auto-refresh</li></ul><p><strong>AI-Powered:</strong></p><ul><li>RAG chat with citations</li><li>Semantic search</li><li>Similar notes by embedding</li><li>Note summarization (cached)</li><li>Tag/link suggestions</li><li>Daily AI brief generation</li><li>Note creation from description</li></ul><h3>Technical Deep Dive</h3><h3>Architecture</h3><p>ObsiTUI is built with TypeScript and Ink (React for terminals). The entire state lives in App.tsx, with 22 presentational components that render terminal UI.</p><p>Key architectural decisions:</p><p><strong>Single Source of Truth:</strong> All state in one place. No Redux, no context chaos — just React hooks and clear data flow.</p><p><strong>Pure Functional Editor:</strong> The vim editor is implemented as pure functions that take EditorState and return new EditorState. This makes testing trivial and reasoning straightforward.</p><p><strong>VaultIndex Cache:</strong> A single-pass cache of all notes builds tags, backlinks, and content search indices. Incremental updates keep it fast even with thousands of notes.</p><p><strong>File Watcher:</strong> Monitors the vault directory with 500ms debounce, so changes from Obsidian (or any editor) are reflected immediately.</p><h3>The AI Stack (Without an AI Stack)</h3><p>Here’s the core of the RAG implementation:</p><pre>// No SDK, just fetch with SSE<br>const response = await fetch(&quot;https://api.anthropic.com/v1/messages&quot;, {<br>  method: &quot;POST&quot;,<br>  headers: {<br>    &quot;anthropic-api-key&quot;: apiKey,<br>    &quot;content-type&quot;: &quot;application/json&quot;,<br>    &quot;anthropic-version&quot;: &quot;2023-06-01&quot;<br>  },<br>  body: JSON.stringify({<br>    model: &quot;claude-sonnet-4-20250514&quot;,<br>    max_tokens: 4096,<br>    stream: true,<br>    messages: [{ role: &quot;user&quot;, content: prompt }]<br>  })<br>});</pre><pre>// Parse SSE stream<br>const reader = response.body.getReader();<br>// ... streaming logic</pre><p>For embeddings, the same pattern with OpenAI or Ollama. The embedding index stores vectors in .obsitui-index/embeddings.json and uses cosine similarity for semantic search:</p><pre>function cosineSimilarity(a: number[], b: number[]): number {<br>  const dot = a.reduce((sum, val, i) =&gt; sum + val * b[i], 0);<br>  const magA = Math.sqrt(a.reduce((sum, val) =&gt; sum + val * val, 0));<br>  const magB = Math.sqrt(b.reduce((sum, val) =&gt; sum + val * val, 0));<br>  return dot / (magA * magB);<br>}</pre><p>No ML frameworks, no tensor libraries — just vanilla math.</p><h3>Obsidian Compatibility</h3><p>ObsiTUI reads your existing Obsidian configuration from .obsidian/:</p><ul><li>Daily notes settings (date format, folder)</li><li>Templates folder</li><li>Periodic notes plugin config</li><li>Templater plugin config</li></ul><p>Your vault is never modified unless you explicitly create, edit, rename, or delete something. ObsiTUI is a consumer of your Obsidian vault, not a competing format.</p><h3>The Testing Philosophy</h3><p>I’m a big believer in comprehensive testing, especially for tools I depend on daily. ObsiTUI has 301 tests across 20 test files covering:</p><ul><li>Vault scanning and note loading</li><li>Markdown parsing with inline formatting</li><li>Fuzzy search scoring</li><li>Vim editor (61 tests for operators, motions, modes)</li><li>Graph building and layout</li><li>Daily notes navigation</li><li>File operations</li><li>AI configuration and embeddings</li><li>Cache invalidation</li><li>And more</li></ul><p>When you’re building developer tools, stability matters. Tests give me confidence to ship quickly and refactor aggressively.</p><h3>What’s Next</h3><p>ObsiTUI is just getting started. Some areas I’m exploring:</p><p><strong>Enhanced AI Features:</strong></p><ul><li>Multi-turn conversations with context from multiple notes</li><li>Automatic knowledge graph generation</li><li>Smart templates that adapt based on note type</li><li>Collaborative AI (multiple agents working together)</li></ul><p><strong>Better Integration:</strong></p><ul><li>MCP (Model Context Protocol) support for Claude Code</li><li>Sync with AI agents via Spectre.ai (our memory platform)</li><li>Real-time collaboration (though that’s tricky in a TUI)</li></ul><p><strong>Performance:</strong></p><ul><li>Incremental parsing for huge vaults</li><li>Smarter caching strategies</li><li>Parallel processing for embedding generation</li></ul><p><strong>Mobile/Remote:</strong></p><ul><li>Running over SSH with full functionality</li><li>Mobile terminal apps (Termux, iSH)</li></ul><h3>Try It Yourself</h3><p>ObsiTUI is open source and available now:</p><pre>git clone https://github.com/atr0t0s/obsitui.git<br>cd obsitui<br>npm install<br>npm run build<br>node dist/cli.js /path/to/your/vault</pre><p>Or with Ollama for fully local AI:</p><pre>{<br>  &quot;ai&quot;: {<br>    &quot;chatProvider&quot;: &quot;ollama&quot;,<br>    &quot;embeddingProvider&quot;: &quot;ollama&quot;,<br>    &quot;ollamaChatModel&quot;: &quot;llama3.2&quot;,<br>    &quot;ollamaEmbeddingModel&quot;: &quot;nomic-embed-text&quot;<br>  }<br>}</pre><h3>Why This Matters</h3><p>We’re in an era where our tools should adapt to our workflows, not the other way around. If you live in the terminal, your notes should too. If you think in vim motions, your editor should speak that language. If you want AI assistance, you should be able to choose your provider and run it your way.</p><p>ObsiTUI is my answer to those needs. It’s not for everyone — if you love Obsidian’s GUI and don’t mind context switching, stick with it. It’s an excellent tool.</p><p>But if you’re a terminal person, if you value vim keybindings, if you want AI integration on your terms, and if you appreciate software that’s fast, tested, and built with a clear philosophy — give ObsiTUI a try.</p><p>I built it because I needed it. Maybe you do too.</p><p><em>Questions? Feedback? Find me on X </em><a href="https://x.com/atr0t0s"><em>@atr0t0s</em></a><em> or open an issue on </em><a href="https://github.com/atr0t0s/obsitui"><em>GitHub</em></a><em>. Star the repo if this resonates with you — it helps others discover the project.</em></p><p><em>Want to contribute? PRs welcome. The codebase is clean TypeScript with comprehensive tests, so it’s easy to jump in.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7f1ebc2fcd15" width="1" height="1" alt=""><hr><p><a href="https://ai.plainenglish.io/obsitui-why-i-built-a-terminal-ui-for-obsidian-7f1ebc2fcd15">ObsiTUI: Why I Built a Terminal UI for Obsidian</a> was originally published in <a href="https://ai.plainenglish.io">Artificial Intelligence in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What we learned building agent memory at scale]]></title>
            <link>https://medium.com/@violaris.org/what-we-learned-building-agent-memory-at-scale-10c1eeec81a4?source=rss-06a73c8d69eb------2</link>
            <guid isPermaLink="false">https://medium.com/p/10c1eeec81a4</guid>
            <category><![CDATA[persistent-memory]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[vector-embeddings]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[vector-database]]></category>
            <dc:creator><![CDATA[George Violaris]]></dc:creator>
            <pubDate>Sun, 15 Feb 2026 13:17:16 GMT</pubDate>
            <atom:updated>2026-02-15T16:03:21.397Z</atom:updated>
            <content:encoded><![CDATA[<p>We run AI agents that orchestrate complex workflows. Early on, we hit a problem that seemed simple but turned out to be fundamental: the agents couldn’t remember anything useful.</p><p>Not just “what did the user say last message” — that’s easy. But things like: what decisions did we make three conversations ago? What are this user’s preferences? What did another agent already try that didn’t work?</p><p>Every existing solution we tried had problems. Here’s what we learned building persistent memory that actually works in production.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZkVL06IxcffHjEV_ltVIFQ.png" /></figure><h3>Why agent memory matters</h3><p>Our agents need to maintain context across sessions. A user might start a task on Monday, continue it Wednesday, then come back Friday with a follow-up question. The agent needs to know what happened, what was decided, and why.</p><p>Traditional approaches break down fast:</p><p><strong>Session storage</strong> works for single conversations but forgets everything when the session ends. The agent starts fresh every time.</p><p><strong>Conversation history</strong> hits token limits. You can’t just dump 50 previous conversations into every prompt. And even if you could, most of that context isn’t relevant.</p><p><strong>Simple database lookups</strong> don’t solve the relevance problem. You can store everything, but how do you know what to retrieve? “Load the last 5 conversations” misses important context from older sessions.</p><p>The real problem isn’t storage — it’s knowing what to remember, when to remember it, and how to retrieve the right context quickly.</p><h3>What we tried first</h3><h3>Vector databases alone</h3><p>We started simple: embed every message, store in Qdrant, retrieve via similarity search when needed.</p><p>This broke immediately in production.</p><p>The agent would get a query like “What did we decide about the API rate limits?” and retrieve 10 conversations that mentioned “API” and “rate” but none of them had the actual decision. Or worse, it would retrieve an old conversation where we were still discussing options, not the final decision.</p><p>Semantic similarity doesn’t understand importance or recency. A message saying “we should probably increase the rate limit” scores just as high as “we increased the rate limit to 1000/hour” — but one is speculation and one is a decision.</p><p>We needed structure on top of similarity.</p><h3>LangChain memory modules</h3><p>We tried the built-in memory abstractions. ConversationBufferMemory, ConversationSummaryMemory, the whole set.</p><p>These are fine for demos but don’t scale. ConversationBufferMemory just grows until you hit token limits. ConversationSummaryMemory compresses old messages into summaries, but summaries lose the details you actually need later.</p><p>The bigger problem: these are designed for single conversations. They don’t handle “user returns 3 days later” or “agent needs to remember what happened in a different conversation thread.”</p><h3>RAG over conversation history</h3><p>We tried treating all past conversations as documents and using a RAG pipeline to retrieve relevant context.</p><p>This was better but still had problems:</p><ul><li>Chunking conversations loses coherence. Where do you split? Mid-conversation breaks context.</li><li>You still hit token limits on retrieval. Even if you find the relevant conversations, you can’t include all of them.</li><li>The agent can’t distinguish between “we discussed X” and “we decided X and implemented it.”</li></ul><p>The conversation format itself isn’t the right unit for memory. Sometimes you need a single fact from a long conversation. Sometimes you need the whole reasoning chain.</p><h3>Session summaries with metadata</h3><p>We tried storing session summaries in Postgres with metadata (user_id, topic, decisions made, date) and loading relevant sessions on new conversations.</p><p>The metadata helped, but we ran into new problems:</p><p>How do you decide what makes a session “relevant” to the current query? We tried keyword matching on topics — too brittle. We tried embedding the summaries — back to the similarity problem.</p><p>And summaries still lose nuance. A summary might say “discussed rate limit changes” but not capture that we tried 500/hour first and it wasn’t enough.</p><h3>The insight that changed our approach</h3><p>The breakthrough came when we realized we were solving the wrong problem.</p><p>We weren’t just building memory for individual agents helping individual users. We had orchestrator agents that needed to coordinate with each other. Multiple agents working on the same task needed to share context.</p><p>An agent helping with data analysis shouldn’t have to rediscover that the user prefers tables over charts. An agent debugging code shouldn’t have to relearn that this user’s codebase uses TypeScript strict mode.</p><p>We needed three types of memory:</p><ol><li><strong>User-specific memory</strong>: Preferences, context, decisions for a specific user</li><li><strong>Session memory</strong>: What happened in this conversation thread</li><li><strong>Shared memory</strong>: Knowledge that’s useful across users and agents</li></ol><p>That third one was the key. Most memory systems assume memory is per-user or per-conversation. But in a multi-agent system, agents need to learn from each other.</p><h3>What we built</h3><p>Our architecture combines Postgres, Qdrant, and Redis:</p><p><strong>Postgres</strong> stores structured memory entries with metadata:</p><ul><li>Memory type (preference, decision, fact, task)</li><li>Scope (user-specific, session-specific, shared pool)</li><li>Source (which agent created it, from which conversation)</li><li>Timestamp and importance score</li><li>The actual content</li></ul><p><strong>Qdrant</strong> stores vector embeddings for semantic search:</p><ul><li>Every memory entry gets embedded</li><li>We search for semantically similar memories</li><li>But we filter and rank based on Postgres metadata</li></ul><p><strong>Redis</strong> caches recent memory lookups:</p><ul><li>Same query pattern? Return cached results</li><li>Invalidate on new memories in scope</li><li>Keeps retrieval fast</li></ul><h3>Memory types and scopes</h3><p>When an agent stores a memory, it tags it:</p><pre>memory = {<br>    &quot;content&quot;: &quot;User prefers concise responses without extra explanation&quot;,<br>    &quot;type&quot;: &quot;preference&quot;,<br>    &quot;scope&quot;: &quot;user&quot;,<br>    &quot;user_id&quot;: &quot;user_123&quot;,<br>    &quot;importance&quot;: 0.8,<br>    &quot;created_by&quot;: &quot;agent_orchestrator_1&quot;,<br>    &quot;session_id&quot;: &quot;session_456&quot;<br>}</pre><p>The scope determines who can access it:</p><ul><li><strong>user scope</strong>: Only agents working with this user</li><li><strong>session scope</strong>: Only within this conversation thread</li><li><strong>shared scope</strong>: Any agent can access (pooled memory)</li></ul><p>Shared memories are things like “trying to import pandas without installing it causes ModuleNotFoundError” — facts that are useful across users.</p><h3>Retrieval strategy</h3><p>When an agent needs context, we don’t just do vector similarity search. We combine multiple signals:</p><ol><li><strong>Semantic similarity</strong> (Qdrant vector search)</li><li><strong>Recency</strong> (newer memories weighted higher)</li><li><strong>Importance</strong> (manually or automatically scored)</li><li><strong>Scope matching</strong> (user memories for this user, shared memories for everyone)</li><li><strong>Type filtering</strong> (looking for preferences vs facts vs decisions)</li></ol><p>Here’s simplified retrieval code:</p><pre>def retrieve_memories(query: str, user_id: str, session_id: str, <br>                     memory_types: list = None, limit: int = 10):<br>    # Check cache first<br>    cache_key = f&quot;mem:{user_id}:{hash(query)}&quot;<br>    cached = redis.get(cache_key)<br>    if cached:<br>        return cached<br>    <br>    # Vector search in Qdrant<br>    query_embedding = embed(query)<br>    similar = qdrant.search(<br>        query_embedding, <br>        limit=limit * 3  # Get more candidates<br>    )<br>    <br>    # Filter and rank in Postgres<br>    memory_ids = [m.id for m in similar]<br>    memories = db.query(&quot;&quot;&quot;<br>        SELECT *, <br>               calculate_relevance_score(<br>                   importance, <br>                   created_at, <br>                   scope,<br>                   type<br>               ) as score<br>        FROM memories<br>        WHERE id IN %s<br>          AND (scope = &#39;shared&#39; <br>               OR (scope = &#39;user&#39; AND user_id = %s)<br>               OR (scope = &#39;session&#39; AND session_id = %s))<br>          AND (%s IS NULL OR type = ANY(%s))<br>        ORDER BY score DESC<br>        LIMIT %s<br>    &quot;&quot;&quot;, (memory_ids, user_id, session_id, memory_types, memory_types, limit))<br>    <br>    # Cache result<br>    redis.setex(cache_key, 300, memories)  # 5 min TTL<br>    <br>    return memories</pre><p>The scoring function combines similarity (from Qdrant rank), recency (exponential decay), importance (stored score), and type match.</p><h3>Memory consolidation</h3><p>We don’t store every message as a separate memory. That would be noise.</p><p>Instead, agents decide what’s worth remembering:</p><ul><li><strong>Preferences</strong>: Explicitly stated or inferred from behavior</li><li><strong>Decisions</strong>: “We decided to use approach X”</li><li><strong>Facts</strong>: Information that’s likely to be needed later</li><li><strong>Task state</strong>: What’s been tried, what worked, what failed</li></ul><p>After each conversation, an agent reviews the session and extracts memories:</p><pre># Simplified version<br>def extract_memories(conversation: list[Message]) -&gt; list[Memory]:<br>    prompt = f&quot;&quot;&quot;<br>    Review this conversation and extract:<br>    1. User preferences (if any stated or strongly implied)<br>    2. Important decisions made<br>    3. Facts that should be remembered<br>    4. Task outcomes (what worked, what failed)<br>    <br>    Conversation:<br>    {format_conversation(conversation)}<br>    <br>    Return as JSON with type, content, importance (0-1), scope.<br>    &quot;&quot;&quot;<br>    <br>    response = llm.complete(prompt)<br>    memories = parse_memories(response)<br>    <br>    for memory in memories:<br>        store_memory(memory)<br>    <br>    return memories</pre><p>This runs asynchronously after the conversation, so it doesn’t slow down responses.</p><h3>Shared memory pooling</h3><p>This is where things get interesting.</p><p>When an agent encounters something useful — a pattern, a solution, a common error — it can flag it for the shared pool:</p><pre>memory = {<br>    &quot;content&quot;: &quot;When using the trading API, rate limit is 100 req/min not 1000&quot;,<br>    &quot;type&quot;: &quot;fact&quot;,<br>    &quot;scope&quot;: &quot;shared&quot;,<br>    &quot;importance&quot;: 0.9,<br>    &quot;source_user&quot;: None,  # Applies to everyone<br>    &quot;verified&quot;: True<br>}</pre><p>Now any agent helping any user can access this knowledge. We’re building an organizational memory, not just per-user memory.</p><p>The tricky part is quality control. We don’t want agents polluting the shared pool with wrong information. Right now we:</p><ul><li>Require high importance scores for shared memories</li><li>Let agents mark memories as “verified” after they’ve proven useful</li><li>Periodically review and prune shared memories that aren’t being accessed</li></ul><h3>What we learned at scale</h3><h3>The retrieval accuracy problem</h3><p>Our biggest issue was agents retrieving wrong context.</p><p>A user would ask “What rate limit did we set?” and the agent would retrieve a conversation about rate limits from a different project, or an old discussion before we made the final decision.</p><p>We fixed this with better metadata and explicit decision marking:</p><ul><li>When a decision is made, we create a memory type=”decision” with the final outcome</li><li>We suppress old “discussion” type memories when a decision exists</li><li>We added session_context field linking related memories together</li><li>We weight exact user+session matches much higher than similar shared memories</li></ul><p>Now retrieval looks like:</p><pre># First: check for explicit decisions in this user&#39;s history<br>decisions = retrieve_memories(<br>    query, user_id, session_id, <br>    memory_types=[&quot;decision&quot;], <br>    limit=5<br>)</pre><pre>if decisions:<br>    return decisions</pre><pre># Second: check preferences and facts<br>context = retrieve_memories(<br>    query, user_id, session_id,<br>    memory_types=[&quot;preference&quot;, &quot;fact&quot;],<br>    limit=10<br>)</pre><pre># Third: check shared pool as fallback<br>if len(context) &lt; 5:<br>    shared = retrieve_memories(<br>        query, user_id=None, session_id=None,<br>        memory_types=[&quot;fact&quot;],<br>        limit=5<br>    )<br>    context.extend(shared)</pre><pre>return context</pre><p>This layered approach dramatically reduced wrong context retrievals.</p><h3>Performance and cost</h3><p>Retrieval latency was acceptable from the start — Qdrant is fast, Postgres queries with proper indexes are fast, Redis caching helps.</p><p>Typical query: 20–40ms for cache hit, 80–150ms for cache miss.</p><p>The real cost is embeddings. Every memory needs to be embedded, and embeddings aren’t free.</p><p>We optimized by:</p><ul><li>Embedding asynchronously (don’t block the conversation)</li><li>Batching embeddings (embed 10–50 memories at once)</li><li>Only re-embedding if content changes significantly</li><li>Using smaller embedding models for less critical memories</li></ul><p>At our current scale (thousands of memories per user, millions in shared pool), storage cost in Postgres and Qdrant is negligible. Embedding cost is the main expense.</p><h3>Memory growth over time</h3><p>Users generate memories faster than we expected. After a few months, active users had hundreds or thousands of memory entries.</p><p>Most of those memories were no longer relevant.</p><p>Early memories like “User asked how to get started” aren’t useful once the user is experienced. Temporary task state like “working on debugging feature X” becomes irrelevant once the task is done.</p><p>We added automatic importance decay:</p><pre># Every week, reduce importance of old memories<br>for memory in old_memories:<br>    age_weeks = (now - memory.created_at).days / 7<br>    decay = 0.95 ** age_weeks  # 5% decay per week<br>    memory.importance *= decay<br>    <br>    if memory.importance &lt; 0.1:<br>        archive_memory(memory)  # Move to cold storage</pre><p>We don’t delete memories, we archive them. They’re still accessible if explicitly searched, but they don’t show up in normal retrieval.</p><p>This keeps the active memory set manageable.</p><h3>Debugging memory issues</h3><p>When an agent gives a wrong answer based on wrong context, debugging is hard.</p><p>We built memory inspection tools:</p><ul><li>UI showing what memories the agent retrieved for a query</li><li>Ability to see why a memory was ranked high (similarity? recency? importance?)</li><li>Manual memory editing and deletion</li><li>Memory provenance (which conversation created this?)</li></ul><p>This revealed issues like:</p><ul><li>Memories with ambiguous content that matched too many queries</li><li>Over-important memories that dominated every retrieval</li><li>Stale memories that hadn’t decayed properly</li><li>Duplicate memories from different phrasings</li></ul><p>We’re still improving the tooling. Memory is invisible to users but critical to agent performance, so observability matters.</p><h3>The hard parts we’re still figuring out</h3><h3>Memory pruning and consolidation</h3><p>Our biggest unsolved problem.</p><p>We can decay importance over time and archive low-importance memories. But that’s crude. What we really need is smart consolidation.</p><p>If an agent has 20 memories about a user’s API preferences that evolved over time, we should consolidate them into one current preference memory, not keep all 20 versions.</p><p>The challenge: how do you do this automatically without losing important context?</p><p>Summarizing loses details. But keeping everything creates clutter.</p><p>We’re experimenting with:</p><ul><li>Periodic consolidation where an LLM reviews related memories and merges them</li><li>Explicit memory superseding (“this memory replaces memories X, Y, Z”)</li><li>Memory chains that link related memories together chronologically</li></ul><p>But we haven’t found a solution that works reliably yet.</p><h3>Measuring memory quality</h3><p>How do you know if your memory system is working?</p><p>We track basic metrics:</p><ul><li>Retrieval latency (how fast)</li><li>Cache hit rate (how often we can skip retrieval)</li><li>Memory growth rate (how many memories per user per day)</li><li>Archived memory percentage (how much gets pruned)</li></ul><p>But these don’t measure quality.</p><p>We’re considering:</p><ul><li>Agent self-reporting: did the retrieved memories help?</li><li>User feedback: was the agent’s response contextually appropriate?</li><li>Manual spot-checking: review a sample of retrievals daily</li><li>A/B testing: different retrieval strategies on similar queries</li></ul><p>Right now we rely on production debugging — when something goes wrong, we investigate. But that’s reactive, not proactive.</p><h3>Cross-agent memory conflicts</h3><p>Multiple agents accessing shared memory can create conflicts.</p><p>Agent A stores “User prefers detailed technical explanations” Agent B stores “User wants concise summaries only”</p><p>Which is correct? Both might be right in different contexts.</p><p>We added context fields to preferences:</p><pre>{<br>    &quot;content&quot;: &quot;Prefers detailed explanations&quot;,<br>    &quot;type&quot;: &quot;preference&quot;,<br>    &quot;context&quot;: &quot;technical documentation&quot;,<br>    &quot;user_id&quot;: &quot;user_123&quot;<br>}</pre><p>But context matching adds complexity to retrieval. And sometimes preferences genuinely change — how do you detect that vs conflicting observations?</p><p>We don’t have a good answer yet.</p><h3>Privacy and data retention</h3><p>Users expect agents to remember things. But they also expect privacy.</p><p>Questions we’re navigating:</p><ul><li>How long should we keep memories?</li><li>Should users be able to see all their stored memories?</li><li>What happens when a user deletes their account?</li><li>Can users selectively forget specific memories?</li><li>Are shared pool memories anonymized enough?</li></ul><p>We built memory export and deletion, but the UX is still rough. Users don’t think in terms of “memory entries”, they think in terms of conversations and context.</p><h3>Cost at scale</h3><p>Right now our costs are manageable:</p><ul><li>Postgres: cheap (text and metadata storage)</li><li>Qdrant: moderate (vector storage is larger but still reasonable)</li><li>Redis: cheap (caching)</li><li>Embeddings: main cost, scales with memory creation rate</li></ul><p>But we’re not at millions of users yet. At scale, embedding costs could become significant.</p><p>We’re watching:</p><ul><li>New embedding models that are cheaper or more efficient</li><li>Quantization and compression for stored embeddings</li><li>Smarter decisions about what to embed vs store as text only</li><li>Batch processing to get better embedding API rates</li></ul><h3>What works in production</h3><p>Some practical advice from what we’ve learned:</p><p><strong>Start with a simple hybrid approach.</strong> Vector search for semantic similarity, Postgres for metadata and filtering. Don’t overcomplicate early.</p><p><strong>Tag everything with metadata from the start.</strong> You can’t add structure retroactively to thousands of memories. Type, importance, scope, timestamp — capture it upfront.</p><p><strong>Build memory inspection tools early.</strong> You need to debug retrieval issues. Being able to see “the agent retrieved these 5 memories, here’s why” is essential.</p><p><strong>Let users see what agents remember.</strong> We added a “memory” section in our UI showing the agent’s top memories about this user. Transparency helps trust.</p><p><strong>Don’t store everything.</strong> Every message doesn’t need to be a memory. Be selective. Extract what’s actually useful.</p><p><strong>Decay importance over time.</strong> Old memories rarely stay important. Reduce their weight gradually.</p><p><strong>Separate concerns.</strong> We use different storage for different purposes — Redis for speed, Postgres for structure, Qdrant for similarity. Don’t try to make one database do everything.</p><p><strong>Make scope explicit.</strong> User, session, or shared. Don’t leave it ambiguous.</p><p><strong>Test with real agents and users.</strong> Memory problems only show up in production. You can’t unit test “did the agent retrieve the right context?”</p><h3>Where this is going</h3><p>Agent memory is still immature as a field. Most frameworks treat it as an afterthought — add a memory module and you’re done.</p><p>But memory is fundamental to agent intelligence. An agent that forgets everything is barely more useful than a stateless API call.</p><p>What’s still missing:</p><p><strong>Better consolidation strategies.</strong> We need automatic ways to merge, summarize, and compress memories without losing critical information.</p><p><strong>Temporal reasoning.</strong> Agents need to understand “this information was true then but not now” vs “this is a permanent fact.”</p><p><strong>Multi-modal memory.</strong> Right now we mostly store text. But agents need to remember images, code, documents, user interactions. Relating these different modalities is unsolved.</p><p><strong>Provenance and confidence.</strong> “I remember X” should come with “because user said X in conversation Y” and a confidence level.</p><p><strong>Better evaluation.</strong> We need standard benchmarks for memory quality, not just retrieval speed.</p><p>The research is moving fast, but production systems are still figuring out basics. The gap between “memory works in a demo” and “memory works with 1000 users over 6 months” is large.</p><h3>Conclusion</h3><p>Building agent memory that works at scale is harder than it looks.</p><p>Vector databases alone aren’t enough. Memory frameworks are too simple. RAG over conversations hits limits quickly.</p><p>What worked for us: hybrid architecture (Postgres + Qdrant + Redis), explicit memory types and scopes, shared memory pools across agents, smart retrieval that combines similarity with metadata, and automatic importance decay.</p><p>What we’re still figuring out: memory consolidation, quality measurement, conflict resolution, and cost optimization at larger scale.</p><p>If you’re building agent systems, you’ll hit these problems too. Start simple, instrument everything, and expect to iterate. There’s no perfect solution yet — just tradeoffs.</p><p>What are you seeing in your agent systems? What memory strategies have worked for you? I’d be interested to hear what others are doing.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10c1eeec81a4" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>