<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Seevali Rathnayake</title><description>Software Architect. Constraint-Driven Architecture, brownfield modernisation, AI-assisted development, and performance engineering.</description><link>https://seevali.dev/</link><item><title>The Orchestration Dividend: What a Month of Mission-Command Cost, to the Cent</title><link>https://seevali.dev/blog/2026-08-05-the-orchestration-dividend/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-08-05-the-orchestration-dividend/</guid><description>Claude Fable 5 parsed every Claude Code transcript on the two machines I used in July 2026 and repriced 77,592 assistant turns at API list rates. The fleet ran the month for $6,648 in API-equivalent spend. The same tokens on the frontier model alone would have billed $15,482.</description><pubDate>Wed, 05 Aug 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Every Claude Code session I opened in July began the same way. Claude Fable 5 took the driver&amp;#39;s seat, effort pinned at ultracode, and the first thing I typed was &lt;code&gt;/mission-command&lt;/code&gt;. Thirty-one days of that habit deserved a number, so on August 4 I opened one more Fable session and asked it to read the meters on both machines I had run it from.&lt;/p&gt;
&lt;p&gt;Some context for anyone arriving cold. Mission-command is a &lt;a href=&quot;https://github.com/seevali/claude-skills/tree/main/mission-command&quot;&gt;Claude Code skill I wrote&lt;/a&gt; on July 4, 2026, midway through my first metered week with Anthropic&amp;#39;s newest frontier model. It is open source under the Apache license and installs as a Claude Code plugin. The skill borrows a doctrine from military staff work: a commander states intent and keeps hold of the decisions that are expensive to get wrong, and the units in the field do the work inside that intent. In my setup the commander is Fable 5, the frontier tier Anthropic sells above Opus at $10 per million input tokens and $50 per million output. The field units are subagents running Claude Sonnet 5 and Claude Opus, which cost a fifth and a half of Fable&amp;#39;s rate. The Opus seat ran 4.8 for most of the month and moved to Opus 5 on July 24, at the same price. Ultracode is the session mode that lets the commander spawn those workers by the dozen.&lt;/p&gt;
&lt;h2&gt;Reading the meter&lt;/h2&gt;
&lt;p&gt;Claude Code writes a transcript of every session to disk, subagent sessions included, and each assistant turn in those files carries its exact token counts. Fable wrote the analysis scripts and ran the whole investigation itself: it parsed the 3,261 transcript files across the two machines, 1.4 GB in all, kept the 77,592 turns stamped July 2026, dropped the duplicate entries that streaming produces, and priced what remained against Anthropic&amp;#39;s published API rate card. The cache rates matter more than the headline rates for this kind of workload, and it applied the full card: cache reads at a tenth of the input price, and cache writes at 1.25 times it for the five-minute tier or double for the one-hour tier.&lt;/p&gt;
&lt;p&gt;July came to &lt;strong&gt;$6,648.02&lt;/strong&gt; in API-equivalent spend. The two machines landed within nine dollars of each other, $3,319.59 and $3,328.43, despite carrying different projects on different daily rhythms. That closeness tells me the pattern reproduces.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Rate ($/MTok, in/out)&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Turns&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Output tokens&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Claude Fable 5&lt;/td&gt;
&lt;td&gt;10 / 50&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;7,231&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;9.89M&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;$2,879.06&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.8&lt;/td&gt;
&lt;td&gt;5 / 25&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;13,927&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;8.63M&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;$1,990.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5&lt;/td&gt;
&lt;td&gt;2 / 10 (intro)&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;53,497&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;4.03M&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;$1,648.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5&lt;/td&gt;
&lt;td&gt;5 / 25&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1,172&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;0.50M&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;$115.55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 4.5&lt;/td&gt;
&lt;td&gt;1 / 5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1,765&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;0.01M&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;$15.08&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I pay $200 a month for the Max 20x subscription. The same tokens on pay-as-you-go credits would have billed thirty-three times the subscription price, and the plan had paid for itself by July 4.&lt;/p&gt;
&lt;h2&gt;Where the turns ran&lt;/h2&gt;
&lt;p&gt;The transcripts distinguish main-session turns from subagent turns, and the split reads like an org chart. Fable logged 7,231 turns and 83 percent of them sat in main sessions, holding the plan. Of Sonnet&amp;#39;s 53,497 turns, 98.4 percent ran inside subagents.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Main session&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Subagents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Claude Fable 5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;6,024 turns · $2,666&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1,207 turns · $213&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.8&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;3,370 turns · $1,021&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10,557 turns · $969&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;833 turns · $59&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;52,664 turns · $1,589&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;456 turns · $63&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;716 turns · $53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 4.5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;-&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1,765 turns · $15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Fable held nine percent of the month&amp;#39;s turns and wrote 43 percent of its output tokens. Those 9.89 million tokens are the plans and the finished deliverables, the work I wanted frontier judgment on. The volume went downstream: 86 percent of all turns ran inside subagents on cheaper tiers.&lt;/p&gt;
&lt;h2&gt;Repricing the month&lt;/h2&gt;
&lt;p&gt;Claude Code fans out subagents for anyone. On its defaults, the workers take the model already in the chair, so a Fable session breeds Fable workers at Fable prices. Mission-command supplies the staffing policy: it carries a tier map and sends each dispatch to the cheapest model fit for the job, holding Fable back for the decisions where judgment compounds.&lt;/p&gt;
&lt;p&gt;That default is the counterfactual I care about, the one where I skip the skill and let Fable do everything itself, since Fable is where every session started. Billing July&amp;#39;s full token volume at Fable&amp;#39;s rates gives &lt;strong&gt;$15,482&lt;/strong&gt;. The fleet ran the same volume for $6,648, which keeps $8,834 in the account, a 57 percent cut. A milder counterfactual prices everything at Opus 4.8 rates and lands at $7,741, so the routing beats even a sensible mid-tier default while keeping frontier judgment in the loop the whole time.&lt;/p&gt;
&lt;p&gt;Per unit, the spread is wide. Fable&amp;#39;s own main-session turns cost $0.44 each. Blended across all 77,592 turns, the fleet&amp;#39;s average was $0.09. &lt;a href=&quot;https://github.com/seevali/claude-skills/tree/main/mission-command&quot;&gt;mission-command&lt;/a&gt; bought Fable&amp;#39;s decision quality at close to Sonnet&amp;#39;s unit price, and it did so twice, on two machines whose workloads never touched.&lt;/p&gt;
&lt;h2&gt;The credit ledger&lt;/h2&gt;
&lt;p&gt;I want the credit ledger clean, because one discount in this data is easy to misattribute. Without prompt caching, July&amp;#39;s tokens would have billed $38,356 fresh. That 83 percent reduction comes from Claude Code&amp;#39;s own harness, which sets cache breakpoints for every user automatically, so the platform earns that credit. Fan-out even has a cache cost of its own: each fresh subagent pays the 1.25x write premium before it reads anything from the cache, and cache writes made up a third of my bill.&lt;/p&gt;
&lt;p&gt;The routing is the skill&amp;#39;s own work. Sonnet and Haiku handled 71 percent of all turns for 25 percent of the cost, and repricing just their workload at Opus rates adds about $2,500 back onto the bill. That matches what I measured the week I adopted the skill, when the premium-model share of my tokens fell from 67 percent to 29 percent.&lt;/p&gt;
&lt;p&gt;There is one more thing the repricing hides. It assumes the same 77,592 turns could have run one after another on a single frontier context. The fleet moved 9.7 billion context tokens across 28 active days, and much of that happened while I was away from the keyboard or asleep, with up to twenty-some workers in flight at once.&lt;/p&gt;
&lt;p&gt;The subscription&amp;#39;s own meters make the same point. Max measures usage weekly, and Fable may take at most half of the all-model allowance, so a solo Fable month parks the other half of the subscription unused no matter how hard it runs. The fleet drained both pools. Week after week I closed at around 95 percent of the Fable allowance and around 95 percent of the overall allowance, with Fable&amp;#39;s half going to judgment and the worker tiers spending the rest.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://assets.seevali.dev/blog/inline/2026-08-05-the-orchestration-dividend/quota-meters-2026-07-21.webp&quot; alt=&quot;Claude subscription usage meters showing the all-model pool at 89 percent used and the Fable pool at 89 percent used, both resetting in 1 hour 54 minutes&quot;&gt;
&lt;em&gt;Two hours before the weekly reset, both pools at 89 percent and still climbing. The week closed near 95 percent.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;The dividend&lt;/h2&gt;
&lt;p&gt;Sonnet&amp;#39;s introductory pricing dies on August 31, and at standard rates July restates to $7,472. On September 1 I&amp;#39;ll hand Fable the meters again.&lt;/p&gt;
&lt;p&gt;But the number I keep coming back to isn&amp;#39;t the $8,834 the fleet kept in the account. It&amp;#39;s the shape of the month itself: 77,592 turns, 9.7 billion tokens of context, most of it moving while I slept. At fleet prices, that month cost me $200. Run serially on the model that made every decision in it, the month doesn&amp;#39;t get slower or pricier - it stops existing. Half the subscription sits unreachable, the queue outlives the quota, and no budget buys a second thread on a single mind.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the orchestration dividend. Not a discount - a month you couldn&amp;#39;t otherwise have.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. The measurements describe my personal Claude subscription usage and do not represent any organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>The Breed</title><link>https://seevali.dev/blog/2026-03-28-the-breed/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-28-the-breed/</guid><description>The AI era didn&apos;t create new knowledge. It created an expression layer for people who already had it. This is for that breed.</description><pubDate>Sat, 28 Mar 2026 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;The people who win in this era aren&amp;#39;t necessarily the ones who use AI the most. They&amp;#39;re the ones who had the most to say — and finally have a way to say it.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve spent sixteen years in software — eight of them as an architect. Sixteen years of decisions made under pressure, patterns observed across systems, mistakes that taught me something specific that I never saw written anywhere. None of it was published. Not because there was nothing to say. Because the expression layer didn&amp;#39;t exist.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m not a writer by birth. The knowledge accumulated. The words didn&amp;#39;t come.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the breed I&amp;#39;m talking about.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What the AI Era Actually Unlocked&lt;/h2&gt;
&lt;p&gt;Every conversation about AI focuses on what the model can generate. New content. New ideas. Synthesised summaries of things already written elsewhere.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the wrong frame.&lt;/p&gt;
&lt;p&gt;The unlock isn&amp;#39;t generation. It&amp;#39;s expression.&lt;/p&gt;
&lt;p&gt;A language model cannot write what it doesn&amp;#39;t know. It cannot describe the architectural decision I made at 11pm on a Tuesday when the deadline was the next morning and three approaches were on the table. It cannot surface the pattern I noticed across four different projects — the same mistake made by different teams for different reasons. It has no access to that.&lt;/p&gt;
&lt;p&gt;But I do. It&amp;#39;s in the vault.&lt;/p&gt;
&lt;p&gt;What the model can do — extraordinarily well — is take what I know and give it shape in prose. The structure, the clarity, the readability. The things I couldn&amp;#39;t produce alone, not because I lacked the knowledge but because I lacked the time, the confidence, and the habit of writing.&lt;/p&gt;
&lt;p&gt;The AI era didn&amp;#39;t create my expertise. It created the infrastructure to express it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The System Is the Expression Layer&lt;/h2&gt;
&lt;p&gt;The vault is the life&amp;#39;s work. Sixteen years worth, growing daily.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://seevali.dev/builds/lyra-the-ai-pipe/&quot;&gt;Lyra&lt;/a&gt; is the expression layer. She reads it, finds the thread, shapes it into something a person can read in five minutes and walk away having understood something real about software architecture, about building systems, about how decisions actually get made in practice.&lt;/p&gt;
&lt;p&gt;The output — this series, the blog, the LinkedIn posts, the decisions made public — is the signal. Not manufactured. Not synthetic. Extracted from a body of work that existed long before any of this was published.&lt;/p&gt;
&lt;p&gt;The second brain doesn&amp;#39;t publish itself through magic. It publishes itself because the system was designed to close the distance between what I know and what I can express.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;You Own the Context&lt;/h2&gt;
&lt;p&gt;Here is something the AI tools conversation almost never addresses: who owns the knowledge?&lt;/p&gt;
&lt;p&gt;My KB lives in plain Markdown files on a server I control. Not in Claude&amp;#39;s memory. Not in ChatGPT&amp;#39;s conversation history. Not in any tool that could change its pricing, shut down a feature, or disappear next year.&lt;/p&gt;
&lt;p&gt;When I have a useful conversation with any AI tool — any tool, on any platform — I extract the insight and send it to Lyra via WhatsApp. It gets routed into the KB. The knowledge survives the session.&lt;/p&gt;
&lt;p&gt;Whatever AI agent works with me next year — or five years from now — will have immediate access to everything I&amp;#39;ve captured: every decision, every incident, every pattern. The context doesn&amp;#39;t reset. The depth accumulates.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m not betting on one AI platform. I&amp;#39;m building something that outlasts every platform.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s what it means to own your knowledge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Answer&lt;/h2&gt;
&lt;p&gt;The first signal in this constellation opened with a comment: someone questioning whether AI-assisted writing is real. &lt;em&gt;&amp;quot;I can smell LLM from miles away.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is the full answer.&lt;/p&gt;
&lt;p&gt;The sentences in these posts — Claude wrote them. That&amp;#39;s true and I&amp;#39;ve never pretended otherwise. The model handles structure, clarity, and prose. That&amp;#39;s its job.&lt;/p&gt;
&lt;p&gt;But the architectural decisions are mine. The sixteen years of patterns are mine. The incidents that taught me what failure actually looks like in a distributed system — those are mine. The knowledge behind every sentence in this series accumulated across a career that predates large language models entirely.&lt;/p&gt;
&lt;p&gt;The question isn&amp;#39;t who wrote the sentences. It&amp;#39;s whose knowledge the sentences are expressing.&lt;/p&gt;
&lt;p&gt;If the answer is &amp;quot;knowledge I&amp;#39;ve spent a career building, captured faithfully in a system I designed and control&amp;quot; — then the smell test passes. It&amp;#39;s not the AI&amp;#39;s knowledge dressed up as mine. It&amp;#39;s my knowledge, dressed in prose.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;For the Breed&lt;/h2&gt;
&lt;p&gt;This system took a few months of obsessing to build. The philosophy took longer to understand.&lt;/p&gt;
&lt;p&gt;If you are someone who has spent years accumulating expertise — in engineering, in medicine, in finance, in any domain where depth comes from doing — and you&amp;#39;ve never published because the expression layer wasn&amp;#39;t there: it&amp;#39;s there now.&lt;/p&gt;
&lt;p&gt;The AI era isn&amp;#39;t for the people who have the most to generate. It&amp;#39;s for the people who have the most to say.&lt;/p&gt;
&lt;p&gt;Build the vault. Own the context. Let the system express it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It has a name: Metis.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Signal 7 of 7 in &lt;a href=&quot;https://seevali.dev/constellation/the-second-brain-that-publishes-itself&quot;&gt;The Second Brain That Publishes Itself&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Three Months of Capture</title><link>https://seevali.dev/blog/2026-03-28-three-months-of-capture/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-28-three-months-of-capture/</guid><description>What the vault actually looks like after three months of PARA and three weeks of automated routing. Real numbers from a real system.</description><pubDate>Sat, 28 Mar 2026 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Numbers from a real vault. Not projections — a snapshot of what actually accumulated when the system ran.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Timeline&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;January 6, 2026&lt;/em&gt; — PARA structure applied to Obsidian. The KB went from a pile of unsorted notes to a navigable system overnight.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;March 9, 2026&lt;/em&gt; — &lt;a href=&quot;https://seevali.dev/builds/nanoclaw/&quot;&gt;NanoClaw&lt;/a&gt; connected. &lt;a href=&quot;https://seevali.dev/builds/the-capture-layer/&quot;&gt;Capture became automated&lt;/a&gt;. WhatsApp in, structured vault out.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;March 28, 2026&lt;/em&gt; — This post.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Three months of structured knowledge base. Nineteen days of fully automated routing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Numbers&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;24&lt;/strong&gt; architectural decisions captured across 7 active projects — three work projects alongside Kleos, Nyx, seevali.dev, and seevali.me (the newest addition — the personal, human side of publishing, still taking shape). Every significant technical choice has a context, a tradeoff, and a recorded outcome. Not just &lt;em&gt;what&lt;/em&gt; was decided — &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35&lt;/strong&gt; clippings saved. Articles, research threads, technical deep dives — anything worth revisiting has a home. Not bookmarks. Structured notes with frontmatter and content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21&lt;/strong&gt; resource notes across domains — .NET &amp;amp; C#, AI &amp;amp; ML, Architecture Patterns. Reference material that used to live in browser tabs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12&lt;/strong&gt; progress notes — daily records of what moved on which project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12&lt;/strong&gt; issues tracked. Problems encountered, investigated, resolved — or flagged as still open.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10&lt;/strong&gt; blog drafts in &lt;code&gt;09_Showcase/&lt;/code&gt; — 5 of which were written this afternoon.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What the Numbers Don&amp;#39;t Show&lt;/h2&gt;
&lt;p&gt;Two journal entries.&lt;/p&gt;
&lt;p&gt;That number is low — not because nothing was captured, but because the WhatsApp routing was the last piece to stabilise. The journal is where informal capture lives: quick observations, end-of-day reflections, moments worth logging that don&amp;#39;t belong in a project folder. That pipeline is working now. The journal will fill.&lt;/p&gt;
&lt;p&gt;The achievements folder: empty. That&amp;#39;s the most honest signal in the vault. A year of significant work — architecture decisions made, systems shipped, patterns that solved real problems — and none of it formally recorded as achievements. They exist in decision notes and project folders. But a structured achievement record that could feed a performance review, a LinkedIn post, or a career narrative? Not yet built.&lt;/p&gt;
&lt;p&gt;That gap is next.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Velocity Observation&lt;/h2&gt;
&lt;p&gt;Seven posts in one afternoon.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s what the output side of a loaded vault looks like. Not writing from scratch — reading the journal, the decision notes, the incident logs, the timeline — and shaping what was already there. The knowledge existed before today. Today it got expressed.&lt;/p&gt;
&lt;p&gt;Before this system: those seven posts might have taken seven weeks, or never happened at all. The bottleneck was never the knowledge. It was the distance between the knowledge and a published form.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Property Nobody Mentions&lt;/h2&gt;
&lt;p&gt;Because the vault is plain files, it&amp;#39;s AI-platform agnostic. The knowledge doesn&amp;#39;t live in Claude&amp;#39;s memory or any chatbot&amp;#39;s conversation history — it lives in files I control. Whatever AI works with me next year has the same full context as today. The last post in this series covers what that actually means.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What Three Months Proves&lt;/h2&gt;
&lt;p&gt;The structure works. The routing works. The output works.&lt;/p&gt;
&lt;p&gt;The next question is what it looks like after a year.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Signal 6 of 7 in &lt;a href=&quot;https://seevali.dev/constellation/the-second-brain-that-publishes-itself&quot;&gt;The Second Brain That Publishes Itself&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>The LLM Smell Problem</title><link>https://seevali.dev/blog/2026-03-28-the-llm-smell-problem/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-28-the-llm-smell-problem/</guid><description>Someone said my blog has LLM smell. They were right about the writing. They were wrong about where the knowledge came from.</description><pubDate>Sat, 28 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Someone commented on one of my posts: &lt;em&gt;&amp;quot;This has LLM smell all over it.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;They were right.&lt;/p&gt;
&lt;p&gt;The sentences are clean. The structure is deliberate. There are no rough edges that a human writer usually leaves — the kind that come from not quite knowing what you want to say until you&amp;#39;ve said it three times.&lt;/p&gt;
&lt;p&gt;AI wrote those sentences. I&amp;#39;m not going to pretend otherwise.&lt;/p&gt;
&lt;p&gt;But the comment assumed something I want to push back on. It assumed that &amp;quot;AI wrote the sentences&amp;quot; means &amp;quot;AI generated the knowledge.&amp;quot; That the ideas, the patterns, the decisions, the hard-won lessons — all of it came from a model that read everything on the internet and learned to sound like a software architect.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s not what happened.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;What actually happened is this:&lt;/p&gt;
&lt;p&gt;At 11:42pm on a Tuesday, I sent a WhatsApp message to myself: &lt;em&gt;&amp;quot;Integration platform — don&amp;#39;t build it for the current clients, build it for the ones you&amp;#39;ll have in 18 months. Everything else is rework.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That message became a journal entry. The journal entry fed into a decision note. The decision note shaped a blog post. The blog post became a signal on my site.&lt;/p&gt;
&lt;p&gt;That kind of message, sent at odd hours, from wherever I happen to be. Decisions made under pressure. Patterns noticed after the third incident. A thought in the shower that turned out to be the clearest thing I&amp;#39;d said all week.&lt;/p&gt;
&lt;p&gt;The AI didn&amp;#39;t generate any of that. It routed it, structured it, and eventually — yes — helped write the sentences that expressed it.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s a different thing.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The commenter was asking the right question. &lt;em&gt;Who wrote this?&lt;/em&gt; is exactly the question that matters. But it has two layers:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who wrote the sentences?&lt;/strong&gt; Claude did. I gave it my thinking and it gave me prose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who wrote the knowledge?&lt;/strong&gt; I did. Sixteen years of systems, incidents, migrations, decisions, failures, and patterns. None of that came from a model. It came from doing the work.&lt;/p&gt;
&lt;p&gt;The distinction matters because there are two very different ways to use AI for content:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mode 1:&lt;/strong&gt; Give the AI a topic and let it synthesise from the internet. The sentences will be clean. The knowledge will be generic. This has LLM smell because it IS LLM — the model is the source, not just the writer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mode 2:&lt;/strong&gt; Build a system that captures your organic thinking continuously, structures it, and feeds it to the AI as the source material. The sentences will still be clean. The knowledge will be yours.&lt;/p&gt;
&lt;p&gt;I built Mode 2. This series is about how.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Over the next six signals I&amp;#39;m going to show the system from the inside:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://seevali.dev/builds/the-capture-layer/&quot;&gt;The capture layer&lt;/a&gt; — how a WhatsApp message becomes a KB note at midnight&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seevali.dev/decisions/why-para-why-obsidian/&quot;&gt;The structure&lt;/a&gt; — why PARA, why Obsidian, what I tried first that didn&amp;#39;t work&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seevali.dev/builds/lyra-the-ai-pipe/&quot;&gt;The AI pipe&lt;/a&gt; — what Lyra actually does (and what she doesn&amp;#39;t)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seevali.dev/builds/the-output-layer/&quot;&gt;The output layer&lt;/a&gt; — how KB content becomes site signals&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seevali.dev/blog/2026-03-28-three-months-of-capture/&quot;&gt;The numbers&lt;/a&gt; — three months of this running live&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://seevali.dev/blog/2026-03-28-the-breed/&quot;&gt;The Breed&lt;/a&gt; — what this era actually unlocks for people who spent years accumulating expertise before anyone was watching&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;#39;ve ever wondered whether AI writing can be authentic — not just whether the AI is &amp;quot;honest,&amp;quot; but whether the knowledge underneath is real — this is for you.&lt;/p&gt;
&lt;p&gt;The LLM smell is real. The knowledge underneath it is mine.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This is the first signal in the constellation &lt;a href=&quot;https://seevali.dev/constellation/the-second-brain-that-publishes-itself&quot;&gt;The Second Brain That Publishes Itself&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>An AI Pipeline That Reads Your Post Before It Draws the Picture</title><link>https://seevali.dev/blog/2026-03-26-ai-image-pipeline-for-blog-covers/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-26-ai-image-pipeline-for-blog-covers/</guid><description>How to generate blog cover images that actually reflect your content — using a three-tool pipeline built around visual metaphor, not stock photo logic.</description><pubDate>Thu, 26 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Stock photos are a lie. They look professional, they&amp;#39;re completely detached from the content, and every developer has seen the same three &amp;quot;hands on laptop in coffee shop&amp;quot; images recycled across a hundred different articles. The image has nothing to do with the post. It&amp;#39;s just filling a rectangle.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s what I built instead.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Concept Before the Image&lt;/h2&gt;
&lt;p&gt;The mistake with AI image generation is treating it like a search engine. You describe what you want (&amp;quot;dark moody tech blog cover&amp;quot;), you get something vague that matches the description, and it still has nothing to do with what you actually wrote.&lt;/p&gt;
&lt;p&gt;The right approach is abstraction first. Every technical post has an underlying concept — something the words are pointing at. Before generating anything, translate that concept into a visual metaphor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A post about git submodules in a monorepo → nested translucent cubes connected by thin blue threads&lt;/li&gt;
&lt;li&gt;A post about constraint-driven architecture → walls forming a corridor, the constraints themselves becoming the passage&lt;/li&gt;
&lt;li&gt;A post about giving AI context in a large codebase → a constellation of 100+ nodes, one central light giving them structure&lt;/li&gt;
&lt;li&gt;A post about a dev workflow that follows you everywhere → a single luminous thread weaving through multiple distinct spaces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The metaphor becomes the prompt. The prompt produces an image that a reader can understand before reading a word — not because it illustrates the content, but because it &lt;em&gt;is&lt;/em&gt; the concept in a different medium.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Stack: Three Tools&lt;/h2&gt;
&lt;p&gt;The pipeline uses three CLI tools chained together.&lt;/p&gt;
&lt;h3&gt;1. &lt;code&gt;generate-image&lt;/code&gt; — NanoClaw image skill&lt;/h3&gt;
&lt;p&gt;A wrapper around the Gemini AI image generation API (free tier, ~500 requests/day). Text-to-image, 16:9 aspect ratio, two models:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;generate-image &amp;quot;Three abstract geometric structures converging at a luminous point...&amp;quot; \
  --aspect 16:9 \
  --output /path/to/cover.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command outputs JSON metadata to stdout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;path&amp;quot;: &amp;quot;/path/to/cover.png&amp;quot;,
  &amp;quot;size&amp;quot;: 1323727,
  &amp;quot;model&amp;quot;: &amp;quot;gemini-2.5-flash-image&amp;quot;,
  &amp;quot;aspect_ratio&amp;quot;: &amp;quot;16:9&amp;quot;,
  &amp;quot;mode&amp;quot;: &amp;quot;generate&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generated images come out as PNG at around 1–1.5 MB.&lt;/p&gt;
