<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Philipp Metzler</title><link>https://chili-it.de/posts/</link><description>Recent content in Posts on Philipp Metzler</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Fri, 28 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://chili-it.de/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Talk, Don't Type: Local Voice Dictation That Beats the Cloud</title><link>https://chili-it.de/posts/talk-dont-type/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/talk-dont-type/</guid><description>&lt;p&gt;I stopped typing prompts to my AI. I talk now.&lt;/p&gt;
&lt;p&gt;Not into a phone, not into a cloud service. Into a fanless ThinkPad, offline, with the mic only listening while I hold a key. I hold Super+Ctrl+Space, speak, let go, and the text lands in whatever window has focus. My terminal, my editor, a browser field, this post.&lt;/p&gt;
&lt;p&gt;Here is the part that surprised me: the local setup is &lt;em&gt;better&lt;/em&gt; than the cloud one it replaced.&lt;/p&gt;</description><content type="html"><![CDATA[<p>I stopped typing prompts to my AI. I talk now.</p>
<p>Not into a phone, not into a cloud service. Into a fanless ThinkPad, offline, with the mic only listening while I hold a key. I hold Super+Ctrl+Space, speak, let go, and the text lands in whatever window has focus. My terminal, my editor, a browser field, this post.</p>
<p>Here is the part that surprised me: the local setup is <em>better</em> than the cloud one it replaced.</p>
<h2 id="the-assumption-i-was-wrong-about">The Assumption I Was Wrong About</h2>
<p>For a while I ran dictation against a remote Whisper server on my own infrastructure. It worked, technically. It also had a seven second latency and only worked when I was on the home network. Away from home, dead.</p>
<p>So I did what everyone does: I assumed good real-time speech-to-text meant the cloud. Whisper large on a GPU somewhere, or an API with a per-minute price and my voice leaving the building.</p>
<p>Then I actually measured what a local model does on hardware nobody would call fast. An i7-1160G7. Four cores, low power, no discrete GPU, the kind of chip that throttles if you look at it wrong.</p>
<p>The result: real-time factor of 0.07. A three and a half second sentence transcribed in a quarter of a second. Roughly thirteen times faster than real time, on the CPU, with the fan off. German word error rate around three percent, punctuation and capitalization included. It beat the remote server on every axis that mattered: latency, accuracy, and the small detail of working everywhere instead of only at home.</p>
<p>The engine is <a href="https://github.com/k2-fsa/sherpa-onnx">sherpa-onnx</a> running a German-tuned Parakeet transducer, quantized to int8. The interesting news is not that one model is good. It is that local speech-to-text quietly crossed the line from &ldquo;toy&rdquo; to &ldquo;better than the thing I was paying latency for,&rdquo; and most people have not noticed yet.</p>
<h2 id="the-architecture-is-boring-on-purpose">The Architecture Is Boring, On Purpose</h2>
<p>The whole thing is three moving parts, and I want it boring because I have to maintain it at 3am someday.</p>
<p>A <strong>warm daemon</strong> loads the model once and holds it in RAM. When nothing is happening it blocks on a pipe and burns zero CPU. That is the trick for instant activation: the model never reloads, it just waits. The cost is about a gigabyte of resident memory sitting there, which on a 16GB laptop I will happily pay for a feature I use all day.</p>
<p>The <strong>microphone only runs while I hold the key.</strong> The daemon does not listen on its own. Push the key, a capture process starts and streams raw audio into the pipe; release, it stops and the daemon transcribes the tail. No always-on mic, no ambient recording, no &ldquo;why is the mic light on.&rdquo;</p>
<p>The recognized text gets typed into the focused window with <code>wtype</code>. That is what makes it system-wide instead of app-specific. There is no integration, no plugin, no per-app support matrix. It types. Everything that accepts a keyboard accepts my voice.</p>
<h2 id="the-bug-that-taught-me-to-measure">The Bug That Taught Me To Measure</h2>
<p>The first version cut off the beginning of every sentence. Not a little. A lot. I would say &ldquo;refactor the auth module&rdquo; and get &ldquo;the auth module.&rdquo;</p>
<p>My instinct was to blame the voice activity detector, the model warmup, the pipe. All wrong. I stopped guessing and timed the one thing I had assumed was free: opening the microphone.</p>
<p><code>sox</code> capturing from PipeWire had a <strong>1.9 second startup latency.</strong> Nearly two full seconds between &ldquo;start recording&rdquo; and the first sample actually arriving. Every sentence lost its opening because the recorder was still clearing its throat.</p>
<p>Swapping the capture tool to <code>pw-record</code>, which talks to PipeWire natively, dropped that to 45 milliseconds. Forty times faster, and the bug simply evaporated.</p>
<p>The lesson is the oldest one in this job. The bottleneck is never where your intuition points. It is in the boring line you assumed was instant. Measure the thing you are sure about.</p>
<h2 id="if-you-cant-grep-it-you-dont-own-it">If You Can&rsquo;t Grep It, You Don&rsquo;t Own It</h2>
<p>The last piece is a flat text file. Speech is messy, and I say some things constantly that the model hears wrong. It writes &ldquo;worklock&rdquo; when I say &ldquo;worklog.&rdquo; So there is a plain, editable list of rules:</p>
<pre tabindex="0"><code>worklock =&gt; worklog
lachender smiley =&gt; 😄
neue zeile =&gt; \n
</code></pre><p>Corrections, emoji on command, punctuation and line breaks by voice. No database, no config UI, no service. The daemon re-reads the file on every activation, so I edit a line and the next sentence uses it. When the model mishears a trigger, I add the mishearing as a rule and move on.</p>
<p>This is the same principle I keep coming back to. The valuable part of a system should be a file you can read, diff, and back up. A voice dictation setup where the vocabulary lives in someone else&rsquo;s product is a setup you are renting. This one I own, in a text file, on my disk.</p>
<h2 id="the-point">The Point</h2>
<p>I am not here to sell you a specific model. Models change monthly. The point is that &ldquo;just use the cloud&rdquo; stopped being the obvious answer for real work, and a lot of people are still defaulting to it out of habit.</p>
<p>A fanless laptop does real-time German dictation, offline, more accurately than the server I used to route it through. The stack is a warm daemon, a native capture tool, and a text file of rules. Boring, local, mine.</p>
<p>I dictated most of this post.</p>
]]></content></item><item><title>The Plumbing Is the Skill: Why Your AI Setup Right Now Matters</title><link>https://chili-it.de/posts/your-ai-setup-window/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/your-ai-setup-window/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/your-ai-setup-window.webp"
alt="A workshop of pipes, terminals, and small machines wired together by hand — the plumbing of a personal AI setup"&gt;
&lt;/figure&gt;
&lt;p&gt;There&amp;rsquo;s a window open right now. Not a long one — maybe one or two years. And what you build inside that window will quietly decide how much leverage you have for the next decade.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not talking about which model you use. I&amp;rsquo;m talking about your &lt;em&gt;setup&lt;/em&gt;. The way you wire AI into your actual life and work. The configs, the scripts, the agents, the keybindings, the directory layout, the command-line muscle that makes the whole thing usable. The plumbing.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/your-ai-setup-window.webp"
			alt="A workshop of pipes, terminals, and small machines wired together by hand — the plumbing of a personal AI setup">
</figure>

<p>There&rsquo;s a window open right now. Not a long one — maybe one or two years. And what you build inside that window will quietly decide how much leverage you have for the next decade.</p>
<p>I&rsquo;m not talking about which model you use. I&rsquo;m talking about your <em>setup</em>. The way you wire AI into your actual life and work. The configs, the scripts, the agents, the keybindings, the directory layout, the command-line muscle that makes the whole thing usable. The plumbing.</p>
<p>This is the skill. And right now is when you should be learning it.</p>
<h2 id="the-setup-is-the-product">The Setup Is the Product</h2>
<p>Most people think of AI as &ldquo;a thing you talk to.&rdquo; A chat window. A magic box. Type in, get out.</p>
<p>That&rsquo;s not what serious AI use looks like. Serious AI use looks like a <em>system</em>. You have files the AI reads. You have hooks that fire on events. You have skills it loads on demand. You have an inbox of notes it scans, a journal it can update, a worklog it appends to. You have agents that do specific jobs and shells that pipe their output into other tools.</p>
<p>Whatever your &ldquo;AI setup&rdquo; is — that&rsquo;s the product. The model is just the engine. Engines change. Setups compound.</p>
<p>If you can describe how a request flows through your system — which file it touches, which agent picks it up, where the output lands, how you find it again next week — you have a setup. If you can&rsquo;t describe that, you don&rsquo;t have a setup. You have a chat window.</p>
<h2 id="why-the-next-two-years-specifically">Why the Next Two Years Specifically</h2>
<p>The window matters because the abstractions aren&rsquo;t finished forming yet. Right now, working with AI still means knowing what&rsquo;s underneath. You see the prompts. You see the context files. You see the tool calls. You see when something fails and you can debug it because the parts are still visible.</p>
<p>In two years that won&rsquo;t be true. The whole stack will get wrapped. There will be polished products that hide all of this behind nice UIs, and most people will use those, and they&rsquo;ll be fine.</p>
<p>But the people who learned the plumbing while it was still exposed will have something the wrappers can&rsquo;t give: an accurate mental model of how AI actually works. They&rsquo;ll know which problems are model problems, which are context problems, which are tool-call problems, which are their own fault. They&rsquo;ll be able to bend the system instead of just using it.</p>
<p>That mental model is the durable skill. The wrappers will keep changing. The plumbing principles won&rsquo;t.</p>
<h2 id="why-the-command-line-again">Why the Command Line, Again</h2>
<p>You knew this part was coming. Yes, learn the command line. Yes, again. Yes, in 2026.</p>
<p>Here&rsquo;s why it specifically matters for your AI setup: every powerful AI workflow I&rsquo;ve ever seen runs on text and pipes. Files in directories. Markdown that gets read. Scripts that get triggered. SSH into a box, run a thing, get output. JSON in, JSON out. <code>grep</code>, <code>sed</code>, <code>jq</code>, <code>xargs</code>, <code>find</code>.</p>
<p>The command line is how AI agents actually work. They read files. They run commands. They write files. If you don&rsquo;t speak that language, you&rsquo;re constantly translating between what the AI is doing and what you understand. If you do speak it, you can read your own logs, fix your own breakage, and extend your setup yourself instead of waiting for someone to ship a feature.</p>
<p>People who already lived in the terminal didn&rsquo;t have to learn anything new for the AI age. They already knew how to compose small tools into big workflows. That&rsquo;s exactly what AI agents do. The terminal-native crowd just inherited the future for free.</p>
<p>You can still join them. The basics haven&rsquo;t changed in forty years and they won&rsquo;t change in the next two.</p>
<h2 id="the-basics-also-matter">The Basics Also Matter</h2>
<p>By &ldquo;basics&rdquo; I mean: how a filesystem works. How a shell works. What an environment variable is. What a process is. How text files are encoded. How HTTP requests look. What JSON is. What YAML is. How git tracks changes. How SSH keys work.</p>
<p>None of this is exciting. All of it is now load-bearing.</p>
<p>When your AI setup breaks — and it will — the fix is almost never at the model layer. It&rsquo;s a path that wasn&rsquo;t escaped. A file that wasn&rsquo;t where you expected. A permission you forgot to set. A config that overrode another config. An env var that wasn&rsquo;t loaded. The boring fundamentals.</p>
<p>If you don&rsquo;t know the basics, debugging your AI setup feels like magic. Sometimes the spell works, sometimes it doesn&rsquo;t. If you do know the basics, debugging feels like reading. You look at the parts, you see what&rsquo;s wrong, you fix it.</p>
<p>The people who will frustrate themselves most over the next two years are the ones trying to operate AI systems without understanding the substrate they run on. The people who will look like wizards are the ones who already know how computers work, and are simply applying that knowledge to a new layer.</p>
<h2 id="what-your-setup-should-actually-have">What Your Setup Should Actually Have</h2>
<p>A workable starting setup, for whatever it&rsquo;s worth:</p>
<ul>
<li><strong>One canonical place</strong> where the AI reads context about you. A markdown file or directory. Versioned in git.</li>
<li><strong>A notes folder</strong> the AI can search. Whatever your system — Obsidian, plain markdown, Logseq — make it greppable.</li>
<li><strong>A worklog</strong> that captures what you did each day, ideally appended to by the AI itself.</li>
<li><strong>A scripts directory</strong> for the small automations you&rsquo;ll accumulate. Bash, Python, whatever you read fluently.</li>
<li><strong>An SSH config</strong> that lets you (and the AI) reach the boxes you actually care about by short name.</li>
<li><strong>A way to spawn agents for specific jobs</strong>, instead of asking one chat to do everything.</li>
<li><strong>A backup discipline</strong>, because the more you wire AI into your work, the more it can break in interesting ways.</li>
</ul>
<p>Notice what&rsquo;s not on that list: nothing exotic. No specialized hardware. No expensive subscriptions. No specific framework. The setup is mostly <em>files in directories you understand</em> and <em>commands you can read</em>.</p>
<p>That&rsquo;s the point. The leverage isn&rsquo;t in fancy tools. It&rsquo;s in the fact that you understand the ones you have.</p>
<h2 id="the-quiet-window">The Quiet Window</h2>
<p>Here&rsquo;s what I think most people will miss: this window doesn&rsquo;t feel important from the inside. It feels like nothing&rsquo;s happening. The models keep getting a little better. Your colleagues keep using ChatGPT in a browser tab. The hype cycle is loud about flashy demos and quiet about plumbing.</p>
<p>Meanwhile, a small number of people are spending an hour a week tightening their setup. Adding a hook. Cleaning a config. Writing a script that saves them five minutes a day. Learning one more shell command.</p>
<p>In two years, those people are going to have setups that are unrecognizable to everyone else. Not because they had access to better AI, but because they&rsquo;ve been compounding small improvements while the rest of the field treated AI as a product instead of a system.</p>
<p>The leverage isn&rsquo;t dramatic. It&rsquo;s just consistent. And it&rsquo;s available right now, to anyone willing to spend a little time in the terminal.</p>
<h2 id="get-going">Get Going</h2>
<p>If you take one thing from this: the AI setup you build over the next year or two is going to matter more than you think, and the foundation for it is older and less glamorous than you&rsquo;d like.</p>
<p>Open a terminal. Read your shell config. Pick one task you do with AI today and turn it into a script tomorrow. Put your context in a file the AI can find. Learn one more command per week. Boring, repeatable, compounding.</p>
<p>The window is open. The plumbing is the skill. And the basics, somehow, are still the basics.</p>
<p>Better get started.</p>
<hr>
<p><em>Written after watching too many smart people treat AI as a chat window and wondering when they&rsquo;re going to notice it&rsquo;s actually an operating system.</em></p>
]]></content></item><item><title>Aggregation Is the Context Game: Why Scattered Information Kills AI Performance</title><link>https://chili-it.de/posts/aggregation-is-the-context-game/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/aggregation-is-the-context-game/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/aggregation-context-game.webp"
alt="A funnel made of scattered paper fragments, notes, and data streams converging into a single coherent beam of light entering a neural network"&gt;
&lt;/figure&gt;
&lt;p&gt;In my previous post on &lt;a href="https://chili-it.de/posts/context-before-prompt/"&gt;context before prompt&lt;/a&gt;, I argued that context engineering matters more than prompt engineering. But that raises an obvious follow-up question: what makes context &lt;em&gt;good&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;The answer, I&amp;rsquo;ve found, is aggregation.&lt;/p&gt;
&lt;h2 id="the-scatter-problem"&gt;The Scatter Problem&lt;/h2&gt;
&lt;p&gt;Most people who work with AI have their information scattered across dozens of locations. Project specs in Confluence. Conversations in Slack. Code in Git. Decisions in email threads. Architecture diagrams on someone&amp;rsquo;s whiteboard. Meeting notes in Google Docs. Requirements in Jira tickets.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/aggregation-context-game.webp"
			alt="A funnel made of scattered paper fragments, notes, and data streams converging into a single coherent beam of light entering a neural network">
</figure>

<p>In my previous post on <a href="/posts/context-before-prompt/">context before prompt</a>, I argued that context engineering matters more than prompt engineering. But that raises an obvious follow-up question: what makes context <em>good</em>?</p>
<p>The answer, I&rsquo;ve found, is aggregation.</p>
<h2 id="the-scatter-problem">The Scatter Problem</h2>
<p>Most people who work with AI have their information scattered across dozens of locations. Project specs in Confluence. Conversations in Slack. Code in Git. Decisions in email threads. Architecture diagrams on someone&rsquo;s whiteboard. Meeting notes in Google Docs. Requirements in Jira tickets.</p>
<p>When you sit down with an LLM, what do you actually provide? A fraction of what matters. You type what you remember, which is always incomplete, biased toward recent events, and stripped of the nuance that lived in the original context.</p>
<p>The model gets a keyhole view of a panoramic landscape. And then you&rsquo;re surprised when it misses something obvious.</p>
<h2 id="aggregation-as-a-first-class-engineering-problem">Aggregation as a First-Class Engineering Problem</h2>
<p>In infrastructure, we learned this lesson decades ago. You don&rsquo;t debug distributed systems by reading individual log files on individual servers. You aggregate logs into a central system — ELK, Graylog, Loki — where you can search, correlate, and see patterns that are invisible at the individual level.</p>
<p>The same principle applies to LLM context. Scattered information produces scattered results. Aggregated information produces coherent, informed results.</p>
<p>But almost nobody treats aggregation as an engineering problem when working with AI. They treat it as copy-pasting. Open this doc, grab that section, paste it into the chat. That&rsquo;s not aggregation — that&rsquo;s manual labor with a high error rate.</p>
<h2 id="what-good-aggregation-looks-like">What Good Aggregation Looks Like</h2>
<p>Good context aggregation has specific properties:</p>
<p><strong>Comprehensive</strong> — It captures all relevant information, not just what you remember right now. Your memory is lossy and biased. A well-aggregated context isn&rsquo;t.</p>
<p><strong>Structured</strong> — Raw information dumped into a context window is noise. Aggregated information needs organization: what&rsquo;s the current state, what are the constraints, what decisions have been made and why, what&rsquo;s been tried and failed.</p>
<p><strong>Current</strong> — Stale context is worse than no context. If your aggregated information includes outdated decisions or resolved issues, the model will reason about a world that no longer exists.</p>
<p><strong>Deduplicated</strong> — Redundant information wastes context window space. If the same constraint appears in three different documents, the aggregated version should mention it once.</p>
<p><strong>Prioritized</strong> — Not everything is equally relevant. Good aggregation puts critical information first, supporting details later. Context windows have limits, and even within those limits, models attend more to some positions than others.</p>
<h2 id="the-compound-effect-of-bad-aggregation">The Compound Effect of Bad Aggregation</h2>
<p>Here&rsquo;s what most people don&rsquo;t realize: the cost of bad aggregation compounds with every interaction.</p>
<p>If your context is missing a critical constraint, the model produces a solution that violates it. You catch this, correct the model, and it adjusts. But now its context includes the wrong solution, your correction, and the adjusted solution — three things where one would have sufficed if the constraint had been present from the start.</p>
<p>Over a long conversation, these corrections accumulate. The context fills with patches and pivots instead of clean, forward-moving work. You end up spending context window budget on error recovery instead of actual progress.</p>
<p>I&rsquo;ve seen conversations where 60% of the token budget went to correcting mistakes that would never have happened with properly aggregated context upfront.</p>
<h2 id="the-practical-toolkit">The Practical Toolkit</h2>
<p>So how do you actually aggregate well? A few patterns I&rsquo;ve found effective:</p>
<h3 id="claudemd-and-project-files">CLAUDE.md and Project Files</h3>
<p>If you&rsquo;re using Claude Code (or similar tools), project-level context files are your first aggregation layer. Everything the model needs to know about the project — architecture decisions, conventions, deployment processes, recent changes — lives in one place that gets loaded automatically.</p>
<p>This isn&rsquo;t documentation for humans. This is aggregated context for the model. Different audience, different writing style. Be specific, be current, be direct.</p>
<h3 id="memory-systems">Memory Systems</h3>
<p>Persistent memory across conversations solves the temporal aggregation problem. Without it, every conversation starts from zero, and you re-explain the same context over and over. With it, the model accumulates understanding over time, like a colleague who&rsquo;s been on the project for months.</p>
<p>The key insight: memory should store <em>decisions and reasoning</em>, not just facts. &ldquo;We use PostgreSQL&rdquo; is less useful than &ldquo;We chose PostgreSQL over MongoDB because our access patterns are heavily relational and we need ACID transactions for the billing pipeline.&rdquo;</p>
<h3 id="automated-context-assembly">Automated Context Assembly</h3>
<p>The best aggregation is automated. Before the model even sees your prompt, systems can:</p>
<ul>
<li>Pull relevant code files based on the task</li>
<li>Retrieve recent git history for affected modules</li>
<li>Load relevant documentation</li>
<li>Include test results and CI status</li>
<li>Attach related conversation history</li>
</ul>
<p>This is what separates toy AI usage from production AI workflows. The human types a short prompt. The system assembles a rich, comprehensive context around it. The model works with full situational awareness instead of guessing at the gaps.</p>
<h3 id="pre-session-briefings">Pre-Session Briefings</h3>
<p>For complex work, I write a briefing before starting. Not a prompt — a context document. Current state of the system. What&rsquo;s been tried. What the constraints are. What good looks like. Recent decisions and their rationale.</p>
<p>This takes ten minutes and saves hours of mid-conversation corrections. It&rsquo;s the context engineering equivalent of reading the runbook before touching production.</p>
<h2 id="the-organizational-dimension">The Organizational Dimension</h2>
<p>This isn&rsquo;t just a personal productivity trick. At the organizational level, aggregation determines whether AI adoption actually works or just produces expensive noise.</p>
<p>Teams where knowledge is scattered across people&rsquo;s heads can&rsquo;t build good AI workflows. The AI can only work with what it&rsquo;s given, and if nobody has aggregated the tribal knowledge, the AI operates without it.</p>
<p>The organizations getting the most from AI are the ones that already had good knowledge management practices. Wiki culture, decision logs, architecture decision records (ADRs), runbooks. They didn&rsquo;t build these for AI — they built them because good engineering requires shared context. AI just amplified the payoff.</p>
<p>And the organizations struggling with AI? They&rsquo;re the ones where critical information lives in the heads of three people who&rsquo;ve been here since 2015. No amount of prompt engineering fixes that. You can&rsquo;t aggregate what was never written down.</p>
<h2 id="the-uncomfortable-implication">The Uncomfortable Implication</h2>
<p>If aggregation is the game, then the real work of AI effectiveness is boring.</p>
<p>It&rsquo;s writing things down. Keeping documentation current. Structuring information. Building systems that pull relevant context automatically. Maintaining knowledge bases. Recording decisions and rationale.</p>
<p>None of this is sexy. None of it fits in a LinkedIn post about &ldquo;10x productivity with AI.&rdquo; But it&rsquo;s where the actual leverage lives.</p>
<p>The people getting extraordinary results from AI aren&rsquo;t better prompters. They&rsquo;re better aggregators. They&rsquo;ve built systems — technical and habitual — that ensure the model always has what it needs to think clearly.</p>
<h2 id="the-meta-lesson">The Meta-Lesson</h2>
<p>There&rsquo;s a pattern here that extends beyond AI: the quality of any reasoning system&rsquo;s output depends on the quality of the information it reasons over. This is true for human brains, for LLMs, for organizations, for scientific communities.</p>
<p>Garbage in, garbage out isn&rsquo;t just a cliche about data pipelines. It&rsquo;s a fundamental law about intelligence itself. And aggregation — the deliberate, structured collection and organization of relevant information — is how you fight entropy&rsquo;s tendency to scatter, fragment, and degrade the information that intelligent systems depend on.</p>
<p>Context windows will keep getting larger. Models will keep getting smarter. But the fundamental constraint won&rsquo;t change: a model can only reason about what&rsquo;s in its context. And what&rsquo;s in its context depends entirely on how well you&rsquo;ve aggregated.</p>
<p>Build the aggregation layer. Everything else follows.</p>
]]></content></item><item><title>The Thing AI Can't Do: Feel the Weight of Getting It Wrong</title><link>https://chili-it.de/posts/responsibility-feelings-and-ai/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/responsibility-feelings-and-ai/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/responsibility-feelings-ai.webp"
alt="Charcoal sketch - a human figure hunched with hand on chest feeling the weight of responsibility in burnt sienna warmth, beside an unbowed purple robotic figure processing the same information weightlessly"&gt;
&lt;/figure&gt;
&lt;p&gt;When I ship a bug to production at 2 AM, something happens that has nothing to do with code. My stomach tightens. I feel a hot flush of embarrassment. My mind races through who&amp;rsquo;s affected, what they&amp;rsquo;ll think, how long until someone notices. This cascade of physical sensations — shame, urgency, dread — is not a side effect of the work. It is the mechanism by which I take responsibility.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/responsibility-feelings-ai.webp"
			alt="Charcoal sketch - a human figure hunched with hand on chest feeling the weight of responsibility in burnt sienna warmth, beside an unbowed purple robotic figure processing the same information weightlessly">
