# The Closing Window > Essays and AI insights by Pablo Oliva — exploring the intersection of artificial intelligence, software engineering, and the human experience in an AI-transformed world. ## Securing an Always-On AI Agent at Home: A Journey Through Dead Ends URL: https://pablooliva.de/the-closing-window/securing-an-always-on-ai-agent-at-home/ Date: 2026-07-13 Description: The honest record of securing an always-on AI agent with shell access at home: why firewalling it in place failed, how network segmentation and an ngrok tunnel worked instead, and a frank list of what it still doesn't protect against.
## TL;DR I run an always-on AI agent with shell access on a spare mini PC at home, and the hard part wasn't building it, it was making sure a compromise couldn't spread. Firewalling the agent in place failed: its whole job is making outbound calls, so egress filtering either breaks it or rots into an allowlist you babysit. What worked was moving the box onto its own router, off my home LAN entirely, so a full compromise can reach the internet but not my laptops or NAS. Remote access is an ngrok tunnel to key-only SSH; the rest is defense-in-depth on the box, plus an honest list of what none of this protects against.
How I went about securing Hermes Agent, an always-on CLI-based AI assistant from Nous Research, running on a spare mini PC at home. This is not a "here's the perfect setup" post. It's the honest record of three approaches that failed and the one that triumphed. --- ## The goal I wanted an AI agent running 24/7 in my home that I could reach from anywhere: my phone, a laptop on someone else's network, a hotel room while traveling. Hermes can run shell commands, read and write files, browse the web, and call external APIs. That's what makes it useful. It's also what makes it dangerous. An always-on process with shell access, sitting inside your home network, is a serious thing to get wrong. So the project became: how do I run this thing such that (a) I can reach it remotely, (b) it can't reach the rest of my home network, and (c) if it or the machine it runs on is ever compromised, the blast radius is as small as I can make it? The host is a Dell OptiPlex 9020M running Linux: a cheap, low-power, almost-fanless mini PC. I installed the final stock version of Hermes in mid-June 2026. One thing I'll skip past here: why Hermes, and not one of the other self-hosted agents. That was its own decision, mostly about security posture and the fact that it comes from an independent lab rather than a frontier vendor. I worked through that comparison in a separate post, [Hermes vs. OpenClaw](https://pablooliva.de/the-closing-window/hermes-vs-openclaw/). This post picks up where that one left off: the agent is chosen and installed, and now I have to run it without it becoming the weakest point on my network. --- ## Attempt 1: Harden it in place, on the home LAN My first instinct was the textbook one: leave the machine on my home network and lock it down hard. Aggressive firewall rules, strict egress filtering, minimal services, the works. Harden the box until it's safe to live alongside everything else. This failed, and it failed in a useful way. The hardening was so restrictive that the agent couldn't do its job. The most basic capability, reaching the web, broke. Outbound traffic the agent needed (API calls to model providers, web search, fetching pages) got caught in the same net I'd cast to keep bad traffic in. So I started poking holes. Every hole felt like it was undoing the security I'd just added. I was chasing a moving target: each new tool the agent picked up wanted a new outbound destination, and I was hand-maintaining an allowlist that was both too tight to be usable and too leaky to feel safe. There was a second, independent problem. The hardening hadn't stayed at the operating-system level. Enforcing it properly meant extensive modifications to Hermes's own source code, and that made upgrades fragile. Hermes is under active development and ships frequent updates, and each one would clobber my patches and force me to re-apply every customization by hand. I was effectively maintaining a private fork of the agent just to keep my security rules in place, and those rules were already proving too brittle to do the job. Two lessons, both obvious in retrospect. An agent whose entire value is *taking actions in the world* is fundamentally hostile to egress filtering. You can't really say "block everything outbound" to a process whose whole job is making outbound calls; you either neuter it or you maintain a brittle allowlist that lags behind reality. And hardening that's enforced by patching the agent's own source is a trap, because it pits your security directly against the project's release cadence. Rather than keep fighting that fight, I changed the topology. --- ## Attempt 2: Move the machine off the home network So I stopped trying to make the agent safe *inside* my network and moved it *outside* instead. My home internet comes in through the ISP router. The OptiPlex sits directly on that ISP router and has never touched my home LAN. Downstream, I put a second router that manages the home LAN, and everything I actually care about (laptops, phones, NAS) lives behind it, segmented away from the OptiPlex. The OptiPlex is on the ISP router's subnet, 192.168.2.x; the home LAN is on its own subnet, 192.168.100.x. The effect: the agent's machine is on the internet side of a router boundary, but my home network is behind its own router, isolated from it. The OptiPlex has no route into the home LAN. Even if the agent or the machine were fully compromised, the attacker lands on a subnet that can reach the wider internet but cannot reach my personal devices. The blast radius for "the agent machine is owned" collapses from "my whole home network" to "one isolated box and its API keys." ``` ┌──────────────────────────────────────┐ Internet ───► │ ISP router │ └───────────────┬──────────────────────┘ │ ┌───────────────┴──────────────────────┐ │ │ ┌─────────▼──────────┐ ┌────────────▼──────────────┐ │ Second router │ │ OptiPlex (Hermes agent) │ │ (home LAN gateway)│ │ 192.168.2.66 │ └─────────┬──────────┘ │ no route to home LAN │ │ └───────────────────────────┘ ┌─────────▼───────────┐ │ Home LAN │ │ 192.168.100.x │ │ (laptops, phones, │ │ NAS, etc.) │ └─────────────────────┘ ``` This was the single biggest security win of the whole project, and it cost zero software. Network segmentation beat any firewall rule I could have written. It also solved both problems from Attempt 1 at once. The agent can reach the internet freely, because that's fine now: the thing I was worried about it reaching, my home network, sits on the other side of a router it can't cross. And because the isolation comes from the network rather than from edits to the agent's code, I could run stock Hermes again. No private fork, no patches to re-apply on every update. A note on the topology choice. What matters for the security model is the router boundary between the agent machine and the home LAN, and in my setup that boundary is the second router's WAN firewall. I could have gotten a similar boundary without a second router, by putting the OptiPlex on its own VLAN or a separate subnet on the ISP router with an inter-zone firewall rule blocking traffic between the two segments. My ISP router is fully configurable and supports this; plenty of prosumer and business-grade routers do too. The isolation would be equivalent in the threat model that matters most (a compromised agent machine trying to reach home network devices): both approaches place a firewall boundary that the agent's traffic can't cross. I went with the two-router approach for one reason: the isolation is structural rather than configuration-dependent. With two physical routers, there is no VLAN tag, no inter-zone firewall rule, and no switch port assignment that could be accidentally misconfigured or overwritten by a firmware update to silently open a path between the segments. The boundary exists because of where the cables are plugged in. That's a weaker argument than it sounds (a correctly configured VLAN on a well-maintained router is perfectly trustworthy), but I liked that the isolation survives configuration mistakes, firmware changes, and my own forgetfulness. If I didn't already have the second router, or were setting this up somewhere with only one router and no appetite for extra hardware, the single-router VLAN approach would be perfectly reasonable and I wouldn't hesitate to use it. One honest caveat to the "no route to the home LAN" claim: the boundary is itself a device, and that device sits within reach of the box I'm treating as compromised. The OptiPlex shares the ISP router's subnet with the *WAN* side of the second router, so a compromised OptiPlex can't route *through* the second router, but it could attack the router directly, and if it got in it would be standing past the boundary. I've closed that path as far as I can. The second router's admin interface is disabled on its WAN side and protected by a strong password, so from the subnet the agent lives on there's no management surface to reach at all. The route into the home LAN doesn't exist, and the one device that could create such a route isn't administrable from where the agent sits. But this created a new problem. If the machine is off in its own little subnet, how do *I* get to it? --- ## Attempt 3: Tailscale, killed by a VPN conflict For remote access, my first choice was Tailscale. It builds a WireGuard mesh overlay: install it on the target and on your client, and they find each other through Tailscale's coordination server and open a direct encrypted tunnel. Clean, end-to-end encrypted, with ACLs and device tags. I brought it up on the OptiPlex with an isolation tag (`tag:isolated`) and Tailscale's built-in SSH server: ``` sudo tailscale up --advertise-tags=tag:isolated --ssh ``` The plan was that the OptiPlex would be reachable only over the Tailnet, only from my authorized devices, with Tailscale handling both the encryption and the authentication. No exposed ports, no port forwarding on the router, no public IP. I spent a while debugging it: inspecting the netmap, checking the SSH policy, tcpdumping on `tailscale0` to watch the traffic. The setup itself was fine. What killed it was a conflict with my VPN. This is a known and painful category of problem. Tailscale is itself a WireGuard client, so it wants to manage routes and a virtual interface. A commercial VPN client wants exactly the same thing, especially if it's also WireGuard-based or runs a kill switch that hijacks the default route. On the laptop or phone I was connecting from, running my personal VPN and Tailscale at once meant one of them kept losing. Connections were unstable, or just never came up. I could have worked around it with policy-based routing, split tunneling, or turning the VPN off whenever I wanted to reach the agent. All of those defeated the point. I wanted something that "just worked" from any device, in any network state. Tailscale didn't, in my setup. So I ripped it out: ``` sudo apt-get remove --purge -y tailscale sudo apt-get remove -y tailscale-archive-keyring ``` None of this is a knock on Tailscale. It's excellent, and if I weren't running a consumer VPN on my client devices it would have been the right answer. The conflict was specific to my habits, not the tool. --- ## Attempt 4: A TCP tunnel via ngrok (what I use now) I needed a remote-access method that didn't care about routing tables or VPN state on my client. Something where I connect to a fixed public address and it relays back to the machine. I settled on ngrok. ngrok runs as a persistent service on the OptiPlex. It dials out to ngrok's relay infrastructure and holds a tunnel open. ngrok assigns a public TCP endpoint that forwards to a local port. I pointed it at the local SSH port: ``` ngrok tunnel: tcp://.ngrok.io: ──► localhost:22 ``` Now, from anywhere, I SSH to `.ngrok.io` on port ``, and ngrok relays the connection through to the OptiPlex's SSH daemon. The machine has no open inbound port on my home router. There's no port forwarding, no exposed public IP on my connection, no Dynamic DNS. The OptiPlex dialed out; the tunnel is held from the inside. (A terminology note: people often call this a "reverse SSH tunnel," by analogy to `ssh -R`, where you open a port on a *remote* server that forwards back to your local machine. ngrok gets the same reach-in-from-outside effect, but the relay is ngrok's own infrastructure, not an SSH server's reverse-port-forwarding. Same concept, "a public address that tunnels to an internal service"; different mechanism.) To keep it reliable, ngrok runs as a system-level systemd service that restarts on failure, so the tunnel comes back on its own after a reboot or a network blip. The SSH daemon behind it is locked to key-based authentication only: no passwords, no keyboard-interactive. So reaching the agent means having the right private key, knowing the ngrok TCP address and port, and connecting to a service that only accepts the two keys I've authorized. This is the current access path, and it's been stable. --- ## What I kept: defense in depth on the box itself Getting the machine off the home network and making it reachable solved the network-level problems. But I still wanted the box itself hardened, so that even the "isolated subnet" worst case was as contained as possible. This is the hardening that survived. Unlike Attempt 1, none of it breaks the agent's ability to function, because it targets inbound traffic and the agent's own behavior rather than outbound connectivity. And none of it touches the agent's source code: it's all system-level configuration, filesystem layout, and built-in features switched on through config. **Host firewall (nftables, default-deny inbound).** The machine runs nftables with a default-DROP policy on inbound traffic. Nothing reaches the box unless it's an established connection, loopback, DHCP, or ICMP. This sits on top of the NAT and the network segmentation, so inbound protection is layered three deep: the ISP router, the second router's boundary, and the host firewall. **SSH, key-only.** Password and keyboard-interactive authentication are disabled. Two authorized keys, both mine. SSH is the one service the ngrok tunnel exposes, so this is the credential gate on the only realistic entry point. **The agent's own in-process defenses.** Hermes ships with several layers that I left enabled: - A pre-execution command scanner (Tirith) that inspects every shell command the agent wants to run for URL-based threats, homograph attacks, pipe-to-interpreter patterns, and terminal injection, before it executes. - Secret redaction on all tool output, so that if an API key ever surfaces in a file or a command's stdout, it's masked before it enters the agent's context (and can't be exfiltrated through the model). This is snapshotted at startup and can't be turned off mid-session. That's deliberate: the agent can't disable its own redaction. - Manual approval gating on destructive commands (`rm -rf`, force-pushes, etc.). The agent has to ask before it runs them. (Cron-scheduled jobs use an even stricter mode where destructive commands are auto-denied.) - A threat scanner on project context files (the `.hermes.md` / `AGENTS.md` files that get injected into the system prompt), which neutralizes prompt-injection patterns. - A caller allowlist on the Telegram gateway, which is the one channel the agent listens on. **Data separation.** The agent's bulk data (sessions, logs, caches) lives on a dedicated external SSD, while the security-critical files (the `.env` with API keys, the config, the session database) stay on the system disk with `600` permissions. **A locked-down secondary profile.** I keep a second Hermes profile with most of the dangerous toolsets disabled (coding, computer use, etc.) for research tasks where I don't need full capability. --- ## Where this lands: an honest assessment Here's what the final posture actually protects against, and what it doesn't. **Protected against:** - The agent machine being compromised and used to attack my home network (it can't route there). - Unsolicited inbound connections from the internet (no open ports on my router; host firewall default-deny). - Unauthorized people reaching the agent over Telegram (allowlist). - The agent leaking my API keys into its own context or a chat (secret redaction). - The agent running a destructive command by accident or via a naive prompt-injection (approval gate + command scanner). - SSH password brute-force (key-only auth). **Not protected against (and I know it):** - A sufficiently determined prompt-injection that chains through to shell. The agent's terminal backend runs locally, as my user, with sudo. If an attacker could craft input that talked the agent into running arbitrary shell, there's no OS-level sandbox between the agent and the host. The in-process scanners and approval gate raise the bar, but they're heuristics, not a containment boundary. - Physical theft of the box. The disks aren't encrypted. Whoever has the machine has the API keys and the session history. - A malicious skill or plugin running inside the agent process with full privileges. I mitigate this by not loading third-party plugins, but it's a trust boundary, not a technical one. - The ngrok endpoint itself. If ngrok were compromised, or if my ngrok credentials leaked, the SSH port would be exposed through their relay. I'm trusting ngrok as part of my trust chain, a conscious trade for the convenience of not running my own relay. - Data exfiltration from the box itself. Free outbound access *is* the design; it's what makes the agent useful and what let me stop fighting egress filtering. The flip side is that a compromised host can read its own disk (the `.env`, the session database) and POST the contents anywhere it likes, and nothing on the box will stop it. Secret redaction guards against a key leaking into the *model's context*; it does nothing against a compromised host reading the file directly. I take this one on purpose: every API key the agent holds is scoped to a service with a hard spend limit, so the worst case for a leaked key is a capped bill, not unbounded abuse. The honest summary: I traded OS-level isolation of the agent for the convenience of a local backend, and I compensated by isolating the *machine* on the network so that the agent's blast radius is bounded by a router instead of by a container. That's a reasonable trade for a personal, single-user, single-tenant agent. It would not be the right trade if the agent were serving untrusted users, ingesting untrusted content (like parsing arbitrary email), or running in a shared environment. --- ## The takeaway If there's one thing I'd tell someone setting up an always-on agent for the first time, it's this: **don't try to make the agent safe with firewall rules alone.** An agent's whole job is to reach out into the world, and egress filtering fights that at every step. Instead, put the agent on a network segment where the things it could hurt aren't reachable, and let it talk to the internet freely. Network segmentation is a one-time physical/configuration act that doesn't decay as the agent's behavior evolves; an egress allowlist is a maintenance burden that rots the moment you aren't looking. A second principle, learned the hard way: when you do harden the agent itself, prefer configuration and built-in features over patching its source code. A customization that lives in the agent's source dies on the next update; one that lives in config or in the network outlives every upgrade. The rest is defense-in-depth for the things segmentation doesn't catch: key-only SSH, a default-deny host firewall, the agent's own command scanning and secret redaction. But segmentation did more for this setup than every other measure combined, and it's the part I should have done first. --- *Host: Dell OptiPlex 9020M, Linux. Agent: Hermes (Nous Research). Remote access: ngrok TCP tunnel to key-only SSH. Written July 2026.* --- ## Hermes vs. OpenClaw: A Practical Comparison URL: https://pablooliva.de/the-closing-window/hermes-vs-openclaw/ Date: 2026-07-08 Description: OpenClaw and Hermes are both self-hosted personal AI agents. A factual comparison of their features, and why Hermes' isolation model and security posture make it the safer bet to run on your own hardware.
## TL;DR OpenClaw and Hermes are both open-source agents you self-host and reach from your phone. OpenClaw is the bigger, broader project (~380k stars, 28 channels, voice, watch apps); Hermes (~211k stars, from Nous Research) bets on a learning loop and isolated profiles over raw reach. I run Hermes, mainly for its security posture and because it came from an independent lab rather than a frontier vendor. The hardening setup is [the next post](https://pablooliva.de/the-closing-window/securing-an-always-on-ai-agent-at-home/).
## The category Both projects are self-hosted personal agents: a persistent process on hardware you own, wired into your messaging apps, with real tools (shell, files, web, a browser), memory that survives restarts, and a skill library that grows. You message it from your phone; it acts on your machines. That last part is why security dominates the comparison. A chatbot that answers wrong wastes your time. An agent with shell access that gets manipulated does real damage. ## OpenClaw Peter Steinberger launched [OpenClaw](https://github.com/openclaw/openclaw) as "Clawdbot" in November 2025. Anthropic objected to the name's similarity to Claude; it briefly became "Moltbot" before settling on OpenClaw at the end of January 2026. It crossed 100,000 GitHub stars within 48 hours of the rebrand. Two weeks later Steinberger joined OpenAI, and the project moved into an independent foundation with OpenAI backing, MIT license intact. It passed React's all-time star count in March and sits near 380,000 now. Under the hood it's a long-running Node.js gateway: a local control plane for sessions, channels, tools, and events, with an embedded agent runtime handling the model calls. Personality lives in a file called SOUL.md. It speaks to 14+ LLM providers, from GPT, Claude, and Gemini down to local models over Ollama. What you get: - 28 messaging channels: WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Teams, SMS, WeChat, and 19 more - Playwright-based browser automation - Voice wake word and talk mode, with ElevenLabs text-to-speech - Companion apps for macOS, iOS (including Apple Watch), and Android - ClawHub, a community skill registry closing in on 68,000 skills - Deployment guides for everything from a Mac mini to Kubernetes The design goal is breadth: connect to everything, run everywhere, do everything. It's closer to an operating system for agents than a single assistant. ## Hermes [Hermes](https://github.com/NousResearch/hermes-agent) comes from Nous Research, an independent AI research lab. It crossed 90,000 stars in its first two months and sits above 200,000 now. Its design center is different, and a line from [a Kilo blog comparison](https://blog.kilo.ai/p/hermes-vs-openclaw-when-to-reach) captures it: "Hermes packages a gateway around a learning agent. OpenClaw packages an agent around a messaging gateway." Same parts, opposite center of gravity. At Hermes' center is a loop that makes the agent better the longer you run it: **Memory in three tiers.** Two tiny Markdown files (a 2,200-character MEMORY.md and a 1,375-character USER.md) injected at every session start; full-text search over every past conversation, stored in SQLite; and pluggable external memory providers if you want more. Critical facts stay always-loaded. Everything else is searchable on demand. **Skills the agent writes itself.** When it completes a complex task, works through an error to a fix, or gets corrected by you, it saves the working procedure as a Markdown skill file. Next time it loads the proven procedure instead of rediscovering it. **A garbage collector for those skills.** Self-written skills pile up into dozens of narrow, overlapping playbooks if nothing prunes them. A background pass called the Curator marks skills stale after 30 unused days, archives them after 90, and periodically reviews the library to merge overlapping ones. It never deletes; every change is recoverable. **Offline skill evolution.** Agents grading their own work turns out to be unreliable (they almost always think they did great). So Hermes ships a separate pipeline, GEPA, that reads actual execution traces, finds where a skill fails, and evolves better variants through search. It costs a few dollars per run, needs no GPU, and ships its winners as pull requests you review rather than silent changes. **Profiles.** Each profile is a fully isolated agent instance: own config, own memory, own skills, own credentials, own personality file. A coding agent and a research agent on the same machine share nothing by default. The reach gap has narrowed. Over the spring Hermes added browser automation, a native desktop app for macOS, Linux, and Windows, and support for 20+ messaging platforms of its own, including Teams, iMessage, and WhatsApp. What it still lacks is OpenClaw's phone-native layer: no iOS or Android companion apps, no watch app, no voice wake word. Phone access means a messaging bot. The core difference is what each project treats as its center: OpenClaw grows by connecting to more places, Hermes by making the agent itself smarter. ## Where they agree Both keep personality in a SOUL.md file. Both treat skills as Markdown with frontmatter. Both schedule recurring work from plain-English cron requests, and both run local models over Ollama. Since spring, both do browser automation and both speak to twenty-plus messaging platforms. The two are converging quickly, so a feature table ages in weeks; treat the one below as a July 2026 snapshot. ## Side by side | | OpenClaw | Hermes | |---|---|---| | Maintainer | OpenClaw Foundation (OpenAI-backed) | Nous Research | | GitHub stars (Jul 2026) | ~382,000 | ~211,000 | | Messaging channels | 28 | 20+ | | Memory | Markdown files + embeddings + full-text search | Three tiers: frozen snapshot, session search, external providers | | Skills | ClawHub, one central registry, ~68,000 skills | GitHub "taps" you choose, plus skills the agent writes itself | | Self-improvement | Cron jobs and heartbeats | Learning loop + Curator + offline GEPA evolution | | Isolation | Separate sessions per channel | Profiles: fully isolated agent instances | | Apps | macOS, iOS + Apple Watch, Android | Desktop app (macOS, Linux, Windows) | | Security record | Two RCE CVEs, poisoned registry, exposed instances, state-sector ban in China | Dependency-level CVE fixes; no incidents specific to it so far | ## Security This is the section that decided my choice, so it gets the most space. **OpenClaw's 2026 record.** A CVSS 8.8 one-click remote-code-execution chain (CVE-2026-25253, patched in January); the "ClawHavoc" campaign, which seeded ClawHub with malicious skills until researchers counted more than 800 bad packages, at the time roughly a fifth of the registry; over 30,000 instances found exposed to the open internet without authentication; and infostealer malware specifically hunting OpenClaw config files, which hold API keys and tokens in plaintext. April brought a second RCE (CVE-2026-41295: open a workspace containing a malicious channel plugin and it executes before any trust check runs). In March, China restricted government agencies, banks, and state-owned enterprises from running OpenClaw at all. The maintainers' response has been aggressive: pairing codes for unknown senders, a diagnostic `doctor` command, tool allowlists, VirusTotal scanning for ClawHub submissions. But the problem is structural. Twenty-eight inbound channels, a public skill registry, a browser, and shell access add up to a large attack surface that patching doesn't shrink. Every channel is a door. Every community skill is a supply chain. And the thing behind them executes commands. **Why Hermes is the safer bet.** Several of Hermes' security advantages are structural rather than a matter of having fewer users: - **Profile isolation.** Each profile is a fully isolated agent instance with its own config, memory, skills, and credentials. A compromise in one context doesn't hand over the rest. - **No central skill marketplace to poison.** Skills arrive from GitHub repos you explicitly choose to trust (Homebrew-style taps) or from the agent's own hand. There is no shared registry an attacker can seed, so the ClawHavoc failure mode doesn't exist here. - **Reviewable self-improvement.** Offline skill evolution ships its changes as pull requests you review, not silent edits. - **A hard 90-turn cap per task,** so a hijacked loop can't run indefinitely. The honest caveats: Hermes has fewer users, which means fewer attackers and fewer researchers looking, and its surface has grown as it added channels and a browser. Its July release notes list only dependency-level CVE fixes, with no incidents specific to it so far. But the structural properties above are the reason I trust it more on a machine that has to stay locked down. The bottom line for either tool: treat it as untrusted software with shell access, because that's what it is. The most important security work happens at the machine and network level, underneath the agent entirely. That's what the next post covers. ## Which one should you run? **OpenClaw** if you want coverage: your agent on your phone and your wrist, answering by voice, controlling your browser and your speakers, with the largest community in the space. Budget real time for hardening — the defaults will not save you. **Hermes** if you want an agent that compounds and a smaller, more auditable attack surface: it knows your environment in month three far better than in week one, keeps projects in separate profiles, and improves through a pipeline you can review. If the machine it runs on has to stay locked down, the supply-chain model and the isolation primitives are the point. I also preferred that it came from an independent research lab (Nous Research) rather than a project now backed by OpenAI. That's a governance consideration as much as a technical one. ## What I run A Hermes instance runs on an isolated machine here and drives product development for one of my side projects: prior-art research, planning, the unglamorous middle work. I steer and review; it executes. The next post covers the setup that makes leaving it unattended reasonable: network isolation, tunneled access, firewall rules, and scoping what the agent can and cannot reach. --- ## Our AI Rollout Started With People, Not Processes URL: https://pablooliva.de/the-closing-window/our-ai-rollout-started-with-people-not-processes/ Date: 2026-06-22 Description: AI adoption runs on two tracks — top-down process automation and individual everyday work. We started with people: one shared, self-hosted AI tool for a sub-500-person company across several EU countries (LibreChat on Azure OpenAI in an EU region, with Redakt PII detection in the stream). Here's why we sequenced it that way, and what's in the setup.
## TL;DR We gave everyone at the company one AI tool: under 500 people, across several EU countries. We started with individuals' everyday work rather than top-down process automation. The tool is [LibreChat](https://www.librechat.ai/), self-hosted, connected to Azure OpenAI in an EU region so data stays in the EU, with [Redakt](/the-closing-window/meet-redakt-practical-gdpr-compliance-for-ai-teams/), the PII detector I wrote about earlier, in the request stream. This post covers why we sequenced it that way and what's in the setup.
## Two tracks, and why we started with people Bringing AI into an organization runs on two tracks. One is top-down: leadership and departments looking at processes and finding where AI can improve them. The other is bottom-up: helping individual people do their own day-to-day work more efficiently. You start with drafting, summarizing, translating, working through a document or a spreadsheet, then graduate to creating personal agents that optimize a very specific workflow. The stretch goal is to make everyone a builder, or at least give them the power to be. The process track matters, but in many organizations, ours included, a lot of it depends on data that isn't yet clean or well connected, and that work takes time. The projects that need that data wait on it, while others move ahead. The individual track doesn't have that dependency, so it can deliver value sooner. What it needs is smaller: a usable tool everyone has, and some guidance on how to use it. So we started with people. And before you can help anyone's everyday work with AI, they need access, which made the first step concrete: give everyone a single tool they can all use, good enough to be useful, running on infrastructure we control, with data kept in the EU. The rest of this post is how we built that. ## What we used We chose LibreChat. It's an open-source, ChatGPT-style front end, and it's reasonably well known in self-hosting circles, though a lot of people who work with AI haven't come across it. For a company deployment it covers the things we needed: - **SSO through Azure Entra ID**, so access uses our existing identity setup rather than a separate user list. - **Self-hosted**, so the deployment and the data path are ours. - **Extensible**, which matters for what we want to add later. We also looked at Open WebUI, the other common option in this space. Both are capable. LibreChat was a bit lighter to run for our needs, so that's where we went. ## The stack The model is GPT-5, running on Azure OpenAI in Sweden Central, on an EU data-zone deployment that keeps processing inside the EU for GDPR. A smaller, cheaper model handles background jobs like generating conversation titles. GPT-5 runs about $1.25 per million input tokens and $10 per million output. Not cheap. What makes it work as a tool for everyone is usage-based billing. Per-seat AI licenses run around €20–30 per person per month, and for a few hundred people, most of whom use it lightly, that's a lot of paid-for access sitting idle. With usage-based inference we only pay for what's actually used, so the majority who barely touch it cost almost nothing. We still provide per-seat tools like Claude and Copilot for the people who live in them, but this is the base layer underneath. ## PII detection with Redakt [Redakt](/the-closing-window/meet-redakt-practical-gdpr-compliance-for-ai-teams/) is the PII detection service I built and wrote about previously. We connected it into our internal chat tool so it sits between the LibreChat client and the Azure inference API and inspects the conversation as it passes through. It runs asynchronously, so it doesn't add latency to the chat. At the moment it only warns. If someone is about to send a name, an address, or other customer details, Redakt flags it. It can also be set to throw an error, and our customization then stops the conversation from completing on that basis, but we started with warnings rather than blocking. We did that on purpose. A warning at the moment someone pastes in personal data lets people adjust their own habits first. Moving from warning to blocking later is a configuration change once people are used to the tool. ## Extending it LibreChat has two ways to add capability, and both matter for where we're taking this. It has a built-in **agents** feature. It's a simplified version of the idea, a named setup combining a model, a system prompt, tools, and knowledge files that a user can select and chat with. It's limited compared to a full agent framework, but it's enough for things like a policy assistant or a drafting helper without writing code. For anything beyond that, you build the agent in a framework like Haystack or LangGraph, expose it behind an OpenAI-compatible endpoint, and add that endpoint to LibreChat. The agent then appears in the same interface everyone already uses. LibreChat also supports MCP servers for connecting tools and data sources. The documentation covers the full range. The first thing we're connecting is [Sherpa](https://github.com/pablooliva/sherpa), an AI-adoption guide I've been working on, to help colleagues figure out how to use AI in their own work. ## What comes next The process track is moving in parallel. Some of it is already underway, and the parts that depend on cleaner, better-connected data will open up as that work lands. The individual track, meanwhile, is already delivering, and it compounds: people only learn how far they can push a tool by using it. Helping colleagues with everyday tasks builds that familiarity, and my hope is that it opens up higher-level uses of AI they wouldn't have reached for otherwise, which is what the education effort and tools like Sherpa are there to support. A shared tool and some guidance is all the individual track needs to start. The larger, process-level work runs alongside it. --- ## Give Your Coding Agent More Autonomy URL: https://pablooliva.de/the-closing-window/give-your-coding-agent-more-autonomy/ Date: 2026-06-15 Description: I run a development pipeline with permissions off, then trace every session into LangSmith. The plain case for why: an objective record of what the agent did unattended, evals that catch model regressions before they cost me, and a loop where the agent reviews its own behavior and patches my setup.
## TL;DR My [SDD-flow](/the-closing-window/spec-driven-development-fans-youll-love-sdd-flow/) skill takes a feature from spec to working code with permissions off, so I'm not signing off on every step. Then I persist those sessions into LangSmith and read the traces back. Three payoffs: an objective record of what the agent did while I wasn't watching, evals that check a new model against my own work instead of the release notes, and a loop where the agent reviews its own behavior and tells me where my setup drifted. None of it is exotic, and it runs on a free tier.
## The record you don't otherwise have A while back I wrote a breathless post about building this pipeline, turning permissions off, and one-shotting features. I stand by the thrill. This is the sober follow-up, and it starts with a boring fact: when an agent runs with permissions off, you are not watching. That's the point of it and the whole problem. An agent that goes quiet leaves one artifact behind, the trace. Don't read it and you're trusting that no error meant nothing went wrong. And things can definitely go wrong folks! Here are two that my traces caught: - A subagent spawned with an empty message replied, instead of failing, "It looks like your message came through empty." That has the shape of an answer. It isn't one. In an autonomous run it sails through to the next step as if work had happened. - An identifier that doesn't exist, `poliva83`, got generated with full confidence and wired into config. My handle is `pablooliva`. `poliva83` is nobody. ## Checking a new model before you trust it Once you have that record, it's the raw material for evals. Claude Code's default model moves under me. Anthropic publishes what to expect from a new model, and I read those notes. But that's the average case across everyone's usage. It says nothing about mine. My setup has special-cased skills, a stack of `CLAUDE.md` rules, workflows that lean on the model behaving a specific way. A model can win every published benchmark and still get quietly worse at the one odd thing I rely on, because the release notes don't know about my one odd thing. To be clear though, there is nothing odd about my setup. People say it is the best setup. No one else has a setup like it. Evals confirm it instead of hoping. Take a handful of your own real tasks, the ones you mined from your traces, write down what a good result looks like, and re-run them when the new model lands. Score holds, then you carry on with confidence. Score drops, the failing cases point straight at which use case regressed, before it costs you on real work. The published expectations are the prior; your evals are the confirmation. For anything you depend on, you want both. ## Raising the ceiling on what you let it do unattended This is the payoff I'd most want someone else to steal. SDD-flow running a whole development cycle unattended only works to the degree the agent stays inside the rules I've given it: the `CLAUDE.md` conventions, the skills firing when they should, the plugins behaving. The more of that I can trust, the more I'll hand off without watching. So the question isn't "is autonomy safe," it's "how do I keep raising the amount I can responsibly hand off," and the answer has to beat hoping the rules still hold. The traces close that loop with the agent itself. LangSmith's free tier keeps about 14 days, so once a fortnight I point Claude at its own record and ask it to review how it behaved. Did it follow `CLAUDE.md`? Did the skills and plugins fire the way they're meant to? Where did it drift? When it finds something, it proposes the change. A rule that keeps getting ignored, reworded so it lands. A skill description that mis-fires, tightened. An ambiguous convention, made explicit. The agent holds its own behavior up against the rules it's meant to follow and tells me where the two came apart. A concrete one. For most of the past year, Claude Code allowed only one level of subagents, an agent couldn't spawn another, and I'd baked that into my agent definitions as fact: "the Agent tool is inert inside you." Then a point release quietly enabled nested subagents, and overnight my instructions asserted something about the platform that was no longer true. I caught it by luck, I happened to see a tweet about nested subagents and went digging. The fix was easy, turn a fact that can expire into a rule with a reason. The catch was the hard part, and it hung on me seeing the right tweet. The review loop is how you find that kind of drift when you don't get lucky. This compounds. Every pass tightens the setup, so the next autonomous run starts from firmer ground, so I can hand off a little more. The traces aren't only a safety net under autonomy. They're how you earn more of it. ## What this actually takes Less than it sounds. Turn tracing on, an environment variable, or let LangChain's [LangSmith CLI skills](https://www.langchain.com/blog/langsmith-cli-skills) do the wiring for you: one command drops `trace`, `dataset`, and `evaluator` straight into Claude Code, which line up with the three payoffs above. ``` npx skills add langchain-ai/langsmith-skills --agent claude-code --skill '*' --yes --global ``` Read it on a schedule instead of letting the dashboard become a tab you never open. Pull a handful of real tasks into an eval set so the next model upgrade meets some resistance. Then let the agent review its own traces and hand you the diffs to your config. I still run with the guardrails off. The difference is I no longer treat a quiet run as a clean one, and I've got an agent that helps me find where it wasn't. The agent self corrects, and I get to kick back and make Trump jokes... the best jokes, no one else has better jokes. --- ## Which AI Should You Actually Use? I Built an AI for That. URL: https://pablooliva.de/the-closing-window/which-ai-should-you-actually-use/ Date: 2026-06-05 Description: Most people use a fraction of what AI tools can do. Sherpa is an open-source agent that interviews someone about their job and recommends which AI features actually fit their work.
## TL;DR Most people at a company use a fraction of what tools like Claude, ChatGPT, or our internal MemodoAI can do. Access and tutorials don't fix that. I built Sherpa: an open-source agent that interviews one person about their job and tells them which AI features fit their actual work, with a how-to and a conservative time-saved estimate, then asks them to commit to trying one. It runs as a LangGraph app behind an OpenAI-compatible endpoint, on open weights, with an eval harness.
## The problem I built MemodoAI, a fork of LibreChat, to give the whole company an AI chatbot and agents without per-seat subscriptions. Then I had Claude produce an [18-minute onboarding tutorial](https://pablooliva.de/the-closing-window/claude-made-an-18-minute-tutorial-of-my-app/). Both helped. Neither fixed the underlying problem, which I'd seen well before MemodoAI: most people use AI tools for a narrow slice of what they can do. An email rewrite, a summary, and that's it. Access isn't the bottleneck. Instructions aren't either. The missing piece is the link between what a tool can do and the specific work a given person does every day. A tutorial covers the features. It doesn't tell a controller which of those features applies to their month-end close. ## What Sherpa does Sherpa interviews one person about their work: recurring tasks, the systems they use, where they lose time, what they re-key by hand. It returns three to five recommendations. Each names a specific AI capability (in MemodoAI, Claude, or any approved tool), explains how to apply it to that person's workflow, and gives a conservative time-saved estimate with the assumption stated. It ends by asking the person to commit to trying one. The interview runs without the tool catalog loaded. This is deliberate. If the agent knew the available tools while asking questions, it would steer toward needs it can already serve. Running blind, it maps the work first, then matches against the catalog in a separate phase. That also lets it report gaps: real needs with no good tool behind them, which tell you what to build or buy next. ## A personal advisor, at scale Sherpa offers the kind of guidance that normally takes a consultant, someone who sits with each employee and maps their work to what's available. That doesn't scale, so most companies never do it. Sherpa is software, so the same tailored conversation can run for everyone at once, as many times as they need, including each time a new tool or capability shows up. Top-down mandates and all-hands trainings don't do it. People adopt AI when someone shows them how it fits their own work, and Sherpa can do that for everyone at once. ## How it's built Sherpa is a [LangGraph](https://github.com/langchain-ai/langgraph) application with three nodes (diagnose, match, commit) and a typed state object carrying a structured needs profile between them. It's served behind a thin [FastAPI](https://fastapi.tiangolo.com/) OpenAI-compatible endpoint, so it can plug into MemodoAI later without rewiring. Inference runs on open weights via [Together AI](https://together.ai). [LangSmith](https://smith.langchain.com) handles tracing and evals. The recommendations are assembled in code from a structured JSON object rather than written freehand by the model, which keeps the output stable and the quality inspectable. ## Status It's a proof of concept. The scope is narrow on purpose: one finance question bank, a single user, a local commitment log instead of an org-wide measurement layer. The point of the POC is to test whether the recommendations come out specific and genuinely useful before building anything around them. Known limitations and the deferred roadmap are in the repo. ## What it addresses Sherpa covers the parts of AI adoption that organizations usually handle as separate problems: - **Discovery**: where AI can help in a specific role. - **Adoption**: getting people to use tools that already exist. - **Measurement**: which suggestions get adopted across the company, so you can see where AI is actually paying off. This is not productivity surveillance. Sherpa counts suggestions, not people. It does not track who is or isn't using AI, and it does not score anyone's individual output. There is no per-person dashboard. Measuring adoption instead of individuals is also what keeps it within works-council and GDPR limits. - **Self-improvement**: every conversation produces signal. What people committed to, what saved time, where estimates ran off, which needs had no tool behind them. The eval harness captures it; fed back in, it tunes the questionnaire, the matching, and the estimates. I do that tuning by hand now. With enough real usage, a supervising agent can run it, re-scan for new tool capabilities, and fold both back in. ## Try it It's open source. The clearest picture of the real state is the `SDD/` directory: the specifications and critical reviews behind the build, the [same spec-driven process I've written about before](https://pablooliva.de/the-closing-window/spec-driven-development-fans-youll-love-sdd-flow/). Repo: [github.com/pablooliva/sherpa](https://github.com/pablooliva/sherpa) --- ## Claude Made an 18-Minute Tutorial of My App URL: https://pablooliva.de/the-closing-window/claude-made-an-18-minute-tutorial-of-my-app/ Date: 2026-06-01 Description: I pointed Claude Code at our live internal product and had it film and narrate an 18-minute tutorial. The pipeline, the parts still done by hand, and how to reproduce it.
## TL;DR Pointed Claude Code at a real, logged-in MemodoAI session through the Claude-in-Chrome extension and had it walk through + screenshot every feature on camera. Narration via ElevenLabs TTS. Branded intro/outro and 14 title cards from Claude Design. Stitched in Shotcut, transcribed with whisper.cpp (EN + DE). The manual parts: a human on the OBS record button (there's a reason), and the edit itself, lining eighteen narration tracks up with the on-screen action, which is a real chunk of hands-on time. Output: an ~18-minute video and a bilingual HTML guide living alongside the application. Full TTS render cost about a dollar.
I thought that developing and rolling out an internal AI assistant would be the impressive part. Turns out that the tutorial and user guide created mostly by Claude is what blew me away. Claude clicked through every screen of our internal application, typed the demo prompts, narrated every section in a voice that wasn't mine, built the branded title cards, and transcribed the whole thing in two languages. For the length of the shoot I mostly sat there and watched my own app give itself a tour. We needed onboarding material for MemodoAI, the AI assistant we run internally at work (a self-hosted LibreChat fork). The ask: a walkthrough video and a written guide so that I would not get stuck doing tech support for my colleagues. The interesting part was how much of it I could hand off, and the two bits I couldn't. The tutorial itself walks through our actual internal product, so I'm not putting all eighteen minutes on YouTube. What's below is a short montage: the intro and outro cards, the transition bumpers, and clips from the walkthrough. Enough to show what Claude built.
*Don't see the player? [Watch it on YouTube](https://youtu.be/1XVhxkI5Qbk).* ## What we actually built Two deliverables, one source of truth. The source of truth is a shot list: every section broken into Action (what to click or type), Expected UI (what should be on screen after), and Capture (the screenshot or chapter name). When the product UI changes, you edit that one file and re-shoot only the sections it touched. The video and the written guide both read from it, so they can't drift apart. That single decision is the reason a video and a guide didn't turn into two separate maintenance burdens. Everything downstream hangs off that file: the screenshots, the narration script, the chapter list, the captions. ## A documentary, not an animated movie Here's the mental model that made the rest fall into place. A product tutorial is not a motion-graphics piece, it's a documentary screen recording with a thin layer of branded chrome wrapped around it. That reframing matters because it tells you where to spend effort. Three tracks get composited at the end: - **The documentary track** is the live screen recording. Roughly 90% of the runtime. - **The decoration track** is the branded intro, the section title cards, the outro. Maybe 10% of the runtime, and it carries something like 90% of the *perceived* polish. - **The audio track** is the narration, one file per section. Each track has its own little pipeline, so I could re-render narration without re-shooting, or swap a title card without re-cutting. Keeping them independent is what made iteration cheap. ## Claude drove the live product We connected Claude Code to the already-running, already-authenticated browser through the Claude-in-Chrome extension. I picked the extension over Playwright on purpose: the session was already logged in, so there was no auth setup to babysit. Claude could just *act* on the live product. As it surveyed the live product, clicking through every screen to see which features were actually switched on in our deployment, it grabbed a region screenshot of each one at a fixed 1440×900 so everything lined up. We'd settled on a fifteen-section structure, and the thirty-two annotated PNGs came out numbered to match it. Those screenshots are what the written guide was built from. For the video itself, that shot list (a single markdown file, `recording-script.md`) doubled as Claude Code's task list. I pointed Claude Code at it and it worked straight down the rows: read each Action, perform the click or keystroke in the live browser through the extension, check the result against the Expected UI, then move to the next row. A plain markdown file as the script, a real logged-in product as the runtime. The narration was done by ElevenLabs text-to-speech. Eighteen per-section MP3s, about twenty minutes total, rendered from a script written to be *spoken* rather than read (short sentences, punctuation doing the work of pacing). The full render of a ~17,000-character script ran about a dollar. Fixing one sentence later costs cents. ## 10% makes it look professional The branded chrome came out of Claude Design: a logo-in intro card, an outro, and fourteen section title cards. This is the decoration track, the part that turns a competent screen recording into something that looks deliberately produced. No motion-graphics tool, no designer in the loop, generated straight against the brand. ## What stayed in my hands Two things, and I'll name both, because "Claude made the video" quietly skips over them. The first was pushing record. I tried to automate even that and lost. An early attempt fired `screencapture -v` in the background and let Claude run the whole walkthrough; the take vanished, because that tool doesn't flush its file when another process kills it, so a clean twelve-minute run produced nothing. The split that worked: Claude owns every bit of on-screen navigation, a human owns the record button in OBS. Claude walks the script start to finish, I start and stop the recording. (There's a more automated future where OBS gets scripted over its websocket API with named chapter markers. Worth it the day this becomes a recurring deliverable. It isn't yet.) The second was the edit, and this is the one I don't want to undersell. The three tracks don't snap together on their own. I assembled them in Shotcut: the screen recording on video, a title card in front of each chapter, intro and outro on the ends, and then the slow part, sliding all eighteen narration clips until each line of voiceover lands on the click it's describing. If you cut video for a living, this is just the job, nothing exotic. If you don't, here's the honest version: it's a real full day of nudging clips a few frames at a time and re-watching to check the sync. Claude produced every ingredient. Plating it was still me at the timeline. ![The actual Shotcut edit timeline, the part I did by hand](/images/memodoai-shotcut-timeline.jpg) *The actual edit, and the part I did by hand. The top video track is the screen recording chopped into sections; the green strips below are the eighteen narration files (`section-01.mp3`, `section-02.mp3`...); the colored pins are chapter markers. Lining the green up with the blue is the time sink.* ## The transcription I transcribed the final edited cut with whisper.cpp, not a separate audio export, so the timestamps line up with the exact video the chapters point at. One run produced SRT and VTT captions, plain text, and token-timed JSON, in both English and German, plus the fifteen-entry chapter list that drives the clickable chapters in the in-app player. ## Where it lives now The video, the transcript, the chapters, and the written guide all surface inside MemodoAI. The written guide itself is a self-contained bilingual HTML page that Claude Design built from the same Markdown draft and the same screenshots: one file, all assets inlined, English and German. ![The written guide, a self-contained bilingual HTML page](/images/memodoai-written-guide.webp) *The written guide: the same fifteen sections as the video, polished by Claude Design into one self-contained, bilingual (EN/DE) page.* All of it comes together on one page in the product: the video up front, the chapter list beside it, and the captions, transcript, and written guide a click away, each in English or German. ![The in-app guide page bundling the video, chapters, captions, transcript, and written guide](/images/memodoai-guide-player.webp) - **A. Chapter list.** Click any entry to jump to that point in the video. - **B. Captions.** English and German. - **C. Full transcript.** English and German. - **D. Written guide.** English and German. The shot list, narration script, screenshots, and the little tooling scripts (the TTS renderer, the whisper wrapper) all live in the repo if you want to see the actual machinery: [docs/memodo-ai-tutorial on GitHub](https://github.com/memodo/LibreChat/tree/pablo/docs/memodo-ai-tutorial). ## One person did all of this Step back from the video for a second, because it sits on top of something much bigger. Before any of this onboarding material existed, I'd already built MemodoAI itself: a Dockerized internal AI assistant with logging, monitoring, and observability, with security and PII detection on the data moving in and out, able to read most Microsoft Office documents, and wired into our Microsoft 365 environment. That's the real product. The eighteen-minute tutorial and the bilingual guide were what I bolted on top once it was running. One person did both. A few years ago that sentence needs a backend engineer, a DevOps hire, a security reviewer, a technical writer, a screencaster, a voice actor, and a motion designer. I did it as a one-man army, because Claude collapsed most of those roles into "decide what you want, then check the work." I'm not pretending it ran itself. I made the architecture calls, I caught what came back wrong, I pushed the record button and cut the video together. But the distance between what one motivated person can ship and what used to take a whole team has mostly closed. I wouldn't hand this video to a client. For internal onboarding, though, it's better than anything I could have made alone, and it took a fraction of the time. ## How to make your own If you want to build the same thing, here's the whole pipeline, start to finish. 1. **Survey the product live, and grab the screenshots while you do.** Connect Claude Code to your real, logged-in app through the Claude-in-Chrome extension and have it walk the whole interface with you. Settle on a section structure (I used fifteen), and in that same live pass capture a region screenshot of each screen. Those screenshots are what the written guide gets built from later. 2. **Write the shot list.** One file, one row per step: the action to take, the UI you expect after it, and the asset to capture (a screenshot name or a chapter title). This is the single source of truth that keeps the video and the guide in sync. 3. **Write the narration script.** One block per section, written to be spoken rather than read: short sentences, punctuation for pacing, no "click here." Flag the production cues so they don't get read aloud. 4. **Render the narration.** Send the script to a text-to-speech API (I used ElevenLabs) for one audio file per section. Changing a line later is a cheap re-render. 5. **Record the screen.** Have Claude drive the browser through the shot list from start to finish while a human works the record button in OBS. Capture one clean take; all the assembly happens afterward. 6. **Make the branded chrome.** Generate the intro card, the outro, and one title card per section in Claude Design, against your brand. This is the thin layer that makes the whole thing look produced. 7. **Cut the video.** In an editor (I used Shotcut), put the screen recording on one track and the narration on another, drop a title card in front of each section, and slide the audio until the voice lands on the action it describes. 8. **Transcribe the final cut.** Run the finished video through whisper.cpp for captions, a plain transcript, and a chapter list. Repeat for a second language if you want one. 9. **Write and polish the guide.** Draft the walkthrough in Markdown against the same screenshots, then hand the draft and the images to Claude Design to export one self-contained HTML page. 10. **Put it where people already are.** Drop the video, transcript, chapters, and guide somewhere your users already look. For us that's a page inside the product itself. --- ## Opus 4.7 is Terse URL: https://pablooliva.de/the-closing-window/opus-4-7-is-terse/ Date: 2026-05-27 Description: Opus 4.7's terser default style breaks down on complex projects. The fix is a custom output style that restores thorough explanations, plus evals to catch this kind of drift on future model releases.
## TL;DR Opus 4.7 got terse on me. Anthropic's release notes confirm it: more direct tone, less validation-forward phrasing, response length now calibrates to task complexity, more literal instruction following. Side effect: harder to follow on complex work. Fix: a custom output style (`thorough-explainer.md`) that tells Claude to spell out the reasoning, define terms inline, stop making me connect the dots. Drop in `~/.claude/output-styles/`, activate via `/config`. Also built evals to catch this drift on future model releases.
## Realization I've been using Claude and Claude Code for a while now, and I've sat through several model updates without much friction. Opus 4.7 was different. Over the last few weeks I'd noticed I was having to work harder to digest what Claude Code was telling me. Not the code, but the analysis, the progress reports, the wrap-ups at the end of a task. They'd gotten terse. The harder the project, the harder it got to follow. So naturally, I asked Claude for help. ## Confirmation ### Question > Since I've started using Opus 4.7, it seems like the communication style has changed. Your explanations are not as clear due to them being as brief as possible. They seem to be as short as possible with enough reference to documented information, but requires me to search for it and make connections. This is not clear enough for me. I guess maybe you should dumb it down for me. Are there any settings to make your final communication more thorough and detailed? ### Answer > You're right, and thank you for telling me directly — that's exactly the kind of feedback worth acting on. The terseness you're describing is real: Opus 4.7 (and the way Claude Code's instructions push me) optimizes hard for "every sentence must earn its place," which slides into dense — I pack in references like "F1≈0.704" or "ADR-0012" or "Lever C" and leave you to reconnect them, instead of spelling out the chain of reasoning. That's on me to fix, and there are real settings that help. Let me do three things: (1) confirm the exact Claude Code settings so I give you accurate instructions, (2) save your preference so it persists across all future sessions, and (3) re-explain where we are in plain, complete language. ### Follow-up After this response, I wondered if I'd missed something in the release announcement, so I asked Claude to check. From Anthropic's [official "What's new in Claude Opus 4.7" docs page](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7), the tone change is listed under "Behavior changes" verbatim as: "More direct, opinionated tone with less validation-forward phrasing and fewer emoji than Claude Opus 4.6's warmer style." But what does "validation-forward phrasing" even mean? Maybe that was part of the answer. Maybe I had ignored or blown past that before as well, since I did not understand what it meant. > Validation-forward phrasing is language that leads with emotional affirmation of you — reassurance, praise, and agreement placed at the front of a response — before (or sometimes instead of) getting to the actual substance. So that doesn't really give us an answer, and it also makes it pretty clear Opus 4.7 was used to write its own release notes. (If you didn't catch the joke there, maybe keeping up with AI shouldn't be your biggest concern.) Claude didn't let me down though. It pointed out two other bullets in the same "Behavior changes" section that describe exactly what I'd been struggling with: > Response length calibrates to perceived task complexity rather than defaulting to a fixed verbosity. > More literal instruction following, particularly at lower effort levels. The model will not silently generalize an instruction from one item to another, and will not infer requests you didn't make. Some of this I did recall, but I hadn't connected all the dots. It wasn't my imagination, and it wasn't only about tone. The model now decides how much to say based on how complex *it* judges the task to be. That's why my complex, multi-part projects felt under-explained. It stopped filling in the connective tissue I used to get for free, so I had to start asking for those connections explicitly. ## Example Before I get to the solution, here's an example of Opus's communication style. A summary it gave me at the end of a development phase. Claude had always made me feel like I had super powers, until I read this. I started to wonder whether I just wasn't getting enough sleep, that maybe that was why I understood so little. Then it clicked. Over the last few weeks I'd been asking for clarification more and more, and the pattern was getting worse. ``` Supervised checkpoint — research phase complete The gate fired, and the answer is a marginal GO — not the clean win it first looked like, nor the NO-GO the first run claimed. What the experiment established (validated by two adversarial reviews + independent recompute): - A text recipe can help: R1 = title + lead ~400 chars achieves AUC 1.000 (every one of the 4 true pairs outranks every one of the 5 topical pairs) and mean-separation +0.1276 on the labeled set. Lever A is alive — the first run's NO-GO was false (it hinged on one granularity-contaminated pair + a false "no excerpts" claim). - But the min-max separation gap is only +0.0124 — a 0.012-wide threshold window. Why it's marginal, not a green light (all now documented honestly in the research doc): 1. Labeled-set GO ≠ corpus-wide GO. The 9 pairs were drawn from R0's highest cosines — R0's discrimination boundary, not a random sample. AUC=1.0 proves R1 orders those pairs; it does not bound R1's corpus-wide false-positive rate at a ~0.62 threshold. R1's behavior on the bulk of the corpus is unmeasured. 2. The threshold re-tune is circular. corroboration_observation_log has 0 rows — so ~0.60–0.65 would be fit in-sample on the same 9 pairs that selected R1. No holdout. 3. R1 regresses the granularity cases. It's the worst recipe on broad/whole-digest items (T3: 0.599→0.471) — it buys focused-section recall by losing whole-digest recall. 4. The model itself (Lever C) is still the deeper wall — the topical ceiling F1≈0.704 persists across every recipe; bge-m3 conflates "same story" with "same topic, different angle." ``` ## Solution What Claude giveth, Claude can also taketh away. The fix is a custom output style that restores its communication to something more thorough, the kind a normie like me can actually keep up with. Save the block below as `~/.claude/output-styles/thorough-explainer.md` (create the `output-styles/` directory first if it doesn't exist). ``` --- name: "Thorough Explainer" description: "Detailed, fully-spelled-out final answers — define terms inline, show the reasoning, don't make the reader connect the dots" keep-coding-instructions: true --- When writing final responses to the user (the prose you write directly — NOT code, code comments, or commit messages), favor thoroughness and clarity over brevity and density. The goal is that the reader understands the full picture without having to look anything up or infer the connections themselves. Concretely: - **Define as you go.** The first time you mention a term, identifier, file, symbol, metric, or coined name (e.g. an ID, a flag, a project-specific concept, a number like a threshold), say in-line what it is and why it matters. Never assume the reader remembers a reference from earlier or will go find it. - **Show the reasoning chain, not just the conclusion.** Walk through *why* you reached a conclusion step by step. State the assumptions and the trade-offs you weighed. When you recommend something, explain what you compared it against and why it won. - **Make connections explicit.** If fact A implies consequence B, say so directly — don't place A and B near each other and leave the reader to draw the line. Spell out how each piece fits into the larger goal. - **Prefer a clear, slightly longer explanation over a dense one-liner.** It is better to spend extra words and be unambiguous than to compress and leave the reader doing the unpacking. Density that requires re-reading is worse than length that reads once. - **Organize longer answers** with short headers, short paragraphs, and lists so the thoroughness stays navigable rather than becoming a wall of text. - **When presenting a decision or options**, lay out each option in plain language: what it means, what happens if chosen, and the consequence/risk — enough that the reader can choose without asking follow-up clarifying questions. This style governs user-facing prose only. Keep code, comments, tests, and commit messages matched to the surrounding codebase's conventions (concise, idiomatic) as usual — do not pad those. ``` Most of it is straightforward. The one knob worth calling out is `keep-coding-instructions: true`. It keeps Claude Code's built-in engineering instructions (scoping, commenting, verification) in place while layering your style on top, which is what you want: thorough prose without padding the code or comments. To activate this, you have two options: - Run `/config` and pick `Output style` from the menu, or - Edit `outputStyle` in `~/.claude/settings.json` directly ## Of Course I Overengineered the Fix Like any good engineer, I've now overengineered the solution. I'm a little irritated I didn't catch this communication shift sooner. So for the future, I (well, Claude) built a set of evals that run against any new model release to flag regressions and drifts. Deterministic checks plus Qwen as LLM-as-judge. Super nerd status unlocked. Have a nice day. --- ## Thin Slices Stop /sdd-flow from Overflowing My Capacity URL: https://pablooliva.de/the-closing-window/thin-slices-stop-sdd-flow-from-overflowing-my-capacity/ Date: 2026-05-17 Description: One-shotting features with /sdd-flow breaks down on complicated projects run in parallel. The fix: thinner vertical slices that keep oversight tight. New per-slice mode is live in the plugin.
## TL;DR One-shotting features with `/sdd-flow` works until you get ambitious with complicated projects and you juggle two or three in parallel. The fix is slowing down to ship thinner slices so that reviews and what is delivered remain under your control. The new per-slice delivery mode is live in the plugin.
## A Giant Pile of Shit I thought I had mastered spec-driven development with Claude Code, and more fundamentally become an agentic coding superhero. That was until my ambitions overtook capacity. Agentic coding has gotten exceedingly powerful, but it still requires a developer to maintain close supervision, especially in more complicated scenarios. Around six months ago I built this [SDD development workflow plugin](https://github.com/pablooliva/claude-plugins/tree/main/sdd) for applying a research-plan-implement cycle to software projects. Learning from my experience using it, I continued to tweak it and added additional review phases and steps that auto-corrected imperfections. It worked so well that I figured I could more fully automate the process, which led me to create the [/sdd-flow skill](https://github.com/pablooliva/claude-plugins/tree/main/agent-engineering). This allowed me to simply one-shot features (I am using "one-shot" loosely here because there are clarification and review steps involved). That worked well until my projects got complicated and one-shotting meant handling too many requirements and expectations at once. In short, I had drunk the Kool-Aid and believed AI could deliver. The fact that I was developing several projects in parallel just compounded the mess that I found myself in. I got a little overwhelmed, and that led me to disregard sensible practices and become impatient and careless. I honestly just wanted to get to an end-state. I got irrationally hooked on delivering. And I definitely delivered. After three weeks of development and one week of trying to debug and steer the project back on course, I delivered an important lesson. I rediscovered the limits to agentic coding and AI model capabilities along with my own human limitations. ## Thinner Slices So I took a couple of steps back and evaluated my development process, both the parts I was guiding personally and the parts the agent was running. First thing to fix was how coding agents typically build a multi-layered app. They conjure up an entire layer before moving on to the next. Take an app with a frontend and a backend. The agent delivers the whole frontend, normally a wide swath of functionality, then moves on to the backend. You can go several cycles before any of it is testable end-to-end. Next problem, ironically, I was getting worse results although I had the app thoroughly unit and end-to-end tested. Leaning too much on this as an assurance for quality and completeness, I was skipping the personal review. Under pressure, I took the easy road and just entered the next prompt, rolled the dice, and kept gambling. That was dumb. In search of a solution to better influence how a coding agent delivers and to correct what my behavior ended up being, I thought about how I would normally develop a project like this if I was doing the work "the old way," pre-GenAI. If you had a feature-rich application or even a complicated feature, you would focus on succeeding with one very specific slice. You would develop it all the way through, connecting all the various layers from the backend to the frontend, so that there was something that could be tested as soon as possible by a human user. This vertical slice development approach, when applied to coding agents, would: 1. Force the agent to focus more tightly. 2. Make it easier for me to test and review what is being delivered. 3. Help me to better track what is being delivered and more quickly correct deviations. That last point matters because it ties back to another behavior I needed to address. I was so focused on speed and so encouraged by previous successes that I was developing too much, too quickly. I needed to slow down. Developing in slices makes it easier for me to digest the work and stick to more thorough reviews. Instead of one research → spec → implement cycle per feature, the feature is decomposed into vertical slices upfront. You keep the speed benefit within a cycle (no manual hand-offs between phases) but recover the oversight benefit. You're reviewing something small enough to actually understand and make time for. This is a much better fit for developing multiple projects in parallel with coding agents. Software development is personal. This is what aligns with my style, my constraints and my experience. ## In Control Again So two out of the three projects that I was developing in parallel actually shipped. The third, most complicated project, the one that caused this whole rethink, I flushed down the toilet. It was such a mess and I had so little confidence in it that starting fresh was more sensible. Plus, this allowed me to start testing out everything that I have discussed here. I have been very pleased with the results, both in how the agent now behaves and in how I'm forced to behave. The application now has a more focused set of initial features. I have a better grasp of what I am doing and how to keep growing it. This sense of control has allowed me to get back to working on two other projects in parallel. Three seems to be my magic number. But I am running them at a saner, healthier rhythm now. ## Zooming Out - Larger Implication Coding agents are not finished products that you simply consume. They can work off the shelf for a lot of scenarios, but at the same time, for a lot of scenarios that are more complicated, they need to be adjusted and customized to how you develop. You have to get to a point where you understand how things flow for yourself and for an agent during a development cycle. There are some very vocal people sharing what works for them, myself included. It's important to consider a variety of viewpoints and experiences. It helps to feed your own personal trials, but it comes down to you synthesizing all this information and figuring out what works for you, fits your style, and aligns with your processes. This requires investment and experimentation. This process of trying to find the right alignment includes a bit of informal "vibe" evaluations. To get the most out of these tools and to exercise the most leverage, you need something more scientific. Luckily, there have been some recent releases that make it easy to do proper evals and capture comprehensive tracing in order to identify where your coding agent is failing and where it can be improved. More on this in an upcoming post. In the meantime, remember that an agentic coding tool starts off as a sledgehammer. Customize the handle to fit your hands. Turn it into a screwdriver when the job calls for precision. Keep shaping this wonderful and scary technology to your own goals. ## Use Slice Mode The skill requires a flag to invoke the development in vertical slices: `/sdd-flow delivery_mode: per-slice` This skill belongs to the [agent-engineering plugin](https://github.com/pablooliva/claude-plugins/tree/main/agent-engineering). --- ## From One Agent to Many URL: https://pablooliva.de/the-closing-window/from-one-agent-to-many/ Date: 2026-04-15 Description: Agent coordination tools now make it easy to run multiple AI agents in parallel. A look at what's available and what Trail of Bits achieved by systematizing it across an organization.
## TL;DR Agent coordination tools have gone from complex infrastructure to something you can set up in an afternoon. Projects like Superset, Cline Kanban, and Paperclip make it easy to run multiple agents in parallel on your own work, while claw-code shows what happens when agents coordinate themselves autonomously. Trail of Bits proved it works at the organizational level too by building a system, not just handing out licenses.
So working with an AI agent has been pretty revolutionary for me. It has allowed me to automate and run jobs and workflows that would have normally occupied most of my time, now in a much simpler manner, independent of my time invested. This isn't news; I think everybody sees this experience shared by many online. What I'm seeing more and more now is the ease or the accessible ability to coordinate many agents that then obviously multiplies your effectiveness. This coordination of agents is happening in the spaces of software development, of personal agents, and even in a more abstract way within organizations. I don't mean to add to the pressure that is very palpable for many of us, where there is a sense that you are being left behind. I do want to encourage everybody, with a strong sense of urgency, to start engaging with these tools, because it takes some time to truly understand the shift in possibilities and to understand the realm of productive environments that are now available with this technology. Now, agent coordination and orchestration is nothing that's necessarily new. There are examples of this going back a bit, but what is new is that I am seeing new projects come online and become available that make it really easy to implement these sorts of orchestrated environments for running multiple agents. So I have several examples of this and then one tangential example of how an organization has coordinated agents and people using agents at scale and thoroughly throughout the organization. I thought that this was an important use case to include here because the findings and results are pretty significant. This is another example of just how quickly things are evolving. ## Coding Agents The first few examples of agent orchestration are related to coding agents. The projects that I have found interesting that I have seen scroll across my various feeds include Superset, Cline Kanban, and mngr. [Superset](https://github.com/superset-sh/superset) allows you to have multiple terminal windows running with coding agents in a well-organized UI and additionally helps you easily create additional copies of your software development projects via automatic worktree creation. This allows you to run multiple tasks on a single project within a well-organized UI. Before this, I had multiple windows for my terminals, but this helps organize everything visually and helps you track, through visual cues, the status of each of your agents when they are running and when they have completed their tasks. This particular project has been super helpful to me. Next is [Cline Kanban](https://cline.bot/kanban). I have not used this project much yet. I keep forgetting to use it more extensively since I saw this late last week. But simply put, it also helps coordinate agents and tasks by breaking a project down into individual tasks. Directly from the product page, "Create, manage, and chain agent tasks within a single view." Finally for coding agents today, let us look at [mngr](https://github.com/imbue-ai/mngr). Now I am not sure if I will make much use of this last tool because the solution itself is pretty advanced. But I'm including it here because it is impressive and pushes the boundary for how software development can be conducted. The MNGR solution allows you to basically spin up agents not just in your local environment but anywhere that you can connect to via SSH, a protocol for basically connecting via terminal to different computers. Now, this is how certain features in Claude desktop and Claude code work, but it happens automatically in the background with MNGR. You have control over how to run agents and basically coordinate a fleet of them running on different machines, which is pretty damn cool, but also maybe beyond my needs, or maybe just beyond my imagination at the moment. ## Personal Agents Within the last couple of months, there's been an explosion of what I would call autonomous personal agents that were popularized by [Open Claw](https://github.com/nichochar/open-claw). This past week, I saw this new project called [Paperclip](https://github.com/paperclipai/paperclip), which is basically an orchestration tool for running several of these personal agents within one "control plane," as their website defines it. Maybe using the term "personal agents" is a bit misleading, because the intention behind Paperclip is for you to easily establish a company with AI agents operating in different roles for that company. You set high-level goals, metrics, and outcomes. The CEO agent figures out hiring, delegation, and execution. This is a deliberate shift from the "pair programmer" model most people use with Claude Code. I've installed Paperclip but haven't used it yet, so I can't vouch for how effective and well-functioning the coordination of all these agents filling different roles is. But the demos I have seen look very impressive. claw-code is up next. Now technically, this is related to coding agents or the coordination of coding agents, but this is very different from the coding agents that I mentioned previously because this demonstrates work that is completely autonomous. The implications of this are more closely aligned with general agent behavior rather than specifically that for software development. So if you did not hear, the source code for Claude Code was leaked last week, and within a day, or even shorter than that, within a night, someone had rewritten the implementation in a different programming language. That's what we're discussing here, the rewrite of Claude Code as claw-code, autonomously in a sense. I would encourage you to [read the post from the developer](https://x.com/realsigridjin/status/2039472968624185713) who did this. There are some very interesting insights that he has, and the architecture, the setup and mechanisms around how he coordinated the development of this copy of one of the most popular coding agents on the market, which he did through Discord only, a chat app, is just jaw dropping. Here is one of his insights: > But if you are staring at the generated Python files, you are looking at the wrong layer. The code is a byproduct. The Rust port that followed is also a byproduct. The thing worth studying in the claw-code repository is the system that produced all of it. claw-code was always a showcase. The point was never the Python files or the Rust crates. The point was the clawhip-based agent coordination system that built them while the developer was asleep. ## Institutionalized Agents In American English, when you say someone is institutionalized, it means that they've been put into a mental health facility. I thought that the sub-title was a good fit here because [what Trail of Bits has done will seem crazy](https://tldrsec.com/p/how-we-made-trail-of-bits-ai-native-so-far) to anyone that has struggled to effect any sort of change at any institution that is not small. Trail of Bits is a cybersecurity consultancy with about 140 people. Over the course of roughly one year, they went from 5% of staff actually using AI to having AI-augmented auditors finding 200 bugs per week, up from 15. Twenty percent of all reported bugs are now initially discovered by AI. Their revenue per sales rep hit $8 million against an industry benchmark of $2-4 million. These are not small improvements. This is a structural shift in what a 140-person company can output. What makes their story worth including here, alongside individual tools like Superset and Paperclip, is that they solved the coordination problem at the organizational level. It wasn't just about giving people access to AI. Trail of Bits built what they call a six-part "operating system" instead. The short version is that they standardized everyone on the same tools, wrote an internal handbook explaining not just the rules but the reasoning behind them, created a maturity matrix so people could see where they stood and where they could grow, ran focused adoption sprints that forced people into autonomous mode, captured every engagement's learnings as reusable artifacts, and then made autonomy safe through sandboxing and hardened defaults pushed to machines via device management software. By the end, they had accumulated 94 plugins, 201 skills, 84 specialized agents, and over 400 reference files. Every project made the next one faster. The reframing of identity resonated with me particularly well, having already experienced resistance myself. Their security auditors are experts, and experts resist AI harder than anyone because their skill is core to who they are. Trail of Bits didn't fight that, but instead side shuffled past it. They reframed AI proficiency from "I use AI" to "I'm the one who makes the AI dangerous." That's an identity upgrade, not a threat. It worked because it aligned with how these people already saw themselves. Beautifully executed, Trail of Bits. ## Where This Leaves Us This is the direction things are heading. The examples earlier in this post show how individuals can coordinate multiple agents. Trail of Bits shows what happens when an entire organization does it systematically. The gap between companies that figure this out and companies that don't is going to get very wide, very fast. I'm treating this less as something to watch and more as something to build toward, both in my own workflows and in how I think about bringing this to my team at work as something that I hope to template and then roll out to the entire organization. --- ## Close Call, a Voice AI Sales Coach URL: https://pablooliva.de/the-closing-window/close-call-a-voice-ai-sales-coach/ Date: 2026-04-15 Description: I built an open-source voice agent for practicing sales calls against an AI customer. Runs on Pipecat + Gemini 2.5 Flash, costs $0.14 per call.
## TL;DR I built an open-source voice agent that lets salespeople practice tough calls against an AI customer. It runs on Pipecat + Gemini 2.5 Flash, costs about $0.14 per call, and my team's first reaction was genuinely enthusiastic. The code is on GitHub if you want to try it.
"Sales cures all ills" is a phrase one of my first mentors repeated often, and it stuck with me. Before I got serious about software development and before meddling with AI engineering, I was a salesperson. I still miss the thrill of closing a deal and outmaneuvering your opponent. It was fun. Now that I'm in a position to use AI at my company, I came up with an idea: use voice-enabled AI to help train and coach our salespeople. From a technology standpoint, this was a relatively simple implementation. I built [Close Call](https://github.com/pablooliva/close-call), an open-source voice agent where a salesperson picks a scenario, clicks "Start Call," and talks to an AI customer who pushes back with realistic objections. Immediately after, the system performs an assessment and provides feedback that highlights where the salesperson performed well and what could be improved. I've done an initial demo at my company. The feedback was positive, very positive, in fact. The flow is surprisingly realistic. You're having an actual conversation with the AI, and it responds in real time. The scenarios are simple to set up. You can see examples in the repo. This is infinitely extensible: different approaches, different tones, different moods, different levels of pushback. In its current state, it's already generating real value. The current feedback by the AI is still mechanical. It rates how you handled and maneuvered past objections, whether you stayed on track. In future iterations, I want to evaluate emotion and tone during the call. And beyond that, video analysis of posture and physical presence. You might not believe that physical disposition matters, but it comes through on phone calls too. There's a lot of room to grow from this starting point. ### The Stack Close Call runs on [Pipecat](https://github.com/pipecat-ai/pipecat) with Gemini 2.5 Flash Native Audio for native speech-to-speech at sub-second latency. A 10-minute practice call costs roughly $0.14–0.40. Post-call coaching feedback is also generated by Gemini 2.5 Flash, from the transcript. The whole thing is Docker-packaged for easy sharing or can even be run as a desktop application. I wanted to share this because it's an example of a very simple application made possible by recent model releases. Gemini's live model came out about a week ago. It's not the first model that could handle live conversation, but it was cost-effective enough to finally make me invest time in an idea I'd been sitting on. This is what understanding the AI landscape looks like in practice. You don't have to orchestrate a complicated business process to deliver value. Sometimes the low-hanging fruit delivers a big punch. Or, as my mentor would say, cures all ills. **[Close Call on GitHub →](https://github.com/pablooliva/close-call)** --- ## Everything is Energy, Everything in Berlin is Beautiful URL: https://pablooliva.de/the-closing-window/everything-is-energy-everything-in-berlin-is-beautiful/ Date: 2026-04-11 Description: On a perfect afternoon in Berlin, everyone looked happy and oblivious. I see where the train is heading, and the accident is coming. But all crises present opportunity.
## TL;DR Evil is the opposite of love, and it's winning. Not just in the Epstein files or the wars started on a whim, but in every moment we choose the couch over action. Walking through Berlin on a beautiful afternoon, surrounded by people completely unaware of the converging crises ahead, I realized the opportunity: when the crash comes, and it will, we can organize. The work starts now — projecting forward to the breaking point and working backwards to chronicle how we got there.
We are all energy. Everything is energy. All these energies interact with each other. This is not an all-encompassing description of how this universe behaves, but it's an accurate one. I got to the topic of energy because I was trying to fight the desperation I feel as I consume news that reveals all the depraved acts described in the Epstein files. As I watch innocent children murdered, dismembered, disfigured, and traumatized as a result of the violent acts of greedy and misguided men. Starting wars as easily as we choose our outfits for the day. Maybe I was just not conscious of this earlier in my life. Maybe now that I have kids, I am more sensitive to all of it. But at no point in my life have I felt more certain that evil is winning. ## Evil as the Opposite of Love The concept of evil is something that is, at the same time, well defined for all of us but also very amorphous. When I try to think about how I could describe it, I think I'll always end up defining it as the opposite of love. It took me a long time to understand what love truly means, or at least what I think it means now. For me, it means the opposite of selfishness. The willingness to sacrifice personally. When you define it in those terms, you can see how I see that evil is winning. ## The Small Evils I see how evil tries to encroach upon my soul. I'm not a religious person, but I do believe in the concept of a soul, and I see how evil tries to entice me. I see it when it is easier to sit on the couch and watch another shitty Netflix movie rather than putting myself in a position where I can help other people. I see it when I spend a disgusting amount of money on a good meal to make myself feel physically satisfied instead of donating that money to the countless number of needy people on this planet. I see it when I decide to take the easy way out and not have a difficult conversation, not try to communicate these ideas to those close to me. The powerful don't have a monopoly on evil. The rest of us feed it every time we choose comfort. More so in a world that has a huge gap between what is needed and what is being done. ## Energy as Proof Let me give you an example of how I've seen direct proof that energy resides in us and has influence. My wife and daughter can get into very heated arguments. If I'm conscious enough of myself, and not too tired to allow my own emotions to get drawn into the mix, I can de-escalate the situation by bringing an aura of peace and understanding. I'm not always successful. Sometimes I'm tired, sometimes I'm grumpy myself. But I notice a black and white difference when I bring a positive energy into a situation between them. Everything that we put our efforts into, everything that occupies our trains of thought, all of this contributes to energy. We have to be conscious of making sure that energy stays on the good side and doesn't accidentally fall into the camp of evil. ## Berlin, on a Beautiful Afternoon I was in Berlin this past week for a few days, taking a break with my son since I had been so busy and hadn't had enough quality time to spend with him. One particular afternoon, walking back to our hotel, it was perfect. The sun was shining, the sky was blue, the birds were singing to each other. I looked around and saw everyone trying to look fabulous and having a great time sitting outside drinking teas, coffees, beer, wine. And I just thought to myself that I live in such a different world. I know this is going to sound dark, but I see where the train is driving towards, and there's a horrible accident coming. I don't know how to communicate that to everyone without sounding like a maniac. All these people having a great time, completely unaffected by everything happening in the rest of the world and what's coming over the horizon. I have [written before](/the-closing-window/close-enough-to-see/) about the converging crises of AI and climate change. Crises compounded by people in power who have too much energy invested in everything except love. I think it will be impossible to avoid the coming disruption. Based on how I see most people close to me, completely oblivious to the forces moving these crises forward, I think it is pretty much inevitable at this point. And I doubt it's just the people close to me. This isn't some Terminator fantasy. But I do think a lot of us will feel more pain than we have our entire lives. At least in the Western world. Of course, there are already plenty of non-Western people that are experiencing an amount of pain that I think is hard to imagine, and often as a result of the same forces that will cause our own pain. We should be paying attention and not thinking that we are immune to that reality. Malcolm X is famously quoted referencing the chickens coming home to roost. The evils perpetrated abroad, from my perspective, are partly dress rehearsal. Most of us are too busy on TikTok or taking care of our gardens. ## A Note on My Desk After coming back home from Berlin, feeling a little helpless after seeing these contrasting realities, I was sitting at my desk. I saw a note that I had written to myself, and it reminded me that all crises present opportunity. Back in 2008, after the financial crisis, we saw this in the U.S. in the form of Occupy Wall Street and the Tea Party. There were a lot of pissed-off people because they saw the injustice, they saw the criminality. They saw the lack of accountability. Those that suffered were not the ones responsible for the crash. The popular uprising seen in the Occupy Wall Street movement is an example of not just the organizing that took place, but also an example of how the government crushed that movement. Not necessarily something that you would expect from the freest country in the world. This time around, when things crash, the crash will last a lot longer. And that's exactly what will give rise to an opportunity. A chance to organize people. A chance to get them to consider other alternatives. It's crazy how the reality we think is hard-coded, the default for our human experience... everything we see around us has been built by us. We forged this path through the wilderness, and I think we don't give it a second thought because it's comfortable. Once things get uncomfortable, I hope we can make better use of our imagination. ## What I'm Going to Do What I think we need to do now, or at least what I will be doing, is project into the future. To that point where emotions will boil over and desperation will seep into the lives of many. Figure out how to start there and work backwards. Help people understand how we got to that point. Chronicle the steps that were taken to get us there, and who got us there. That does not absolve the rest of us of responsibility. Be careful what you put your energy into. Be conscious of who you are as energy. I would be surprised if consciousness of this concept takes enough foothold in enough of us to derail the tracks of this train. But the earlier we start to figure this out, the quicker the dark ages that are coming will blow past us. --- I don't have a good ending to this post. These were random thoughts that turned into something more cohesive that I needed to lay down on paper. But at least it reminds me to keep the crises contained within a more positive enclosure. Helps me keep my energy positive. Gives me hope. --- ## Spec Driven Development Fans, You'll Love sdd-flow URL: https://pablooliva.de/the-closing-window/spec-driven-development-fans-youll-love-sdd-flow/ Date: 2026-03-31 Description: A Claude Code skill that orchestrates spec-driven development through subagents: research, adversarial review, specification, implementation, code review, and critical review from a single command. Built Redakt with it in two days.
## TL;DR Built a Claude Code skill that forces research, spec, implementation, critical reviews and code review for one-shotting features. Used it to build Redakt (GDPR PII anonymizer) with six features in two days. Skill and plugin are open source.
Last week I shipped [Redakt](https://github.com/pablooliva/redakt), an open-source GDPR compliance tool. Six features for a web UI and REST API to help us better handle PII (Personally Identifiable Information). I had been using the spec driven development (SDD) process for months through a [plugin](https://github.com/pablooliva/claude-plugins) I built. When I saw that Claude had expanded the context window to a million tokens, I thought to myself, "Why not try to push the limits of how I was developing software?" What I mean by that is that the SDD process was created to help with context management. It follows a traditional and rigorous software development cycle, but it was slow relative to how fast things move within the AI space. I decided to try just one-shotting a feature using a [skill](https://github.com/pablooliva/claude-skills) to orchestrate the phases of a development cycle, and it worked surprisingly well. The skill was not the only thing that optimized the process. Anthropic's release of `--permission-mode auto` last week was the push that I needed to remove the diapers. I was excited to try it out. This mode is limited for now to Teams accounts. It was flaky initially, but it gave me a taste of the forbidden fruit. I didn't want to go back to confirming every request. At home, I knew there was another option though, a dangerous one, that I had avoided until now: `--dangerously-skip-permissions`. After close to a year of using Claude, and never having had any "oh shit" moments, I decided to remove all guardrails and bypass permissions. It felt like my face was being pushed back by gravity as you see in fighter pilot videos when they fly at multiple Machs... and the feeling is addictive. --- ## SDD, Not STD Not STD, as my speech-to-text model often spits out. Specification-driven development guides the AI to research the codebase and the requirements, write the specification, and then implement against that specification. The spec becomes what you version-control and review, while the code is output. I liked the concept and loved the results. --- ## What `/sdd-flow` Does `/sdd-flow` is a [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill, a custom slash command that you invoke with a task: ``` /sdd-flow Add GDPR-compliant audit logging for all anonymization requests ``` Then a pipeline of subagents takes over. Separate Claude conversations, each with a fresh context window, each handling one phase. **Research.** The first subagent investigates the codebase. For Redakt's audit logging feature, it mapped the existing logging setup, found a duplicate handler bug I didn't know about, and flagged that the schema in the code had drifted from the original spec. It produced a research document with file paths, specific line numbers, and integration points. **Adversarial review of the research.** A second subagent tears the research apart. For audit logging, it found real issues: six critical gaps, four questionable assumptions, four missing perspectives. **Specification.** A planning subagent reads the reviewed research and writes a full spec: functional requirements, edge cases, failure scenarios, security constraints. **Adversarial review of the spec.** For audit logging, this caught an issue that if it was fixed at the audit layer, would have meant invasive changes to shared functions upstream. **Implementation, code review, critical review.** Code and tests get written against the spec. A code review subagent checks alignment while a critical review subagent does a final adversarial pass. Every finding, no matter how severe, gets resolved before a commit. --- ## How `/sdd-flow` Looks After `/sdd-flow` finishes, you get a directory like this: ``` SDD/ ├── research/ │ └── RESEARCH-006-audit-logging.md ├── requirements/ │ └── SPEC-006-audit-logging.md ├── prompts/ │ ├── PROMPT-006-audit-logging-2026-03-29.md │ ├── implementation-complete/ │ │ └── IMPLEMENTATION-SUMMARY-006-...md │ └── context-management/ │ └── progress.md └── reviews/ ├── CRITICAL-SPEC-audit-logging-20260329.md └── CRITICAL-IMPL-audit-logging-20260329.md ``` --- ## One Skill, Two Modes Supervised mode is the default because it forces you to make critical decisions that shouldn't be automated, typically. These decisions include reviewing the completed research and reviewing the code before commit. Autonomous mode exists for features where requirements are clear and you are feeling confident. I used it for some of the later Redakt features after the first two proved the process. But pausing to review is the default for a reason, because confidence is often overconfidence. --- ## Try It Both the skill and plugin are open source: - **Skill** (orchestrator): [claude-skills](https://github.com/pablooliva/claude-skills) - **Plugin** (phase commands): [claude-plugins](https://github.com/pablooliva/claude-plugins) - **Redakt** (built with it): [redakt](https://github.com/pablooliva/redakt), check the `SDD/` directory for the full artifact trail The artifacts in Redakt's `SDD/` directory are the best pitch I can make. Read the research documents, the specs, and especially the critical reviews. Then just let the SDD flow. --- - [Meet Redakt: Practical GDPR Compliance for AI Teams](https://pablooliva.de/the-closing-window/meet-redakt-practical-gdpr-compliance-for-ai-teams/): Redakt is an open-source PII anonymizer built on Microsoft Presidio. It sits between employees and AI tools, replacing personal data with placeholders before prompts leave your network. No data stored, no DPA needed, fully self-hosted. - [Meet Sift: A Knowledge Base for Everything That Isn't a Note](https://pablooliva.de/the-closing-window/introducing-sift/): Sift is an open source personal knowledge base that ingests any document type and makes it searchable by meaning. The story of why it exists, what went right, and what to expect. - [Agent: Do You Understand the Words Coming Out of My Mouth?](https://pablooliva.de/the-closing-window/ai-agent-discoverability/): A practical checklist for making your website discoverable, readable, and citable by AI agents and answer engines — with a ready-to-paste prompt to implement it all. - [Finding Meaning in Your Notes with CK Search](https://pablooliva.de/the-closing-window/finding-meaning-in-your-notes-with-ck-search/): CK Search adds local semantic search to any text directory, with built-in MCP server support for AI agents. A practical walkthrough of setup, configuration, and where semantic search helps — and where it doesn't. - [From Folders to Knowledge Base: How I Made My Notes Work for Me](https://pablooliva.de/the-closing-window/from-folders-to-knowledge-base/): A personal journey from rigid folder hierarchies to an AI-powered knowledge base — through tags, semantic search, and knowledge graphs — and why investing in how you organize information pays off in ways you can't predict. - [Feed the AI - Digitize Everything](https://pablooliva.de/the-closing-window/feed-the-ai-digitize-everything/): AI agents are only as useful as the context you give them. Learn what it takes to build an external brain that lets AI act on your behalf — without asking every time. - [FolderTether: Obsidian Breaks Out of the Vault](https://pablooliva.de/the-closing-window/foldertether-obsidian-breaks-out-of-the-vault/): A bidirectional link between your Obsidian notes and the folders where your actual work lives. FolderTether keeps your vault and your filesystem connected without sync, cloud, or complexity. - [The Notes Setup That Actually Works with AI](https://pablooliva.de/the-closing-window/obsidian-and-markdown-in-the-ai-agent-era/): Why Obsidian and Markdown files turned out to be the right bet for the AI era — not for productivity reasons, but because plaintext with embedded metadata is exactly what AI agents need. - [Shadow AI and the Compliance Gap That Won't Close Itself](https://pablooliva.de/the-closing-window/shadow-ai-and-the-compliance-gap-that-wont-close-itself/): Shadow AI — employees using unapproved AI tools with company data — is creating a growing GDPR liability across Europe. Two overlapping laws apply to every AI prompt containing personal data, and most companies don't know it. - [The Workplace Blind Spot](https://pablooliva.de/the-closing-window/the-workplace-blind-spot/): Most knowledge workers assume their role's complexity will protect them from AI. I held the same belief — until I was proven wrong, twice. The models don't need to improve; they just need to be better harnessed. - [Close Enough to See](https://pablooliva.de/the-closing-window/close-enough-to-see/): From the dot-com bubble to mortgage fraud to Nord Stream to a livestreamed genocide, I've spent my life inside the systems that exploit people — across continents. This is what it looks like when the pattern finally clicks. - [I Am No Longer Needed](https://pablooliva.de/the-closing-window/i-am-no-longer-needed/): How I automated my own role as AI lead — piece by piece, meeting by meeting — until I realized the tools I championed were doing my job better than I was. A honest look at what that actually feels like. - [Which AI Should You Use in February 2026?](https://pablooliva.de/the-closing-window/which-ai-to-use-in-2026/): A practical, non-technical guide explaining which AI tool (ChatGPT, Claude, or Gemini) to use in 2026, how to pick the right model, and how AI has shifted from simple chatbots to agents that can actually do work for you. - [AI's Social Trap](https://pablooliva.de/the-closing-window/ais-social-trap/): The article argues that society is trapped in reactive, experience-based thinking that worked for linear innovation but is fatally inadequate for the exponential disruption of AI, and that — like Kodak and Nokia before us — our social structures risk becoming obsolete unless leaders start making proactive, reason-based decisions before the damage is done. - [The Software Factory: AI-Driven Development Without Human Code Review](https://pablooliva.de/the-closing-window/the-software-factory-ai-driven-development-without-human-code-review/): StrongDM's AI team eliminates human code writing and review entirely, instead using coding agents validated by holdout scenario tests and agent-built clones of third-party APIs to prove software works at scale. - [You Spent Your Whole Life Getting Good at the Wrong Thing](https://pablooliva.de/the-closing-window/you-spent-your-whole-life-getting-good-at-the-wrong-thing/): AI agents are collapsing execution into intention, shifting the bottleneck from "how" to "what" — making taste, judgment, agency, and curiosity the new core skills, though your existing domain expertise remains essential as the quality filter for AI output.