A permission classifier sitting between an AI agent and a shell A permission classifier sitting between an AI agent and a shell

Claude Code's auto mode denied me 301 times in seven weeks

TL;DR Claude Code’s auto permission mode puts a classifier between the agent and any tool call that is not already covered by an allow rule. I ran in that mode all summer. In the transcripts I still have (mid-August to early October), 301 tool results mention that classifier. About 288 are outright denials, about ten are “no verdict” errors, and a few just quote the phrase. Of roughly 57,700 tool calls in September, that is about 0.5%. Almost all of it landed in September (285 of the 301), and it peaked on September 6 with 29 in one day. Two things helped: adding environment text that tells the classifier what my infrastructure is, and adding narrow allow rules so routine commands never reach it. Neither one was a clean win, and I will show where the data does and does not support that. Sometime between late September and early October I switched the default mode to bypass. The thing that makes that tolerable is a deterministic hook that runs before permission rules in every mode. The classifier was not wrong often. It was expensive in attention, and it was the wrong tool for unattended work. This is part of a small batch about the classifiers I run around agents. The rewrite of my destructive-command guard came first, and a field guide to all of them will follow. ...

October 10, 2026 · 11 min · zolty
A CI gate matching a test change against an exact fingerprint A CI gate matching a test change against an exact fingerprint

Never delete tests to get green: exact-fingerprint exceptions

TL;DR In July I wrote about a test-deletion sentinel: CI hard-fails if a merge request deletes tests, unless the commit message carries a [test-refactor] tag and an issue number. I was pleased with it. It turns out a tag in a commit message is a bypass that an agent (or I, on a bad day) can write in one line, and it excused every test change in the MR, not just the one I meant. ...

October 7, 2026 · 8 min · zolty
A shell command being parsed into argv before a guard decides whether to block it A shell command being parsed into argv before a guard decides whether to block it

Rewriting my agent's destructive-command guard to parse, not grep

TL;DR I run a PreToolUse hook that blocks a short list of unrecoverable shell commands before my coding agent can run them. Version 1 was a list of regular expressions over the raw command string. Replaying 90 historical blocks and about 82,000 historical commands showed that roughly 40% of v1’s blocks were not commands at all. They were grep patterns, commit messages, and markdown heredocs that happened to contain a scary phrase. v2 tokenizes the command the way a shell would and applies each rule only where a program actually runs. On the same replay it allowed every false text match, still caught every real destructive command v1 caught, caught a few that v1 missed because of flag order, threw zero parser errors, and took about 51 ms per call end to end. It fails toward blocking. If it cannot parse something it falls back to text rules, and if Python is missing the shell wrapper falls back to the old regex list. It is a tripwire for an agent that makes mistakes, not a security boundary against one that is trying to hide. I show a case it does not catch below. I will write up my experience with Claude Code’s auto-mode classifier separately. The short version is that this hook, not the classifier, is what stands between the agent and my cluster’s worst commands. ...

October 3, 2026 · 11 min · zolty
Debugging quiet failures on a fleet of desktop machines used as cluster nodes Debugging quiet failures on a fleet of desktop machines used as cluster nodes

Six ways my surplus all-in-one k3s nodes failed quietly

TL;DR I joined eight surplus all-in-ones to my office k3s cluster as workers (the build is in the previous post). Six things went wrong, and all six failed quietly. A scheduling taint left 96 cores running zero workload pods while three small VMs ran 66. Prose inside a YAML folded scalar became k3s command-line arguments, and k3s silently dropped every flag after it. A DHCP lease and a pinned node IP disagreed. The login greeter suspended WiFi-only machines after 20 minutes. One mokutil command broke TPM auto-unlock. A weekly patch play skipped its own job for weeks. The common fix is boring: read back what the system actually did and assert on that, not on the exit code. 1. The taint that idled the fleet My original plan, from a proposal doc, was to taint the panels so nothing but the kiosk would run on them. What shipped was a softer version, PreferNoSchedule, because I also wanted Longhorn on the panels, and Longhorn’s DaemonSets would be locked out by a hard taint. Changing Longhorn’s toleration setting needs every volume detached, which means downtime just to satisfy a taint. ...