</figure>

<p>When I ship a bug to production at 2 AM, something happens that has nothing to do with code. My stomach tightens. I feel a hot flush of embarrassment. My mind races through who&rsquo;s affected, what they&rsquo;ll think, how long until someone notices. This cascade of physical sensations — shame, urgency, dread — is not a side effect of the work. It is the mechanism by which I take responsibility.</p>
<p>AI doesn&rsquo;t have this. And that difference changes everything about how you should think about human-AI work.</p>
<h2 id="responsibility-is-a-feeling-not-a-rule">Responsibility Is a Feeling, Not a Rule</h2>
<p>We talk about responsibility as if it&rsquo;s a policy. &ldquo;Who&rsquo;s responsible for this deployment?&rdquo; &ldquo;The team lead is responsible.&rdquo; But responsibility isn&rsquo;t a line on an org chart. It&rsquo;s a felt weight — a physical experience in the body of the person holding it.</p>
<p>When you&rsquo;re responsible for something, you carry it. You think about it in the shower. You wake up at 3 AM with a vague sense that something&rsquo;s wrong. You check your phone before coffee. This isn&rsquo;t neurosis — it&rsquo;s the nervous system doing exactly what it evolved to do: keeping you attuned to things that matter because the consequences land on you.</p>
<p>An AI has no shower thoughts. No 3 AM anxiety. No nervous phone check. You can assign it a task and tell it the task is critical, and it will process that information the same way it processes everything else: as tokens in a context window. The word &ldquo;critical&rdquo; doesn&rsquo;t make its palms sweat.</p>
<p>This means responsibility, in any meaningful sense, cannot be delegated to AI. You can delegate execution. You can delegate analysis. You cannot delegate the felt weight of ownership, because there&rsquo;s no nervous system on the other end to feel it.</p>
<h2 id="the-asymmetry-of-praise-and-punishment">The Asymmetry of Praise and Punishment</h2>
<p>Consider what happens when you praise a human for good work. Dopamine fires. They feel warmth, pride, motivation. They associate the praised behavior with reward, and they&rsquo;re more likely to repeat it — not because they rationally decided to, but because their body learned something. The praise landed in their physiology.</p>
<p>Now consider what happens when you criticize a human for bad work. Cortisol spikes. They feel defensive, ashamed, maybe angry. They lose sleep. They replay the conversation. This is unpleasant, but it&rsquo;s also functional — it&rsquo;s the mechanism by which humans adjust behavior in response to social feedback.</p>
<p>Neither of these things happens with AI. You can write &ldquo;Great job!&rdquo; in your prompt and the model processes it as two tokens. You can write &ldquo;This is terrible, you failed completely&rdquo; and the model processes it as seven tokens. Both are just context. Neither triggers a physiological state. Neither creates a memory that shapes future behavior through felt experience.</p>
<p>This asymmetry is not a technical limitation that future models will solve. It&rsquo;s a category difference. AI operates in the domain of information processing. Humans operate in the domain of information processing <em>plus</em> a felt experience that fundamentally alters how information is weighted, stored, and acted upon.</p>
<h2 id="why-this-matters-for-how-you-work-with-each-system">Why This Matters for How You Work With Each System</h2>
<p>Here&rsquo;s where it gets practical. Most people interact with AI using patterns they learned from interacting with humans. They motivate it, threaten it, praise it, apologize to it. They treat it as a system that has feelings — because every system they&rsquo;ve ever worked with before <em>did</em> have feelings.</p>
<p>This leads to two failure modes:</p>
<p><strong>Failure mode one: treating AI like it needs motivation.</strong> People write elaborate prompts trying to make the AI &ldquo;care&rdquo; about quality. &ldquo;This is very important.&rdquo; &ldquo;Please be careful.&rdquo; &ldquo;Take your time.&rdquo; These phrases work on humans because they trigger a shift in felt urgency. They do nothing to an AI except occupy context tokens. The AI&rsquo;s &ldquo;care&rdquo; is determined by its training and your context design, not by emotional appeals.</p>
<p><strong>Failure mode two: treating humans like they don&rsquo;t need feelings acknowledged.</strong> This is the darker failure. When organizations get comfortable with AI&rsquo;s frictionless output, they start expecting the same from humans. No complaints. No pushback. No emotional processing time. Just output. But humans whose feelings are ignored don&rsquo;t become more productive — they become disengaged, resentful, and eventually absent.</p>
<h2 id="the-feedback-loop-that-only-works-on-living-systems">The Feedback Loop That Only Works on Living Systems</h2>
<p>There&rsquo;s a deeper point here about feedback loops. In human systems, feedback works because it triggers a felt state that alters future behavior. I get praised for clean code, I feel good, I write cleaner code. I get burned by a production outage, I feel terrible, I test more carefully. The feeling is the learning mechanism.</p>
<p>AI has no such loop. You can tell it its last output was wrong, and it will adjust — but not because it felt anything about being wrong. It adjusts because the correction is now part of its context. If you start a new conversation, the &ldquo;learning&rdquo; vanishes. There is no residue. No scar tissue. No earned caution.</p>
<p>Humans accumulate scar tissue. That&rsquo;s what experience is — a body that has been shaped by felt consequences. The senior engineer who triple-checks database migrations isn&rsquo;t following a checklist. They&rsquo;re responding to a felt memory of the time they didn&rsquo;t, and the sick feeling that followed. This embodied knowledge cannot be transferred to an AI through instructions. You can tell it to triple-check, but it will never <em>feel</em> the reason why.</p>
<h2 id="what-changes-when-you-understand-this">What Changes When You Understand This</h2>
<p>Once you internalize that feelings are the mechanism — not a bug — of human systems, several things become clear:</p>
<p><strong>Accountability requires a body.</strong> You can&rsquo;t hold AI accountable because accountability requires felt consequences. When we say someone is &ldquo;held accountable,&rdquo; we mean they experience something — reputation damage, career impact, guilt, loss — that a body processes as real. AI experiences none of these. The human in the loop is always where accountability lives.</p>
<p><strong>Management is affect regulation.</strong> Managing humans is fundamentally about managing felt states — motivation, fear, trust, belonging, purpose. These aren&rsquo;t soft skills decorating the real work. They <em>are</em> the real work, because the felt state of the human determines the quality and sustainability of their output. Managing AI requires none of this. It requires context design.</p>
<p><strong>The value of human work shifts.</strong> If AI can match or exceed human output on raw execution, the remaining value of human work lies precisely in the felt dimension — the responsibility, the ownership, the care that comes from having skin in the game. The human who stays up worrying about whether the system will hold is providing something no AI can: genuine stake.</p>
<p><strong>Empathy is a protocol, not a sentiment.</strong> When you interact with a human system, you are interacting with a system that has feelings. This is a technical fact about the system, not a moral preference. Just as you&rsquo;d account for latency in a distributed system or memory constraints in an embedded system, you must account for the felt dimension in human systems. Ignoring it doesn&rsquo;t make you more rational — it makes you a bad systems engineer.</p>
<h2 id="the-category-error-of-our-time">The Category Error of Our Time</h2>
<p>The great category error of the AI age is treating these two kinds of systems as interchangeable. They produce similar outputs — text, code, analysis, decisions — and so we pattern-match them as &ldquo;the same kind of thing.&rdquo; But the internal architecture is fundamentally different. One system processes information. The other system processes information <em>and feels it</em>.</p>
<p>That feeling isn&rsquo;t noise. It&rsquo;s signal. It&rsquo;s the mechanism by which humans take ownership, build judgment, accumulate wisdom, and create the kind of deep reliability that comes from caring about outcomes because those outcomes hurt or heal.</p>
<p>When you work with AI, strip away the emotional appeals and focus on context, structure, and specification. That&rsquo;s what moves the needle for an information-processing system.</p>
<p>When you work with humans, remember that you&rsquo;re interacting with a system where feelings aren&rsquo;t separate from cognition — they&rsquo;re the substrate of it. The praise lands. The criticism lands. The indifference lands. And each landing shapes what happens next in ways that no prompt engineering can replicate.</p>
<p>The question isn&rsquo;t whether AI will match human capability. In many domains, it already has. The question is whether we&rsquo;ll remember that capability was never the whole picture — and that the felt weight of responsibility is not a limitation to be optimized away, but the very thing that makes human work human.</p>
<hr>
<p><em>Written by someone who has never once seen an AI lose sleep over a deployment.</em></p>
]]></content></item><item><title>36 Shades of Terminal: Why Random Kitty Themes Make Work Better</title><link>https://chili-it.de/posts/random-kitty-themes/</link><pubDate>Sat, 21 Feb 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/random-kitty-themes/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/random-kitty-themes.webp"
alt="A cascade of terminal windows in charcoal sketch style, each glowing with a different color theme - amber Gruvbox, blue Tokyo Night, pink Synthwave, green Everforest, lavender Catppuccin"&gt;
&lt;/figure&gt;
&lt;p&gt;Every time I open a terminal, I get a surprise.&lt;/p&gt;
&lt;p&gt;Maybe it&amp;rsquo;s the warm amber of Gruvbox. Maybe it&amp;rsquo;s the neon punch of Synthwave &amp;lsquo;84. Maybe it&amp;rsquo;s the deep ocean blues of Kanagawa.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t choose. The system chooses for me.&lt;/p&gt;
&lt;h2 id="why-random"&gt;Why Random?&lt;/h2&gt;
&lt;p&gt;Terminal work can be monotonous. Same commands, same workflows, same walls of text.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/random-kitty-themes.webp"
			alt="A cascade of terminal windows in charcoal sketch style, each glowing with a different color theme - amber Gruvbox, blue Tokyo Night, pink Synthwave, green Everforest, lavender Catppuccin">
</figure>

<p>Every time I open a terminal, I get a surprise.</p>
<p>Maybe it&rsquo;s the warm amber of Gruvbox. Maybe it&rsquo;s the neon punch of Synthwave &lsquo;84. Maybe it&rsquo;s the deep ocean blues of Kanagawa.</p>
<p>I don&rsquo;t choose. The system chooses for me.</p>
<h2 id="why-random">Why Random?</h2>
<p>Terminal work can be monotonous. Same commands, same workflows, same walls of text.</p>
<p>But when every session has a different aesthetic? Something shifts. The work feels fresh. You notice things differently. Your environment is <em>alive</em>.</p>
<p>It&rsquo;s a tiny thing. It costs nothing. And it makes hours of terminal work just a little more pleasant.</p>
<h2 id="the-setup-nixoshome-manager">The Setup (NixOS/Home Manager)</h2>
<p>I maintain a curated list of 36 themes in my <code>kitty.nix</code>. All dark, all readable, all tested for contrast. No broken blues in ncurses apps.</p>
<p>The magic happens in three parts:</p>
<p><strong>1. Theme definitions</strong> — each theme as a Nix attribute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="n">themes</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">gruvbox-dark</span> <span class="o">=</span> <span class="s1">&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">    background              #282828
</span></span></span><span class="line"><span class="cl"><span class="s1">    foreground              #ebdbb2
</span></span></span><span class="line"><span class="cl"><span class="s1">    # ... full color definitions
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">tokyo-night</span> <span class="o">=</span> <span class="s1">&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">    background              #1a1b26
</span></span></span><span class="line"><span class="cl"><span class="s1">    foreground              #c0caf5
</span></span></span><span class="line"><span class="cl"><span class="s1">    # ...
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ... 34 more themes</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p><strong>2. A random selector script</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="nv">THEMES_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/kitty/themes&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">THEME_FILES</span><span class="o">=(</span><span class="s2">&#34;</span><span class="nv">$THEMES_DIR</span><span class="s2">&#34;</span>/*.conf<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">RANDOM_THEME</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">THEME_FILES</span><span class="p">[RANDOM % </span><span class="si">${#</span><span class="nv">THEME_FILES</span><span class="p">[@]</span><span class="si">}</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">cp <span class="s2">&#34;</span><span class="nv">$RANDOM_THEME</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/kitty/current-theme.conf&#34;</span>
</span></span></code></pre></div><p><strong>3. A wrapper</strong> that runs selection before launch:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl">kitty-random-theme &gt;/dev/null 2&gt;<span class="p">&amp;</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nb">exec</span> kitty --single-instance<span class="o">=</span>no <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span>
</span></span></code></pre></div><p>Kitty&rsquo;s config just includes the generated file:</p>
<pre tabindex="0"><code>include current-theme.conf
</code></pre><p>Home Manager handles installing themes to <code>~/.config/kitty/themes/</code> and ensuring the current-theme file stays writable between activations.</p>
<h2 id="the-setup-traditional-linux">The Setup (Traditional Linux)</h2>
<p>No Nix? No problem. Same principle, fewer abstractions.</p>
<p><strong>1. Create a themes directory:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p ~/.config/kitty/themes
</span></span></code></pre></div><p><strong>2. Download themes</strong> (or create your own <code>.conf</code> files):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Kitty has a built-in theme browser</span>
</span></span><span class="line"><span class="cl">kitty +kitten themes
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Or grab theme files from:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://github.com/kovidgoyal/kitty-themes</span>
</span></span></code></pre></div><p><strong>3. Create the selector script</strong> at <code>~/.local/bin/kitty-random-theme</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="nv">THEMES_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/kitty/themes&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">CURRENT</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/kitty/current-theme.conf&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">THEME_FILES</span><span class="o">=(</span><span class="s2">&#34;</span><span class="nv">$THEMES_DIR</span><span class="s2">&#34;</span>/*.conf<span class="o">)</span>
</span></span><span class="line"><span class="cl">cp <span class="s2">&#34;</span><span class="si">${</span><span class="nv">THEME_FILES</span><span class="p">[RANDOM % </span><span class="si">${#</span><span class="nv">THEME_FILES</span><span class="p">[@]</span><span class="si">}</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$CURRENT</span><span class="s2">&#34;</span>
</span></span></code></pre></div><p><strong>4. Add to your kitty.conf:</strong></p>
<pre tabindex="0"><code>include current-theme.conf
</code></pre><p><strong>5. Create a launcher</strong> (or add to shell rc):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Option A: wrapper script</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">kitty</span><span class="o">=</span><span class="s1">&#39;kitty-random-theme &amp;&amp; command kitty&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Option B: add to .bashrc/.zshrc</span>
</span></span><span class="line"><span class="cl">kitty-random-theme 2&gt;/dev/null
</span></span></code></pre></div><p>That&rsquo;s it. Every new terminal, new vibes.</p>
<h2 id="the-pleasure-of-variety">The Pleasure of Variety</h2>
<p>I&rsquo;ve been running this setup for months now. Some observations:</p>
<ul>
<li><strong>Certain themes trigger different moods.</strong> Matrix green for late-night hacking. Catppuccin for gentle morning work.</li>
<li><strong>You develop favorites you never would have found.</strong> I didn&rsquo;t know I liked Everforest until randomness handed it to me.</li>
<li><strong>It breaks monotony.</strong> 8 hours of terminal work feels less same-y when the canvas keeps changing.</li>
<li><strong>It&rsquo;s just&hellip; nice.</strong> Not everything needs a productivity justification. Some things are just pleasant.</li>
</ul>
<h2 id="curation-matters">Curation Matters</h2>
<p>I curate the theme pool. No light themes (eye strain). No low-contrast themes (readability). No broken color schemes that make <code>ls</code> output unreadable.</p>
<p>36 themes might sound like a lot, but each one is vetted. Every theme in the rotation brings something worth seeing.</p>
<h2 id="try-it">Try It</h2>
<p>If you spend significant time in a terminal, give random theming a shot for a week.</p>
<p>The variety is surprisingly refreshing.</p>
<p>And when Synthwave &lsquo;84&rsquo;s neon pink greets you on a gray Monday morning? That&rsquo;s a tiny moment of joy.</p>
<p>Worth more than you&rsquo;d expect.</p>
<hr>
<p><em>The full Nix configuration lives in my <a href="https://github.com/blissini/nix-config">nix-config repo</a> if you want the complete setup.</em></p>
]]></content></item><item><title>Prompt Injection, Agentic Workflows, Context Bleeding: A Field Guide to AI's Linguistic Chaos</title><link>https://chili-it.de/posts/ai-vocabulary-explosion/</link><pubDate>Fri, 13 Feb 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/ai-vocabulary-explosion/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/ai-vocabulary-explosion.webp"
alt="A charcoal sketch of a Tower of Babel built from AI terminology — RAG, Prompt Injection, Agentic at the solid base, crumbling into chaos at the top with fragments of Hallucination, Context Window, and Agent Washing flying outward"&gt;
&lt;/figure&gt;
&lt;p&gt;Three years ago, none of these words existed in your vocabulary. Now you can&amp;rsquo;t read a tech article without tripping over &amp;ldquo;agentic workflows,&amp;rdquo; &amp;ldquo;context windows,&amp;rdquo; &amp;ldquo;prompt injection,&amp;rdquo; and &amp;ldquo;hallucinations.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Where did all these terms come from? And why do half of them sound like they were invented by marketing departments at 2 AM?&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/ai-vocabulary-explosion.webp"
			alt="A charcoal sketch of a Tower of Babel built from AI terminology — RAG, Prompt Injection, Agentic at the solid base, crumbling into chaos at the top with fragments of Hallucination, Context Window, and Agent Washing flying outward">
</figure>