&lt;h3&gt;2. &lt;code&gt;sharp&lt;/code&gt; — WebP conversion&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://sharp.pixelplumbing.com/&quot;&gt;sharp&lt;/a&gt; Node.js library handles the conversion. Quality 85 is the target — typically brings a 1.3 MB PNG down to 20–80 KB WebP, well under the 200 KB performance budget.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node -e &amp;quot;
const sharp = require(&amp;#39;./node_modules/sharp&amp;#39;);
sharp(&amp;#39;cover.png&amp;#39;)
  .webp({ quality: 85 })
  .toFile(&amp;#39;cover.webp&amp;#39;)
  .then(i =&amp;gt; console.log(&amp;#39;WebP size:&amp;#39;, (i.size/1024).toFixed(1), &amp;#39;KB&amp;#39;));
&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. &lt;code&gt;wrangler r2 object put&lt;/code&gt; — Cloudflare R2 upload&lt;/h3&gt;
&lt;p&gt;Wrangler uploads directly to a Cloudflare R2 bucket. The image becomes immediately available via Cloudflare&amp;#39;s CDN on a custom domain.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;wrangler r2 object put {your-bucket}/blog/covers/{slug}-cover.webp \
  --file cover.webp \
  --content-type image/webp \
  --remote
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;The Prompt Structure&lt;/h2&gt;
&lt;p&gt;Every cover follows the same visual identity. The style system is fixed; only the subject metaphor changes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[SUBJECT — the post&amp;#39;s concept abstracted into a visual metaphor]

STYLE AND COMPOSITION:
Dark atmospheric digital art. Deep indigo-blue void (#0f0f23 to #1a1b2e).
Abstract, geometric, architectural forms. Three depth layers.
Single soft accent glow (#7aa2f7). No text, no people, no faces.
Edge-blended, cinematic, matte painting quality. 16:9 wide composition.

AVOID:
Realistic scenes, text, faces, neon cyberpunk, busy effects, stock photo feel.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The palette — deep indigo background, soft blue accent — matches the &lt;a href=&quot;https://github.com/enkia/tokyo-night-vscode-theme&quot;&gt;Tokyo Night&lt;/a&gt; colour scheme the site runs on. The images feel native to the design rather than imported from somewhere else.&lt;/p&gt;
&lt;p&gt;Two hard rules: no text in images (titles belong in HTML, not burned into pixels), and no people or faces (abstract and structural, always).&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Frontmatter&lt;/h2&gt;
&lt;p&gt;Three fields in the blog post markdown:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;coverImage: &amp;#39;blog/covers/{slug}-cover.webp&amp;#39;
coverAlt: &amp;#39;Accurate description of what the image shows&amp;#39;
coverCaption: &amp;#39;prompt · &amp;quot;the visual metaphor that became this image&amp;quot;
— NanoBanana&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;coverAlt&lt;/code&gt; is accessibility — describe what&amp;#39;s actually in the image, not what the post is about. &lt;code&gt;coverCaption&lt;/code&gt; carries the provenance: the prompt that produced the image, attributed to NanoBanana (the image generation system). The caption is the image&amp;#39;s creative DNA, visible in the rendered figcaption.&lt;/p&gt;
&lt;p&gt;Astro renders these via Cloudflare Image Transformations — the same R2 object serves at different sizes and formats depending on context (400px for stream card thumbnails, 1200px for the cover hero, cropped 1200×630 for OG/social). The CDN handles resizing at the edge; you only store one file.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Full Run&lt;/h2&gt;
&lt;p&gt;For each new blog post:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 1. Understand the post concept, compose the visual metaphor
# 2. Generate
generate-image &amp;quot;...&amp;quot; --aspect 16:9 --output {slug}-cover.png

# 3. Convert
node -e &amp;quot;const sharp = require(&amp;#39;./node_modules/sharp&amp;#39;); sharp(&amp;#39;{slug}-cover.png&amp;#39;).webp({quality:85}).toFile(&amp;#39;{slug}-cover.webp&amp;#39;).then(i =&amp;gt; console.log((i.size/1024).toFixed(1), &amp;#39;KB&amp;#39;))&amp;quot;

# 4. Upload
wrangler r2 object put {your-bucket}/blog/covers/{slug}-cover.webp \
  --file {slug}-cover.webp --content-type image/webp --remote

# 5. Add frontmatter to blog post, push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The whole thing takes two minutes. The bottleneck is thinking of a good metaphor — the tools don&amp;#39;t slow you down.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What the Pipeline Doesn&amp;#39;t Do&lt;/h2&gt;
&lt;p&gt;It doesn&amp;#39;t generate architecture diagrams or flow charts. For anything that needs to be precise — request lifecycles, crate dependency graphs, data flow between services — AI image generation is the wrong tool. Those get Mermaid or SVG.&lt;/p&gt;
&lt;p&gt;The pipeline is specifically for atmospheric concept art: images that capture a mood or an idea, not a technical specification. That distinction matters. Knowing which type of image a section needs keeps you from reaching for the wrong tool.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Meta Moment&lt;/h2&gt;
&lt;p&gt;The cover image for this post was generated by this pipeline. The visual metaphor: a Gemini-generated abstract composition flowing through a conversion stage and arriving at a storage bucket — three geometric forms in sequence, connected by light.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the loop. The pipeline describes itself.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are drawn from my personal projects and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Five World-Class Designers in Your IDE</title><link>https://seevali.dev/blog/2026-03-26-design-council-skill/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-26-design-council-skill/</guid><description>Design Council is a Claude Skill that activates five distinct philosophical lenses on demand — Ive, Rams, Hadid, Ingels, Norman — each asking a different question about the same design.</description><pubDate>Thu, 26 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/seevali/claude-skills/tree/main/design-council&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; class=&quot;github-box&quot;&gt;View on GitHub &amp;rarr;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You&amp;#39;re looking at a sidebar. It shows signal counts — how many posts of each type are in the feed. The data is accurate. The styling is clean. Something is still wrong.&lt;/p&gt;
&lt;p&gt;You could stare at it longer. Or you could ask five of the most influential designers who ever lived what they think.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s what the Design Council does.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What It Is&lt;/h2&gt;
&lt;p&gt;Design Council is a Claude Skill — a reusable context file you install into Claude Code that activates a multi-lens design evaluation framework on demand.&lt;/p&gt;
&lt;p&gt;When you invoke &lt;code&gt;/design-council&lt;/code&gt;, five distinct philosophical lenses become active. Each one examines your design from a fundamentally different angle:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Designer&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;The Question They Ask&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jony Ive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Product design&lt;/td&gt;
&lt;td&gt;Does this respect its medium? Does every detail earn its place?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dieter Rams&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Industrial design&lt;/td&gt;
&lt;td&gt;Is this as simple as it can possibly be? What can be removed?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zaha Hadid&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;How does the user &lt;em&gt;move&lt;/em&gt; through this? Where are the thresholds?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bjarke Ingels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Where is the delight? What element serves two purposes?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Don Norman&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cognitive science&lt;/td&gt;
&lt;td&gt;Does this teach you how to use it? Does it prevent errors?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;These aren&amp;#39;t personas. They&amp;#39;re lenses — five different ways of seeing the same object.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Why Five&lt;/h2&gt;
&lt;p&gt;Any single design philosophy, applied without counterweight, produces extremes.&lt;/p&gt;
&lt;p&gt;Pure Dieter Rams produces work that&amp;#39;s honest but cold. Pure Bjarke Ingels produces work that&amp;#39;s delightful but cluttered. Pure Zaha Hadid produces spatial drama that users can get lost in.&lt;/p&gt;
&lt;p&gt;The value of five is in the &lt;em&gt;tension&lt;/em&gt;. When three lenses accept a design decision and two reject it, you&amp;#39;ve found something worth examining. When all five reject it, you&amp;#39;ve found something that needs to go. When all five accept it — that&amp;#39;s a rare and confident signal.&lt;/p&gt;
&lt;p&gt;Back to that sidebar. Here&amp;#39;s what the council said:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rams:&lt;/strong&gt; Kill it. Passive data in an interactive panel. Duplicates what the type pills already show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Norman:&lt;/strong&gt; Either kill it or make it interactive. No passive element should live alongside interactive ones — it creates false affordance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ive:&lt;/strong&gt; Reduce to two sections maximum. Mixed passive and interactive elements feel like a drawer full of mismatched utensils.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hadid:&lt;/strong&gt; The sidebar should respond to context — it should appear with the stream and disappear with it. It&amp;#39;s the stream&amp;#39;s companion, not the page&amp;#39;s furniture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ingels:&lt;/strong&gt; Add one ambient element that communicates aliveness. &amp;quot;Last signal: 2 days ago.&amp;quot;&lt;/p&gt;
&lt;p&gt;The decision became clear: remove the signal counts, keep only interactive elements, add the timestamp. None of that came from &amp;quot;I think this looks better.&amp;quot; It came from four out of five lenses reaching the same conclusion for different reasons.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;How to Use It&lt;/h2&gt;
&lt;p&gt;Install via the Claude skills system. In Claude Code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/design-council
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can invoke it at any point during design work — when reviewing a component, evaluating a layout decision, or stress-testing an interaction pattern. It also triggers automatically when Claude Code detects design-related work in context.&lt;/p&gt;
&lt;p&gt;The evaluation sequence runs each lens in order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Material Truth&lt;/strong&gt; (Ive) — what is this made of, and does the design honour that?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Honest Restraint&lt;/strong&gt; (Rams) — apply the ten principles, identify what can go&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Movement Through Space&lt;/strong&gt; (Hadid) — trace the user&amp;#39;s path, find the thresholds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Playful Function&lt;/strong&gt; (Ingels) — find where delight and utility can share a form&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human-Centered Affordance&lt;/strong&gt; (Norman) — does it teach itself? Does it prevent mistakes?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each lens produces a score (1–5) and a verdict. The tensions between verdicts are often more informative than the scores themselves.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What It Catches That You&amp;#39;d Miss&lt;/h2&gt;
&lt;p&gt;The most valuable moments are the ones where a lens flags something that felt obviously correct.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hadid on static sidebars:&lt;/em&gt; Any element that doesn&amp;#39;t respond to the page&amp;#39;s narrative is a piece of furniture in a room that&amp;#39;s supposed to feel alive. You wouldn&amp;#39;t notice it on your own — the sidebar &lt;em&gt;looks&lt;/em&gt; fine. Only the spatial-movement lens asks &amp;quot;what happens to this element as the user moves through the page?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Norman on decorative pills:&lt;/em&gt; A UI element that looks clickable but isn&amp;#39;t is a violation of perceived affordance — Don Norman&amp;#39;s term for the gap between what something is and what it tells you it can do. A purely visual review won&amp;#39;t catch this. A cognitive-science lens catches nothing else.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Rams on anything you&amp;#39;re proud of:&lt;/em&gt; The principle &amp;quot;as little design as possible&amp;quot; is not about minimalism for its own sake. It&amp;#39;s about removing every element that doesn&amp;#39;t serve the user&amp;#39;s purpose. The things you worked hardest on are exactly the things Rams will ask you to justify.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Methodology, Extracted&lt;/h2&gt;
&lt;p&gt;The underlying pattern here is reusable beyond this specific skill:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Choose evaluators with distinct, articulable philosophies&lt;/strong&gt; — not &amp;quot;perspectives,&amp;quot; but documented frameworks you can actually apply&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run diagnosis before generating solutions&lt;/strong&gt; — analyse the current state through each lens before proposing changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generate principles, not features&lt;/strong&gt; — ask what &lt;em&gt;should&lt;/em&gt; be true about this design, attributed to a specific evaluator&amp;#39;s reasoning&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let rejection patterns constrain the solution space&lt;/strong&gt; — when multiple lenses reject the same thing for different reasons, the rejection &lt;em&gt;pattern&lt;/em&gt; points toward what the right solution must not be&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use productive tension as a design goal&lt;/strong&gt; — resolving a genuine conflict between two valid philosophies often produces something neither would have produced alone&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Design Council is an implementation of this pattern. The five designers are a specific instantiation — calibrated for web and product design. The methodology is general.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>The Dev Workflow That Follows You Everywhere</title><link>https://seevali.dev/blog/2026-03-26-the-dev-workflow-that-follows-you-everywhere/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-26-the-dev-workflow-that-follows-you-everywhere/</guid><description>How NanoClaw, the BMAD Method, and Claude Code Remote Control combine into a single workflow that goes from late-night insight to shipped code — regardless of what device you are on.</description><pubDate>Thu, 26 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;It&amp;#39;s 11pm. You&amp;#39;re not at your desk. A sharp idea surfaces — the exact right way to structure the next feature, the architectural decision you&amp;#39;ve been circling for a week. You know how this usually goes: you either lose it to sleep, or you context-switch hard enough to disrupt the next day&amp;#39;s momentum.&lt;/p&gt;
&lt;p&gt;I stopped accepting that trade-off. Here&amp;#39;s the workflow I built instead.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Three Pieces, One System&lt;/h2&gt;
&lt;p&gt;The stack has three components. Each solves a different problem. Together they cover the full arc from idea to shipped code — regardless of where you are or what device you&amp;#39;re on.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://seevali.dev/builds/nanoclaw/&quot;&gt;NanoClaw&lt;/a&gt; + &lt;a href=&quot;https://seevali.dev/builds/lyra-the-ai-pipe/&quot;&gt;Lyra&lt;/a&gt;&lt;/em&gt; — a personal AI layer that runs across every channel (WhatsApp, Discord, Telegram, Slack) and maintains a persistent knowledge base. This is the capture and planning surface. It&amp;#39;s always on, it&amp;#39;s mobile-first, and it remembers everything.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://seevali.dev/builds/claude-skills/&quot;&gt;BMAD Method&lt;/a&gt;&lt;/em&gt; — an open-source AI development methodology with 21 specialized agents for every phase: analysis, product management, architecture, sprint planning, implementation, code review, retrospectives. Think of it as a structured AI-powered development team living inside Claude Code.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Claude Code Remote Control&lt;/em&gt; — the ability to start a full Claude Code session on your local machine and connect to it from any device. Your phone becomes a window into your development environment, complete with filesystem, tools, and MCP servers.&lt;/p&gt;
&lt;p&gt;The insight that makes this work: &lt;em&gt;Lyra handles pre-keyboard thinking. Claude Code handles execution. The handoff is seamless because they share the same knowledge base.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;How It Actually Works&lt;/h2&gt;
&lt;h3&gt;Phase 1: The 11pm Idea&lt;/h3&gt;
&lt;p&gt;Back to that late-night insight. Instead of fighting it or losing it, I send a message to Lyra:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brainstorm nyx: federated config system — each crate declares its own config schema
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lyra &lt;a href=&quot;https://seevali.dev/builds/the-capture-layer/&quot;&gt;captures it to the knowledge base vault&lt;/a&gt;, does a quick brainstorm session in chat, and surfaces any related notes or decisions from the KB. By the time I&amp;#39;m done with a five-minute phone conversation, the idea has structure. It&amp;#39;s no longer at risk of evaporating.&lt;/p&gt;
&lt;p&gt;No context switching. No opening a laptop. No breaking flow.&lt;/p&gt;
&lt;h3&gt;Phase 2: Planning from the Couch&lt;/h3&gt;
&lt;p&gt;The next morning, still on my phone, I hand the idea to the PM agent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nyx/pm: create PRD for federated config system based on last brainstorm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lyra adopts the PM persona (John from the BMAD roster), reads the brainstorm note from the KB, and runs a guided facilitation — asking the right product questions, surfacing assumptions, producing a structured PRD. When that&amp;#39;s done:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nyx/sm: create story for federated config — Epic 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bob (the Scrum Master agent) takes the PRD, sequences it into the sprint, and produces a story file with acceptance criteria, task breakdown, and implementation context. It lands in the project&amp;#39;s &lt;code&gt;docs/implementation-artifacts/&lt;/code&gt; directory, fully formed, waiting for the Dev agent.&lt;/p&gt;
&lt;p&gt;All of this happened on a phone. No IDE, no terminal.&lt;/p&gt;
&lt;h3&gt;Phase 3: Starting the Session&lt;/h3&gt;
&lt;p&gt;When I&amp;#39;m ready to code, I send one message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nyx: start dev session — Story 3-1: Federated Config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NanoClaw runs &lt;code&gt;claude remote-control&lt;/code&gt; on my machine, in the project directory. The session registers with Anthropic and becomes visible in the Claude app — a computer icon with a green dot in the session list. I tap it from my phone (or open it in a browser, or pick it up later on desktop). I&amp;#39;m now in a full Claude Code session with my local filesystem, MCP servers, and the complete Rust workspace available.&lt;/p&gt;
&lt;p&gt;The process runs on my machine. Nothing moved to the cloud.&lt;/p&gt;
&lt;h3&gt;Phase 4: The BMAD Dev Agent&lt;/h3&gt;
&lt;p&gt;In the session, I load the Dev agent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/bmad-agent-bmm-dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Amelia (the Dev persona) reads the story file completely before writing a single line of code. She executes tasks in order, writes tests alongside implementation, marks tasks complete only when both the code and tests pass. For this project, she runs &lt;code&gt;cargo build&lt;/code&gt; and &lt;code&gt;cargo nextest run&lt;/code&gt; after every task.&lt;/p&gt;
&lt;p&gt;The enforced discipline is the point. The agent can&amp;#39;t &amp;quot;mark it done&amp;quot; without actually doing it. That sounds obvious until you&amp;#39;ve worked with an AI that cheerfully told you everything was fine while the tests were on fire.&lt;/p&gt;
&lt;h3&gt;Phase 5: Code Review Without Mercy&lt;/h3&gt;
&lt;p&gt;When implementation is done, I start a fresh session:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nyx: start dev session — Code Review: Story 3-1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the new session (fresh context — important), I run the review workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/bmad-bmm-code-review
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This workflow reads the story file, runs &lt;code&gt;git diff&lt;/code&gt;, cross-references every changed file against the story&amp;#39;s claimed File List, validates every Acceptance Criterion is actually implemented, and audits every &lt;code&gt;[x]&lt;/code&gt; task to confirm it&amp;#39;s truly done. The target is 3–10 specific issues minimum. It&amp;#39;s explicitly adversarial — the review doesn&amp;#39;t have the dev context that might soften its stance.&lt;/p&gt;
&lt;p&gt;The findings come back structured: CRITICAL for anything marked done that isn&amp;#39;t, HIGH for missing ACs, MEDIUM for documentation gaps, LOW for everything else.&lt;/p&gt;
&lt;h3&gt;Phase 6: The Knowledge Stays&lt;/h3&gt;
&lt;p&gt;After the session ends, everything routes back to the KB vault:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nyx: sync
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Decisions, implementation notes, progress logs — they all land in the right folders. The next time I start a session for a related feature, Lyra has full context. The KB is the persistent memory that survives across sessions, across agents, across weeks.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What Changes When This Works&lt;/h2&gt;
&lt;p&gt;The traditional dev workflow assumes a specific physical and temporal setup: you at your desk, with your tools open, with uninterrupted blocks of time. Reality doesn&amp;#39;t cooperate.&lt;/p&gt;
&lt;p&gt;This stack collapses the gap between where ideas happen and where code gets written.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Capture happens in chat.&lt;/em&gt; You don&amp;#39;t need your dev environment to log an issue, make a decision, or capture a brainstorm. You need a message window.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Planning happens asynchronously.&lt;/em&gt; PRDs, architecture decisions, story breakdowns — these can happen on a phone between meetings. The structured AI methodology means you&amp;#39;re not producing vague notes; you&amp;#39;re producing artifacts that the Dev agent can act on directly.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Execution happens on your machine.&lt;/em&gt; Remote Control means the session runs locally with full environment access. The mobile app is a viewport, not a sandbox. You&amp;#39;re not constrained to what runs in a browser.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Context is never lost.&lt;/em&gt; Because everything flows through the KB, the handoff between phases doesn&amp;#39;t require you to re-explain context. Lyra knows the project. The Dev agent gets the story file. The review agent gets the diff and the story. No one is starting from scratch.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Practical Setup&lt;/h2&gt;
&lt;p&gt;If you want to build something similar:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;BMAD Method&lt;/em&gt; is open-source: &lt;code&gt;github.com/bmad-code-org/BMAD-METHOD&lt;/code&gt;. Install with &lt;code&gt;npx bmad-method install&lt;/code&gt;. The agent roster is large — start with the Quick Flow solo dev (&lt;code&gt;/bmad-agent-bmm-quick-flow-solo-dev&lt;/code&gt;) to get a feel before committing to the full phase structure.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Claude Code Remote Control&lt;/em&gt; is available now in Claude Code. &lt;code&gt;claude remote-control --name &amp;quot;session-name&amp;quot;&lt;/code&gt; starts a session. The Claude iOS app, Android app, and claude.ai/code all support connecting to it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The KB layer&lt;/em&gt; is the piece you build for yourself. An Obsidian vault with a consistent folder structure and routing rules is enough to start. The value compounds with time — every session that captures its outputs makes the next session richer.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Bigger Pattern&lt;/h2&gt;
&lt;p&gt;What I&amp;#39;ve described is a specific instantiation of a more general principle: &lt;em&gt;AI tools amplify whatever context they&amp;#39;re given.&lt;/em&gt; A Dev agent with a well-formed story file, a project_context.md with hard constraints, and a KB full of prior decisions will produce far better code than the same agent starting cold.&lt;/p&gt;
&lt;p&gt;The workflow described here is one way to solve the context problem at scale. The specific tools will evolve. The principle won&amp;#39;t.&lt;/p&gt;
&lt;p&gt;The best architecture ideas don&amp;#39;t happen at the keyboard. The question is whether your workflow lets you act on them anyway.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are drawn from my personal projects and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Constraint-Driven Architecture: Five Principles for Building in the Real World</title><link>https://seevali.dev/blog/2026-03-24-constraint-driven-architecture/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-24-constraint-driven-architecture/</guid><description>Architecture advice assumes you start fresh. You never do. Here are five principles for designing software when constraints are the starting point, not an afterthought.</description><pubDate>Sun, 22 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;The best architecture advice I ever ignored:&lt;/p&gt;
&lt;p&gt;&amp;quot;Always use microservices for scalability.&amp;quot; &amp;quot;Never mix legacy and modern in the same system.&amp;quot; &amp;quot;Rewrite it properly or don&amp;#39;t touch it at all.&amp;quot;&lt;/p&gt;
&lt;p&gt;I ignored all of it. Systems kept running. Features shipped. Teams stayed productive. And somewhere in those years of decisions — inside large, mature codebases with teams that couldn&amp;#39;t stop shipping long enough to rewrite anything — a framework crystallised.&lt;/p&gt;
&lt;p&gt;Not from theory. From repetition.&lt;/p&gt;
&lt;h2&gt;The problem with architecture advice&lt;/h2&gt;
&lt;p&gt;Most of it assumes you&amp;#39;re starting fresh. Clean slate, unlimited runway, a team of senior engineers who all agree on the direction.&lt;/p&gt;
&lt;p&gt;That is not reality.&lt;/p&gt;
&lt;p&gt;Reality is a legacy system that even your own engineers don&amp;#39;t fully understand. Business pressure to ship features while you modernise. A mix of experience levels where the same mistake recurs in different forms. Environments where zero-downtime isn&amp;#39;t aspirational — it&amp;#39;s a hard requirement.&lt;/p&gt;
&lt;p&gt;Standard advice fails here because it optimises for correctness, not for what&amp;#39;s &lt;em&gt;possible given your constraints&lt;/em&gt;. The question is never &amp;quot;what&amp;#39;s the best architecture?&amp;quot; The question is always &amp;quot;what can we actually do, with this team, this system, this timeline, and this risk tolerance?&amp;quot;&lt;/p&gt;
&lt;p&gt;That reframe is the whole thing. Everything else follows from it.&lt;/p&gt;
&lt;h2&gt;Five principles for constrained environments&lt;/h2&gt;
&lt;h3&gt;1. Unify context, not just code&lt;/h3&gt;
&lt;p&gt;Multi-repo systems create a cost that rarely appears in any metric: duplicated thinking. Teams brainstorm the same problem twice, document discovery twice, mentally reload context every time they cross a repository boundary. In large codebases spread across multiple repositories sharing a common data layer, every significant feature touches at least two repos — which means two CI pipelines, diverging conventions, and tooling that can&amp;#39;t see the full picture.&lt;/p&gt;
&lt;p&gt;The obvious answer is a full monorepo migration. Collapse everything, import all histories, unify the tooling. Textbook solution. Also the one that disrupts every team&amp;#39;s daily workflow overnight and carries substantial migration risk in high-availability environments.&lt;/p&gt;
&lt;p&gt;A better approach: build the unified layer additively. An Nx monorepo using Git submodules as a bridge, for example, can wire large multi-project codebases together with centralised tooling and documentation while letting individual teams continue working within their existing repo if they choose. The monorepo becomes the source of truth without being a forcing function. Teams opt in as confidence grows.&lt;/p&gt;
&lt;p&gt;The insight generalises: when consolidation carries disruption risk, find the additive path. Create the unified view first. The migration follows when the value is proven.&lt;/p&gt;
&lt;h3&gt;2. Optimize for outcomes, not metrics&lt;/h3&gt;
&lt;p&gt;Performance problems in legacy systems are typically everywhere. N+1 query patterns, inefficient ORM translations, shared database objects degrading query plans, audit mechanisms that compound under load. The obvious move: profile everything, fix everything, make it all faster.&lt;/p&gt;
&lt;p&gt;Don&amp;#39;t do that.&lt;/p&gt;
&lt;p&gt;Instead, identify the specific business outcome you&amp;#39;re trying to reach. Multi-tenant deployment. Sub-second response on a critical user path. Headroom for a projected load increase. Then — and only then — identify which bottlenecks are directly in the path of that outcome. Not everything slow. The subset of issues blocking the specific goal.&lt;/p&gt;
&lt;p&gt;Fix those. Explicitly defer everything else.&lt;/p&gt;
&lt;p&gt;Teams that apply this principle find that a small fraction of the issues — often fewer than ten — account for the constraint that actually matters. Ugly code that doesn&amp;#39;t block the goal gets left alone. Not because it&amp;#39;s acceptable, but because touching it consumes time and introduces risk without moving toward the outcome. &amp;quot;Make it faster&amp;quot; is not a goal. It&amp;#39;s a direction without a destination. Tie every optimisation to a specific business outcome or you optimise forever and ship nothing.&lt;/p&gt;
&lt;h3&gt;3. Strangle safely&lt;/h3&gt;
&lt;p&gt;Here is the terrifying truth about legacy systems: even the people who built them don&amp;#39;t know the full feature catalog. Decades of development, team turnover, undocumented business rules embedded in stored procedures and migration scripts — it&amp;#39;s a system that works for reasons nobody fully understands anymore.&lt;/p&gt;
&lt;p&gt;A big-bang rewrite fails in this environment. You don&amp;#39;t know what you&amp;#39;re rewriting. You can&amp;#39;t test what you&amp;#39;ve never specified.&lt;/p&gt;
&lt;p&gt;The Strangler Fig pattern is the right model, but with one constraint that most descriptions leave out: in high-availability environments, you cannot fail mid-flight. Every migration step must be reversible. Every new service must prove itself in production before the old path is deprecated. The legacy path stays live until the foundation underneath it is genuinely solid.&lt;/p&gt;
&lt;p&gt;Before a line of code moves, the responsible approach is a documentation pass: feature catalog, API contracts, data migration rules, rollback procedures. That work isn&amp;#39;t ceremony — it&amp;#39;s the risk management work. Teams that skip it discover missing business rules mid-migration, when the cost of discovery is highest. Understanding what you have before deciding what you&amp;#39;re building is what separates modernisation from gambling.&lt;/p&gt;
&lt;p&gt;Legacy modernisation is not a technical project. It&amp;#39;s a risk management operation. The question isn&amp;#39;t &amp;quot;how do we rewrite this?&amp;quot; It&amp;#39;s &amp;quot;how do we guarantee we don&amp;#39;t break what we don&amp;#39;t fully understand?&amp;quot;&lt;/p&gt;
&lt;h3&gt;4. Equalize with AI&lt;/h3&gt;
&lt;p&gt;Not all engineers produce consistent output. This is something architects don&amp;#39;t say out loud, but it shapes every decision about process, review gates, and team structure. Senior developers catch things juniors miss. Institutional knowledge walks out the door when people leave. The same anti-patterns recur because human discipline erodes under pressure.&lt;/p&gt;
&lt;p&gt;You can try to fix this with process: more code reviews, more documentation, more training. All of that is valuable and all of it is insufficient. Process requires discipline to follow, and discipline is exactly what degrades when the team is under delivery pressure.&lt;/p&gt;
&lt;p&gt;A more durable approach: embed consistency in the tools rather than the team. AI commands covering the common patterns and anti-patterns specific to your codebase. Agent definitions loaded with project-specific context — the naming conventions, the data access boundaries, the service layer contracts, the soft-delete and auditing rules. Integration with the planning and ticket system so that the relevant specifications load automatically into each AI session.&lt;/p&gt;
&lt;p&gt;In projects where I&amp;#39;ve applied this — including my own: NanoClaw, Kleos, Nyx — the pattern holds. A developer with well-configured AI tooling produces work that clears a higher bar, not because they became more senior overnight, but because the AI surfaces what they&amp;#39;d miss. The floor rises. The variance shrinks.&lt;/p&gt;
&lt;p&gt;AI doesn&amp;#39;t replace engineers. It raises the floor. Stop trying to make humans consistent. Make your tooling consistent instead.&lt;/p&gt;
&lt;h3&gt;5. Prove through practice&lt;/h3&gt;
&lt;p&gt;This piece is principle five.&lt;/p&gt;
&lt;p&gt;I spent years doing this work without being able to articulate it. Strong technical contributions, real impact — and no durable surface to show for it. Not because the work wasn&amp;#39;t there. Because I hadn&amp;#39;t built the infrastructure to make it visible.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://seevali.dev/&quot;&gt;seevali.dev&lt;/a&gt; exists as a proof-of-work system. Not a portfolio page with a list of technologies. A Signal Feed: a stream of thoughts, decisions, build logs, and numbers that shows how I think, not just what I&amp;#39;ve shipped. The git submodules post is a war story. The shower post is pattern recognition in real time. This post is the framework that ties it all together.&lt;/p&gt;
&lt;p&gt;The evidence I can point to publicly: &lt;a href=&quot;https://seevali.dev/builds/nanoclaw/&quot;&gt;NanoClaw&lt;/a&gt; — a multi-channel AI assistant with a knowledge base wired in as live context, built and running. &lt;a href=&quot;https://seevali.dev/builds/kleos/&quot;&gt;Kleos&lt;/a&gt; and &lt;a href=&quot;https://seevali.dev/builds/nyx/&quot;&gt;Nyx&lt;/a&gt; — projects scaffolded and tracked through a vault-based development lifecycle I built to manage my own work. These are not claims. They are systems.&lt;/p&gt;
&lt;p&gt;Don&amp;#39;t tell people you&amp;#39;re a good architect. Show them what you&amp;#39;ve architected. The credibility is in the specificity.&lt;/p&gt;
&lt;h2&gt;The meta-principle&lt;/h2&gt;
&lt;p&gt;Architecture is constraint management, not technology selection.&lt;/p&gt;
&lt;p&gt;Every principle above starts with constraints: the team that can&amp;#39;t absorb overnight disruption, the business goal that defines what &amp;quot;better performance&amp;quot; actually means, the legacy system that will break in unknown ways if you move too fast, the human variance that erodes in any process-heavy solution.&lt;/p&gt;
&lt;p&gt;The constraint shapes the solution. On a greenfield project with an unlimited runway, I&amp;#39;d make different decisions — and I&amp;#39;d still start by mapping the constraints, because there are always constraints. Timeline. Team. Budget. Existing integrations. Customer commitments. Regulatory environment.&lt;/p&gt;
&lt;p&gt;The architect&amp;#39;s job is not to find the theoretically correct answer. It&amp;#39;s to navigate the constraint space toward an outcome that&amp;#39;s better than where you started, using approaches the team can actually adopt and with risk the organisation can actually absorb.&lt;/p&gt;
&lt;p&gt;Textbook answers assume the constraints away. Real architecture works within them.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the whole framework. I&amp;#39;m writing it down because the next time someone is staring at a system they didn&amp;#39;t build and being told &amp;quot;the only real answer is to rewrite it all&amp;quot; — I want there to be a different answer in the room.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nx.dev/docs/kb/what-is-a-monorepo&quot;&gt;Nx: What is a Monorepo?&lt;/a&gt; — the case for a single project graph and shared tooling, and what you gain before any repository actually moves&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sre.google/sre-book/service-level-objectives/&quot;&gt;Google SRE Book: Service Level Objectives&lt;/a&gt; — how to turn &amp;quot;make it faster&amp;quot; into a target you can argue about, measure, and declare done&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig&quot;&gt;Microsoft Learn: Strangler Fig pattern&lt;/a&gt; — implementation guidance for incremental replacement, including routing, coexistence, and rollback&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/articles/patterns-legacy-displacement/&quot;&gt;Martin Fowler: Patterns of Legacy Displacement&lt;/a&gt; — a catalogue of legacy-replacement patterns framed around risk rather than technology choice&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>The Architecture Happens in the Shower</title><link>https://seevali.dev/blog/2026-03-21-architecture-happens-in-the-shower/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-21-architecture-happens-in-the-shower/</guid><description>The best architectural decisions happen away from the keyboard. The discipline is having a system that captures them before they fade.</description><pubDate>Sat, 21 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;On March 13th I was sitting at my desk at 8:30pm, talking through a problem with my AI assistant. &lt;a href=&quot;https://seevali.dev/builds/kleos/&quot;&gt;Kleos&lt;/a&gt; — a voice-first PWA I&amp;#39;d been building for months — had a redundant AI layer. The local Ollama model doing categorisation was a weaker version of something my agent backend already handled. The voice capture and PWA shell were good. The brain was wrong.&lt;/p&gt;
&lt;p&gt;We worked through the pivot: strip the local AI, wire Kleos into the &lt;a href=&quot;https://seevali.dev/builds/nanoclaw/&quot;&gt;NanoClaw&lt;/a&gt; backend as a web dashboard. Chat interface. KB browser. Voice conversation with the full knowledge base behind it. Clean decision. I wrote it up. Three artifacts in the KB.&lt;/p&gt;
&lt;p&gt;Then I went to take a shower.&lt;/p&gt;
&lt;h2&gt;The shower is where it clicked&lt;/h2&gt;
&lt;p&gt;Standing under hot water, not thinking about code, the picture rearranged itself.&lt;/p&gt;
&lt;p&gt;I&amp;#39;d been building three projects as separate things: NanoClaw (the agent backend), Kleos (a web app), and &lt;a href=&quot;https://seevali.dev/builds/nyx/&quot;&gt;Nyx&lt;/a&gt; (a Rust terminal I hadn&amp;#39;t started yet). In that moment, I saw them as &lt;a href=&quot;https://seevali.dev/blog/2026-03-24-one-brain-three-interfaces/&quot;&gt;one system with three surfaces&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Lyra (NanoClaw — Claude + KB)
├── WhatsApp / Telegram  — mobile, async
├── Kleos (PWA)          — web, voice, rich UI
└── Nyx (terminal)       — dev workflow, local AI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same brain. Same knowledge. Different interface for different contexts. WhatsApp for quick captures while walking the dog. Kleos for deep voice conversations with dashboards. Nyx for dev sessions where terminal context — current directory, active project, git branch — makes the AI responses sharper.&lt;/p&gt;
&lt;p&gt;More importantly, I saw what Nyx should &lt;em&gt;not&lt;/em&gt; be. The tempting idea — &amp;quot;let Lyra execute commands through Nyx&amp;quot; — was a trap. Claude Code already does that, built by a team with resources I don&amp;#39;t have. Nyx&amp;#39;s job isn&amp;#39;t execution. It&amp;#39;s ambient context: detect the project you&amp;#39;re in, surface relevant KB knowledge, let you log progress without leaving the terminal.&lt;/p&gt;
&lt;p&gt;I got out, dried off, sat back down. Twenty minutes later I had a brainstorm document with the full ecosystem diagram, the integration direction (build this / don&amp;#39;t build this), and the open source narrative: NanoClaw as the platform, Kleos and Nyx as reference clients.&lt;/p&gt;
&lt;h2&gt;This is not about showers&lt;/h2&gt;
&lt;p&gt;The point is not &amp;quot;go take a shower.&amp;quot; The point is that pattern recognition — the kind that connects three separate projects into one coherent system — doesn&amp;#39;t fire when you&amp;#39;re staring at a diff. It fires when you step away. When you stop holding the problem actively and let the background process run.&lt;/p&gt;
&lt;p&gt;Every experienced engineer knows this. The fix that comes to you while making coffee. The design that crystallises on the drive home. The naming that lands while you&amp;#39;re falling asleep.&lt;/p&gt;
&lt;p&gt;What most of us don&amp;#39;t have is the infrastructure to capture it.&lt;/p&gt;
&lt;p&gt;I came back from that shower and had a &lt;a href=&quot;https://seevali.dev/decisions/why-para-why-obsidian/&quot;&gt;structured knowledge base&lt;/a&gt; waiting for me. A routing system that knew brainstorm notes go in &lt;code&gt;01_Projects/Nyx/Brainstorm/&lt;/code&gt;. An AI assistant that could take a raw idea and help me shape it into a decision document, a set of scoped artifacts, and a content plan update — all in twenty minutes, before the clarity faded.&lt;/p&gt;
&lt;h2&gt;The real investment&lt;/h2&gt;
&lt;p&gt;Building a personal knowledge system is not about productivity hacks. It&amp;#39;s about reducing the latency between insight and artifact. The gap between &amp;quot;I see it&amp;quot; and &amp;quot;it&amp;#39;s written down, structured, and findable.&amp;quot;&lt;/p&gt;
&lt;p&gt;That evening session produced a project pivot, an ecosystem architecture, three new content pieces for this site, and the clearest product vision I&amp;#39;d had in weeks. Not because I had a breakthrough. Because I had the system to catch it.&lt;/p&gt;
&lt;p&gt;The architecture happens in the shower. The discipline is what&amp;#39;s waiting when you get out.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>What a Twenty-Year-Old Codebase Could Become</title><link>https://seevali.dev/blog/2026-03-24-what-a-twenty-year-old-codebase-could-become/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-24-what-a-twenty-year-old-codebase-could-become/</guid><description>The architectural north star worth holding when a monolith has outlived its original design.</description><pubDate>Thu, 19 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Nobody builds a twenty-year-old monolith on purpose. You build a reasonable application, ship it, and then two decades of features, team rotations, and regulatory changes happen. What you end up with is a system that works — reliably, in production, under load — but one that nobody fully understands anymore.&lt;/p&gt;
&lt;p&gt;If you maintain one of these systems, you recognize the profile. Hundreds of projects in a single solution. Multiple interconnected applications sharing a single database. Business logic buried in stored procedures that nobody wrote tests for. Audit trails implemented through database triggers with non-trivial performance overhead. A custom schema migration tool that rebuilds everything on every deployment regardless of what changed.&lt;/p&gt;
&lt;p&gt;The system works. That&amp;#39;s both the achievement and the trap. &amp;quot;Works&amp;quot; is not the same as &amp;quot;built for what comes next.&amp;quot;&lt;/p&gt;
&lt;p&gt;This post is not a migration plan. It&amp;#39;s the architectural north star — the end state worth holding in mind when you&amp;#39;re making tactical decisions inside the constraints of a legacy enterprise codebase. Not what you build tomorrow. What you&amp;#39;re building toward.&lt;/p&gt;
&lt;h2&gt;The domain boundaries already exist&lt;/h2&gt;
&lt;p&gt;A monolith like this knows everything about everything. Every functional area — compliance, operations, scheduling, inventory, reporting — woven through the same codebase, the same database, the same deployment pipeline. Change one domain and you risk breaking all of them.&lt;/p&gt;
&lt;p&gt;But if you listen to how the business talks about the system, the boundaries are already there. Different departments own different capabilities. Different regulations govern different concerns. These aren&amp;#39;t arbitrary technical divisions — they&amp;#39;re how the business itself is organized.&lt;/p&gt;
&lt;p&gt;The vision starts with Domain-Driven Design — not as a philosophy exercise, but as a practical decomposition strategy. Each bounded context becomes an independently deployable microservice with its own database, its own deployment lifecycle, and eventually its own team. An operator creating a task in one domain shouldn&amp;#39;t need to worry about — or risk breaking — the scheduling system in another.&lt;/p&gt;
&lt;p&gt;The decomposition isn&amp;#39;t just technical. It&amp;#39;s organizational. When a domain owns its own service, the team responsible for that domain can deploy, scale, and iterate independently. The blast radius of any change shrinks from &amp;quot;the entire platform&amp;quot; to &amp;quot;one bounded context.&amp;quot;&lt;/p&gt;
&lt;h2&gt;The facade that makes migration safe&lt;/h2&gt;
&lt;p&gt;You cannot rewrite a twenty-year-old system in one go. The feature catalog alone is unknowable without months of discovery work. Undocumented business rules live in stored procedures that nobody wrote tests for because nobody expected them to still be running two decades later.&lt;/p&gt;
&lt;p&gt;The Strangler Fig pattern is the answer, and an API Gateway is what makes it surgical.&lt;/p&gt;
&lt;p&gt;The gateway sits between clients and the system. On day one, it routes everything to the monolith. As microservices come online — starting with the cleanest domain boundaries — the gateway reroutes specific requests to the new service. The monolith never notices. The clients never notice. The risk is contained to one domain at a time.&lt;/p&gt;
&lt;p&gt;The gateway handles more than routing. Authentication, rate limiting, request transformation, response aggregation, monitoring — all the cross-cutting concerns that individual services shouldn&amp;#39;t carry. It&amp;#39;s the seam between old and new, and it&amp;#39;s designed to be temporary. As the monolith shrinks, the gateway&amp;#39;s routing table shifts. Eventually, there&amp;#39;s nothing left to strangle.&lt;/p&gt;
&lt;p&gt;The critical discipline: every migration step must be reversible. Every new service proves itself in production before the old code path is deprecated. You don&amp;#39;t decommission the monolith piece by piece — you earn the right to, one domain at a time.&lt;/p&gt;
&lt;h2&gt;Events over shared state&lt;/h2&gt;
&lt;p&gt;In a monolith, components communicate through shared database tables. Module A writes a row, Module B reads it. They&amp;#39;re coupled through data rather than explicit contracts. Nobody can map the full dependency graph because it lives in SQL queries scattered across thousands of files.&lt;/p&gt;
&lt;p&gt;The target architecture inverts this. Apache Kafka becomes the backbone — an event bus where microservices publish what happened and subscribe to what they care about.&lt;/p&gt;
&lt;p&gt;Consider what this looks like in practice. An operator creates a new work order in a regulated enterprise system:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Operations service writes the work order to its own database and, in the same transaction, writes a &amp;quot;WorkOrderCreated&amp;quot; event to an outbox table&lt;/li&gt;
&lt;li&gt;A background process — or Debezium, doing change data capture against the outbox — picks up the event and publishes it to Kafka&lt;/li&gt;
&lt;li&gt;The Compliance service consumes the event and runs impact checks. Does this work order trigger any regulatory holds?&lt;/li&gt;
&lt;li&gt;If a compliance hold is required, it emits a &amp;quot;ComplianceHoldTriggered&amp;quot; event&lt;/li&gt;
&lt;li&gt;The Scheduling service picks that up and adjusts timelines for affected resources&lt;/li&gt;
&lt;li&gt;Notifications fire downstream to the relevant teams&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each service reacts to events and emits its own. No service calls another directly. No shared database tables. If the Scheduling service is temporarily unavailable, the saga orchestrator triggers compensating actions — revert the compliance check, roll back to a known state, retry when the service recovers.&lt;/p&gt;
&lt;p&gt;The Outbox pattern is what makes this reliable. The database write and the event publication happen in a single local transaction. You never end up in a state where the work order was created but the event was lost, or the event was published but the work order wasn&amp;#39;t saved. Debezium watches the outbox table via change data capture and streams unprocessed events to Kafka, ensuring eventual consistency without polling.&lt;/p&gt;
&lt;p&gt;This is choreography-based saga orchestration — long-running transactions that span multiple services without requiring them to know about each other&amp;#39;s internals. Loose coupling that actually holds up under failure.&lt;/p&gt;
&lt;h2&gt;One database, one truth — per service&lt;/h2&gt;
&lt;p&gt;In a legacy monolith, the single biggest coupling surface is often the shared database. Every application, every feature, every domain — one database instance with hundreds of scalar functions degrading query plans, cross-domain joins nobody dares refactor, and a schema carrying the archaeology of decades of business decisions.&lt;/p&gt;
&lt;p&gt;The vision is database-per-service. Operations owns its schema. Compliance owns its own. No shared tables. No cross-service joins. A schema migration in one domain cannot bring down another.&lt;/p&gt;
&lt;p&gt;This introduces a genuine challenge: complex reads that currently rely on cross-domain joins. The answer is CQRS — Command Query Responsibility Segregation. Writes go to each service&amp;#39;s own database through its own models. Reads are served from materialized views that aggregate data across domains, kept current by consuming events from Kafka. A Redis caching layer handles hot-path queries. For extreme scale, database replication for read-heavy workloads and sharding for horizontal growth.&lt;/p&gt;
&lt;p&gt;Beyond isolation, database-per-service is the path out of vendor lock-in. Legacy .NET systems are often deeply coupled to SQL Server — not just the engine, but stored procedures carrying business logic, database-scheduled agent jobs, trigger-based audit trails, and proprietary migration tooling. Entity Framework Core is the bridge. Map the schema incrementally. Move business logic from stored procedures into the application layer where it can be unit tested, versioned, and debugged with modern tooling. Replace brute-force migration tools with EF Core&amp;#39;s incremental migrations — targeted changes, versioned, no unnecessary re-runs.&lt;/p&gt;
&lt;p&gt;Trigger-based audit trails deserve special attention. SQL triggers are implemented differently across database vendors, making them a hard vendor lock. Three alternatives are worth exploring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Change Data Capture&lt;/strong&gt; — database-level change logging, vendor-provided but more portable than custom triggers. The most pragmatic first step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event Sourcing&lt;/strong&gt; — store domain events as the source of truth, rebuild state at any point in time. Architecturally pure, but adds significant application-level complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured audit logging&lt;/strong&gt; — application-level audit events shipped to a purpose-built store via frameworks like Serilog with an ELK backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each has trade-offs. But all three are more portable than triggers wired to a specific database vendor.&lt;/p&gt;
&lt;h2&gt;Seeing everything&lt;/h2&gt;
&lt;p&gt;In many legacy enterprise systems, the monitoring story is fragmented. Some applications write to log files. Others rely on Windows Event Viewer. Background process failures can go unnoticed for days. When something breaks, the first question is often &amp;quot;where do I even look?&amp;quot;&lt;/p&gt;
&lt;p&gt;Unified observability changes that equation. The vision is an ELK stack — Elasticsearch, Logstash, Kibana — with Beats agents collecting from every surface:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Filebeat&lt;/strong&gt; on every server generating log files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Winlogbeat&lt;/strong&gt; on Windows servers monitoring Event Viewer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logstash&lt;/strong&gt; parsing and enriching logs with environment and application metadata&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt; indexing everything into a searchable, time-series store&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kibana&lt;/strong&gt; turning raw data into dashboards and real-time visibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ElastAlert&lt;/strong&gt; triggering notifications on error rate spikes, service degradation, and silent failures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn&amp;#39;t debugging. It&amp;#39;s confidence. When you can see every service, every background job, every failure mode — you deploy with conviction instead of anxiety. You catch silent failures in minutes instead of days. You see resource exhaustion building before it takes systems offline.&lt;/p&gt;
&lt;p&gt;Observability is the foundation that makes everything else in this vision possible. You can&amp;#39;t safely strangle a monolith if you can&amp;#39;t see what&amp;#39;s happening on both sides of the migration.&lt;/p&gt;
&lt;h2&gt;The north star, not the roadmap&lt;/h2&gt;
&lt;p&gt;A vision like this is not a plan. There&amp;#39;s no timeline, no budget, no approval chain required for it to be valuable. It&amp;#39;s the picture that gives tactical decisions a direction.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re an architect inside a legacy enterprise system, you face constraint-driven decisions every day. The team can&amp;#39;t absorb overnight disruption. The business needs features now, not infrastructure. Regulatory environments punish failure. In that context, having a north star — even one you can&amp;#39;t reach this quarter — changes how you evaluate every fork in the road. Without it, every tactical decision is a coin flip. With it, every tactical decision is a step — maybe small, maybe sideways — toward something coherent.&lt;/p&gt;
&lt;p&gt;Some elements of a vision like this are achievable incrementally. Moving background jobs from database-scheduled agents to application-level schedulers. Adopting an ORM for new development paths while leaving legacy data access untouched. Centralizing documentation and tooling across previously siloed repositories. These are steps that don&amp;#39;t require anyone to approve the full decomposition.&lt;/p&gt;
&lt;p&gt;The full target — true microservices, event-driven communication, database-per-service — is a horizon goal. And that&amp;#39;s fine. A system that has survived twenty years didn&amp;#39;t get there by rushing. The next twenty years don&amp;#39;t need to rush either. What they need is a direction — clear enough that when an opportunity opens, when a new feature could be built as a service instead of another module bolted onto the monolith, you know which way to walk.&lt;/p&gt;
&lt;p&gt;The system has survived by being good enough. The north star is about what comes after good enough.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/StranglerFigApplication.html&quot;&gt;Martin Fowler: Strangler Fig Application&lt;/a&gt; — the canonical description of replacing a system incrementally behind a facade, one capability at a time&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microservices.io/patterns/data/transactional-outbox.html&quot;&gt;microservices.io: Transactional Outbox&lt;/a&gt; — writing the event in the same local transaction as the state change, then publishing it from the outbox&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/CQRS.html&quot;&gt;Martin Fowler: CQRS&lt;/a&gt; — separating the read model from the write model, and an honest account of the cost that separation carries&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/docs/reference/beats&quot;&gt;Elastic: Beats&lt;/a&gt; — the log and metric shippers behind an ELK-style observability stack, including Filebeat and Winlogbeat&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Giving AI Context in Large Brownfield Codebases</title><link>https://seevali.dev/blog/2026-03-24-giving-ai-context-in-large-brownfield-codebases/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-24-giving-ai-context-in-large-brownfield-codebases/</guid><description>AI generates plausible but wrong code in large legacy codebases — not randomly wrong, but wrong in ways that quietly erode conventions. Here&apos;s how to prevent it.</description><pubDate>Mon, 16 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;If you drop an AI coding assistant into a large brownfield codebase without guardrails, it will generate plausible, wrong code. Not randomly wrong. Wrong in ways that look idiomatic, compile cleanly, and pass a casual review — and then quietly violate the conventions that years of engineers built on top of, in ways that only surface when something breaks.&lt;/p&gt;
&lt;p&gt;This post is about how to prevent that. Specifically: how to give AI assistants the structured, ambient context they need to work correctly in complex legacy systems, and why that context — not the model, not the prompt — is the actual multiplier.&lt;/p&gt;
&lt;h2&gt;The failure mode&lt;/h2&gt;
&lt;p&gt;In a large multi-project .NET codebase, a common pattern is maintaining multiple generations of ORM simultaneously. A legacy application using EF6 coexists with newer services using EF Core, both touching the same underlying database. Different mapping strategies, different transaction patterns, different rules about which layer owns which tables.&lt;/p&gt;
&lt;p&gt;When you drop an AI assistant into that environment without providing that context, you tend to get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EF Core LINQ syntax in codebases that use EF6&lt;/li&gt;
&lt;li&gt;Wrong primitive types on timestamp columns (e.g., &lt;code&gt;DateTime&lt;/code&gt; where the convention requires &lt;code&gt;DateTimeOffset&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Direct database access that bypasses the service layer&lt;/li&gt;
&lt;li&gt;Entity or table naming that ignores the project&amp;#39;s established prefix conventions&lt;/li&gt;
&lt;li&gt;Repository implementations that silently omit soft-delete handling&lt;/li&gt;
&lt;li&gt;Cross-system calls that ignore the documented integration boundaries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these is catastrophic in isolation. Collectively, over weeks, they&amp;#39;re how a codebase accrues the kind of subtle inconsistency that makes future changes expensive. And unlike a compile error or a failing test, these violations are invisible until they aren&amp;#39;t.&lt;/p&gt;
&lt;p&gt;The problem is not that the AI is bad at code. The problem is that it&amp;#39;s generating code calibrated to the median codebase — not your codebase. It doesn&amp;#39;t know your codebase&amp;#39;s rules unless you tell it.&lt;/p&gt;
&lt;h2&gt;The anchor: project_context.md&lt;/h2&gt;
&lt;p&gt;The first structural fix is a single versioned file: &lt;code&gt;docs/project_context.md&lt;/code&gt;, placed at the repository root and explicitly referenced in both &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Not a wiki page. Not a Confluence article that lives outside the IDE. A file that loads into every AI session automatically, as ambient context, before any code is generated.&lt;/p&gt;
&lt;p&gt;The file is a list of rules written in the declarative negative: &lt;strong&gt;NEVER mix ORM versions in the same repository.&lt;/strong&gt; &lt;strong&gt;NEVER bypass the service layer.&lt;/strong&gt; &lt;strong&gt;NEVER use &lt;code&gt;DateTime&lt;/code&gt; for timestamp columns — use &lt;code&gt;DateTimeOffset&lt;/code&gt;.&lt;/strong&gt; &lt;strong&gt;NEVER create database queries directly in controllers.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Then the positive conventions: naming schemes, soft-delete patterns, which systems are allowed to call each other, which database tables belong to which application tier.&lt;/p&gt;
&lt;p&gt;This is not prompt engineering in the clever sense. It&amp;#39;s documentation that happens to be machine-readable. The same file a new engineer should read on day one is the same file the AI loads before it writes a line. The audience is both.&lt;/p&gt;
&lt;p&gt;The key discipline is writing the rules as constraints, not as descriptions. &amp;quot;Use &lt;code&gt;DateTimeOffset&lt;/code&gt; for all timestamp columns&amp;quot; is actionable. &amp;quot;We have a history of inconsistent timestamp types&amp;quot; is noise. AI models respond to constraints. They don&amp;#39;t self-correct from prose complaints.&lt;/p&gt;
&lt;p&gt;Here is a starting template. The specific rules are yours — but the structure matters. Hard rules first, conventions second, boundaries third.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# Project Context — [Your Project Name]

## Architecture
- Runtime: [e.g., .NET 8 / .NET Framework 4.8 / mixed]
- ORM: [e.g., EF Core for new services, EF6 for legacy]
- Database: [vendor and version]

## Hard Rules (NEVER do these)
- NEVER [your critical constraint #1]
- NEVER [your critical constraint #2]
- NEVER [your critical constraint #3]

## Conventions
- Naming: [entity prefixes, table naming, etc.]
- Data types: [e.g., DateTimeOffset for all timestamps]
- Service boundaries: [which layer owns what]

## Integration Boundaries
- [System A] may call [System B] via [mechanism]
- [System C] is read-only from [System A]&amp;#39;s perspective
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Scaling workflow with BMAD&lt;/h2&gt;
&lt;p&gt;Documentation alone handles the guardrails. It doesn&amp;#39;t handle workflow complexity.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/bmadcode/BMAD-METHOD&quot;&gt;BMAD Method&lt;/a&gt; (Breakthrough Method for Agile AI-Driven Development) provides the framework for the rest. BMAD structures AI-assisted development around role-based agents — Analyst, Scrum Master, Developer — each with defined responsibilities and commands. The base framework is open-source. What you build on top of it is a custom module for your specific codebase.&lt;/p&gt;
&lt;p&gt;That module can produce a substantial library of commands and agent definitions covering the full development lifecycle: feature discovery, PRD generation, architecture documentation, sprint planning, story implementation. Each command pre-loads the relevant context — the project conventions, the active feature, the affected components. Each agent definition is a version-controlled file, reviewed and improved over time.&lt;/p&gt;
&lt;p&gt;The maintenance cost is real. When BMAD released a new major version, the upgrade required a full module rewrite — the hook and workflow API changed significantly. That&amp;#39;s an accepted cost. The alternative is maintaining nothing and accepting the inconsistency tax permanently.&lt;/p&gt;
&lt;h2&gt;The Golden Path workflow&lt;/h2&gt;
&lt;p&gt;A well-structured BMAD module defines a canonical workflow:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;init-feature → feature-discovery → create-prd → create-architecture → sprint-planning → dev-story&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;init-feature&lt;/code&gt; takes a work item identifier, creates synchronized branches across the affected repositories, and writes an &lt;code&gt;active-feature.yaml&lt;/code&gt; file: ticket identifier, branch name, affected components, current phase.&lt;/p&gt;
&lt;p&gt;Every subsequent command reads that file. &lt;code&gt;feature-discovery&lt;/code&gt; scans the codebase for relevant files and generates a research document. &lt;code&gt;create-architecture&lt;/code&gt; proposes an approach aligned with existing integration patterns. &lt;code&gt;dev-story&lt;/code&gt; loads the full feature context — PRD, architecture, active ticket, affected systems — before writing a single line.&lt;/p&gt;
&lt;p&gt;The branch name itself becomes ambient context. Extracting the active work item identifier from the current branch name lets agents auto-detect which feature they&amp;#39;re working on between sessions. No manual re-explanation. No &amp;quot;I was working on...&amp;quot; preamble. The context travels with the branch.&lt;/p&gt;
&lt;p&gt;This is the Golden Path: a workflow where every AI interaction arrives pre-loaded with the context it needs to do correct work.&lt;/p&gt;
&lt;h2&gt;What changes&lt;/h2&gt;
&lt;p&gt;The measurable difference is in context assembly time. Without structured context, a developer spends significant time at the start of every AI session re-explaining the codebase, the conventions, and the current feature. With it, that overhead drops to near zero — the agent arrives pre-loaded. The productivity gain comes from eliminating repeated setup, not from the AI writing faster code.&lt;/p&gt;
&lt;p&gt;In complex codebases, a significant portion of task time isn&amp;#39;t writing code — it&amp;#39;s finding the relevant files, understanding the dependencies, verifying the fix approach against conventions. When an agent is pre-loaded with that context, it handles the assembly. The engineer handles the judgment.&lt;/p&gt;
&lt;p&gt;The subtler change is this: AI agents that systematically read a codebase against an explicit standard surface what human developers acclimate to ignoring. Unused mapping files. Missing indexes. Legacy patterns that were &amp;quot;known in a vague way&amp;quot; but never catalogued, never prioritized, never fixed. When you give an AI agent the task of auditing against an explicit ruleset, it doesn&amp;#39;t get tired or skip the boring parts.&lt;/p&gt;
&lt;h2&gt;The honest trade-off&lt;/h2&gt;
&lt;p&gt;The upfront investment is significant. Building and wiring a full command library and agent definition set doesn&amp;#39;t happen in an afternoon. Feature context systems — making work item detection reliable across submodules, handling edge cases in branch naming — take time to stabilize.&lt;/p&gt;
&lt;p&gt;And framework upgrades are a maintenance commitment. Major BMAD versions have required full module rewrites. Future major versions will require the same.&lt;/p&gt;
&lt;p&gt;The alternative — raw prompts, ad hoc context, no shared vocabulary — is also a maintenance commitment. It&amp;#39;s just invisible, paid continuously in small amounts of inconsistency, repeated context-setting, and the slow erosion of codebase coherence.&lt;/p&gt;
&lt;p&gt;The visible cost beats the invisible one.&lt;/p&gt;
&lt;h2&gt;The principle&lt;/h2&gt;
&lt;p&gt;AI doesn&amp;#39;t replace domain knowledge. It amplifies whatever domain knowledge it receives.&lt;/p&gt;
&lt;p&gt;Give it nothing, and it generates plausible code calibrated to the median codebase. Give it the specific conventions, the specific anti-patterns, the specific architecture of your system — and it generates code that fits.&lt;/p&gt;
&lt;p&gt;The guardrails are the multiplier. Not the model. Not the prompt length. The structured, version-controlled, systematically loaded context that tells the AI where it is, what the rules are, and what it must never do.&lt;/p&gt;
&lt;p&gt;In a large legacy codebase with multiple ORM generations, dozens of engineers, and years of accumulated conventions, that context is not optional. It&amp;#39;s the difference between AI that makes things worse in hard-to-see ways, and AI that makes the team genuinely faster.&lt;/p&gt;
&lt;p&gt;Getting the context right is the engineering work. Once it&amp;#39;s right, the multiplication follows.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bmadcode/BMAD-METHOD&quot;&gt;BMAD Method&lt;/a&gt; — open-source framework for AI-assisted agile development, including role-based agents and the full feature workflow described above&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/memory#claudemd&quot;&gt;Claude Code: CLAUDE.md&lt;/a&gt; — how Claude Code loads project-level context files before every session&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&quot;&gt;GitHub Copilot Custom Instructions&lt;/a&gt; — repository-level AI context for Copilot, the same principle applied to a different tool&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cursor.sh/docs&quot;&gt;Cursor: .cursorrules&lt;/a&gt; — project-level rules for Cursor AI&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>When Git Submodules Make Sense in a Monorepo</title><link>https://seevali.dev/blog/2026-03-21-git-submodules-inside-nx-monorepo/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-21-git-submodules-inside-nx-monorepo/</guid><description>The textbook answer is always a full monorepo merge. But in large codebases with independent release cadences, the right answer is often a stepping stone — and submodules can be that stepping stone.</description><pubDate>Thu, 12 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;The internet will tell you Git submodules are a mistake. That advice assumes you have the luxury of a clean migration. In large enterprise codebases, you often don&amp;#39;t.&lt;/p&gt;
&lt;p&gt;There is a pattern worth knowing: the Nx monorepo with Git submodules as a deliberate, time-boxed bridge. It&amp;#39;s not the textbook answer. In the right circumstances, it&amp;#39;s the correct one.&lt;/p&gt;
&lt;h2&gt;The problem this pattern solves&lt;/h2&gt;
&lt;p&gt;Imagine a platform that has grown across multiple repositories — a legacy web application, a modern API, a second API handling orchestration. They share a database. Almost every significant feature touches at least two of them.&lt;/p&gt;
&lt;p&gt;This is a common trajectory for enterprise systems. What starts as reasonable separation becomes friction at scale: independent branch strategies with no coordination, AI assistants that can&amp;#39;t see across repo boundaries, development tooling that has to be configured separately per repo. The cost isn&amp;#39;t duplicated code. It&amp;#39;s duplicated thinking — context the team has to mentally reload every time they cross a boundary.&lt;/p&gt;
&lt;p&gt;The standard prescription is a full monorepo merge: pull all histories into one, unify CI, standardize the branching strategy. It&amp;#39;s the right long-term answer. It&amp;#39;s also the answer that can break working teams overnight.&lt;/p&gt;
&lt;h2&gt;Why &amp;quot;full merge immediately&amp;quot; fails as a default&lt;/h2&gt;
&lt;p&gt;If your codebase has hundreds of projects spread across multiple repos, each with its own CI pipeline and its own release cadence, a hard cut to a unified monorepo carries real disruption risk. Developers who are productive today have to change their workflow entirely, immediately. Teams operating under regulated deployment constraints often can&amp;#39;t accept that kind of disruption on a short timeline.&lt;/p&gt;
&lt;p&gt;The failure mode to avoid is treating monorepo migration as a binary choice: either you&amp;#39;re fully merged or you&amp;#39;re still siloed. That framing ignores the option of a structured transition with incremental value delivery at each phase.&lt;/p&gt;
&lt;h2&gt;The bridge pattern&lt;/h2&gt;
&lt;p&gt;The approach: create an Nx parent monorepo that wires existing repositories together as Git submodules under &lt;code&gt;apps/&lt;/code&gt;. The parent provides unified tooling — &lt;code&gt;nx run&lt;/code&gt;, &lt;code&gt;nx affected&lt;/code&gt;, centralized documentation, cross-project AI context. The app repos remain fully independent. A developer who only needs to work in one of those repos still clones it directly. Nothing in their workflow changes until they choose to adopt the unified layer.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nx affected&lt;/code&gt; is particularly valuable in this setup. It analyses the project graph to determine exactly which projects were impacted by a change — including changes that span submodule boundaries — so CI only rebuilds and retests what actually changed. For .NET projects, this requires the &lt;a href=&quot;https://www.nx-dotnet.com/&quot;&gt;&lt;code&gt;@nx/dotnet&lt;/code&gt; plugin&lt;/a&gt;, which teaches Nx to parse &lt;code&gt;.csproj&lt;/code&gt; files and construct the .NET project graph. Without it, Nx cannot reason about dependencies inside your .NET submodules.&lt;/p&gt;
&lt;p&gt;This matters because &lt;strong&gt;adoption can be opt-in&lt;/strong&gt;. The monorepo is additive, not mandatory. Teams that want cross-project tooling get it immediately. Teams that don&amp;#39;t aren&amp;#39;t disrupted.&lt;/p&gt;
&lt;p&gt;The centralized &lt;code&gt;docs/&lt;/code&gt; layer is where a lot of the value lives. A single authoritative document captures the conventions that all projects share: naming rules, soft-delete patterns, service layer boundaries, ORM version distinctions. Both humans and AI agents load this context at session start. You eliminate the quiet inconsistencies that accumulate when developers are context-switching between repositories that each have their own undocumented norms.&lt;/p&gt;
&lt;h2&gt;Cross-repo feature coordination&lt;/h2&gt;
&lt;p&gt;The hardest part of this pattern isn&amp;#39;t the structural wiring — it&amp;#39;s branch coordination. When a feature touches multiple submodule repos, you need branches created consistently across all of them. If you leave this to manual discipline, you&amp;#39;ll get drift: a feature branch exists in two repos but not the third, AI agents are confused about which ticket they&amp;#39;re working on, merge order goes wrong and the parent repo points at stale submodule commits.&lt;/p&gt;
&lt;p&gt;The solution is to script the coordination explicitly. An &lt;code&gt;init-feature&lt;/code&gt; command takes a ticket ID and creates matching branches in the parent repo and all submodules simultaneously. A lightweight YAML file persists the active feature context so tooling can detect it automatically from the branch name. This removes the &amp;quot;which repo are we in?&amp;quot; confusion without requiring anyone to change how they interact with individual repos.&lt;/p&gt;
&lt;p&gt;Merge discipline matters too: submodule repos merge first, then the parent. Document it. Automate the check if you can.&lt;/p&gt;
&lt;h2&gt;The trade-offs you&amp;#39;re accepting&lt;/h2&gt;
&lt;p&gt;Submodules introduce real friction. Developers run &lt;code&gt;git submodule update --init --recursive&lt;/code&gt; on first clone. The detached HEAD state inside submodules is confusing in many Git clients. Any workflow that bypasses the coordination tooling creates incomplete context.&lt;/p&gt;
&lt;p&gt;These are real costs. The question is whether they&amp;#39;re smaller than the cost of the alternative. If the alternative is a migration that disrupts a productive team for weeks or months to satisfy an architectural ideal, the submodule friction may be the better bet — especially if you instrument and document it carefully.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve found that &amp;quot;submodule complexity is documented and tooled around&amp;quot; is a manageable state. &amp;quot;Cognitive load from boundary-crossing is undocumented and growing&amp;quot; is not.&lt;/p&gt;
&lt;h2&gt;A three-phase structure&lt;/h2&gt;
&lt;p&gt;If you adopt this pattern, design it as a time-boxed bridge with explicit phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phase 1:&lt;/strong&gt; Submodule wiring, centralized docs, cross-project AI tooling, feature coordination scripts. Delivers immediate value. Validates the approach.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2:&lt;/strong&gt; Move shared libraries to the monorepo root. Reduce duplication at the code level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 3:&lt;/strong&gt; Remove submodules entirely. Full integration. This is the textbook answer — arrived at on a timeline the team can actually absorb.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Phase 3 might happen in a year. It might not happen at all, because Phase 1 turned out to be enough. That&amp;#39;s a legitimate outcome. The architecture serves the team, not the other way around.&lt;/p&gt;
&lt;h2&gt;What to measure&lt;/h2&gt;
&lt;p&gt;When evaluating whether this pattern is right for your situation, don&amp;#39;t just count duplicated code. Count the number of times developers mentally reload context when working across boundaries. Ask how often AI assistants give advice that makes sense in one codebase but violates conventions in another. Measure how long it takes a new developer to get productive across the full surface area.&lt;/p&gt;
&lt;p&gt;Cognitive duplication is harder to quantify than code duplication. It&amp;#39;s just as expensive.&lt;/p&gt;
&lt;p&gt;If the textbook answer requires disrupting a working team on a short timeline — question the textbook. The best architectural decision is the one your team can actually adopt.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nx.dev/&quot;&gt;Nx Documentation&lt;/a&gt; — monorepo tooling and project graph visualization&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nx-dotnet.com/&quot;&gt;@nx/dotnet Plugin&lt;/a&gt; — Nx integration for .NET projects&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Submodules&quot;&gt;Git Submodules&lt;/a&gt; — official Git documentation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nx.dev/ci/features/affected&quot;&gt;Nx Affected&lt;/a&gt; — incremental build and test based on changed projects&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>When to Build an Integration Platform Instead of Another One-Off</title><link>https://seevali.dev/blog/2026-03-24-building-the-integration-platform-before-you-need-it/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-24-building-the-integration-platform-before-you-need-it/</guid><description>How to recognize the moment when the next integration request should become a platform investment — and what that platform actually needs to include.</description><pubDate>Sun, 08 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;It&amp;#39;s common for this kind of work to be scoped as a small feature request: connect the platform to an external accounting system, add a scheduled export job, ship it. If you only count the feature being asked for, that scope might even be accurate.&lt;/p&gt;
&lt;p&gt;The problem shows up in the system that already exists. If your platform has grown three or four integrations and each one was built differently — different retry behaviour, different error handling, different config patterns — you&amp;#39;re accumulating something that looks manageable in isolation and becomes a liability at scale. Some integrations silently eat failures. Some have no alerting. None of them share infrastructure with each other. Each one is its own snowflake.&lt;/p&gt;
&lt;p&gt;When the next integration request lands, you face a choice: build another snowflake, or stop and ask whether this is the last time you&amp;#39;ll be making that choice.&lt;/p&gt;
&lt;h2&gt;The cost of ad-hoc integrations&lt;/h2&gt;
&lt;p&gt;The individual cost of each snowflake integration is low. Connect to the external system, wire up an HTTP call, handle errors roughly, ship it. Done in days.&lt;/p&gt;
&lt;p&gt;The systemic cost accrues invisibly. You don&amp;#39;t notice it until you&amp;#39;re debugging a production failure at 10pm and you realize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There&amp;#39;s no standard way to find out which integrations are registered and what state they&amp;#39;re in&lt;/li&gt;
&lt;li&gt;One integration retries three times before giving up; another retries forever; a third doesn&amp;#39;t retry at all&lt;/li&gt;
&lt;li&gt;A config change was made in production weeks ago and nobody remembers by whom or why&lt;/li&gt;
&lt;li&gt;An export job failed silently and the operations team only found out when a downstream system complained&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these problems is solvable individually. Add logging here, add a retry here, add a config record there. The patches accumulate. The architecture doesn&amp;#39;t improve. The next integration — and there&amp;#39;s always a next integration — inherits the same gaps.&lt;/p&gt;
&lt;p&gt;The trigger for taking this seriously is usually a sufficiently complex request: cross-system data sync with an external provider, automated exports on a schedule, configuration that needs to be managed by the operations team without developer involvement. Too complex for a snowflake. Too visible to fail silently.&lt;/p&gt;
&lt;h2&gt;What building the platform actually means&lt;/h2&gt;
&lt;p&gt;The first decision is to define the surface area. An integration platform, in this context, means a shared layer that every concrete integration is built on top of — not a system you implement once for one integration.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s what that layer needs to include:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shared contracts.&lt;/strong&gt; Every integration implements the same interfaces. The platform doesn&amp;#39;t care what the integration does — it cares that it can register it, invoke it, and observe it consistently. This is the foundation; without it, none of the rest of this is reusable.&lt;/p&gt;
&lt;p&gt;In .NET, this typically takes the form of a single handler interface that each integration implements:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public interface IIntegrationHandler
{
    string IntegrationName { get; }
    Task&amp;lt;IntegrationResult&amp;gt; ExecuteAsync(IntegrationContext context, CancellationToken ct);
}

// Each integration implements this interface.
// The platform discovers and invokes handlers via DI —
// adding a new integration means registering a new handler.
services.AddIntegration&amp;lt;AccountingExportHandler&amp;gt;(&amp;quot;accounting-export&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The platform discovers all registered handlers at startup, routes by name, and observes them uniformly. Adding a new integration is registering a new handler — no changes to shared infrastructure code required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Circuit breaker.&lt;/strong&gt; When an external system is unavailable, stop sending requests. The circuit breaker opens after a threshold of failures and blocks requests until a cooldown period expires. Without this, a temporarily unavailable external system causes cascading load on both sides — the kind of problem that turns a minor outage into a major one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/App-vNext/Polly&quot;&gt;Polly&lt;/a&gt; is the standard .NET library for this. A minimal circuit breaker configuration looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;// Using Polly (https://github.com/App-vNext/Polly)
var circuitBreakerPolicy = Policy
    .Handle&amp;lt;HttpRequestException&amp;gt;()
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 3,
        durationOfBreak: TimeSpan.FromMinutes(1));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After three consecutive failures, the circuit opens for one minute. Calls during that window fail fast rather than piling up against an unavailable system. Tune the threshold and break duration to match your external system&amp;#39;s expected recovery time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Retry with exponential backoff.&lt;/strong&gt; Not retry-three-times-immediately. Configurable retry policies where the wait between attempts grows: 30 seconds, then 2 minutes, then 5 minutes. Transient failures become permanent ones when you retry too aggressively. Give downstream systems time to recover.&lt;/p&gt;
&lt;p&gt;With Polly, this is a &lt;code&gt;WaitAndRetryAsync&lt;/code&gt; policy with explicit delay intervals:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;var retryPolicy = Policy
    .Handle&amp;lt;HttpRequestException&amp;gt;()
    .WaitAndRetryAsync(new[]
    {
        TimeSpan.FromSeconds(30),
        TimeSpan.FromMinutes(2),
        TimeSpan.FromMinutes(5)
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The delays here are a reasonable starting point for integrations that hit external HTTP APIs. For integrations with longer recovery windows — batch jobs, nightly exports — you may want larger intervals. The key constraint is that the policy lives in one place, configured per integration type, not reimplemented inline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Failure alerting.&lt;/strong&gt; When retries are exhausted, the operations team needs to know — not &amp;quot;check the logs,&amp;quot; but an actual notification. Silent failures are a design smell. If the system can detect a failure, it should tell someone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Config audit logging.&lt;/strong&gt; Every change to integration configuration — credentials, schedules, field mappings — writes an immutable record: who changed it, when, what it was before, what it is now. This sounds bureaucratic until you&amp;#39;re in a production incident and the first question is &amp;quot;what changed?&amp;quot;&lt;/p&gt;
&lt;p&gt;The implementation pattern is an append-only audit table — no updates, no deletes. Columns: &lt;code&gt;ChangedBy&lt;/code&gt;, &lt;code&gt;ChangedAt&lt;/code&gt;, &lt;code&gt;FieldName&lt;/code&gt;, &lt;code&gt;OldValue&lt;/code&gt;, &lt;code&gt;NewValue&lt;/code&gt;, &lt;code&gt;CorrelationId&lt;/code&gt;. Querying for &amp;quot;what changed in the last 24 hours&amp;quot; becomes a single SELECT against a table that can never be retroactively modified.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manual retry UI.&lt;/strong&gt; The operations team can retry a failed integration job without filing a ticket and waiting for a developer. This single capability removes a whole category of support escalation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dry-run mode.&lt;/strong&gt; Run the integration against real infrastructure without committing changes. This makes testing against external systems safe — you can verify field mappings, authentication, and data shape before anything actually moves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OAuth2 client credentials flow.&lt;/strong&gt; If your integrations use OAuth2 — and many external systems require it — implement it once as a reusable provider rather than per-integration. Tokens are managed centrally, with refresh handled transparently.&lt;/p&gt;
&lt;p&gt;None of these are sophisticated ideas in isolation. Every one of them exists in some form in most serious integrations. The point is to build them once, in a shared layer, before implementing the first concrete integration on top of them.&lt;/p&gt;
&lt;h2&gt;The honest trade-off&lt;/h2&gt;
&lt;p&gt;The first integration through a new platform takes longer to deliver than a snowflake would have. Building the shared layer adds real time to the timeline before the first end-to-end integration runs.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the cost you pay, and it&amp;#39;s real. If this were the only integration you&amp;#39;d ever build, the platform would be the wrong call. The snowflake would be faster, cheaper, and entirely sufficient.&lt;/p&gt;
&lt;p&gt;But the platform pays back within the same body of work if that work is complex enough. A scheduled export job — one that runs nightly, maps field data through a configuration-driven transform layer, authenticates via OAuth2, and reports failures to the operations team — can be built almost entirely by wiring together what the platform already provides. The circuit breaker and retry logic are already there. The alerting is already there. The audit trail is already there. The OAuth2 provider is already there.&lt;/p&gt;
&lt;p&gt;And the next integration after that takes a fraction of what the first one took. Not because the engineers are faster — because the integration surface they need already exists.&lt;/p&gt;
&lt;h2&gt;When the investment is worth it&lt;/h2&gt;
&lt;p&gt;The snowflake approach is rational when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The integration is genuinely one-off with no likely successors&lt;/li&gt;
&lt;li&gt;The feature is small and the blast radius of failure is limited&lt;/li&gt;
&lt;li&gt;Speed to ship is the dominant constraint and the team will never look at this code again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The platform investment starts paying off when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&amp;#39;re looking at the third or fourth integration and recognizing the same problems being solved again&lt;/li&gt;
&lt;li&gt;Failure modes matter — silent failures or cascading failures have real operational cost&lt;/li&gt;
&lt;li&gt;The operations team needs to manage integrations without developer involvement&lt;/li&gt;
&lt;li&gt;You want to be able to look at the system in two years and understand what&amp;#39;s registered, what&amp;#39;s running, and what&amp;#39;s failing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specific trigger is usually scope and visibility. An integration that spans scheduling, data transformation, external authentication, and operational tooling is not something you can reasonably bolt onto shared infrastructure that doesn&amp;#39;t exist yet. The cost of doing it wrong is too visible.&lt;/p&gt;
&lt;h2&gt;The pattern&lt;/h2&gt;
&lt;p&gt;When you build the platform before the feature, you&amp;#39;re making a bet: that you&amp;#39;ll build enough integrations over a long enough period that the compounding return on shared infrastructure exceeds the upfront cost. That bet pays off more often than you&amp;#39;d think, because integration requests rarely stop at one.&lt;/p&gt;
&lt;p&gt;The mistake that costs the most isn&amp;#39;t building the platform too early — it&amp;#39;s building five or six snowflakes before admitting you need a platform. By that point the problem isn&amp;#39;t just the next integration. It&amp;#39;s the ones you already have that all need to be retrofitted or left as permanent debt.&lt;/p&gt;
&lt;p&gt;Build the platform when the cost of not having it becomes visible. Not when you&amp;#39;re drowning in snowflakes. Not so early you&amp;#39;re abstracting problems you don&amp;#39;t have yet. When the next integration is complex enough that you can see — clearly, specifically — what you&amp;#39;re going to build a second and third time if you don&amp;#39;t stop and generalize it now.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s when you stop building the feature and start building the platform.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/App-vNext/Polly&quot;&gt;Polly&lt;/a&gt; — .NET resilience and transient-fault-handling library; the standard implementation for circuit breaker and retry in the .NET ecosystem&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/CircuitBreaker.html&quot;&gt;Martin Fowler: Circuit Breaker&lt;/a&gt; — canonical pattern description with state machine diagram&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/architecture/patterns/&quot;&gt;Microsoft: Cloud Design Patterns&lt;/a&gt; — covers circuit breaker, retry, health endpoint monitoring, and related patterns with implementation guidance&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.enterpriseintegrationpatterns.com/&quot;&gt;Enterprise Integration Patterns&lt;/a&gt; — Hohpe &amp;amp; Woolf&amp;#39;s reference for messaging and integration architecture; the vocabulary most integration platform discussions build on&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>One Brain, Three Interfaces</title><link>https://seevali.dev/blog/2026-03-24-one-brain-three-interfaces/</link><guid isPermaLink="true">https://seevali.dev/blog/2026-03-24-one-brain-three-interfaces/</guid><description>NanoClaw, Kleos, and Nyx are three different ways to talk to the same AI. Here is why that architecture exists and how it came together.</description><pubDate>Tue, 03 Mar 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;The problem with building a capable AI assistant is not the AI. It&amp;#39;s the interface.&lt;/p&gt;
&lt;p&gt;WhatsApp works brilliantly for a quick capture mid-commute. It handles async, mobile-first interaction well. But it renders markdown as plain text. It has no panels. You can&amp;#39;t have a voice conversation with it while a KB browser sits open beside the transcript. And it has absolutely no awareness of what directory you&amp;#39;re in when you&amp;#39;re three hours into a debugging session.&lt;/p&gt;
&lt;p&gt;Different situations need different surfaces. But the intelligence — the memory, the context, the knowledge base — should not be split across those surfaces. You don&amp;#39;t want three mediocre brains. You want one excellent brain with three mouths.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the architecture I&amp;#39;m building: &lt;a href=&quot;https://seevali.dev/builds/nanoclaw/&quot;&gt;NanoClaw&lt;/a&gt;, &lt;a href=&quot;https://seevali.dev/builds/kleos/&quot;&gt;Kleos&lt;/a&gt;, and &lt;a href=&quot;https://seevali.dev/builds/nyx/&quot;&gt;Nyx&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;NanoClaw: the platform&lt;/h2&gt;
&lt;p&gt;NanoClaw is the backend. It&amp;#39;s an open-source Node.js orchestrator that receives messages from channels (WhatsApp, Telegram, Slack, Discord, Gmail) and routes them to Claude agents running in isolated Docker containers. Each agent group has its own filesystem and memory. The main agent — Lyra — has an Obsidian knowledge base mounted read-write into every container it runs.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Channel message arrives
  └── Orchestrator picks it up
        └── Spawns container (isolated filesystem + mounts)
              └── Claude Agent SDK reads CLAUDE.md, accesses KB
                    └── Response routed back out
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The knowledge base is a &lt;a href=&quot;https://seevali.dev/decisions/why-para-why-obsidian/&quot;&gt;PARA-structured Obsidian vault&lt;/a&gt;: projects, areas, resources, journal, showcase. Every conversation Lyra has goes through that context. Progress notes, decisions, incidents — they land in the vault. The next conversation starts with that history already in scope.&lt;/p&gt;
&lt;p&gt;NanoClaw is the brain and the memory. The channels are just delivery pipes.&lt;/p&gt;
&lt;h2&gt;Kleos: the voice pivot&lt;/h2&gt;
&lt;p&gt;Kleos started as something different. A voice-first PWA for capturing invisible senior engineer work — the incidents you solved, the architecture decisions you made, the mentoring conversations nobody logs. React 19, Vite 7, WebSpeech API, local Ollama handling categorisation. Epic 1 shipped. The voice capture worked.&lt;/p&gt;
&lt;p&gt;Then I looked at what it was actually doing and the redundancy was obvious.&lt;/p&gt;
&lt;p&gt;Lyra already categorises, tags, and routes notes into the correct KB location — with full project context and knowledge base access behind every call. The local Ollama model doing isolated categorisation was a weaker version of something that already existed. I was running two AI layers where one was strictly better than the other.&lt;/p&gt;
&lt;p&gt;The pivot decision was clean: strip the Ollama backend, wire Kleos into NanoClaw as the official web client. Keep everything that was good — the voice infrastructure, the PWA shell, the shadcn/ui design system, the offline capability. Replace what was wrong.&lt;/p&gt;
&lt;p&gt;The result is what Kleos should have been from the beginning: a voice-first dashboard that lets you have a natural conversation with Lyra while a KB browser panel sits beside the transcript. Streaming Claude responses with full markdown rendering. Project status, active tasks, recent journal entries — all surfaced from the same vault that every other interaction writes to.&lt;/p&gt;
&lt;p&gt;Don&amp;#39;t compete with what the platform already does. Redirect the good infrastructure.&lt;/p&gt;
&lt;h2&gt;Nyx: the anti-pattern decision&lt;/h2&gt;
&lt;p&gt;Nyx is a GPU-accelerated Rust terminal. The developer&amp;#39;s preferred interface — fast rendering, built for people who spend their working life in a terminal window.&lt;/p&gt;
&lt;p&gt;The obvious tempting design is: give Lyra a terminal executor. Let the AI run commands on your behalf. Autonomous execution, right there in your shell.&lt;/p&gt;
&lt;p&gt;I ruled that out explicitly and documented why. Claude Code already exists. It does agentic terminal execution extremely well, built by a team with resources I don&amp;#39;t have, iterated on faster than I can maintain a reinvented version. NanoClaw already provides remote Claude Code sessions as a built-in capability. Building the same thing again in Nyx is maintenance burden without differentiated value.&lt;/p&gt;
&lt;p&gt;What Nyx actually does is different and more interesting. When you &lt;code&gt;cd&lt;/code&gt; into a project directory, Nyx detects it and loads the relevant KB context automatically. You can run &lt;code&gt;lyra log &amp;quot;resolved the DPAPI session issue&amp;quot;&lt;/code&gt; and it writes a progress note to the correct project in the vault, with the timestamp and context already filled in. When a known error pattern appears in command output, the relevant runbook surfaces in a sidebar panel. Active tasks for the current project live in the status bar.&lt;/p&gt;
&lt;p&gt;Nyx is not an executor. It&amp;#39;s a developer-context-aware surface for the same brain. The terminal knows where you are. That context — current directory, active project, git branch — makes Lyra&amp;#39;s responses sharper in ways a WhatsApp message never can.&lt;/p&gt;
&lt;p&gt;The Lyra integration is also explicitly one optional module, not Nyx&amp;#39;s core identity. Nyx needs to work brilliantly as a local AI-powered terminal without any network dependency. When Lyra is available, it gets better. That&amp;#39;s the right relationship between a client and a platform.&lt;/p&gt;
&lt;h2&gt;The shared layer&lt;/h2&gt;
&lt;p&gt;The knowledge base is what ties this together.&lt;/p&gt;
&lt;p&gt;One Obsidian vault, mounted into every NanoClaw container. Kleos writes a voice note → it lands in the vault. A WhatsApp message captures a decision → it lands in the vault. Nyx logs a debugging session → same vault. The next session on any surface picks up where any other surface left off.&lt;/p&gt;
&lt;p&gt;The brain doesn&amp;#39;t care which mouth is talking. The knowledge accumulates regardless of interface.&lt;/p&gt;
&lt;p&gt;This is also the open source story. NanoClaw is the agent platform — the reference implementation for anyone who wants to run their own containerised Claude assistant with a persistent knowledge layer. Kleos is the reference web client, demonstrating how to build a voice dashboard on top of NanoClaw&amp;#39;s API. Nyx is the reference terminal client, demonstrating how to wire developer context into the same backend. Three separate repositories. One coherent system.&lt;/p&gt;
&lt;h2&gt;The principle&lt;/h2&gt;
&lt;p&gt;The interface is not the intelligence.&lt;/p&gt;
&lt;p&gt;A single great chat interface with mediocre AI does less than three average interfaces sharing one excellent brain. The tendency in this space is to over-invest in the surface — to build a beautiful product experience around a model that isn&amp;#39;t actually that capable in the specific context you need. The better investment is the other direction: make the brain excellent, make the memory deep, then build whatever surfaces you actually need for the situations you actually find yourself in.&lt;/p&gt;
&lt;p&gt;WhatsApp for async mobile. Kleos for voice conversation with rich rendering. Nyx for dev sessions where terminal context is the most relevant thing about where you are.&lt;/p&gt;
&lt;p&gt;Same brain. Same knowledge. Different mouth for different moments.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The views expressed here are my own. Examples and scenarios are composites drawn from broad industry experience and do not represent any specific organization, product, or system.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>