September 18, 2026 · 8 min · zolty
A bloated instruction file being trimmed down to a short entry point A bloated instruction file being trimmed down to a short entry point

Putting my CLAUDE.md on a diet: 19 KB down to 7.6 KB

TL;DR On September 6 I audited every file Claude Code loads at the start of a session on my main machine. The global CLAUDE.md went from 19,286 bytes to 7,607 (down 60.6%). The four biggest project memory indexes went from 64,900 bytes combined to 6,513 (down 90.0%). The file had more than doubled in under three months. A backup from June 18 was 9,242 bytes. Every addition had seemed reasonable when I made it. The biggest single category of cuts was model information: release lists, retirement dates, introductory pricing, spend estimates. I replaced all of it with a rule, “use only the models and efforts the active CLI offers”, and kept the judgment about which tier fits which task. The honest caveat: this is a document check. I verified that files parse, links resolve, and the constraints I care about survived. I did not benchmark whether Claude behaves better or worse, and I did not take a token count from a live session. Bytes are not tokens. If you have read my earlier post on re-tuning my setup for a new model, this is the sequel with a different lesson. That time I learned not to trust a model’s suggestions about what to delete. This time I put a validator and a rollback around the edits. ...

September 8, 2026 · 9 min · zolty
A checklist-driven auditor catching defects in a supervisor's review A checklist-driven auditor catching defects in a supervisor's review

The cheaper model audited the expensive one and found two bugs

TL;DR On July 31 a fleet of long-running Claude manager sessions that I was using to supervise parallel work collapsed badly enough that I had to cold-start the whole thing. My first hypothesis was that the supervisor model was not smart enough. That hypothesis was checkable, and false. The transcripts showed the managers really were running Opus 5. They still failed. The dominant failure markers in their ledgers were stale and duplicate, which are bookkeeping problems that a script gets right every time and a model gets right most of the time. The same week, a Sonnet auditor at medium effort, given an explicit list of gates to check, found two real defects that the Opus supervisor had missed. The Opus supervisor was better at something else: writing the gate list and noticing what was not on it. I turned that into a table that allocates model and effort per type of turn, with scripts doing the work that is decidable. I am going to be careful about how much that one afternoon proves, which is not much. It is one observation, and I did not run a benchmark. I wrote earlier about sending finished code to a rival model for review. This is the same instinct from the other direction: reviewer and author should not share a blind spot, but they also do not need to share a price tier. ...

September 4, 2026 · 9 min · zolty
Diagnosing remote machines that report success while failing Diagnosing remote machines that report success while failing

Failures that look like success: a field guide to fixing machines you can't touch

TL;DR The dangerous remote failure isn’t the one that errors. It’s the one that reports success — exit code 0, green tick, desktop loads, nothing in the logs. Five real ones from the last two months, each with the wrong hypothesis I actually held. Your diagnostic tool can be the thing lying to you. A packet capture showed traffic arriving on a port that was being dropped, because capture taps before the firewall does. Provisioning needs sentinel files, not vibes. “It looks finished” is not a completion signal; a file the process writes at the end is. Where agents genuinely help: assembling the four facts a diagnosis needs, and recalling the failure signature from three weeks ago. Where they hurt: confidently narrating a plausible cause for a symptom they can’t see. Every one of these produced a page in the knowledge base with the wrong hypothesis recorded, because that’s the field that saves the next hour. Success is not a signal I run machines I can’t put my hands on: a bench of surplus desktops at an office across town, cluster nodes, a couple of workstations belonging to other people. Remote hands come from a KVM device and SSH, and I’ve written about that hardware setup already. ...