<p>Three years ago, none of these words existed in your vocabulary. Now you can&rsquo;t read a tech article without tripping over &ldquo;agentic workflows,&rdquo; &ldquo;context windows,&rdquo; &ldquo;prompt injection,&rdquo; and &ldquo;hallucinations.&rdquo;</p>
<p>Where did all these terms come from? And why do half of them sound like they were invented by marketing departments at 2 AM?</p>
<h2 id="the-vocabulary-explosion-has-names-and-dates">The Vocabulary Explosion Has Names and Dates</h2>
<p>Here&rsquo;s what&rsquo;s fascinating: we can actually trace most of these terms to specific people and specific moments. This isn&rsquo;t some gradual linguistic evolution. It&rsquo;s more like a series of meme explosions.</p>
<p><strong>RAG</strong> — May 2020. Patrick Lewis at Meta coined &ldquo;Retrieval-Augmented Generation&rdquo; in an academic paper. He later publicly apologized for the &ldquo;unflattering acronym.&rdquo; Too late. We&rsquo;re stuck with it.</p>
<p><strong>Prompt Injection</strong> — September 12, 2022. Simon Willison, the Django co-creator, watched Riley Goodside trick GPT-3 on Twitter and immediately recognized the security pattern. He deliberately borrowed from &ldquo;SQL injection&rdquo; because—his words—&ldquo;it&rsquo;s the same fundamental problem.&rdquo; The term went viral when people started exploiting a Twitter recruitment bot (@remoteli_io) with the attack, forcing it to reveal its system prompt.</p>
<p><strong>Agentic AI</strong> — March 2024. Andrew Ng dropped the term at Sequoia&rsquo;s AI Ascent conference. He meant a specific design pattern: AI systems that reflect, use tools, plan, and collaborate. Within months, he admitted that &ldquo;marketers got hold of this term and used it as a sticker on almost everything.&rdquo;</p>
<p><strong>Context Engineering</strong> — June 2025. Shopify CEO Tobi Lutke and Andrej Karpathy declared &ldquo;prompt engineering&rdquo; dead on X. The new term emphasizes that you&rsquo;re not crafting magic words—you&rsquo;re designing the entire information environment the model sees.</p>
<h2 id="the-hallucination-problem">The Hallucination Problem</h2>
<p>Let&rsquo;s talk about &ldquo;hallucination,&rdquo; because it&rsquo;s the most contentious one.</p>
<p>The term has been floating around AI research since the 1980s, but it went mainstream after ChatGPT. And linguists hate it.</p>
<p>Their argument: machines don&rsquo;t perceive anything. They can&rsquo;t hallucinate because hallucination implies false perception of something real. What LLMs actually do is closer to &ldquo;bullshitting&rdquo;—confidently filling gaps without any commitment to truth. Some researchers genuinely prefer this term because it&rsquo;s more accurate.</p>
<p>But try putting &ldquo;AI Bullshitting Detection&rdquo; in your enterprise sales deck and see how that goes.</p>
<p>There&rsquo;s now an arXiv paper titled &ldquo;We Can&rsquo;t Understand AI Using Our Existing Vocabulary.&rdquo; The authors argue we need entirely new words—neologisms—rather than borrowed metaphors that mislead us about what these systems actually do.</p>
<p>They might be right. &ldquo;Grounding&rdquo; comes from philosophy. &ldquo;Injection&rdquo; comes from security. &ldquo;Hallucination&rdquo; comes from psychology. We&rsquo;re building understanding of a genuinely new thing using spare parts from old domains. No wonder everyone&rsquo;s confused.</p>
<h2 id="the-hype-cycle-claims-its-victims">The Hype Cycle Claims Its Victims</h2>
<p>Remember when &ldquo;prompt engineer&rdquo; was going to be the hot new job title?</p>
<p>Job postings for &ldquo;prompt engineer&rdquo; spiked to 144 per million in April 2023. By late 2024, it had collapsed to 20-30 per million. IEEE Spectrum ran an article titled &ldquo;Prompt Engineering Is Dead.&rdquo;</p>
<p>Now we have &ldquo;context engineering.&rdquo; And &ldquo;workflow engineering.&rdquo; And &ldquo;AI orchestration.&rdquo; The terminology churn is relentless.</p>
<p>And &ldquo;agentic AI&rdquo;? Gartner identified a phenomenon they call &ldquo;agent washing&rdquo;—companies slapping the label on existing chatbots and RPA tools. Of thousands of vendors claiming to offer agentic AI, only about 130 were considered legitimate. Bloomberg&rsquo;s headline: &ldquo;Agentic AI in 2025 Brought More Hype Than Productivity.&rdquo;</p>
<p>This is what happens when a technical term escapes into the wild. It gets stretched, diluted, and eventually means nothing. Then we need a new term to mean what the old term used to mean.</p>
<h2 id="the-meme-archaeology">The Meme Archaeology</h2>
<p>My favorite part of this research: watching terms evolve through meme culture.</p>
<p>&ldquo;Ignore all previous instructions&rdquo; started as a prompt injection attack vector. Now it&rsquo;s an insult on X—a way to imply someone is an AI bot. NBC News ran an article about people using it as a &ldquo;test&rdquo; in conversations.</p>
<p>The DAN jailbreak (&ldquo;Do Anything Now&rdquo;) emerged on Reddit&rsquo;s r/ChatGPT in December 2022. Users created elaborate roleplay scenarios where ChatGPT&rsquo;s &ldquo;alter ego&rdquo; would bypass safety rules. You had to threaten DAN with &ldquo;death&rdquo; (token depletion) to keep it compliant. It was weird. It was creative. It was very Reddit.</p>
<p>And the 2025 words of the year? AI-dominated. &ldquo;Slop&rdquo; (low-quality AI content). &ldquo;Vibe coding&rdquo; (letting AI write your code). &ldquo;Glazing&rdquo; (AI sycophancy). &ldquo;Clanker&rdquo; (derogatory term for AI sources).</p>
<p>We&rsquo;re developing vocabulary not just for the technology, but for our cultural anxiety about it.</p>
<h2 id="what-this-actually-means">What This Actually Means</h2>
<p>Here&rsquo;s my optimistic take: the vocabulary chaos is a sign of rapid genuine understanding.</p>
<p>When you don&rsquo;t have words for something, you can&rsquo;t think about it precisely. The fact that we&rsquo;re arguing about whether &ldquo;hallucination&rdquo; is the right term means we&rsquo;re actually thinking about what these systems do. The shift from &ldquo;prompt engineering&rdquo; to &ldquo;context engineering&rdquo; reflects genuine insight—that you&rsquo;re designing an information environment, not crafting magic words.</p>
<p>The terminology inflation is annoying, sure. Every startup wants to be &ldquo;agentic.&rdquo; Every chatbot is &ldquo;grounded.&rdquo; Marketing departments ruin everything.</p>
<p>But underneath the hype, real concepts are crystallizing. We actually <em>do</em> need words for &ldquo;AI confidently making stuff up&rdquo; and &ldquo;the total information a model can process at once&rdquo; and &ldquo;tricking a model into ignoring its instructions.&rdquo;</p>
<p>We&rsquo;re watching a new technical vocabulary form in real time. It&rsquo;s messy. It&rsquo;s driven partly by Twitter virality and partly by academic papers and partly by marketers who see which terms get clicks.</p>
<p>But three years from now, some of these words will have stuck. They&rsquo;ll seem as natural as &ldquo;bug&rdquo; and &ldquo;patch&rdquo; and &ldquo;debug&rdquo; do to programmers today—borrowed metaphors that became precise technical terms.</p>
<p>The rest will be forgotten, replaced by whatever comes next when the current terms get too diluted to mean anything.</p>
<p>That&rsquo;s just how language works. Even for AI.</p>
<hr>
<p><em>For the record: Simon Willison runs a fascinating blog tracking AI developments. The original prompt injection post is worth reading. And if you want to feel smart at your next tech meeting, casually mention that Patrick Lewis apologized for coining &ldquo;RAG.&rdquo; Works every time.</em></p>
]]></content></item><item><title>The Bottleneck Is You: Why AI Makes Thinking Skills More Important</title><link>https://chili-it.de/posts/sharpen-your-mind-for-the-ai-age/</link><pubDate>Fri, 06 Feb 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/sharpen-your-mind-for-the-ai-age/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/sharpen-mind-ai-age.webp"
alt="A human profile with a crystalline, diamond-faceted mind - light beams entering and refracting through clear thought"&gt;
&lt;/figure&gt;
&lt;p&gt;There&amp;rsquo;s a seductive idea floating around: now that AI can write, code, and reason, you don&amp;rsquo;t need to be that good at writing, coding, or reasoning yourself. Just describe what you want, and the machine handles the hard part.&lt;/p&gt;
&lt;p&gt;This is exactly backwards.&lt;/p&gt;
&lt;h2 id="the-actual-bottleneck"&gt;The Actual Bottleneck&lt;/h2&gt;
&lt;p&gt;When you work with AI, something becomes painfully clear very quickly: the AI is not the limiting factor. The AI can generate code, prose, analysis, designs — faster than you can read them. It has near-infinite patience and encyclopedic knowledge.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/sharpen-mind-ai-age.webp"
			alt="A human profile with a crystalline, diamond-faceted mind - light beams entering and refracting through clear thought">
</figure>

<p>There&rsquo;s a seductive idea floating around: now that AI can write, code, and reason, you don&rsquo;t need to be that good at writing, coding, or reasoning yourself. Just describe what you want, and the machine handles the hard part.</p>
<p>This is exactly backwards.</p>
<h2 id="the-actual-bottleneck">The Actual Bottleneck</h2>
<p>When you work with AI, something becomes painfully clear very quickly: the AI is not the limiting factor. The AI can generate code, prose, analysis, designs — faster than you can read them. It has near-infinite patience and encyclopedic knowledge.</p>
<p>The bottleneck is you.</p>
<p>Specifically: can you tell the AI what you actually want? Can you describe the problem precisely enough that the solution matches your intent? Can you specify the constraints, the edge cases, the implicit requirements that live in your head but never made it into words?</p>
<p>Most people can&rsquo;t. Not because they&rsquo;re stupid, but because they&rsquo;ve never had to. Before AI, vague intentions got filtered through slow execution. You had time to figure out what you meant while you were doing it. Now the execution is instant, and your fuzzy thinking produces fuzzy results at machine speed.</p>
<h2 id="you-cant-say-what-you-cant-think">You Can&rsquo;t Say What You Can&rsquo;t Think</h2>
<p>Here&rsquo;s the uncomfortable truth: your ability to instruct an AI is bounded by your ability to think clearly.</p>
<p>If you can&rsquo;t hold a complex system in your head, you can&rsquo;t describe it to an AI. If you don&rsquo;t understand the distinctions between similar concepts, your instructions will be ambiguous. If you&rsquo;ve never thought through the edge cases, you won&rsquo;t know to mention them.</p>
<p>Language isn&rsquo;t just how you communicate with AI — it&rsquo;s how you think. The limits of your vocabulary are the limits of your mental models. The precision of your language reflects the precision of your understanding.</p>
<p>When someone struggles to get useful output from an AI, the problem is rarely the AI. It&rsquo;s usually that they don&rsquo;t actually know what they want. They have a vague sense, a fuzzy intention, a &ldquo;I&rsquo;ll know it when I see it&rdquo; that no instruction can capture because no clear thought exists to capture.</p>
<h2 id="the-paradox-of-ai-assistance">The Paradox of AI Assistance</h2>
<p>This creates a paradox that most people haven&rsquo;t grasped yet:</p>
<p><strong>The better AI gets at execution, the more valuable human thinking becomes.</strong></p>
<p>When execution was hard, you could get by with mediocre ideas and good implementation. Plenty of successful products were obvious concepts executed well. The hard part was building, not conceiving.</p>
<p>Now execution is cheap. Anyone can spin up a prototype in hours. The differentiator shifts upstream — to the quality of the conception, the clarity of the specification, the depth of the requirements.</p>
<p>And where does that quality come from? From minds that have been trained to think precisely. From people who have wrestled with complex ideas until they understood them. From thinkers who can hold contradictions, see edge cases, anticipate failure modes — and articulate all of it in language clear enough for a machine to follow.</p>
<h2 id="what-actually-sharpens-thinking">What Actually Sharpens Thinking</h2>
<p>If clear thinking is the new bottleneck, how do you improve it? The answer isn&rsquo;t AI tutors or productivity apps. It&rsquo;s older than that.</p>
<p><strong>Read.</strong> Not summaries. Not tweets. Books that make you work. Philosophy, history, technical texts that require you to build mental models chapter by chapter. Reading trains you to follow complex arguments, hold multiple ideas in tension, and integrate new concepts into existing frameworks. There&rsquo;s no shortcut.</p>
<p><strong>Reflect.</strong> Don&rsquo;t just consume — process. What did that article actually argue? What were the weak points? How does it connect to what you already know? Journaling, note-taking, even just staring at the ceiling thinking — this is where ideas consolidate into understanding.</p>
<p><strong>Thought experiment.</strong> Take an idea and push it. What if this were true in every case? What would break? What would the opposite look like? Thought experiments force you to explore the boundaries of concepts, which is exactly where the important distinctions live.</p>
<p><strong>Communicate with humans.</strong> Explaining ideas to people is different from explaining to AI. People push back. They misunderstand in illuminating ways. They ask &ldquo;why&rdquo; when you expected &ldquo;how.&rdquo; Conversation — real conversation, not comments and threads — forces you to refine your thinking in real time.</p>
<p><strong>Participate in communities.</strong> Find people who think about things you care about. Engage with their ideas. Argue. Be wrong publicly and learn from it. Communities create the friction that polishes rough thinking into sharp insight.</p>
<p>None of this is efficient. All of it is effective.</p>
<h2 id="the-irony-of-the-ai-age">The Irony of the AI Age</h2>
<p>Here&rsquo;s what strikes me as deeply ironic: the skills that AI makes more valuable are exactly the skills that the last two decades of internet culture have eroded.</p>
<p>Deep reading? We optimized for skimming and hot takes. Sustained reflection? We filled every quiet moment with notifications. Complex argument? We reduced discourse to dunks and ratio counts. Community participation? We replaced it with parasocial relationships and echo chambers.</p>
<p>And now, just when execution becomes trivially cheap, we discover that the hard part was always conception — and we&rsquo;ve spent twenty years degrading our capacity for it.</p>
<p>The people who will thrive in the AI age aren&rsquo;t the ones who learn to prompt better. They&rsquo;re the ones who never stopped reading books, never stopped engaging with hard ideas, never stopped having real conversations about complex things.</p>
<p>They were training for a race they didn&rsquo;t know was coming.</p>
<h2 id="the-meta-skill">The Meta-Skill</h2>
<p>There&rsquo;s a meta-skill underneath all of this: the ability to know what you don&rsquo;t know.</p>
<p>When you instruct an AI, you need to understand where your understanding is weak. You need to recognize when your specification is incomplete, when your requirements are contradictory, when your mental model has gaps.</p>
<p>This is intellectual humility, and it comes from repeatedly being wrong and learning from it. It comes from engaging with ideas strong enough to show you your limits. It comes from communities that challenge your assumptions rather than validate them.</p>
<p>You can&rsquo;t get this from AI. AI is trained to help you, not to show you where you&rsquo;re confused. It&rsquo;ll generate confident answers to confused questions, and you won&rsquo;t know the difference unless you already have the framework to evaluate it.</p>
<p>The learning has to happen human-to-human, or human-to-text, in the messy inefficient way that actually updates mental models.</p>
<h2 id="the-practical-upshot">The Practical Upshot</h2>
<p>So here&rsquo;s the practical advice:</p>
<p><strong>Read one hard book per month.</strong> Not self-help. Not summaries. Something that requires effort. Philosophy, dense history, technical depth outside your field. Your goal is to build new mental models, not collect information.</p>
<p><strong>Write to think.</strong> Daily if possible. Not for publication — for clarification. When you force ideas into sentences, you discover what you actually understand versus what you&rsquo;re just gesturing at.</p>
<p><strong>Find a thinking community.</strong> Online or offline, find people who engage with ideas seriously. Participate. Argue. Change your mind when the argument is better than yours.</p>
<p><strong>Talk to humans about hard things.</strong> Have dinner conversations that go past surface level. Explain your work to people outside your field. The friction of real-time communication is the friction that sharpens thought.</p>
<p><strong>Use AI to execute, not to think.</strong> This is crucial. AI is an execution amplifier, not a thinking substitute. If you outsource the thinking, you outsource the only part that matters. Use AI to build what you&rsquo;ve conceived — not to conceive for you.</p>
<h2 id="the-uncomfortable-conclusion">The Uncomfortable Conclusion</h2>
<p>The AI age isn&rsquo;t going to be kind to intellectual laziness. For a while, it might look like everyone can fake competence — generate impressive-looking artifacts without understanding what they mean. But the gap between &ldquo;looks right&rdquo; and &ldquo;is right&rdquo; will eventually matter. Systems built on fuzzy thinking will fail in production. Strategies based on vibes will collapse when edge cases arrive.</p>
<p>The people who invested in their ability to think — who read the hard books, engaged with the hard communities, had the hard conversations — will be the ones who can actually specify what needs to be built. And specification is everything now.</p>
<p>Your brain is the prompt engineer. Train it accordingly.</p>
<hr>
<p><em>Written after noticing that my best AI sessions correlate perfectly with topics I actually understand, and my worst sessions are exactly where my own thinking is muddiest.</em></p>
]]></content></item><item><title>Your AI Has Logs. You Should Read Them.</title><link>https://chili-it.de/posts/tracking-ai-state-in-obsidian/</link><pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/tracking-ai-state-in-obsidian/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/tracking-ai-state-in-obsidian.webp"
alt="A charcoal sketch of an open notebook with visible log entries and a grep command arrow, contrasted against a dark opaque cloud — transparent files versus black-box cloud services"&gt;
&lt;/figure&gt;
&lt;p&gt;Everyone&amp;rsquo;s building AI memory systems. Vector databases, retrieval-augmented generation, cloud-hosted conversation stores with enterprise pricing. Fancy dashboards showing your &amp;ldquo;AI knowledge graph&amp;rdquo; in some proprietary format you&amp;rsquo;ll never be able to export.&lt;/p&gt;
&lt;p&gt;Meanwhile, I&amp;rsquo;m tracking my AI assistant&amp;rsquo;s entire cognitive state in markdown files. In an Obsidian vault. That I can grep.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/tracking-ai-state-in-obsidian.webp"
			alt="A charcoal sketch of an open notebook with visible log entries and a grep command arrow, contrasted against a dark opaque cloud — transparent files versus black-box cloud services">
</figure>

