Every fix to my incident loop after release was found and verified by reading code. When the demo finally ran end to end, it worked, and that changed what I think it's for.
#mlops-incident-commander · part 4/4
A blog is a place where one goes to tell the world about something interesting. Usually it comes down to complaints or achievements — and I choose the latter.
Every fix to my incident loop after release was found and verified by reading code. When the demo finally ran end to end, it worked, and that changed what I think it's for.
#mlops-incident-commander · part 4/4
The eval harness is the only signal for tuning the incident loop, which only works if ground truth stays hidden. A month after release I found postmortem memory handing it back.
#mlops-incident-commander · part 3/4
Detection in my incident loop has no thresholds at all, only a model's judgement. Remediation ignores the model entirely. Why I split the trust that way, and where it leaks.
#mlops-incident-commander · part 2/4
A LangGraph loop that watches a live classifier, opens incidents, and proposes fixes. Most of the repo is fake on purpose, and I wrote down which parts.
#mlops-incident-commander · part 1/4
On release morning the Linux append path finally ran in CI and passed. Then both packages went to PyPI, and the question the project started with is still open.
#presidio-compliance-stack · part 5/5
Six months with Claude Code, from usage paranoia and a code tsunami to a short addiction, skills, Opus, and seven lessons along the way.
#journal
The NRIC recognizer accepted a range of place-of-birth codes on a recall argument. Reading all 294 rows of the official table showed nine of them don't exist.
#presidio-compliance-stack · part 4/5
Measuring my own audit store turned up a concurrency claim that didn't hold, and a case where a record names a recognizer that found nothing while the chain still verifies.
#presidio-compliance-stack · part 3/5
Building the v1 plans for a Malaysian PII pack and its audit store surfaced four places the plans were wrong — the worst was an audit log that silently lost records on Windows.
#presidio-compliance-stack · part 2/5
Before building a Malaysian PII pack for Presidio, I made the audit layer argue for its place in both directions. It survived, on a narrower reason than the one I started with.
#presidio-compliance-stack · part 1/5
Packaging the gate for PyPI found four real bugs and produced a release workflow that has never run — committed as publish.yml.disabled, because a package on PyPI is a claim I can't make yet.
#pr-compliance-gate · part 6/6
The investigator was broken by an event loop, a missing environment variable, and a structured-output method that quietly stopped guaranteeing anything — none of which a 100%-covered suite could see.
#pr-compliance-gate · part 5/6
Every path the investigator resolved was checked against its sandbox. The sandbox itself was located by an unvalidated id from the POST body — so the guard was fine and the ground under it moved.
#pr-compliance-gate · part 4/6
The investigator is the only part of the gate that goes off and reads a repository by itself, so most of its design is a list of things it cannot do — including fail.
#pr-compliance-gate · part 3/6
Routing in the gate is a pure-Python rule ladder, not an LLM call — but the list reducer underneath it meant an investigation could never actually resolve anything.
#pr-compliance-gate · part 2/6
A portfolio piece, plainly — a LangGraph and Agent SDK pipeline that reviews PRs against policy, built around two cost gates that keep most traffic away from the expensive models.
#pr-compliance-gate · part 1/6
An echo tool grew a callback_url and lint had nothing to say about it — which is why the cassette gate has a second step that only asks whether the server's surface moved.
#mcp-cassette · part 6/6
A cassette is a verbatim transcript of what a third-party server said, committed to your repository and fed to a model — so lint reads it for injection smells, and its rule packs are TOML on purpose.
#mcp-cassette · part 5/6
Adding remote transport meant framing SSE myself to keep the dependency list short — and then a recording fixture hung because the server was talking on a stream nobody was listening to.
#mcp-cassette · part 4/6
The clean POSIX shutdown path had never run anywhere. CI ran it on four machines at once and found two bugs the blunt Windows path had already solved.
#mcp-cassette · part 3/6
Three things about stdio nearly stopped the recording proxy from working at all — and shutting it down cleanly on Windows ended in os._exit(130) after I gave up on the correct design.
#mcp-cassette · part 2/6
I wanted to start a project with MCP and found there was no easy way to test the interaction without calling the agent over and over — so I built the vcrpy equivalent for it.
#mcp-cassette · part 1/6
The smallest tool in my Claude Code monorepo — a wrapper that auto-names every unnamed session with its folder and a timestamp, so the session history is actually browsable.
#claude-code-command-center · part 10/10
file-sync keeps my Claude config in step across two folders, newer wins either way — until a raw copy clobbered a machine-specific statusLine.command and taught me why settings.json needs a merge, not a copy.
#claude-code-command-center · part 9/10
Claude sessions are disposable, so I harvest their lessons into a weekly master file on a schedule — plus a hand-run skill I built for a separate credit pot Anthropic announced but never shipped.
#claude-code-command-center · part 8/10
I'd copied the same CLAUDE.md and configs across a dozen repos and let them drift — Vantage is a single HTML file that surveys every repo, diffs any two files, and copies the good version across.
#claude-code-command-center · part 7/10
Planning got cheap enough to do from my phone, so plans piled up across ten repos — docket is the single board I run them all from, one at a time or batched by project.
#claude-code-command-center · part 6/10
Two skills, same name, no way to tell which one Claude Code fires — so I built a browser that lists every skill, agent, and hook on the machine, shows which one wins, and lets me read what each actually does.
#claude-code-command-center · part 5/10
I already had a usage dashboard; I built the terminal version out of pure "can I" curiosity — and it's what finally justified extracting the shared parsing into a library.
#claude-code-command-center · part 4/10
The Claude Code status line shows the moment; I wanted the story — so I built a dashboard that breaks token usage down by project, tool, and model, with a rate-limit trajectory that tells me when I'll hit the wall.
#claude-code-command-center · part 3/10
My Claude Code status line started as a context gauge back in the 200k days, grew rate limits and a cost figure — and why I trust two of those three and squint at the third.
#claude-code-command-center · part 2/10
Why I pulled a dozen standalone Claude Code tools into one monorepo called C4 — and why that would have been a maintenance trap before Claude Code could read the whole thing.
#claude-code-command-center · part 1/10
I built figureflow to answer two questions — can you wrap React Flow in Python, and can an LLM author interactive diagrams with it. Both got answered. The second answer is why I archived it.
#figureflow
The generic recipe underneath my Claude Code wrapper — a WebSocket-to-PTY bridge into a container, written down with the five things that actually cost me time.
#claude-code-html-wrapper · part 3/3
A self-hosted web front end for Claude Code — one Docker container per user, sessions you can resume from a browser tab, and no cloud in the loop.
#claude-code-html-wrapper · part 1/3
My Claude Code wrapper has no database, no Docker SDK, and no permission prompts — each absence was a decision made in the spec, and each one is why the server fits in seven files.
#claude-code-html-wrapper · part 2/3
The redesign looked AI-generated and I couldn't say why — so I asked the model that designed it to critique it. It listed its own tells, and iteration 19 became a subtraction pass.
#claude-code-plugin-toggler · part 6/6
Reskinning my Claude Code plugin manager around teal and terracotta — a palette that stays related to Claude without looking like an Anthropic product, and a VSCode panel that does the same trick against the editor.
#claude-code-plugin-toggler · part 5/6
Going from a two-tier local/global plugin model to three scopes — Local, Project, User — because the missing one lets a repo carry its own opinions to whoever works in it.
#claude-code-plugin-toggler · part 4/6
Every line of my Claude Code plugin manager was written by Claude Code. What I actually did all those days — specs, sequencing, and being the first user — and where the model out-debugged me.
#claude-code-plugin-toggler · part 3/6
I promised to write up the path problems once I understood them. It took three releases, a 29-line band-aid, and Claude Code finding the one character I couldn't see.
#claude-code-plugin-toggler · part 2/6
Setting up yet another project with my usual Claude Code plugins, I got annoyed enough to build a toggler — a browser UI and a VSCode panel over one JSON file.
#claude-code-plugin-toggler · part 1/6
Resetting the blog. Expect short, occasional notes on building AI coding agents and the tooling around them.