September 2, 2026 · 10 min · zolty
Dashboards with on-demand refresh backed by Kubernetes Jobs Dashboards with on-demand refresh backed by Kubernetes Jobs

Six dashboards, a refresh button that launches a Kubernetes Job, and the metric that changed what I buy

TL;DR Everything in my resale pipeline runs on CronJobs, which is correct until the moment you want fresh data now and the next run is 40 minutes out. Fix: a refresh button on each dashboard that creates a Kubernetes Job from the existing CronJob’s template, via the API, using a dedicated ServiceAccount with a tightly scoped Role. The web app gets permission to create Jobs in one namespace and nothing else. No cluster-admin, no shelling out to kubectl, no shared token. Six dashboards: opportunities, active bids, sold, listings, pickups, contacts. Every table sorts on every column, which is not a nice-to-have — unsorted tables hide your worst-performing categories. The metric that changed my buying wasn’t margin. It was days from sourced to sold, by category — and it says two categories I liked were quietly eating all my working capital. Cron is right until you’re standing in front of the shelf The pipeline is scheduled work. Crawlers every six hours, evaluation hourly, order sync every six hours, pack slips twice an hour. That’s the right architecture: it’s cheap, it’s resilient, it recovers on the next tick, and nothing depends on me being awake. ...

August 28, 2026 · 8 min · zolty
An internal knowledge base written and maintained by agents An internal knowledge base written and maintained by agents

The wiki writes itself now, which is exactly the problem

TL;DR Giving an agent write access to a wiki is a twenty-minute job. The interesting work is stopping it from filling the wiki with plausible sludge. Four page types, each with a contract about what it must contain. An agent writing a runbook and an agent writing a reference page are doing different jobs and need different rules. Staleness is a field, not a vibe. Every page carries who wrote it, when, and how it was verified. Unverified pages are visibly second-class. The write gate is an environment variable, off by default. Read is free; write is a decision. Incidents become pages, or the incident was wasted. That’s the one rule I’d keep if I could only keep one. Things a wiki can’t hold — PDFs, images, rendered one-pagers I need to share with someone outside the cluster — go to a small auth-gated artifact host instead. Don’t paste binaries into a wiki. The easy part I have a self-hosted wiki with a GraphQL API and a few hundred pages. Wiring an agent into it took about two hundred lines of Python — a small MCP server with search, read, list, create, and update, flattening the API’s nested responses into markdown so the agent gets text instead of JSON soup. I wrote about the mechanics of that server, and two others, in the MCP server cookbook. ...

August 18, 2026 · 9 min · zolty
Managing workstation config across a small fleet Managing workstation config across a small fleet

Someone else's laptop, my agent: config management for people who won't read a README

TL;DR I maintain workstations for people who are never going to run git pull — so the config lives in a repo I own and they never see. It is a reference/restore repo, not config management: per-machine folders hold snapshots of what’s actually installed, and restore is a deliberate install -Dm755, not a sync. The no-auto-sync part is the whole point. Silent convergence across machines that are supposed to differ is how you break someone else’s laptop from your desk. The boring 40 minutes — partitioning, encryption, sudo, base packages — is an unattended installer (a Debian preseed, now on its eleventh revision). The interesting 20 minutes is the per-machine folder. The agent’s job isn’t to apply config. It’s to read the repo, diff it against the live box, and tell me what drifted — then write the restore commands for me to approve. What I’d do differently: put the agent’s own profile in git before you have three machines copying it from each other over SSH. The problem with managing other people’s computers I run a handful of machines that aren’t mine. A couple of family desktops, a bench box at the office, and a laptop belonging to someone who does knowledge work and would rather not learn what a dotfile is. ...

August 11, 2026 · 9 min · zolty

Affiliate Disclosure: Some links on this site are affiliate links (Amazon Associates, DigitalOcean referral). As an Amazon Associate, I earn from qualifying purchases. This does not affect the price you pay or my editorial independence — I only recommend products and services I personally use and trust.