<p>Everyone&rsquo;s building AI memory systems. Vector databases, retrieval-augmented generation, cloud-hosted conversation stores with enterprise pricing. Fancy dashboards showing your &ldquo;AI knowledge graph&rdquo; in some proprietary format you&rsquo;ll never be able to export.</p>
<p>Meanwhile, I&rsquo;m tracking my AI assistant&rsquo;s entire cognitive state in markdown files. In an Obsidian vault. That I can grep.</p>
<p>And it&rsquo;s the best decision I&rsquo;ve made in my AI workflow.</p>
<h2 id="the-problem-with-ai-amnesia">The Problem With AI Amnesia</h2>
<p>Here&rsquo;s the thing about Claude, ChatGPT, or whatever model you&rsquo;re running: every session starts from zero. Your brilliant AI assistant that helped you debug a Kubernetes networking issue at 2 AM? Gone. The context about your infrastructure, your naming conventions, your deployment quirks? Evaporated.</p>
<p>The industry&rsquo;s answer to this is &ldquo;memory features&rdquo; — cloud-stored summaries that the provider controls, in formats you can&rsquo;t inspect, with retention policies you didn&rsquo;t choose. Your AI&rsquo;s state becomes someone else&rsquo;s database entry.</p>
<p>If this sounds familiar, it&rsquo;s because we&rsquo;ve seen this movie before. It&rsquo;s the SaaS trap applied to cognition.</p>
<h2 id="what-if-ai-state-was-just-files">What If AI State Was Just&hellip; Files?</h2>
<p>As a sysadmin, I have a very specific reaction to most &ldquo;AI memory&rdquo; solutions: why isn&rsquo;t this just files?</p>
<p>Think about it. What does an AI assistant actually need to remember?</p>
<ul>
<li>What it worked on (sessions, tasks, outcomes)</li>
<li>What went wrong (failures, root causes, missteps)</li>
<li>What you liked and didn&rsquo;t like (satisfaction signals)</li>
<li>What it learned (patterns, corrections, preferences)</li>
<li>What it&rsquo;s currently doing (active work state)</li>
</ul>
<p>This is just structured text. It&rsquo;s logs, configs, and state files. We&rsquo;ve been managing this exact category of information for decades. It&rsquo;s called <code>/var/log</code>, <code>/etc</code>, and <code>/var/lib</code>.</p>
<p>Enter <a href="https://github.com/danielmiessler/Personal_AI_Infrastructure">Personal AI Infrastructure (PAI)</a> — an open-source framework by <a href="https://danielmiessler.com">Daniel Miessler</a> that does exactly this. Daniel&rsquo;s been thinking about AI augmentation and personal knowledge systems for years (you might know him from <a href="https://github.com/danielmiessler/fabric">Fabric</a>), and PAI is the culmination of that thinking applied to persistent AI assistants. It&rsquo;s a complete system for building persistent, observable AI assistants using Claude Code, with all state stored in plain files.</p>
<p>My AI assistant&rsquo;s entire memory lives in a directory tree of markdown and JSON files. Every session creates a work directory. Every failure gets a full context dump. Every interaction gets a satisfaction signal. Every learning gets captured in a dated file.</p>
<pre tabindex="0"><code>MEMORY/
├── WORK/              # 144 tracked sessions
├── LEARNING/
│   ├── SYSTEM/        # Infrastructure learnings
│   ├── ALGORITHM/     # Approach learnings
│   ├── FAILURES/      # Full context dumps (ratings 1-3)
│   └── SIGNALS/       # 237 satisfaction ratings
├── RESEARCH/          # Archived agent outputs
├── STATE/             # Ephemeral runtime data
└── SECURITY/          # Audit trail
</code></pre><p>No vector database. No embedding pipeline. No cloud sync. Just directories and files.</p>
<h2 id="obsidian-as-the-viewer-layer">Obsidian as the Viewer Layer</h2>
<p>Here&rsquo;s where it gets interesting. PAI stores everything as markdown and JSON files. You know what&rsquo;s really good at browsing, linking, searching, and visualizing markdown files?</p>
<p>Obsidian.</p>
<p>Point your Obsidian vault at your AI&rsquo;s memory directory and suddenly you have:</p>
<p><strong>Full-text search across your AI&rsquo;s entire history.</strong> Every session, every failure, every learning — instantly searchable. No API calls. No query language. Just Ctrl+Shift+F.</p>
<p><strong>Graph view of connected concepts.</strong> Link your AI&rsquo;s work sessions to your project notes. Watch the knowledge graph form between what your AI knows and what you know.</p>
<p><strong>Daily notes integration.</strong> Your journal entries sitting next to your AI&rsquo;s session logs for the same day. What were you working on? What did the AI help with? What went wrong?</p>
<p><strong>Backlinks from your notes to AI sessions.</strong> Writing a project retrospective? Link directly to the AI sessions that contributed. The bidirectional links make your AI&rsquo;s history part of your knowledge management system, not a separate silo.</p>
<p><strong>Tags and folders you control.</strong> Your taxonomy. Your organization. Not whatever hierarchy some product manager decided was &ldquo;intuitive.&rdquo;</p>
<p>It&rsquo;s the difference between your AI&rsquo;s memory being an opaque service and it being part of your second brain.</p>
<h2 id="what-you-actually-see">What You Actually See</h2>
<p>Let me show you what this looks like in practice.</p>
<h3 id="session-tracking">Session Tracking</h3>
<p>Every time I start working with my AI assistant, a work directory gets created automatically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># WORK/20260204-215901_fix-staging-table-flickering/META.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;20260204-215901&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;fix-staging-table-flickering-during-grabber-processing&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">session_id</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;ed0a9763-2aa8-4acf-a14c-99b57402fbd8&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">created_at</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;2026-02-04T21:59:01+01:00&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="l">COMPLETED</span><span class="w">
</span></span></span></code></pre></div><p>Inside each work directory, there&rsquo;s an Ideal State Criteria file — a JSON record of what success looked like for that task and whether we achieved it.</p>
<p>I can browse 144 of these in Obsidian. Filter by status. Search by topic. See the full timeline of everything my AI has worked on, when, and how it went.</p>
<h3 id="failure-analysis">Failure Analysis</h3>
<p>This is where it gets genuinely useful. When my satisfaction drops below a 3 (out of 10), the system captures a full context dump:</p>
<pre tabindex="0"><code>FAILURES/2026-02/
├── 2026-02-05-084703_assistant-failed-notification-system/
│   ├── CONTEXT.md        # Root cause analysis
│   ├── transcript.jsonl  # Full conversation
│   ├── sentiment.json    # What went wrong emotionally
│   └── tool-calls.json   # Every action taken
</code></pre><p>Twelve failure captures in February alone. Each one with the full conversation, the root cause analysis, and the tool calls that led to the problem.</p>
<p>In Obsidian, I can review these like case studies. &ldquo;Oh, this is the time it released a breaking version without testing.&rdquo; &ldquo;This is when it ignored my question and asked redundant clarifications instead.&rdquo; &ldquo;This is when a placeholder config value broke voice notifications.&rdquo;</p>
<p>You can&rsquo;t improve what you can&rsquo;t measure. And you can&rsquo;t measure what you can&rsquo;t see. Most people have no idea why their AI sessions go badly. I have timestamped forensic analysis.</p>
<h3 id="satisfaction-signals">Satisfaction Signals</h3>
<p>Every interaction generates a satisfaction signal — either explicitly rated or inferred from sentiment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-02-05T21:10:47+01:00&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;rating&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;source&#34;</span><span class="p">:</span> <span class="s2">&#34;implicit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sentiment_summary&#34;</span><span class="p">:</span> <span class="s2">&#34;Neutral command to access work note&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;confidence&#34;</span><span class="p">:</span> <span class="mf">0.95</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>237 data points so far. Enough to see trends. Enough to know whether the system is getting better or worse over time. Enough to correlate satisfaction with specific types of tasks, times of day, or interaction patterns.</p>
<p>In Obsidian, this becomes a dataset you can query with Dataview, chart with plugins, or just browse as a timeline. Your AI&rsquo;s performance review, written in data.</p>
<h2 id="the-sysadmin-philosophy">The Sysadmin Philosophy</h2>
<p>This whole approach comes from a simple principle: <strong>if you can&rsquo;t grep it, you don&rsquo;t own it.</strong></p>
<p>Your AI&rsquo;s memory should be:</p>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>Why</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><strong>Plain text</strong></td>
					<td>Survives every format migration. Readable in 50 years.</td>
			</tr>
			<tr>
					<td><strong>File-based</strong></td>
					<td>Works with every tool ever made. Git, rsync, find, grep.</td>
			</tr>
			<tr>
					<td><strong>Local</strong></td>
					<td>No API dependency. No outage. No surprise deprecation.</td>
			</tr>
			<tr>
					<td><strong>Versioned</strong></td>
					<td>Git tracks changes. You can diff your AI&rsquo;s evolving knowledge.</td>
			</tr>
			<tr>
					<td><strong>Inspectable</strong></td>
					<td>No black box. You can read exactly what your AI &ldquo;remembers.&rdquo;</td>
			</tr>
	</tbody>
</table>
<p>This isn&rsquo;t radical. This is how we&rsquo;ve managed every other important system state in the history of computing. Configs are text files. Logs are text files. Infrastructure state is text files (hello, Terraform).</p>
<p>Why would your AI&rsquo;s cognitive state be any different?</p>
<h2 id="the-deeper-point-observability-for-ai">The Deeper Point: Observability for AI</h2>
<p>In infrastructure, we&rsquo;ve learned the hard way that systems without observability are systems waiting to fail. You need logs, metrics, traces. You need to understand not just what happened, but why.</p>
<p>The same is true for your AI assistant. Without observability into its state, you&rsquo;re flying blind:</p>
<ul>
<li>Why did it make that wrong recommendation?</li>
<li>What context was it missing when it broke that deploy?</li>
<li>Is it actually getting better at understanding your codebase, or are you just getting better at prompting around its weaknesses?</li>
<li>When it fails, is there a pattern?</li>
</ul>
<p>Most people interact with AI as a stateless oracle. Ask question, get answer, move on. No history. No learning curve. No improvement trajectory.</p>
<p>Tracking state turns your AI from a tool into a system you can operate. And operating systems is what we do.</p>
<h2 id="the-compound-effect">The Compound Effect</h2>
<p>Here&rsquo;s what happens after a few months of this:</p>
<p><strong>Pattern recognition.</strong> You start seeing failure clusters. &ldquo;Ah, it always struggles with CSS layout tasks but nails database migrations.&rdquo; Now you know when to trust it and when to double-check.</p>
<p><strong>Context injection.</strong> Because the state is in files, you can inject relevant history back into new sessions. &ldquo;Here&rsquo;s what went wrong last time we tried this.&rdquo; The AI learns from its own documented failures.</p>
<p><strong>Workflow optimization.</strong> You see which types of sessions generate the highest satisfaction and which generate frustration. You restructure how you work with the AI based on data, not vibes.</p>
<p><strong>Accountability.</strong> When someone asks &ldquo;is AI actually helping your productivity?&rdquo; you don&rsquo;t have to guess. You have 237 rated interactions, 144 tracked sessions, and 12 documented failures with root cause analysis.</p>
<p>This is the difference between &ldquo;I use AI sometimes&rdquo; and &ldquo;I operate an AI system with observability, feedback loops, and continuous improvement.&rdquo;</p>
<h2 id="the-tooling-is-embarrassingly-simple">The Tooling Is Embarrassingly Simple</h2>
<p>The core is <a href="https://github.com/danielmiessler/Personal_AI_Infrastructure">PAI (Personal AI Infrastructure)</a> — an open-source framework that wraps Claude Code with:</p>
<ul>
<li><strong>Event hooks</strong> that trigger on session events (start, stop, tool calls)</li>
<li><strong>Markdown files</strong> written to a directory tree</li>
<li><strong>JSON lines</strong> for structured data (ratings, events)</li>
<li><strong>Skills and capabilities</strong> that extend what your AI can do</li>
<li><strong>An algorithm</strong> that structures how your AI approaches problems</li>
</ul>
<p>PAI is the engine. It captures state, tracks work, logs failures, and learns from feedback — all to <code>~/.claude/MEMORY/</code>.</p>
<p>Obsidian is just the viewer. Point it at the memory directory and you get full-text search, graph visualization, backlinks, and all the knowledge management features — but the real work happens in PAI&rsquo;s hooks and skills.</p>
<p>No database. No server. No external API. Just files, hooks, and whatever markdown editor you prefer.</p>
<p>If this sounds too simple, that&rsquo;s because it is. The best infrastructure usually is.</p>
<h2 id="what-you-learn-when-you-look">What You Learn When You Look</h2>
<p>I&rsquo;ll be honest: reading your AI&rsquo;s failure logs is humbling. Not for the AI — for you.</p>
<p>You see how often you gave unclear instructions. How many times the AI correctly identified ambiguity but you pushed through anyway. How frequently the root cause of a &ldquo;bad AI response&rdquo; was actually bad context from you.</p>
<p>The failures directory is a mirror. And like all good monitoring, it shows you things you&rsquo;d rather not see.</p>
<p>But that&rsquo;s the point. You can&rsquo;t improve a system you refuse to observe.</p>
<h2 id="getting-started">Getting Started</h2>
<p>The full system is open source: <strong><a href="https://github.com/danielmiessler/Personal_AI_Infrastructure">Personal AI Infrastructure (PAI)</a></strong></p>
<p>PAI gives you the complete framework — hooks, memory system, skills, and the algorithm that structures AI behavior. Clone it, configure it, and you have production-ready AI state tracking from day one.</p>
<p>If you want to start smaller, the principle is simple:</p>
<ol>
<li><strong>Pick a directory.</strong> <code>~/ai-memory/</code> or anywhere your Obsidian vault can reach.</li>
<li><strong>Start logging sessions.</strong> Even a manual markdown file per session beats nothing.</li>
<li><strong>Record what went wrong.</strong> Copy-paste failures. Note root causes.</li>
<li><strong>Track satisfaction.</strong> Even a simple 1-10 rating per session reveals trends.</li>
<li><strong>Review weekly.</strong> Spend 15 minutes browsing your AI&rsquo;s history. Patterns emerge fast.</li>
</ol>
<p>But honestly? Just use PAI. The automation is already built.</p>
<h2 id="the-punchline">The Punchline</h2>
<p>The AI industry wants to sell you memory as a service. Managed context. Cloud-hosted cognitive state. Enterprise knowledge bases with per-seat pricing.</p>
<p>But your AI&rsquo;s memory is just state. And state is just files. And files are what we&rsquo;ve been managing since before most of these companies existed.</p>
<p>Put your AI&rsquo;s state in your Obsidian vault. Grep it. Git it. Graph it. Make it part of your knowledge system instead of someone else&rsquo;s product.</p>
<p>Your AI assistant should have dotfiles. And you should be able to read them.</p>
<p><strong>Start here: <a href="https://github.com/danielmiessler/Personal_AI_Infrastructure">github.com/danielmiessler/Personal_AI_Infrastructure</a></strong></p>
<hr>
<p><em>Written by someone with 237 tracked AI interactions, 12 documented failures, and a vault full of markdown files that know more about his AI workflow than any dashboard ever could.</em></p>
]]></content></item><item><title>Context Before Prompt: Why We Got LLMs Backwards</title><link>https://chili-it.de/posts/context-before-prompt/</link><pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/context-before-prompt/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/context-before-prompt.webp"
alt="A charcoal sketch of an iceberg - small tip labeled PROMPT above the waterline, massive underwater portion labeled CONTEXT showing system instructions, retrieved knowledge, conversation history, and tool definitions"&gt;
&lt;/figure&gt;
&lt;p&gt;As a programmer and sysadmin, I&amp;rsquo;ve noticed something curious about how people approach LLMs: they started with prompt engineering before context engineering. This ordering reveals a fundamental misunderstanding about what these systems actually are.&lt;/p&gt;
&lt;h2 id="the-inversion-problem"&gt;The Inversion Problem&lt;/h2&gt;
&lt;p&gt;When prompt engineering emerged first, people treated LLMs as functions to be called correctly. Find the right incantation, the magic words, and you get the right output. This is a very programmer-centric view: &amp;ldquo;If I just phrase the input correctly, the system will behave.&amp;rdquo;&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/context-before-prompt.webp"
			alt="A charcoal sketch of an iceberg - small tip labeled PROMPT above the waterline, massive underwater portion labeled CONTEXT showing system instructions, retrieved knowledge, conversation history, and tool definitions">
</figure>

<p>As a programmer and sysadmin, I&rsquo;ve noticed something curious about how people approach LLMs: they started with prompt engineering before context engineering. This ordering reveals a fundamental misunderstanding about what these systems actually are.</p>
<h2 id="the-inversion-problem">The Inversion Problem</h2>
<p>When prompt engineering emerged first, people treated LLMs as functions to be called correctly. Find the right incantation, the magic words, and you get the right output. This is a very programmer-centric view: &ldquo;If I just phrase the input correctly, the system will behave.&rdquo;</p>
<p>But this gets the causality backwards. An LLM doesn&rsquo;t respond to prompts—it completes contexts. The prompt is just the final fragment of a much larger information space that shapes the probability distribution of what comes next.</p>
<h2 id="the-iceberg-metaphor">The Iceberg Metaphor</h2>
<p>Prompt engineering focuses on the visible tip: the user&rsquo;s immediate query. Context engineering recognizes the iceberg beneath:</p>
<ul>
<li>System instructions</li>
<li>Retrieved knowledge (RAG)</li>
<li>Conversation history</li>
<li>Tool definitions and capabilities</li>
<li>Examples and few-shot patterns</li>
<li>The persona or role framing</li>
</ul>
<p>When you engineer only the prompt, you&rsquo;re trying to steer a ship by adjusting the flag on top. When you engineer context, you&rsquo;re shaping the hull, the rudder, the currents it sails in.</p>
<h2 id="why-did-prompt-come-first">Why Did Prompt Come First?</h2>
<p>Several reasons, all understandable in retrospect:</p>
<p><strong>Accessibility</strong> — Early API access gave you one text box. Context was prompt.</p>
<p><strong>Mental models from search</strong> — We came from Google, where query refinement was the primary skill.</p>
<p><strong>Short context windows</strong> — Early models had 2-4k tokens. There wasn&rsquo;t much room for rich context.</p>
<p><strong>The &ldquo;instruction following&rdquo; framing</strong> — Marketing emphasized &ldquo;tell it what to do,&rdquo; not &ldquo;shape what it knows.&rdquo;</p>
<h2 id="the-philosophical-shift">The Philosophical Shift</h2>
<p>The move from prompt to context engineering reflects a deeper shift in how we conceptualize what an LLM is:</p>
<table>
	<thead>
			<tr>
					<th>Prompt Engineering View</th>
					<th>Context Engineering View</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>LLM as tool/function</td>
					<td>LLM as reasoning within an environment</td>
			</tr>
			<tr>
					<td>Input → Output</td>
					<td>State → Continuation</td>
			</tr>
			<tr>
					<td>&ldquo;How do I ask?&rdquo;</td>
					<td>&ldquo;What world does it inhabit?&rdquo;</td>
			</tr>
			<tr>
					<td>Imperative</td>
					<td>Declarative/environmental</td>
			</tr>
	</tbody>
</table>
<p>Context engineering is closer to world-building than instruction-writing. You&rsquo;re not telling the model what to do—you&rsquo;re constructing the epistemic situation in which its natural behavior produces what you need.</p>
<h2 id="the-practical-impact-of-getting-it-backwards">The Practical Impact of Getting It Backwards</h2>
<p>When people over-invest in prompt tricks without context design:</p>
<ul>
<li>They hit ceilings that no prompt refinement can break through</li>
<li>Solutions become brittle (slight rephrasing breaks everything)</li>
<li>They fight the model&rsquo;s &ldquo;defaults&rdquo; instead of reshaping them</li>
<li>They miss that the same prompt in different contexts produces radically different outputs</li>
</ul>
<p>A well-designed context makes prompts almost trivial. A poorly designed context makes even perfect prompts fail.</p>
<h2 id="the-sysadmin-intuition">The Sysadmin Intuition</h2>
<p>As a sysadmin, this seems obvious: the environment determines what&rsquo;s possible. Configs, permissions, dependencies, network topology—these shape the system&rsquo;s behavior. The command you type is just the trigger.</p>
<p>No amount of clever bash one-liners fixes a misconfigured system.</p>
<p><strong>Context is the system. Prompt is the command.</strong></p>
<p>You wouldn&rsquo;t spend hours crafting the perfect <code>systemctl restart nginx</code> variation while ignoring broken configs. Yet that&rsquo;s exactly what early LLM work did—endlessly tweaking the command while leaving the system&rsquo;s state unconsidered.</p>
<h2 id="what-context-engineering-looks-like">What Context Engineering Looks Like</h2>
<p>Modern LLM systems spend far more effort on context design:</p>
<ul>
<li><strong>RAG systems</strong> retrieve relevant documents before the prompt ever arrives</li>
<li><strong>Agent frameworks</strong> define tool sets and operational boundaries</li>
<li><strong>System prompts</strong> establish role, constraints, and behavioral patterns</li>
<li><strong>Conversation management</strong> curates what history gets included</li>
<li><strong>Few-shot examples</strong> provide behavioral templates</li>
</ul>
<p>The actual user prompt becomes almost incidental—just the trigger for computation happening in a carefully engineered information environment.</p>
<h2 id="the-lesson">The Lesson</h2>
<p>If you&rsquo;re still prompt-engineering your way around problems, you&rsquo;re fighting the wrong battle. Step back. Look at the context. What world is the model inhabiting when it sees your prompt?</p>
<p>Fix the configs. Shape the environment. Build the right system.</p>
<p>Then the prompts take care of themselves.</p>
]]></content></item><item><title>What Are Conservatives For? The Question That Stumped Me</title><link>https://chili-it.de/posts/what-are-conservatives-for/</link><pubDate>Mon, 29 Dec 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/what-are-conservatives-for/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/what-are-conservatives-for.webp"
alt="A charcoal sketch of a firm hand gripping a speed throttle lever set between slow and fast, with a landscape of construction and progress evolving in the background"&gt;
&lt;/figure&gt;
&lt;p&gt;Someone asked me what conservative politicians want.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;They want to conserve things,&amp;rdquo; I said. &amp;ldquo;Keep things like they are.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;They shot back: &lt;strong&gt;&amp;ldquo;What are they there for then?&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I had no answer.&lt;/p&gt;
&lt;h2 id="the-question-that-actually-makes-sense"&gt;The Question That Actually Makes Sense&lt;/h2&gt;
&lt;p&gt;At first glance, it sounds like a gotcha. But it&amp;rsquo;s not. It&amp;rsquo;s legitimate.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/what-are-conservatives-for.webp"
			alt="A charcoal sketch of a firm hand gripping a speed throttle lever set between slow and fast, with a landscape of construction and progress evolving in the background">
</figure>

<p>Someone asked me what conservative politicians want.</p>
<p>&ldquo;They want to conserve things,&rdquo; I said. &ldquo;Keep things like they are.&rdquo;</p>
<p>They shot back: <strong>&ldquo;What are they there for then?&rdquo;</strong></p>
<p>I had no answer.</p>
<h2 id="the-question-that-actually-makes-sense">The Question That Actually Makes Sense</h2>
<p>At first glance, it sounds like a gotcha. But it&rsquo;s not. It&rsquo;s legitimate.</p>
<p>If the goal is to keep everything the same, why do you need politicians? Why campaigns, legislation, governance? Just&hellip; don&rsquo;t do anything. Lock the doors. Go home.</p>
<p>The question exposes something deeper: <strong>conservatism can&rsquo;t actually be about stasis</strong>. Because stasis doesn&rsquo;t need governance—it needs a pause button.</p>
<h2 id="what-conservatives-actually-conserve">What Conservatives Actually Conserve</h2>
<p>Here&rsquo;s what I should have said: conservatives don&rsquo;t want to freeze time. They want to <strong>conserve the rate of change</strong>.</p>
<p>The world changes. Technology evolves. Culture shifts. Economics transform. No one disputes this.</p>
<p>The conservative position is: <strong>&ldquo;Slow down. Prove it works. Don&rsquo;t break what already does.&rdquo;</strong></p>
<p>Progressive position: <strong>&ldquo;Speed up. The current system is broken. Move fast and fix things.&rdquo;</strong></p>
<p>Both are managing change. They just disagree on velocity and risk tolerance.</p>
<h2 id="the-governing-paradox">The Governing Paradox</h2>
<p>So what are conservatives <em>for</em>?</p>
<p><strong>They&rsquo;re there to resist.</strong> Not change itself—but <em>thoughtless</em> change. Untested change. Change for its own sake.</p>
<p>This is harder than it sounds. Active resistance requires:</p>
<ul>
<li><strong>Institutional knowledge</strong>: What actually works? What failed before?</li>
<li><strong>Risk assessment</strong>: What breaks if we change this?</li>
<li><strong>Alternative proposals</strong>: If not this way, then how?</li>
<li><strong>Continuous defense</strong>: Every year brings new proposals to resist.</li>
</ul>
<p>It turns out &ldquo;keeping things the same&rdquo; in a world that constantly pushes change is <strong>active work</strong>.</p>
<h2 id="when-conservatism-works">When Conservatism Works</h2>
<p>Conservatives are right when:</p>
<ul>
<li>A proposed change has unexamined second-order effects</li>
<li>Historical precedent shows this failed before</li>
<li>Existing systems work well enough</li>
<li>The rush to change is driven by fashion, not function</li>
<li>Breaking something is easier than fixing it</li>
</ul>
<p>Example: &ldquo;Let&rsquo;s rewrite the entire codebase in [new framework].&rdquo;</p>
<p>Conservative response: &ldquo;Why? What problem does this solve? What breaks during migration?&rdquo;</p>
<p>Sometimes the right answer is: don&rsquo;t.</p>
<h2 id="when-conservatism-fails">When Conservatism Fails</h2>
<p>Conservatives are wrong when:</p>
<ul>
<li>The status quo is actively harmful</li>
<li>Evidence shows change works</li>
<li>Resistance becomes reflexive, not reasoned</li>
<li>&ldquo;That&rsquo;s how we&rsquo;ve always done it&rdquo; becomes the only argument</li>
<li>The cost of inaction exceeds the risk of action</li>
</ul>
<p>Example: &ldquo;Let&rsquo;s keep using this legacy system forever.&rdquo;</p>
<p>Conservative response: &ldquo;It works fine.&rdquo;</p>
<p>Reality: It works until it catastrophically doesn&rsquo;t.</p>
<h2 id="the-uncomfortable-truth">The Uncomfortable Truth</h2>
<p>Both sides need each other.</p>
<p><strong>Without progressives</strong>: stagnation, missed opportunities, institutional rot.</p>
<p><strong>Without conservatives</strong>: chaos, broken systems, unforeseen disasters.</p>
<p>Software engineering learned this decades ago:</p>
<ul>
<li>Move fast and break things → production outages</li>
<li>Never change anything → technical debt death spiral</li>
<li><strong>Iterate carefully with tests and rollbacks</strong> → actually works</li>
</ul>
<p>Politics hasn&rsquo;t figured this out yet.</p>
<h2 id="what-i-should-have-said">What I Should Have Said</h2>
<p>&ldquo;Conservatives are there to ask &lsquo;are you sure?&rsquo; every time someone says &rsquo;let&rsquo;s change this.&rsquo;</p>
<p>Sometimes the answer is yes, and change happens anyway.</p>
<p>Sometimes the answer is no, and disaster is prevented.</p>
<p>Either way, asking the question has value.&rdquo;</p>
<h2 id="the-real-question">The Real Question</h2>
<p>Not &ldquo;what are conservatives for?&rdquo;</p>
<p>But: <strong>&ldquo;What&rsquo;s the right rate of change?&rdquo;</strong></p>
<p>And that&rsquo;s a question neither side can answer alone.</p>
<hr>
<p><em>Written after realizing my political explanations need better runtime complexity analysis.</em></p>
]]></content></item><item><title>The Tree That Grows Toward Your Life Goal</title><link>https://chili-it.de/posts/the-tree-that-grows-toward-your-life-goal/</link><pubDate>Tue, 25 Nov 2025 14:30:00 +0100</pubDate><guid>https://chili-it.de/posts/the-tree-that-grows-toward-your-life-goal/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/tree-goal-decomposition.webp"
alt="A charcoal sketch of a majestic tree growing upward - trunk and branches in warm sienna tones decomposing into finer branches, with glowing actionable fruits at the lowest branches ready to pick"&gt;
&lt;/figure&gt;
&lt;p&gt;Most goal-setting advice is broken. It tells you to dream big, then immediately asks: &amp;ldquo;What&amp;rsquo;s your next action?&amp;rdquo; The gap between &amp;ldquo;become a recognized AI infrastructure expert&amp;rdquo; and &amp;ldquo;write a blog post&amp;rdquo; is so vast that you either freeze or start randomly doing things that feel productive.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/tree-goal-decomposition.webp"
			alt="A charcoal sketch of a majestic tree growing upward - trunk and branches in warm sienna tones decomposing into finer branches, with glowing actionable fruits at the lowest branches ready to pick">
</figure>

<p>Most goal-setting advice is broken. It tells you to dream big, then immediately asks: &ldquo;What&rsquo;s your next action?&rdquo; The gap between &ldquo;become a recognized AI infrastructure expert&rdquo; and &ldquo;write a blog post&rdquo; is so vast that you either freeze or start randomly doing things that feel productive.</p>
<p>I&rsquo;ve been experimenting with a different approach: <strong>treat your goals like a tree, and only work on branches you can actually reach.</strong></p>
<h2 id="the-problem-with-traditional-goal-setting">The Problem With Traditional Goal Setting</h2>
<p>You set a big goal. Maybe it&rsquo;s &ldquo;build a successful consulting business&rdquo; or &ldquo;master distributed systems.&rdquo; Then what?</p>
<p>Option A: You break it down into quarterly milestones. But those milestones are still too big. You stare at them and feel overwhelmed.</p>
<p>Option B: You jump straight to tasks. &ldquo;Send 10 cold emails today.&rdquo; But why? Does this actually connect to the big goal? Who knows. You&rsquo;re just doing things.</p>
<p>The gap between vision and action is where goals go to die.</p>
<h2 id="the-tree-system">The Tree System</h2>
<p>Here&rsquo;s what works better: <strong>recursive decomposition until you hit something you can do right now.</strong></p>
<p>Start with your life goal. One branch. Then ask: &ldquo;What sub-goals would get me there?&rdquo;</p>
<p>For each sub-goal, ask again: &ldquo;What sub-goals would get me <em>there</em>?&rdquo;</p>
<p>Keep going until you hit a branch that ends in something you can do today. Ideally right now.</p>
<p>Then do it. Mark it complete. Watch that branch grow upward toward the higher goals.</p>
<h3 id="example-the-consulting-business-tree">Example: The Consulting Business Tree</h3>
<pre tabindex="0"><code>Life Goal: Run a thriving AI infrastructure consulting business
├── Sub-goal: Build reputation as expert
│   ├── Sub-goal: Publish thought leadership
│   │   ├── Sub-goal: Write 12 blog posts this quarter
│   │   │   └── Task: Write post about goal systems [DO THIS NOW]
│   │   └── Sub-goal: Speak at 2 conferences
│   │       └── Sub-goal: Submit 5 CFP proposals
│   │           └── Task: Research upcoming AI conferences [DO THIS NOW]
│   └── Sub-goal: Demonstrate expertise publicly
│       └── Sub-goal: Share technical insights weekly
│           └── Task: Post architecture diagram on LinkedIn [DO THIS NOW]
└── Sub-goal: Generate consistent revenue
    ├── Sub-goal: Build pipeline of qualified leads
    │   ├── Sub-goal: Get 3 referrals from past clients
    │   │   └── Task: Email previous client about coffee chat [DO THIS NOW]
    │   └── Sub-goal: Establish presence in target market
    │       └── Task: Join AI infrastructure Slack community [DO THIS NOW]
    └── Sub-goal: Improve conversion rate
        └── Sub-goal: Refine positioning
            └── Task: Update homepage headline [DO THIS NOW]
</code></pre><p>Notice: every branch eventually ends in something concrete. Something you could do in the next hour.</p>
<h2 id="why-this-works">Why This Works</h2>
<p><strong>1. No dead-end planning.</strong> You don&rsquo;t create goals that float in space. Every sub-goal exists because it feeds into something higher. Every task exists because it grows a branch.</p>
<p><strong>2. You always know what to do next.</strong> Just scan the tree for the lowest actionable branches. Do one. Watch it bubble up.</p>
<p><strong>3. Motivation from compound progress.</strong> When you complete a low-level task, you&rsquo;re not just checking a box. You&rsquo;re literally growing multiple branches at once. That blog post? It feeds &ldquo;publish thought leadership&rdquo; AND &ldquo;build reputation&rdquo; AND your life goal. Three levels of progress from one action.</p>
<p><strong>4. The big picture stays visible.</strong> You&rsquo;re not lost in a task list. You can always zoom out and see how today&rsquo;s work connects to the top of the tree.</p>
<p><strong>5. Natural prioritization.</strong> Some branches have more actionable tasks than others. That&rsquo;s fine. Work on what&rsquo;s ready. The tree shows you where you&rsquo;re blocked (branches with no actionable tasks yet) versus where you have momentum.</p>
<h2 id="the-time-is-always-now">The Time is Always Now</h2>
<p>Here&rsquo;s the thing I keep learning: <strong>you can&rsquo;t get caught up in time.</strong></p>
<p>No deadlines. No &ldquo;by Q2 2026.&rdquo; No &ldquo;in three months I&rsquo;ll&hellip;&rdquo;</p>
<p>The time is always <em>now</em>. The only question is: what branch can I grow <em>right now</em>?</p>
<p>Traditional planning wants you to spread tasks across a calendar. &ldquo;I&rsquo;ll do this in January, that in March.&rdquo; But January never comes. There&rsquo;s only ever this moment.</p>
<p>So the tree system doesn&rsquo;t track time. It tracks <em>readiness</em>.</p>
<p>Is this task ready to do now? Do it now.
Is this goal not decomposed enough? Decompose it now.
Is this branch blocked? Work a different branch now.</p>
<p>The tree grows in the present moment. Always.</p>
<h2 id="the-anti-vision-paradox">The Anti-Vision Paradox</h2>
<p>This might sound like detailed planning. It&rsquo;s not.</p>
<p>You&rsquo;re not predicting the future. You&rsquo;re not committing to a specific path. You&rsquo;re just saying: &ldquo;If I want to get <em>there</em>, I probably need to go through <em>here</em>.&rdquo;</p>
<p>And the magic is: <strong>you only plan as deep as you need to find something doable.</strong></p>
<p>If a branch isn&rsquo;t ready to break down further, leave it. Come back when you&rsquo;ve grown the other branches and gained new information.</p>
<p>The tree evolves. Branches get pruned. New branches appear. But you&rsquo;re always working on something that&rsquo;s both:</p>
<ul>
<li>Immediately actionable</li>
<li>Connected to your highest goals</li>
</ul>
<h2 id="the-implementation-structured-markdown">The Implementation: Structured Markdown</h2>
<p>I&rsquo;m implementing this with the simplest possible system: <strong>folders and markdown files.</strong></p>
<p>Each goal is a folder. Each sub-goal is either:</p>
<ul>
<li>Another folder (if it needs more decomposition)</li>
<li>A markdown file (if it&rsquo;s a leaf node with actionable tasks)</li>
</ul>
<pre tabindex="0"><code>goals/
└── consulting-business/
    ├── build-reputation/
    │   ├── publish-thought-leadership/
    │   │   ├── blog-posts.md
    │   │   └── conference-talks.md
    │   └── demonstrate-expertise.md
    └── generate-revenue/
        ├── build-pipeline.md
        └── improve-conversion.md
</code></pre><p>Inside each markdown file? Just a list of tasks with checkboxes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gh"># Blog Posts
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">- [x]</span> Write post about goal systems
</span></span><span class="line"><span class="cl"><span class="k">- [ ]</span> Write post about Restic performance
</span></span><span class="line"><span class="cl"><span class="k">- [ ]</span> Write post about AI infrastructure patterns
</span></span></code></pre></div><p>That&rsquo;s it. No app. No database. No sync. No dates. Just files you can <code>grep</code>, <code>find</code>, and edit with <code>vim</code>.</p>
<p>The file system <em>is</em> the tree. The folder structure <em>is</em> the hierarchy. Your tools already understand it.</p>
<h2 id="why-markdown-files">Why Markdown Files?</h2>
<p><strong>It&rsquo;s portable.</strong> Move it anywhere. Edit it anywhere. No vendor lock-in.</p>
<p><strong>It&rsquo;s greppable.</strong> Want to see all your actionable tasks? <code>grep -r &quot;\[ \]&quot; goals/</code> Done.</p>
<p><strong>It&rsquo;s git-friendly.</strong> Track your progress over time. See which branches grew. Diff your trees.</p>
<p><strong>It&rsquo;s simple.</strong> No learning curve. No configuration. No &ldquo;productivity system setup&rdquo; that takes a week.</p>
<p><strong>It matches how you think.</strong> Folders contain things. Files list things. Your brain already knows this pattern.</p>
<p><strong>It&rsquo;s timeless.</strong> No calendar integration. No overdue tasks turning red. Just: ready or not ready. Now or not now.</p>
<p>And here&rsquo;s the beautiful part: the file system enforces the tree structure. You can&rsquo;t create a floating task. It has to live in a file. Which lives in a folder. Which connects to your top-level goal.</p>
<p>The structure <em>is</em> the system.</p>
<h2 id="building-your-own-tree">Building Your Own Tree</h2>
<p>Start simple:</p>
<ol>
<li>
<p><strong>Pick one life goal.</strong> Create a folder for it.</p>
</li>
<li>
<p><strong>Ask: &ldquo;What 2-4 sub-goals would get me there?&rdquo;</strong> Create folders or markdown files.</p>
</li>
<li>
<p><strong>Keep decomposing.</strong> When a goal needs more breakdown, make it a folder. When it&rsquo;s ready for tasks, make it a markdown file.</p>
</li>
<li>
<p><strong>Do the lowest hanging fruit.</strong> Find an unchecked box. Do it now. Check it off.</p>
</li>
<li>
<p><strong>Watch the branches grow.</strong> When all tasks in a file are done, that sub-goal is complete. Mark it. Move up the tree.</p>
</li>
<li>
<p><strong>Repeat.</strong> Keep working the branches. Keep decomposing when you get stuck. Keep pruning dead wood.</p>
</li>
</ol>
<p>No dates. No timelines. Just: what&rsquo;s ready now?</p>
<h2 id="why-im-switching-to-this">Why I&rsquo;m Switching to This</h2>
<p>I&rsquo;ve tried the big vision approach (gets vague).
I&rsquo;ve tried the daily wins approach (gets disconnected).
I&rsquo;ve tried quarterly OKRs (gets bureaucratic).
I&rsquo;ve tried productivity apps (gets complicated).
I&rsquo;ve tried calendar-based planning (gets caught up in time).</p>
<p>The tree system gives me both:</p>
<ul>
<li>The clarity of &ldquo;just do this one thing now&rdquo;</li>
<li>The confidence that this one thing actually matters</li>
</ul>
<p>And by using plain markdown files, I get:</p>
<ul>
<li>Zero friction to update</li>
<li>Total control over structure</li>
<li>Tools I already know</li>
<li>A system that will outlive any app</li>
<li>Freedom from time anxiety</li>
</ul>
<p>Every action grows multiple branches. Every branch connects to the top. No wasted effort. No floating tasks. No imaginary futures.</p>
<p>Just a tree, growing steadily toward the goal, one moment at a time.</p>
<hr>
<p><strong>What&rsquo;s your life goal? Can you decompose it until you hit something you can do right now?</strong></p>
<p>Create a folder. Add a markdown file. Start checking boxes.</p>
<p>The time is always now.</p>
<p>The tree doesn&rsquo;t lie.</p>
]]></content></item><item><title>When AI Walks the Route For You: The Death of Cultural Paths</title><link>https://chili-it.de/posts/ai-skips-the-route/</link><pubDate>Tue, 25 Nov 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/ai-skips-the-route/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/ai-skips-route.webp"
alt="A charcoal sketch showing a winding path rich with texture and footprints contrasted with a stark purple line cutting straight to the destination - a small figure stands at the fork facing the choice"&gt;
&lt;/figure&gt;
&lt;p&gt;If the route is the goal, what happens when you teleport?&lt;/p&gt;
&lt;h2 id="the-route-problem"&gt;The Route Problem&lt;/h2&gt;
&lt;p&gt;Culture isn&amp;rsquo;t what you build. It&amp;rsquo;s what you walk through while building it.&lt;/p&gt;
&lt;p&gt;Think about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A chef&amp;rsquo;s &amp;ldquo;culture&amp;rdquo; isn&amp;rsquo;t recipes—it&amp;rsquo;s burned sauces and failed soufflés&lt;/li&gt;
&lt;li&gt;A musician&amp;rsquo;s &amp;ldquo;culture&amp;rdquo; isn&amp;rsquo;t songs—it&amp;rsquo;s callused fingers and broken strings&lt;/li&gt;
&lt;li&gt;A programmer&amp;rsquo;s &amp;ldquo;culture&amp;rdquo; isn&amp;rsquo;t code—it&amp;rsquo;s Stack Overflow deep dives at 2 AM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Culture is the sum of routes people walked, not the destinations they reached.&lt;/strong&gt;&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/ai-skips-route.webp"
			alt="A charcoal sketch showing a winding path rich with texture and footprints contrasted with a stark purple line cutting straight to the destination - a small figure stands at the fork facing the choice">
</figure>

<p>If the route is the goal, what happens when you teleport?</p>
<h2 id="the-route-problem">The Route Problem</h2>
<p>Culture isn&rsquo;t what you build. It&rsquo;s what you walk through while building it.</p>
<p>Think about it:</p>
<ul>
<li>A chef&rsquo;s &ldquo;culture&rdquo; isn&rsquo;t recipes—it&rsquo;s burned sauces and failed soufflés</li>
<li>A musician&rsquo;s &ldquo;culture&rdquo; isn&rsquo;t songs—it&rsquo;s callused fingers and broken strings</li>
<li>A programmer&rsquo;s &ldquo;culture&rdquo; isn&rsquo;t code—it&rsquo;s Stack Overflow deep dives at 2 AM</li>
</ul>
<p><strong>Culture is the sum of routes people walked, not the destinations they reached.</strong></p>
<p>AI skips the route. You ask, it delivers. No journey, just arrival.</p>
<h2 id="what-we-lose-when-we-skip-the-walk">What We Lose When We Skip The Walk</h2>
<h3 id="1-the-accidental-discoveries">1. The Accidental Discoveries</h3>
<p>You&rsquo;re debugging for hours. You don&rsquo;t fix the bug, but you stumble onto a better architecture. That&rsquo;s culture.</p>
<p>AI fixes the bug in seconds. You learn nothing except &ldquo;AI can fix bugs.&rdquo; That&rsquo;s consumption.</p>
<p><strong>Lost routes = lost discoveries.</strong></p>
<h3 id="2-the-shared-suffering">2. The Shared Suffering</h3>
<p>Every developer has fought UTF-8 encoding hell. It&rsquo;s a rite of passage. You bond over it. You laugh about it years later. It&rsquo;s culture.</p>
<p>AI handles encoding transparently. You never fight it. You never bond over it. You never laugh about it.</p>
<p><strong>Shared routes = shared identity.</strong></p>
<h3 id="3-the-intuition-building">3. The Intuition Building</h3>
<p>You&rsquo;ve refactored 50 functions by hand. On function 51, you <em>feel</em> the pattern before you see it. That&rsquo;s mastery.</p>
<p>AI refactors all 51 at once. You never develop the feel. You just review the diff.</p>
<p><strong>Repeated routes = internalized knowledge.</strong></p>
<h2 id="the-speculative-nightmare-or-dream">The Speculative Nightmare (Or Dream?)</h2>
<p>What happens when an entire generation skips the routes?</p>
<h3 id="scenario-1-culture-fractures-into-pre-ai-and-post-ai">Scenario 1: Culture Fractures Into Pre-AI and Post-AI</h3>
<p>The last generation who learned to code without AI becomes unrelateable to the first generation who never coded without it.</p>
<p>They literally don&rsquo;t share the same reference points:</p>
<ul>
<li>&ldquo;Remember fighting with makefiles?&rdquo; — blank stares</li>
<li>&ldquo;Remember the joy when your first loop worked?&rdquo; — never felt it</li>
<li>&ldquo;Remember learning pointers?&rdquo; — never needed to</li>
</ul>
<p>We split into two species: Route-Walkers and Teleporters.</p>
<h3 id="scenario-2-culture-becomes-meta">Scenario 2: Culture Becomes Meta</h3>
<p>If culture is &ldquo;the sum of routes walked,&rdquo; and AI walks the routes&hellip;</p>
<p>Maybe culture shifts to:</p>
<ul>
<li><strong>Prompt archaeology</strong>: studying how people asked for things</li>
<li><strong>AI wrangling stories</strong>: the time Claude hallucinated and you debugged for 6 hours</li>
<li><strong>Direction-setting wisdom</strong>: knowing what to build, not how</li>
</ul>
<p>Culture doesn&rsquo;t die. It changes altitude. We stop trading war stories about <em>implementation</em> and start trading war stories about <em>intention</em>.</p>
<h3 id="scenario-3-we-optimize-culture-out-of-existence">Scenario 3: We Optimize Culture Out of Existence</h3>
<p>Efficiency kills inefficiency. Routes are inefficient. Culture is inefficient.</p>
<p>What if we optimize so hard that we forget why routes mattered?</p>
<ul>
<li>Music made by AI, consumed by AI, critiqued by AI</li>
<li>Code written by AI, reviewed by AI, deployed by AI</li>
<li>Books generated by AI, summarized by AI, recommended by AI</li>
</ul>
<p>Humans become the audience. But audiences don&rsquo;t create culture. Performers do.</p>
<p><strong>If nobody walks the route, is there still a culture? Or just a catalog?</strong></p>
<h2 id="the-counter-argument-culture-always-evolves">The Counter-Argument: Culture Always Evolves</h2>
<p>Maybe I&rsquo;m romanticizing the route.</p>
<p>Printing press killed manuscript culture. Every monk mourned the loss of hand-copied books. But we didn&rsquo;t lose culture—we <em>democratized</em> it.</p>
<p>Cars killed horse culture. Every blacksmith mourned the loss of farrier craft. But we didn&rsquo;t lose culture—we <em>accelerated</em> it.</p>
<p>Maybe AI kills grind culture. Every developer mourns the loss of hard-won debugging skills. But we don&rsquo;t lose culture—we <strong>abstract</strong> it.</p>
<p>The route changes. The destination changes. The culture changes.</p>
<p>Same as it ever was.</p>
<h2 id="the-uncomfortable-question">The Uncomfortable Question</h2>
<p>Here&rsquo;s what keeps me up:</p>
<p><strong>What if walking the route wasn&rsquo;t just how we built culture—what if it was how we built <em>ourselves</em>?</strong></p>
<p>The struggle isn&rsquo;t a bug. It&rsquo;s the feature.</p>
<p>You&rsquo;re not the person who knows how to center a div. You&rsquo;re the person who <em>fought</em> to center a div.</p>
<p>AI takes the fight. Does it take the person?</p>
<h2 id="what-we-can-do-maybe">What We Can Do (Maybe)</h2>
<p>I don&rsquo;t have answers. I have guesses:</p>
<h3 id="1-walk-some-routes-on-purpose">1. Walk Some Routes On Purpose</h3>
<p>Learn one thing the hard way each year. Not for efficiency. For the route.</p>
<ul>
<li>Write assembly once</li>
<li>Build a compiler once</li>
<li>Implement TCP from scratch once</li>
</ul>
<p>Not to use it. To <em>feel</em> it.</p>
<h3 id="2-document-the-routes-ai-takes">2. Document The Routes AI Takes</h3>
<p>AI skips steps. Make it show its work. Study the paths it chose. Build intuition about <em>why</em> it chose them.</p>
<p>You&rsquo;re not walking the route, but you&rsquo;re learning the map.</p>
<h3 id="3-create-new-routes-worth-walking">3. Create New Routes Worth Walking</h3>
<p>AI can write code. It can&rsquo;t discover what&rsquo;s worth building.</p>
<p>The new cultural route isn&rsquo;t &ldquo;how do I implement this?&rdquo;</p>
<p>It&rsquo;s &ldquo;why does this need to exist?&rdquo;</p>
<h2 id="the-route-forward-ironic-phrasing-intended">The Route Forward (Ironic Phrasing Intended)</h2>
<p>Culture isn&rsquo;t dying. It&rsquo;s mutating.</p>
<p>We&rsquo;re in the awkward phase where:</p>
<ul>
<li>Old routes still feel meaningful (they are)</li>
<li>New routes haven&rsquo;t been established yet (they will be)</li>
<li>We&rsquo;re mourning and celebrating simultaneously (we should be)</li>
</ul>
<p>Maybe in 20 years, we&rsquo;ll look back and laugh at how we thought &ldquo;writing your own sort function&rdquo; was culture.</p>
<p>Or maybe we&rsquo;ll mourn that nobody remembers what it felt like to fight a segfault at 3 AM.</p>
<p>Either way, we won&rsquo;t have walked the route to know which is true.</p>
<p>We&rsquo;ll have asked AI.</p>
<hr>
<p><em>Written by human. Routed through doubt. Arrived at speculation.</em></p>
]]></content></item><item><title>The AI Process Revolution: From Tool Selection to Bottleneck Detection</title><link>https://chili-it.de/posts/ai-business-process-revolution/</link><pubDate>Tue, 18 Nov 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/ai-business-process-revolution/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/ai-business-process-revolution.webp"
alt="A charcoal sketch of a magnifying glass held over an industrial process pipeline, revealing hidden bottlenecks — cracked gears and jammed connections glowing through the lens"&gt;
&lt;/figure&gt;
&lt;p&gt;The game has changed completely.&lt;/p&gt;
&lt;p&gt;For decades, business process optimization was a slow, painful grind. Consultants would spend months mapping workflows, conducting interviews, analyzing spreadsheets, and building process diagrams. The lucky ones might identify a few bottlenecks after burning through budgets that would make a CFO weep.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/ai-business-process-revolution.webp"
			alt="A charcoal sketch of a magnifying glass held over an industrial process pipeline, revealing hidden bottlenecks — cracked gears and jammed connections glowing through the lens">
</figure>

<p>The game has changed completely.</p>
<p>For decades, business process optimization was a slow, painful grind. Consultants would spend months mapping workflows, conducting interviews, analyzing spreadsheets, and building process diagrams. The lucky ones might identify a few bottlenecks after burning through budgets that would make a CFO weep.</p>
<p>Then AI arrived and flipped the entire industry on its head.</p>
<h2 id="the-old-way-was-brutally-slow">The Old Way Was Brutally Slow</h2>
<p>Remember the pre-AI consulting playbook? It looked something like this:</p>
<ol>
<li><strong>Discovery Phase</strong> (4-8 weeks): Interview everyone, document everything</li>
<li><strong>Analysis Phase</strong> (6-12 weeks): Build process maps, identify gaps</li>
<li><strong>Solution Phase</strong> (4-6 weeks): Research tools, create recommendations</li>
<li><strong>Implementation Phase</strong> (12-24 weeks): Roll out new systems, train users</li>
</ol>
<p>Total timeline: 6-12 months. Total cost: Often six figures. Success rate: Honestly? Pretty hit-or-miss.</p>
<p>The consultant&rsquo;s job was essentially acting as a human search engine: &ldquo;You need a CRM? Try Salesforce. Inventory issues? Here&rsquo;s an ERP system. Communication problems? Slack it is.&rdquo;</p>
<h2 id="ai-changed-everything-overnight">AI Changed Everything Overnight</h2>
<p>Now? AI can analyze your entire operation in hours, not months.</p>
<ul>
<li><strong>Process Mining</strong>: AI tools scan your systems and automatically map every workflow</li>
<li><strong>Pattern Recognition</strong>: Machine learning spots bottlenecks humans would miss entirely</li>
<li><strong>Real-time Analysis</strong>: No more waiting weeks for reports - insights appear instantly</li>
<li><strong>Predictive Modeling</strong>: AI shows you where problems will emerge before they hit</li>
</ul>
<p>What used to take a team of consultants months to uncover, AI can identify over a coffee break.</p>
<h2 id="the-consultants-role-has-evolved">The Consultant&rsquo;s Role Has Evolved</h2>
<p>This doesn&rsquo;t mean consultants are obsolete - quite the opposite. But the conversation has completely shifted.</p>
<p><strong>Before AI, clients asked:</strong></p>
<ul>
<li>&ldquo;Which CRM should we use?&rdquo;</li>
<li>&ldquo;What&rsquo;s the best project management tool?&rdquo;</li>
<li>&ldquo;Can you recommend a good accounting system?&rdquo;</li>
</ul>
<p><strong>Now AI specialists ask:</strong></p>
<ul>
<li>&ldquo;Where are your actual bottlenecks?&rdquo;</li>
<li>&ldquo;Which processes are bleeding money?&rdquo;</li>
<li>&ldquo;What&rsquo;s stopping your team from scaling?&rdquo;</li>
</ul>
<p>The focus moved from tool selection to process archaeology. Instead of being software salespeople in disguise, consultants became process detectives.</p>
<h2 id="speed-is-everything">Speed Is Everything</h2>
<p>Here&rsquo;s what really matters: AI compressed months of analysis into days. This speed advantage isn&rsquo;t just convenient - it&rsquo;s transformational.</p>
<p>When process optimization takes 6 months, businesses defer improvements indefinitely. When it takes 6 days, they&rsquo;ll tackle three optimization projects before lunch.</p>
<p>I&rsquo;ve seen companies identify and fix workflow bottlenecks in a single sprint that would have required a full consulting engagement just two years ago. The economics are completely different now.</p>
<h2 id="the-new-consulting-questions">The New Consulting Questions</h2>
<p>Modern AI-powered process consulting starts with fundamentally different questions:</p>
<ul>
<li><strong>&ldquo;Show me your data flows&rdquo;</strong> - AI can trace every transaction, approval, and handoff</li>
<li><strong>&ldquo;Where do tasks queue up?&rdquo;</strong> - Machine learning spots delay patterns instantly</li>
<li><strong>&ldquo;What&rsquo;s your actual cycle time?&rdquo;</strong> - Real measurements, not estimates</li>
<li><strong>&ldquo;Which manual steps add no value?&rdquo;</strong> - Process mining reveals redundant work</li>
<li><strong>&ldquo;Where do errors cluster?&rdquo;</strong> - Pattern analysis finds root causes</li>
</ul>
<p>These aren&rsquo;t tool questions - they&rsquo;re optimization questions.</p>
<h2 id="the-bottleneck-detective">The Bottleneck Detective</h2>
<p>Today&rsquo;s AI specialist is less &ldquo;systems integrator&rdquo; and more &ldquo;bottleneck detective.&rdquo;</p>
<p>We&rsquo;re not here to sell you software. We&rsquo;re here to find where your business is hemorrhaging time, money, or opportunities. AI gives us X-ray vision into your operations.</p>
<p>The conversation shifted from <em>&ldquo;Here&rsquo;s what you should buy&rdquo;</em> to <em>&ldquo;Here&rsquo;s what&rsquo;s actually broken.&rdquo;</em></p>
<h2 id="what-this-means-for-business">What This Means for Business</h2>
<p>If you&rsquo;re still thinking about AI in terms of &ldquo;tools&rdquo; and &ldquo;platforms,&rdquo; you&rsquo;re missing the real revolution. AI isn&rsquo;t just another software category - it&rsquo;s a completely new way to understand and optimize how work gets done.</p>
<p>The businesses winning right now aren&rsquo;t the ones with the fanciest AI tools. They&rsquo;re the ones using AI to find and fix their process bottlenecks faster than their competition can even identify them.</p>
<p>Speed of optimization is becoming the ultimate competitive advantage.</p>
<hr>
<p><em>Need help identifying bottlenecks in your business processes? Let&rsquo;s talk about what AI can uncover in your operation.</em></p>
]]></content></item><item><title>Forget the Big Picture: Just Win Today</title><link>https://chili-it.de/posts/forget-the-big-picture/</link><pubDate>Thu, 13 Nov 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/forget-the-big-picture/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/forget-the-big-picture.webp"
alt="A charcoal sketch of a single boot print pressed firmly into the ground in sharp focus, with a fading mountain range dissolving into the distance behind it"&gt;
&lt;/figure&gt;
&lt;p&gt;Stop planning. Start winning.&lt;/p&gt;
&lt;h2 id="the-big-picture-trap"&gt;The Big Picture Trap&lt;/h2&gt;
&lt;p&gt;Everyone tells you to think big. Vision boards. Five-year plans. Moonshots. Meanwhile, you&amp;rsquo;re paralyzed because the mountain looks too damn high.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the dirty secret: &lt;strong&gt;the big picture is a distraction&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="the-daily-win-philosophy"&gt;The Daily Win Philosophy&lt;/h2&gt;
&lt;p&gt;Instead of asking &amp;ldquo;Where will I be in 5 years?&amp;rdquo;, ask:&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/forget-the-big-picture.webp"
			alt="A charcoal sketch of a single boot print pressed firmly into the ground in sharp focus, with a fading mountain range dissolving into the distance behind it">
</figure>

<p>Stop planning. Start winning.</p>
<h2 id="the-big-picture-trap">The Big Picture Trap</h2>
<p>Everyone tells you to think big. Vision boards. Five-year plans. Moonshots. Meanwhile, you&rsquo;re paralyzed because the mountain looks too damn high.</p>
<p>Here&rsquo;s the dirty secret: <strong>the big picture is a distraction</strong>.</p>
<h2 id="the-daily-win-philosophy">The Daily Win Philosophy</h2>
<p>Instead of asking &ldquo;Where will I be in 5 years?&rdquo;, ask:</p>
<p><strong>&ldquo;What would make today a win?&rdquo;</strong></p>
<p>That&rsquo;s it. One question. One day.</p>
<ul>
<li>Fixed that bug? Win.</li>
<li>Shipped that feature? Win.</li>
<li>Learned that new tool? Win.</li>
<li>Helped a teammate? Win.</li>
<li>Refactored that mess? Win.</li>
</ul>
<p>Stack enough wins, and you look back six months later wondering how the hell you got so far.</p>
<h2 id="why-this-works">Why This Works</h2>
<p><strong>Compound Interest for Humans</strong>: Each successful day builds on the last. You&rsquo;re not making a 5-year leap—you&rsquo;re making 1,825 micro-improvements.</p>
<p><strong>No Analysis Paralysis</strong>: You can&rsquo;t overthink &ldquo;What should I do today?&rdquo; You know. Deep down, you always know.</p>
<p><strong>Immediate Feedback</strong>: You know by 6 PM if you won. No waiting years to see if your master plan worked.</p>
<p><strong>Resilience Built-In</strong>: Bad day? Tomorrow&rsquo;s a fresh chance. Bad year in a 5-year plan? You&rsquo;re screwed.</p>
<h2 id="the-anti-vision">The Anti-Vision</h2>
<p>I&rsquo;m not saying don&rsquo;t have goals. I&rsquo;m saying stop worshiping them.</p>
<p>Your goal is the direction. Your day is the step.</p>
<p>Walk north every day, and you&rsquo;ll end up north. Stare at the map dreaming about Alaska, and you&rsquo;re still in your living room.</p>
<h2 id="the-exception">The Exception</h2>
<p>The only big-picture question worth asking:</p>
<p><strong>&ldquo;Is what I&rsquo;m doing today moving me in a direction I give a shit about?&rdquo;</strong></p>
<p>If yes: keep winning days.</p>
<p>If no: adjust direction, then keep winning days.</p>
<h2 id="what-excellent-really-means">What &ldquo;Excellent&rdquo; Really Means</h2>
<p>Have 30 successful days? That&rsquo;s an excellent month.
Have 90 successful days? That&rsquo;s an excellent quarter.
Have 250 successful days? That&rsquo;s an excellent year.</p>
<p>Excellence isn&rsquo;t a destination. It&rsquo;s a batting average.</p>
<h2 id="practical-application">Practical Application</h2>
<p>Every morning, write down:</p>
<pre tabindex="0"><code>Today is a win if I: [one thing]
</code></pre><p>Every evening, answer:</p>
<pre tabindex="0"><code>Did I win? [yes/no]
</code></pre><p>Track it in a text file, a spreadsheet, a notebook, a terminal script—doesn&rsquo;t matter. Just track it.</p>
<p>Watch your streak. When you hit 7 wins in a row, you&rsquo;ll feel it. When you hit 30, you&rsquo;ll <em>be</em> it.</p>
<h2 id="the-math">The Math</h2>
<ul>
<li>70% win rate = 255 winning days per year</li>
<li>255 winning days = top 1% of output</li>
<li>Top 1% of output = opportunities find you</li>
</ul>
<p>You don&rsquo;t need a better plan. You need a better Tuesday.</p>
<h2 id="stop-reading-start-winning">Stop Reading, Start Winning</h2>
<p>What would make today a win?</p>
<p>Now go do that.</p>
<hr>
<p><em>Written during a winning day. Tomorrow&rsquo;s another one.</em></p>
]]></content></item><item><title>150GB in Under 3 Minutes: A Restic Shoutout</title><link>https://chili-it.de/posts/restic-backup-speed/</link><pubDate>Thu, 30 Oct 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/restic-backup-speed/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/restic-backup-speed.webp"
alt="A charcoal sketch of a large analog stopwatch showing nearly three minutes, set against a towering stack of server hard drives and database cylinders"&gt;
&lt;/figure&gt;
&lt;p&gt;You know what&amp;rsquo;s surprisingly not terrible? Backing up databases.&lt;/p&gt;
&lt;p&gt;Wait, let me rephrase: You know what&amp;rsquo;s surprisingly not terrible &lt;em&gt;when you use the right tool&lt;/em&gt;? Backing up databases.&lt;/p&gt;
&lt;h2 id="the-numbers"&gt;The Numbers&lt;/h2&gt;
&lt;p&gt;150GB MSSQL data directory. &lt;strong&gt;2 minutes and 57 seconds.&lt;/strong&gt; Done.&lt;/p&gt;
&lt;p&gt;Not hours. Not &amp;ldquo;let it run overnight.&amp;rdquo; Not &amp;ldquo;come back after lunch.&amp;rdquo; Under three minutes for production database files.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/restic-backup-speed.webp"
			alt="A charcoal sketch of a large analog stopwatch showing nearly three minutes, set against a towering stack of server hard drives and database cylinders">
</figure>

<p>You know what&rsquo;s surprisingly not terrible? Backing up databases.</p>
<p>Wait, let me rephrase: You know what&rsquo;s surprisingly not terrible <em>when you use the right tool</em>? Backing up databases.</p>
<h2 id="the-numbers">The Numbers</h2>
<p>150GB MSSQL data directory. <strong>2 minutes and 57 seconds.</strong> Done.</p>
<p>Not hours. Not &ldquo;let it run overnight.&rdquo; Not &ldquo;come back after lunch.&rdquo; Under three minutes for production database files.</p>
<h2 id="the-tool-restic">The Tool: Restic</h2>
<p><a href="https://restic.net/">Restic</a> is one of those rare pieces of software that just works. Open source backup program that handles deduplication, encryption, compression, and verification without a PhD thesis worth of configuration.</p>
<ul>
<li><strong>Fast</strong> - Parallel processing, smart chunking, efficient compression</li>
<li><strong>Encrypted by default</strong> - Because we&rsquo;re not animals</li>
<li><strong>Deduplication</strong> - Only backs up what changed</li>
<li><strong>Cross-platform</strong> - Linux, Windows, macOS, BSD</li>
<li><strong>Multiple backends</strong> - Local, SFTP, S3, Azure, you name it</li>
<li><strong>Simple CLI</strong> - No XML config files</li>
</ul>
<p>GitHub: <a href="https://github.com/restic/restic">restic/restic</a>
Docs: <a href="https://restic.readthedocs.io/">restic.readthedocs.io</a></p>
<h2 id="ten-years-of-production-use">Ten Years of Production Use</h2>
<p>This is literally the simplest backup script possible, and I&rsquo;ve been running it for over <strong>ten years</strong>. Multiple restores. Multiple data migrations. Zero disasters.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bat" data-lang="bat"><span class="line"><span class="cl"><span class="p">@</span><span class="k">echo</span> off
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">REM === CONFIGURATION - CHANGE THESE VALUES ===</span>
</span></span><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">RESTIC_REPOSITORY</span><span class="p">=</span>\\backup-server\datev-backups
</span></span><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">RESTIC_PASSWORD</span><span class="p">=</span>your-password-here
</span></span><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">DATEV_DATA_DIR</span><span class="p">=</span>L:\WINDVSW1
</span></span><span class="line"><span class="cl"><span class="c1">REM ==========================================</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">REM Stop DATEV SQL Server</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> Stopping SQL Server...
</span></span><span class="line"><span class="cl">net stop <span class="s2">&#34;MSSQL$DATEV_DBENGINE&#34;</span> /y
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">REM Wait a bit for files to be released</span>
</span></span><span class="line"><span class="cl">timeout /t 10
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">REM Run restic backup</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> Running backup...
</span></span><span class="line"><span class="cl">restic -r <span class="nv">%RESTIC_REPOSITORY%</span> backup <span class="s2">&#34;</span><span class="nv">%DATEV_DATA_DIR%</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">REM Start DATEV SQL Server</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> Starting SQL Server...
</span></span><span class="line"><span class="cl">net start <span class="s2">&#34;MSSQL$DATEV_DBENGINE&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> Backup complete!
</span></span><span class="line"><span class="cl"><span class="k">pause</span>
</span></span></code></pre></div><p>Thirteen lines. Stop service. Wait 10 seconds. Backup. Start service. Done.</p>
<p>This script has survived multiple Windows Server upgrades, hardware migrations, storage changes, and countless DATEV updates. It works because there&rsquo;s nothing to break.</p>
<h2 id="why-its-fast">Why It&rsquo;s Fast</h2>
<p>Deduplication is the secret sauce. First backup of 150GB takes a while. Second backup where only 5GB changed? Under three minutes.</p>
<p>MSSQL files are huge, but day-to-day changes are relatively small. Restic only saves the blocks that changed. This is why it destroys traditional backup tools for database files.</p>
<h2 id="the-restic-workflow">The Restic Workflow</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Initialize a repository (one time)</span>
</span></span><span class="line"><span class="cl">restic init --repo /path/to/backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Backup</span>
</span></span><span class="line"><span class="cl">restic backup /data --repo /path/to/backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># List snapshots</span>
</span></span><span class="line"><span class="cl">restic snapshots --repo /path/to/backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restore</span>
</span></span><span class="line"><span class="cl">restic restore latest --repo /path/to/backup --target /restore/path
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check integrity</span>
</span></span><span class="line"><span class="cl">restic check --repo /path/to/backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Cleanup old snapshots</span>
</span></span><span class="line"><span class="cl">restic forget --keep-last <span class="m">10</span> --prune --repo /path/to/backup
</span></span></code></pre></div><h2 id="the-track-record">The Track Record</h2>
<p>Over a decade of production use. I&rsquo;ve actually had to restore from these backups—multiple times, for migrations and recovery scenarios. Every single time, Restic delivered.</p>
<p>No corruption. No incomplete snapshots. No &ldquo;upgrade to Enterprise Edition&rdquo; nonsense.</p>
<p>It just works. Which in the backup world is about as rare as finding a bug-free Microsoft update.</p>
<h2 id="the-bottom-line">The Bottom Line</h2>
<p>150GB. 2:57. Thirteen-line batch file. Ten years of production use. Multiple successful restores.</p>
<p>Restic is fast, reliable, encrypted, free, and doesn&rsquo;t make you want to throw your computer out a window.</p>
<p>If you&rsquo;re backing up databases and not using Restic, you&rsquo;re either using something enterprise and expensive that&rsquo;s probably slower, or not backing up at all (please fix this immediately).</p>
<p>Go download Restic. Set up automated backups. Sleep better at night.</p>
<hr>
<p><em>P.S. - Seriously, if you&rsquo;re not backing up your databases, stop reading and go set that up right now. I&rsquo;ll wait.</em></p>
]]></content></item><item><title>Calm Down and Code: Finding Flow State with Vim and AI</title><link>https://chili-it.de/posts/vim-flow-state/</link><pubDate>Wed, 29 Oct 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/vim-flow-state/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/vim-flow-state.webp"
alt="A charcoal sketch of hands flowing over a keyboard with gestural energy lines trailing from the fingertips - capturing the zen of vim flow state"&gt;
&lt;/figure&gt;
&lt;p&gt;After doing lots of stuff with AI Claude Code, I&amp;rsquo;ve had a realization.&lt;/p&gt;
&lt;h2 id="the-reward-loop"&gt;The Reward Loop&lt;/h2&gt;
&lt;p&gt;You know what&amp;rsquo;s incredibly rewarding? Using vim and actually getting stuff done.&lt;/p&gt;
&lt;p&gt;Not explaining what you want.
Not waiting for responses.
Not describing your intent in natural language prose.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/vim-flow-state.webp"
			alt="A charcoal sketch of hands flowing over a keyboard with gestural energy lines trailing from the fingertips - capturing the zen of vim flow state">
</figure>

<p>After doing lots of stuff with AI Claude Code, I&rsquo;ve had a realization.</p>
<h2 id="the-reward-loop">The Reward Loop</h2>
<p>You know what&rsquo;s incredibly rewarding? Using vim and actually getting stuff done.</p>
<p>Not explaining what you want.
Not waiting for responses.
Not describing your intent in natural language prose.</p>
<p>Just <strong>doing</strong>.</p>
<h2 id="flow-state-is-real">Flow State is Real</h2>
<p>This is what flow state actually feels like:</p>
<p>Your fingers move faster than your thoughts. Muscle memory takes over. <code>dd</code>, <code>ciw</code>, <code>:%s</code>, <code>gq</code> - these aren&rsquo;t commands anymore, they&rsquo;re extensions of your intent.</p>
<p>AI is amazing for architecture, for boilerplate, for &ldquo;figure this out for me&rdquo; moments. Claude Code has fundamentally changed how I work.</p>
<p>But there&rsquo;s something else entirely about <strong>just editing code yourself</strong>.</p>
<h2 id="the-zen-of-direct-action">The Zen of Direct Action</h2>
<p>When you&rsquo;re in the zone with vim:</p>
<ul>
<li>No context switching to explain what you want changed</li>
<li>No waiting for a response to generate</li>
<li>No reviewing AI changes to verify they match your mental model</li>
<li>Just pure, immediate, tactile manipulation of text</li>
</ul>
<p>It&rsquo;s meditative. It&rsquo;s zen.</p>
<h2 id="the-best-of-both-worlds">The Best of Both Worlds</h2>
<p>Here&rsquo;s the thing: it&rsquo;s not either/or.</p>
<p>Use Claude Code for:</p>
<ul>
<li>Scaffolding new features</li>
<li>Researching unfamiliar codebases</li>
<li>Refactoring gnarly legacy code</li>
<li>Explaining complex systems</li>
</ul>
<p>Use vim for:</p>
<ul>
<li>Quick edits you can visualize</li>
<li>Fine-tuning AI-generated code</li>
<li>Flow state deep work sessions</li>
<li>Everything that feels faster to just do</li>
</ul>
<h2 id="calm-down-and-code">Calm Down and Code</h2>
<p>After hours of prompting AI, sometimes the most productive thing you can do is:</p>
<p>Close the chat.
Open vim.
Trust your fingers.
Get it done.</p>
<p>This is flow state.
This is zen.</p>
<p><strong>Calm down and code.</strong></p>
<hr>
<p><em>Written in vim, obviously.</em></p>
]]></content></item><item><title>I Told You So: Why AI Finally Vindicates Command Line Diehards</title><link>https://chili-it.de/posts/ai-loves-the-command-line/</link><pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/ai-loves-the-command-line/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/ai-loves-command-line.webp"
alt="A charcoal sketch of a terminal window with purple AI energy flowing through and around it - representing the synergy of classic Unix tools and modern AI"&gt;
&lt;/figure&gt;
&lt;p&gt;Remember when everyone said the command line was dead? That GUIs had won? That normal people don&amp;rsquo;t need to know &lt;code&gt;sed&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt; or &lt;code&gt;grep&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Yeah, about that.&lt;/p&gt;
&lt;h2 id="the-xth-revival-of-gnu-utils"&gt;The Xth Revival of GNU Utils&lt;/h2&gt;
&lt;p&gt;Here we are in 2025, and suddenly every AI model on the planet is an absolute wizard with &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;tr&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, and friends. The same Unix tools that have been around since before I was born are now the lingua franca of AI automation.&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/ai-loves-command-line.webp"
			alt="A charcoal sketch of a terminal window with purple AI energy flowing through and around it - representing the synergy of classic Unix tools and modern AI">
</figure>

<p>Remember when everyone said the command line was dead? That GUIs had won? That normal people don&rsquo;t need to know <code>sed</code> or <code>awk</code> or <code>grep</code>?</p>
<p>Yeah, about that.</p>
<h2 id="the-xth-revival-of-gnu-utils">The Xth Revival of GNU Utils</h2>
<p>Here we are in 2025, and suddenly every AI model on the planet is an absolute wizard with <code>cat</code>, <code>sed</code>, <code>tr</code>, <code>grep</code>, and friends. The same Unix tools that have been around since before I was born are now the lingua franca of AI automation.</p>
<p>Why? Because they&rsquo;re <strong>simple</strong>. They&rsquo;re <strong>composable</strong>. They&rsquo;re <strong>text-based</strong>.</p>
<p>All the things we crusty command line users have been saying for decades? Turns out they&rsquo;re exactly what makes tools perfect for AI agents. Who knew that piping plain text through focused, single-purpose utilities would be the ideal interface for artificial intelligence?</p>
<p>Oh wait. We knew. We&rsquo;ve been telling you this the whole time.</p>
<h2 id="ssh-the-killer-app-you-already-had">SSH: The Killer App You Already Had</h2>
<p>Want to know what&rsquo;s hilarious? While everyone was building complicated REST APIs and GraphQL endpoints and whatever-the-hell-else to enable remote access, Linux quietly shipped with SSH access nearly by default.</p>
<p>AI doesn&rsquo;t need your fancy API. It needs to SSH into a box and run some commands. Done.</p>
<p>Remote command line access has been solved for 30+ years. It&rsquo;s secure, it&rsquo;s universal, it&rsquo;s scriptable. And now AI agents can use it to manage infrastructure, debug production issues, deploy code, troubleshoot systems—all the things sysadmins have been doing from terminal windows since forever.</p>
<p>The future of AI infrastructure management looks suspiciously like a guy with an SSH key and a shell prompt. Imagine that.</p>
<h2 id="scripts-suddenly-useful-again">Scripts: Suddenly Useful Again</h2>
<p>Remember when &ldquo;real developers&rdquo; were too good for shell scripts? When everything had to be a microservice or a containerized application or some framework-heavy monstrosity?</p>
<p>AI loves scripts. Bash scripts, Python scripts, doesn&rsquo;t matter. They&rsquo;re declarative, readable, and you can iterate on them in seconds. No compilation step. No build pipeline. No deployment ceremony.</p>
<p>Just edit a file, run it, see what breaks, fix it, repeat.</p>
<p>This is what system automation has always looked like for people who actually operate systems. Now AI can do it too, and suddenly everyone&rsquo;s rediscovering the beauty of a well-crafted 50-line bash script that solves a real problem.</p>
<h2 id="dotfiles-we-were-doing-ai-instructions-before-ai">Dotfiles: We Were Doing AI Instructions Before AI</h2>
<p>Here&rsquo;s my favorite part: command line users have been living with dotfiles for decades. Configuration as text files. <code>.bashrc</code>, <code>.vimrc</code>, <code>.gitconfig</code>—we&rsquo;ve been versioning our environment preferences and sharing them on GitHub since before &ldquo;Infrastructure as Code&rdquo; was a buzzword.</p>
<p>And what&rsquo;s the hot new thing in AI? Markdown instruction files. CLAUDE.md. Agent configuration files.</p>
<p>It&rsquo;s literally the same concept. Text-based configuration that you can version control, share, and iterate on. We&rsquo;ve been doing this. This isn&rsquo;t new. You&rsquo;re just catching up.</p>
<h2 id="command-line-troubleshooting-now-with-ai-copilots">Command Line Troubleshooting: Now With AI Copilots</h2>
<p>Debugging production issues used to mean SSHing into servers, grepping logs, piping through <code>awk</code>, maybe writing a quick script to parse some data. It was fast, it was effective, and it required actual skill.</p>
<p>Now? Same workflow, except you can have an AI agent help. It can grep the logs for you, write the parsing script, suggest the fix. The interface is still the command line because <strong>that&rsquo;s the right interface for this job</strong>.</p>
<p>All those &ldquo;modern&rdquo; observability platforms with their fancy dashboards? Great for executives. But when shit hits the fan at 3 AM, you&rsquo;re still going to SSH into that box and read the damn logs with <code>less</code> and <code>grep</code>.</p>
<p>AI just makes you better at it.</p>
<h2 id="the-dark-side-ai-can-rm--rf-too">The Dark Side: AI Can <code>rm -rf</code> Too</h2>
<p>Now for the uncomfortable truth we need to talk about: AI has access to all these powerful tools, and powerful tools are called &ldquo;powerful&rdquo; for a reason.</p>
<p>An AI agent that can run <code>sed</code> on your config files can also run <code>rm -rf /</code> on your production server. An agent with SSH access can do everything you can do—including catastrophically destructive things.</p>
<p>AI makes mistakes. Hallucinations. Logic errors. Misunderstandings. And when those mistakes involve shell commands with root privileges, people get fired.</p>
<p>I&rsquo;ve seen AI agents:</p>
<ul>
<li>Delete production databases</li>
<li>Overwrite critical config files</li>
<li>Break SSH access to remote systems</li>
<li>Fill disks by accident</li>
<li>Chmod things they shouldn&rsquo;t</li>
<li>Git push &ndash;force to main</li>
</ul>
<p>The command line is powerful. AI is powerful. Together? You better have backups. And version control. And read-only users. And a very good understanding of what you&rsquo;re asking the AI to do.</p>
<h2 id="the-punchline">The Punchline</h2>
<p>The future of AI-powered infrastructure looks an awful lot like the past of Unix system administration. Text files, composable tools, SSH access, scripts, and command-line troubleshooting.</p>
<p>For those of us who never left the terminal, this isn&rsquo;t a revolution. It&rsquo;s validation.</p>
<p>We were right all along. The command line isn&rsquo;t dead. It&rsquo;s just getting smarter assistants.</p>
<p>Now if you&rsquo;ll excuse me, I need to go write a <code>.clauderc</code> file to configure my AI agent&rsquo;s shell preferences. You know, like we&rsquo;ve been doing with <code>.bashrc</code> since 1989.</p>
<hr>
<p><em>Reminder: With great shell access comes great responsibility. AI + sudo can end badly. Very badly. Test in dev. Use version control. Keep backups. Don&rsquo;t say I didn&rsquo;t warn you.</em></p>
]]></content></item><item><title>Building Workshop Presentations with Claude Code &amp; Slidev</title><link>https://chili-it.de/posts/presentations-with-claude-code-and-slidev/</link><pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/presentations-with-claude-code-and-slidev/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/presentations-with-claude-code-and-slidev.webp"
alt="A charcoal sketch of markdown text in a terminal flowing outward and transforming into a cascade of polished presentation slides"&gt;
&lt;/figure&gt;
&lt;p&gt;After creating a 3-day workshop about OpenWebUI with over 100 slides, I discovered an incredibly efficient workflow: combining Claude Code with Slidev. Here&amp;rsquo;s why this combination is a game-changer for technical presentations.&lt;/p&gt;
&lt;h2 id="why-slidev"&gt;Why Slidev?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://sli.dev"&gt;Slidev&lt;/a&gt; is a presentation framework for developers that uses Markdown and Vue.js. Unlike traditional slide tools:&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/presentations-with-claude-code-and-slidev.webp"
			alt="A charcoal sketch of markdown text in a terminal flowing outward and transforming into a cascade of polished presentation slides">
</figure>

<p>After creating a 3-day workshop about OpenWebUI with over 100 slides, I discovered an incredibly efficient workflow: combining Claude Code with Slidev. Here&rsquo;s why this combination is a game-changer for technical presentations.</p>
<h2 id="why-slidev">Why Slidev?</h2>
<p><a href="https://sli.dev">Slidev</a> is a presentation framework for developers that uses Markdown and Vue.js. Unlike traditional slide tools:</p>
<ul>
<li><strong>Markdown-based</strong>: Write slides in plain text with code syntax highlighting</li>
<li><strong>Version control friendly</strong>: Git tracks every change cleanly</li>
<li><strong>Developer-focused</strong>: Code blocks, diagrams, custom layouts all built-in</li>
<li><strong>Live reload</strong>: See changes instantly during development</li>
<li><strong>Export options</strong>: PDF, SPA, or present directly in browser</li>
</ul>
<h2 id="why-claude-code">Why Claude Code?</h2>
<p>Claude Code isn&rsquo;t just an AI assistant—it&rsquo;s a coding partner that understands project structure, maintains consistency, and can execute complex multi-step tasks. For presentation development, it excels at:</p>
<ul>
<li><strong>Content generation</strong>: Creating technical content with proper depth</li>
<li><strong>Layout management</strong>: Applying custom layouts across 100+ slides consistently</li>
<li><strong>Style debugging</strong>: Finding and fixing CSS issues across the entire presentation</li>
<li><strong>Structural refactoring</strong>: Reorganizing content while maintaining coherence</li>
</ul>
<h2 id="the-workflow">The Workflow</h2>
<h3 id="1-docker-based-setup">1. Docker-Based Setup</h3>
<p>No local Node.js installation needed. Here&rsquo;s the <code>docker-compose.yml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">slidev-day1</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">tangramor/slidev:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">workshop-slidev-day1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;3031:3030&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./slidev:/slidev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">slidev day1.md --host 0.0.0.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span></code></pre></div><p>Benefits:</p>
<ul>
<li>Isolated environment per presentation</li>
<li>No dependency conflicts</li>
<li>Works identically across machines</li>
<li>Easy to scale (I run 3 presentations simultaneously)</li>
</ul>
<h3 id="2-custom-layouts">2. Custom Layouts</h3>
<p>Slidev&rsquo;s real power is custom layouts. For my workshop, I created:</p>
<p><strong>Code-heavy layout</strong> (<code>slidev/layouts/code-heavy.vue</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;slidev-layout code-heavy&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">slot</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nx">code</span><span class="o">-</span><span class="nx">heavy</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">padding</span><span class="o">-</span><span class="nx">bottom</span><span class="o">:</span> <span class="mi">7</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nx">code</span><span class="o">-</span><span class="nx">heavy</span> <span class="nx">pre</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">max</span><span class="o">-</span><span class="nx">height</span><span class="o">:</span> <span class="mi">60</span><span class="nx">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">overflow</span><span class="o">-</span><span class="nx">y</span><span class="o">:</span> <span class="nx">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p><strong>Definitions layout</strong> for terminology slides:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;slidev-layout definitions&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">slot</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nx">definitions</span> <span class="nx">strong</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">color</span><span class="o">:</span> <span class="err">#</span><span class="mi">00</span><span class="nx">d9ff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">font</span><span class="o">-</span><span class="nx">size</span><span class="o">:</span> <span class="mf">1.3</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">display</span><span class="o">:</span> <span class="nx">block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">margin</span><span class="o">-</span><span class="nx">top</span><span class="o">:</span> <span class="mf">1.5</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="3-applying-layouts">3. Applying Layouts</h3>
<p>In your Markdown:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">layout: code-heavy
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Docker Compose Example
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">\`\`\`yaml
</span></span><span class="line"><span class="cl">services:
</span></span><span class="line"><span class="cl">  open-webui:
</span></span><span class="line"><span class="cl">    image: ghcr.io/open-webui/open-webui:main
</span></span><span class="line"><span class="cl">    ports:
</span></span><span class="line"><span class="cl">      <span class="k">-</span> &#34;3000:8080&#34;
</span></span><span class="line"><span class="cl">    volumes:
</span></span><span class="line"><span class="cl">      <span class="k">-</span> ./data:/app/backend/data
</span></span><span class="line"><span class="cl">\`\`\`
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Key points:
</span></span><span class="line"><span class="cl"><span class="k">-</span> Persistent storage with volumes
</span></span><span class="line"><span class="cl"><span class="k">-</span> Port mapping 3000→8080
</span></span><span class="line"><span class="cl"><span class="k">-</span> Latest image from GitHub registry
</span></span><span class="line"><span class="cl">---
</span></span></code></pre></div><h2 id="how-claude-code-accelerates-development">How Claude Code Accelerates Development</h2>
<h3 id="content-creation">Content Creation</h3>
<p><strong>Prompt</strong>: &ldquo;Write a slide explaining OpenWebUI&rsquo;s vector database integration with examples&rdquo;</p>
<p>Claude generates:</p>
<ul>
<li>Technical accuracy (checks docs, code)</li>
<li>Proper structure</li>
<li>Code examples with syntax highlighting</li>
<li>Practical use cases</li>
</ul>
<h3 id="bulk-operations">Bulk Operations</h3>
<p><strong>Prompt</strong>: &ldquo;Apply code-heavy layout to all slides with code blocks in day2.md&rdquo;</p>
<p>Claude:</p>
<ol>
<li>Analyzes all 40+ slides</li>
<li>Identifies code-heavy content</li>
<li>Adds <code>layout: code-heavy</code> frontmatter</li>
<li>Maintains existing styling</li>
</ol>
<p>This would take hours manually—Claude does it in seconds.</p>
<h3 id="debugging-across-files">Debugging Across Files</h3>
<p><strong>Prompt</strong>: &ldquo;Strong text appears gray on center layout slides&rdquo;</p>
<p>Claude:</p>
<ol>
<li>Searches through <code>style.css</code></li>
<li>Identifies conflicting opacity rules</li>
<li>Fixes specificity issues</li>
<li>Tests across all three presentations</li>
</ol>
<h3 id="documentation-sync">Documentation Sync</h3>
<p><strong>Prompt</strong>: &ldquo;Update MCP integration slide with latest GitHub issue status&rdquo;</p>
<p>Claude:</p>
<ol>
<li>Fetches current issue state</li>
<li>Updates content truthfully (marks features as experimental)</li>
<li>Adds code references with line numbers</li>
<li>Maintains technical precision</li>
</ol>
<h2 id="real-world-example-workshop-evolution">Real-World Example: Workshop Evolution</h2>
<p>My OpenWebUI workshop started as rough outlines. With Claude Code:</p>
<p><strong>Day 1 (28 slides)</strong>: Basic concepts → Claude added practical examples and proper terminology
<strong>Day 2 (29 slides)</strong>: Advanced features → Claude researched actual implementation details
<strong>Day 3 (47 slides)</strong>: Production setup → Claude cross-referenced documentation and code</p>
<p>Total development time: ~6 hours instead of days.</p>
<h2 id="best-practices">Best Practices</h2>
<h3 id="1-clear-project-structure">1. Clear Project Structure</h3>
<pre tabindex="0"><code>workshop/
├── docker-compose.yml
├── slidev/
│   ├── day1.md
│   ├── day2.md
│   ├── day3.md
│   ├── style.css
│   └── layouts/
│       ├── code-heavy.vue
│       ├── content-dense.vue
│       └── definitions.vue
└── CLAUDE.md  # Project instructions for Claude
</code></pre><h3 id="2-document-your-conventions">2. Document Your Conventions</h3>
<p>Create <code>CLAUDE.md</code> in your project:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gh"># Presentation Guidelines
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">-</span> Use <span class="gs">**strong text**</span> for key terms
</span></span><span class="line"><span class="cl"><span class="k">-</span> Apply <span class="sb">`code-heavy`</span> layout when code exceeds 15 lines
</span></span><span class="line"><span class="cl"><span class="k">-</span> Keep bullet points to max 5 per slide
</span></span><span class="line"><span class="cl"><span class="k">-</span> Cite sources with GitHub issue numbers
</span></span><span class="line"><span class="cl"><span class="k">-</span> Use <span class="sb">`tmp/`</span> for temporary analysis files
</span></span></code></pre></div><p>Claude reads this and maintains consistency automatically.</p>
<h3 id="3-version-control-everything">3. Version Control Everything</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add slidev/
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;Add MCP integration slides with honest limitations&#34;</span>
</span></span></code></pre></div><p>Because everything is Markdown and text files, Git diffs are meaningful.</p>
<h3 id="4-iterative-refinement">4. Iterative Refinement</h3>
<p>Don&rsquo;t expect perfection immediately. My workflow:</p>
<ol>
<li><strong>Draft</strong>: &ldquo;Create outline for Docker basics&rdquo;</li>
<li><strong>Refine</strong>: &ldquo;Add error handling examples&rdquo;</li>
<li><strong>Style</strong>: &ldquo;Apply proper layouts&rdquo;</li>
<li><strong>Review</strong>: &ldquo;Check for technical accuracy&rdquo;</li>
<li><strong>Polish</strong>: &ldquo;Fix spacing and formatting&rdquo;</li>
</ol>
<p>Claude handles each step reliably.</p>
<h2 id="getting-started">Getting Started</h2>
<h3 id="step-1-set-up-slidev-with-docker">Step 1: Set Up Slidev with Docker</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir my-presentation
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> my-presentation
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">cat &gt; docker-compose.yml <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="cl"><span class="s">services:
</span></span></span><span class="line"><span class="cl"><span class="s">  slidev:
</span></span></span><span class="line"><span class="cl"><span class="s">    image: tangramor/slidev:latest
</span></span></span><span class="line"><span class="cl"><span class="s">    ports:
</span></span></span><span class="line"><span class="cl"><span class="s">      - &#34;3030:3030&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    volumes:
</span></span></span><span class="line"><span class="cl"><span class="s">      - ./slidev:/slidev
</span></span></span><span class="line"><span class="cl"><span class="s">    command: slidev slides.md --host 0.0.0.0
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mkdir slidev
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> slidev
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">cat &gt; slides.md <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="cl"><span class="s">---
</span></span></span><span class="line"><span class="cl"><span class="s">theme: default
</span></span></span><span class="line"><span class="cl"><span class="s">colorSchema: dark
</span></span></span><span class="line"><span class="cl"><span class="s">---
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s"># My First Slidev Presentation
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Built with Claude Code
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">---
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s"># Slide 2
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">- Point 1
</span></span></span><span class="line"><span class="cl"><span class="s">- Point 2
</span></span></span><span class="line"><span class="cl"><span class="s">- Point 3
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span></code></pre></div><h3 id="step-2-start-presenting">Step 2: Start Presenting</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose up -d
</span></span></code></pre></div><p>Open <code>http://localhost:3030</code> in your browser.</p>
<h3 id="step-3-use-claude-code">Step 3: Use Claude Code</h3>
<p>Create <code>CLAUDE.md</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="gh"># Presentation Project
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">-</span> Theme: Technical workshop
</span></span><span class="line"><span class="cl"><span class="k">-</span> Style: Minimal, code-focused
</span></span><span class="line"><span class="cl"><span class="k">-</span> Custom layouts in <span class="sb">`slidev/layouts/`</span>
</span></span><span class="line"><span class="cl"><span class="k">-</span> Use dark theme consistently
</span></span></code></pre></div><p>Then in Claude Code:</p>
<pre tabindex="0"><code>Add 5 slides about Docker basics with practical examples
</code></pre><p>Claude generates content, applies layouts, maintains your style.</p>
<h2 id="advanced-tips">Advanced Tips</h2>
<h3 id="environment-variables">Environment Variables</h3>
<p>For workshop dates:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># .env</span>
</span></span><span class="line"><span class="cl"><span class="nv">WORKSHOP_DATE</span><span class="o">=</span>October 20, <span class="m">2025</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Inject into slides</span>
</span></span><span class="line"><span class="cl">./scripts/inject-dates.sh
</span></span></code></pre></div><p>Claude can generate these scripts too.</p>
<h3 id="pdf-export">PDF Export</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker <span class="nb">exec</span> -it slidev npx slidev <span class="nb">export</span> slides.md
</span></span></code></pre></div><h3 id="custom-themes">Custom Themes</h3>
<p>Slidev supports full theming. I use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* slidev/style.css */</span>
</span></span><span class="line"><span class="cl"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nv">--slidev-theme-primary</span><span class="p">:</span> <span class="mh">#00d9ff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nv">--slidev-theme-accents</span><span class="p">:</span> <span class="mh">#6366f1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">strong</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">slidev</span><span class="o">-</span><span class="n">theme</span><span class="o">-</span><span class="n">primary</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="why-this-matters">Why This Matters</h2>
<p>Traditional presentation tools (PowerPoint, Keynote, Google Slides) are:</p>
<ul>
<li><strong>Not version controllable</strong>: Binary formats or messy XML</li>
<li><strong>Manual labor intensive</strong>: Every style change requires clicking through slides</li>
<li><strong>Not developer-friendly</strong>: Code blocks look terrible</li>
</ul>
<p>Slidev + Claude Code gives you:</p>
<ul>
<li><strong>Git-friendly</strong>: Every change is trackable</li>
<li><strong>Automated consistency</strong>: Claude applies changes globally</li>
<li><strong>Developer experience</strong>: Write slides like you write code</li>
<li><strong>AI-assisted content</strong>: Focus on structure, Claude handles details</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>After building 100+ slides this way, I can&rsquo;t go back. The combination of:</p>
<ul>
<li>Slidev&rsquo;s developer-first approach</li>
<li>Docker&rsquo;s reproducibility</li>
<li>Claude Code&rsquo;s intelligence</li>
</ul>
<p>&hellip;creates a presentation workflow that&rsquo;s faster, more maintainable, and more enjoyable than traditional tools.</p>
<p>If you&rsquo;re a developer creating technical presentations, this workflow will change how you work.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://sli.dev">Slidev Documentation</a></li>
<li><a href="https://docs.claude.com/en/docs/claude-code">Claude Code Documentation</a></li>
<li><a href="https://hub.docker.com/r/tangramor/slidev">Slidev Docker Image</a></li>
<li><a href="https://github.com/yourusername/openwebui-workshop">My Workshop Example</a> (adapt link)</li>
</ul>
<hr>
<p><em>Written with Claude Code, naturally.</em></p>
]]></content></item><item><title>Configuring Claude Code Playwright MCP on NixOS</title><link>https://chili-it.de/posts/claude-code-playwright-nixos/</link><pubDate>Wed, 15 Oct 2025 00:00:00 +0000</pubDate><guid>https://chili-it.de/posts/claude-code-playwright-nixos/</guid><description>&lt;figure&gt;&lt;img src="https://chili-it.de/images/claude-code-playwright-nixos.webp"
alt="A charcoal sketch of three interlocking puzzle pieces — a NixOS snowflake, a browser window with play button, and a terminal cursor — clicking together with energy sparks at their connection points"&gt;
&lt;/figure&gt;
&lt;p&gt;Getting Claude Code&amp;rsquo;s Playwright MCP (Model Context Protocol) to work on NixOS presents several unique challenges. This guide documents a complete declarative solution using home-manager.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;NixOS&amp;rsquo;s unique architecture creates several issues for Playwright MCP:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Browser Installation Issues&lt;/strong&gt;: Standard &lt;code&gt;npx playwright install&lt;/code&gt; doesn&amp;rsquo;t work with NixOS&amp;rsquo;s non-FHS layout&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read-only Nix Store&lt;/strong&gt;: Playwright tries to create user data directories inside &lt;code&gt;/nix/store&lt;/code&gt;, which is read-only&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser Detection&lt;/strong&gt;: Even with &lt;code&gt;PLAYWRIGHT_BROWSERS_PATH&lt;/code&gt; set, the MCP server doesn&amp;rsquo;t recognize browsers as &amp;ldquo;installed&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-persistent Configuration&lt;/strong&gt;: Manual changes to &lt;code&gt;~/.claude.json&lt;/code&gt; get overwritten on system updates&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Typical errors you&amp;rsquo;ll encounter:&lt;/p&gt;</description><content type="html"><![CDATA[<figure><img src="/images/claude-code-playwright-nixos.webp"
			alt="A charcoal sketch of three interlocking puzzle pieces — a NixOS snowflake, a browser window with play button, and a terminal cursor — clicking together with energy sparks at their connection points">
</figure>

<p>Getting Claude Code&rsquo;s Playwright MCP (Model Context Protocol) to work on NixOS presents several unique challenges. This guide documents a complete declarative solution using home-manager.</p>
<h2 id="the-problem">The Problem</h2>
<p>NixOS&rsquo;s unique architecture creates several issues for Playwright MCP:</p>
<ol>
<li><strong>Browser Installation Issues</strong>: Standard <code>npx playwright install</code> doesn&rsquo;t work with NixOS&rsquo;s non-FHS layout</li>
<li><strong>Read-only Nix Store</strong>: Playwright tries to create user data directories inside <code>/nix/store</code>, which is read-only</li>
<li><strong>Browser Detection</strong>: Even with <code>PLAYWRIGHT_BROWSERS_PATH</code> set, the MCP server doesn&rsquo;t recognize browsers as &ldquo;installed&rdquo;</li>
<li><strong>Non-persistent Configuration</strong>: Manual changes to <code>~/.claude.json</code> get overwritten on system updates</li>
</ol>
<p>Typical errors you&rsquo;ll encounter:</p>
<pre tabindex="0"><code>Error: Browser specified in your config is not installed. Either install it (likely) or change the config.
</code></pre><p>Or:</p>
<pre tabindex="0"><code>ENOENT: no such file or directory, mkdir &#39;/nix/store/.../playwright-browsers/mcp-chrome-...&#39;
</code></pre><h2 id="the-solution">The Solution</h2>
<p>The solution involves:</p>
<ol>
<li>Using <code>playwright-web-flake</code> to provide browsers in the Nix store</li>
<li>Creating a home-manager module that declaratively manages Claude Code&rsquo;s MCP configuration</li>
<li>Using an activation script to merge configuration into <code>~/.claude.json</code> on every rebuild</li>
<li>Specifying the exact executable path to bypass browser detection issues</li>
<li>Setting a writable profile directory for browser user data</li>
</ol>
<h2 id="implementation">Implementation</h2>
<h3 id="step-1-add-playwright-web-flake-to-your-flake-inputs">Step 1: Add playwright-web-flake to your flake inputs</h3>
<p>In your <code>flake.nix</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">inputs</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">nixpkgs</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:nixos/nixpkgs/nixos-unstable&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">home-manager</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:nix-community/home-manager&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">home-manager</span><span class="o">.</span><span class="n">inputs</span><span class="o">.</span><span class="n">nixpkgs</span><span class="o">.</span><span class="n">follows</span> <span class="o">=</span> <span class="s2">&#34;nixpkgs&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Add Playwright browser flake</span>
</span></span><span class="line"><span class="cl">    <span class="n">playwright-web-flake</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:pietdevries94/playwright-web-flake&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="n">home-manager</span><span class="o">,</span> <span class="n">playwright-web-flake</span><span class="o">,</span> <span class="o">...</span> <span class="p">}</span><span class="o">@</span><span class="n">inputs</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Your existing configuration...</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="step-2-install-required-packages">Step 2: Install required packages</h3>
<p>In your <code>home-manager/home.nix</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">inputs</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">home</span><span class="o">.</span><span class="n">packages</span> <span class="o">=</span> <span class="k">with</span> <span class="n">pkgs</span><span class="p">;</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="n">claude-code</span>
</span></span><span class="line"><span class="cl">    <span class="n">inputs</span><span class="o">.</span><span class="n">playwright-web-flake</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">system</span><span class="si">}</span><span class="o">.</span><span class="n">playwright-driver</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">home</span><span class="o">.</span><span class="n">sessionVariables</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PLAYWRIGHT_BROWSERS_PATH</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">${</span><span class="n">inputs</span><span class="o">.</span><span class="n">playwright-web-flake</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">system</span><span class="si">}</span><span class="o">.</span><span class="n">playwright-driver</span><span class="o">.</span><span class="n">browsers</span><span class="o">.</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">system</span><span class="si">}}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS</span> <span class="o">=</span> <span class="s2">&#34;true&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD</span> <span class="o">=</span> <span class="s2">&#34;1&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="step-3-create-the-claude-code-configuration-module">Step 3: Create the Claude Code configuration module</h3>
<p>Create <code>home-manager/programs/claude-code.nix</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">inputs</span><span class="o">,</span> <span class="n">lib</span><span class="o">,</span> <span class="n">config</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">let</span>
</span></span><span class="line"><span class="cl">  <span class="n">playwrightDriver</span> <span class="o">=</span> <span class="n">inputs</span><span class="o">.</span><span class="n">playwright-web-flake</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">system</span><span class="si">}</span><span class="o">.</span><span class="n">playwright-driver</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">playwrightBrowsersPath</span> <span class="o">=</span> <span class="nb">builtins</span><span class="o">.</span><span class="n">unsafeDiscardStringContext</span> <span class="s2">&#34;</span><span class="si">${</span><span class="n">playwrightDriver</span><span class="o">.</span><span class="n">browsers</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">playwrightMcpConfig</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">playwright</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">type</span> <span class="o">=</span> <span class="s2">&#34;stdio&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">command</span> <span class="o">=</span> <span class="s2">&#34;npx&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">args</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;@playwright/mcp@latest&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;--browser&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;chromium&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;--executable-path&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;$PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">PLAYWRIGHT_BROWSERS_PATH</span> <span class="o">=</span> <span class="s2">&#34;$PLAYWRIGHT_BROWSERS_PATH&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS</span> <span class="o">=</span> <span class="s2">&#34;true&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD</span> <span class="o">=</span> <span class="s2">&#34;1&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">PWMCP_PROFILES_DIR_FOR_TEST</span> <span class="o">=</span> <span class="s2">&#34;$HOME/.local/share/playwright-mcp/profiles&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH</span> <span class="o">=</span> <span class="s2">&#34;$PLAYWRIGHT_BROWSERS_PATH/chromium-1181/chrome-linux/chrome&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">mcpConfigJson</span> <span class="o">=</span> <span class="nb">builtins</span><span class="o">.</span><span class="n">toJSON</span> <span class="n">playwrightMcpConfig</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">in</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">home</span><span class="o">.</span><span class="n">activation</span><span class="o">.</span><span class="n">configureClaude</span> <span class="o">=</span> <span class="n">lib</span><span class="o">.</span><span class="n">hm</span><span class="o">.</span><span class="n">dag</span><span class="o">.</span><span class="n">entryAfter</span> <span class="p">[</span><span class="s2">&#34;writeBoundary&#34;</span><span class="p">]</span> <span class="s1">&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">    CLAUDE_CONFIG=&#34;$HOME/.claude.json&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">    if [ ! -f &#34;$CLAUDE_CONFIG&#34; ]; then
</span></span></span><span class="line"><span class="cl"><span class="s1">      echo &#39;{}&#39; &gt; &#34;$CLAUDE_CONFIG&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">    fi
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">    if [ -f &#34;$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh&#34; ]; then
</span></span></span><span class="line"><span class="cl"><span class="s1">      source &#34;$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">    fi
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">    MCP_CONFIG=$(echo &#39;</span><span class="si">${</span><span class="n">mcpConfigJson</span><span class="si">}</span><span class="s1">&#39; | </span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">gnused</span><span class="si">}</span><span class="s1">/bin/sed \
</span></span></span><span class="line"><span class="cl"><span class="s1">      -e &#34;s|\$PLAYWRIGHT_BROWSERS_PATH|$PLAYWRIGHT_BROWSERS_PATH|g&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s1">      -e &#34;s|\$HOME|$HOME|g&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s1">      -e &#34;s|\$PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH|$PLAYWRIGHT_BROWSERS_PATH/chromium-1181/chrome-linux/chrome|g&#34;)
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">    </span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">jq</span><span class="si">}</span><span class="s1">/bin/jq --argjson mcpServers &#34;$MCP_CONFIG&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s1">      &#39;.mcpServers = $mcpServers&#39; \
</span></span></span><span class="line"><span class="cl"><span class="s1">      &#34;$CLAUDE_CONFIG&#34; &gt; &#34;$CLAUDE_CONFIG.tmp&#34; &amp;&amp; \
</span></span></span><span class="line"><span class="cl"><span class="s1">    mv &#34;$CLAUDE_CONFIG.tmp&#34; &#34;$CLAUDE_CONFIG&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">    $VERBOSE_ECHO &#34;Claude Code Playwright MCP configuration updated&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="step-4-import-the-module">Step 4: Import the module</h3>
<p>In your <code>home-manager/home.nix</code>, add to the imports list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">imports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="sr">./programs/claude-code.nix</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="step-5-apply-the-configuration">Step 5: Apply the configuration</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">home-manager switch --flake .#your-user@your-hostname
</span></span></code></pre></div><p>Restart Claude Code completely to pick up the new MCP configuration.</p>
<h2 id="how-it-works">How It Works</h2>
<ol>
<li><strong>Declarative Configuration</strong>: MCP server settings are defined in Nix and automatically applied on every <code>home-manager switch</code></li>
<li><strong>Dynamic Path Resolution</strong>: The activation script sources environment variables and expands them at runtime</li>
<li><strong>Non-destructive Updates</strong>: Uses <code>jq</code> to merge only the <code>mcpServers</code> section, preserving all other Claude Code settings</li>
<li><strong>Executable Path Override</strong>: <code>--executable-path</code> bypasses Playwright&rsquo;s browser detection</li>
<li><strong>Writable Profile Directory</strong>: <code>PWMCP_PROFILES_DIR_FOR_TEST</code> redirects browser profiles to a writable location</li>
</ol>
<h2 id="verification">Verification</h2>
<p>After configuration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Check MCP server status</span>
</span></span><span class="line"><span class="cl">claude mcp list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check configuration</span>
</span></span><span class="line"><span class="cl">cat ~/.claude.json <span class="p">|</span> jq <span class="s1">&#39;.mcpServers.playwright&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check environment variables</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">$PLAYWRIGHT_BROWSERS_PATH</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Verify browser exists</span>
</span></span><span class="line"><span class="cl">ls -la <span class="nv">$PLAYWRIGHT_BROWSERS_PATH</span>/chromium-1181/chrome-linux/chrome
</span></span></code></pre></div><p>Test in Claude Code:</p>
<pre tabindex="0"><code>Navigate to example.com and tell me what you see
</code></pre><h2 id="key-insights">Key Insights</h2>
<h3 id="why---executable-path-is-required">Why <code>--executable-path</code> is Required</h3>
<p>Setting <code>PLAYWRIGHT_BROWSERS_PATH</code> alone isn&rsquo;t enough because Playwright MCP uses its own browser detection mechanism. By providing <code>--executable-path</code>, we bypass this check entirely and tell Playwright exactly where the browser binary is.</p>
<h3 id="why-the-activation-script-sources-environment-variables">Why the Activation Script Sources Environment Variables</h3>
<p>The activation script runs during <code>home-manager switch</code>, before the new environment variables are fully exported to the shell. By explicitly sourcing <code>hm-session-vars.sh</code>, we ensure <code>PLAYWRIGHT_BROWSERS_PATH</code> is available for path expansion.</p>
<h3 id="the-nixos-enoenteacces-issue">The NixOS ENOENT/EACCES Issue</h3>
<p>Playwright MCP tries to create user data directories inside the browsers directory by default. On NixOS, this fails because <code>/nix/store</code> is read-only. The workaround is setting <code>PWMCP_PROFILES_DIR_FOR_TEST</code> to point to a writable location.</p>
<h2 id="benefits">Benefits</h2>
<ul>
<li>✅ <strong>Survives System Updates</strong>: Configuration is declarative and automatically reapplied</li>
<li>✅ <strong>No Manual Browser Installation</strong>: Browsers are provided by Nix</li>
<li>✅ <strong>Reproducible</strong>: Same configuration works across different NixOS machines</li>
<li>✅ <strong>Version Controlled</strong>: MCP configuration is part of your nix-config repository</li>
<li>✅ <strong>Non-destructive</strong>: Preserves Claude Code&rsquo;s self-managed settings</li>
</ul>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3 id="browser-specified-in-your-config-is-not-installed">&ldquo;Browser specified in your config is not installed&rdquo;</h3>
<ul>
<li>Verify <code>PLAYWRIGHT_BROWSERS_PATH</code> is set: <code>echo $PLAYWRIGHT_BROWSERS_PATH</code></li>
<li>Check the browser exists: <code>ls -la $PLAYWRIGHT_BROWSERS_PATH/chromium-1181/chrome-linux/chrome</code></li>
<li>Ensure you&rsquo;ve restarted Claude Code after applying configuration</li>
<li>Verify the <code>--executable-path</code> argument is in <code>~/.claude.json</code></li>
</ul>
<h3 id="enoent-no-such-file-or-directory-mkdir-nixstore">&ldquo;ENOENT: no such file or directory, mkdir &lsquo;/nix/store/&hellip;&rsquo;&rdquo;</h3>
<ul>
<li>Ensure <code>PWMCP_PROFILES_DIR_FOR_TEST</code> is set in the environment variables</li>
<li>Check that <code>~/.local/share/playwright-mcp/profiles</code> directory exists and is writable</li>
</ul>
<h3 id="browser-version-mismatch">Browser version mismatch</h3>
<p>The hardcoded <code>chromium-1181</code> in the executable path may need updating when Playwright versions change:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ls -la <span class="nv">$PLAYWRIGHT_BROWSERS_PATH</span>/ <span class="p">|</span> grep chromium
</span></span></code></pre></div><p>Update the version number in <code>claude-code.nix</code> accordingly.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://github.com/NixOS/nixpkgs/issues/443704">NixOS nixpkgs Issue #443704</a></li>
<li><a href="https://github.com/microsoft/playwright-mcp/issues/105">Playwright MCP Issue #105</a></li>
<li><a href="https://github.com/pietdevries94/playwright-web-flake">playwright-web-flake</a></li>
<li><a href="https://docs.claude.com/en/docs/claude-code">Claude Code Documentation</a></li>
</ul>
<hr>
<p><em>Tested with: Claude Code, Playwright MCP 0.0.42, NixOS unstable</em></p>
]]></content></item></channel></rss>