A trip page maintained by agents and a scheduled conditions bot A trip page maintained by agents and a scheduled conditions bot

The Trip Page Nobody Edits: An Agent-Kept Git Site

TL;DR I built a private trip page for a week at a backcountry cabin in a large Ontario park, and nobody ever edited its HTML. Four JSON page specs go through a generator; every change is a typed operation that becomes a self-merging merge request. A GitLab CI schedule runs every 3 hours, pulls public Canadian weather, park and fire data, and commits a “conditions: refresh”. It made about 120 of those commits. The conditions script is stdlib-only, deterministic and offline-safe. If a source is down it keeps the last good copy, marks it stale, and never invents a number. The cabin has no cell service. Power and internet came from a Starlink Mini, a portable power station and a 120 W panel, and the whole workflow assumed a slow, metered link. Two real bugs: a bot that pushed as the wrong identity and got a 403, and a “fire” layer that turned out to be a static 2011 zone map. Why a page at all A week in the backcountry with a friend means a pile of shared decisions: gear, a meal plan with calorie math, a route with a water taxi and a portage, a charging plan for the drive, and some sense of whether we would be crossing a big lake in a 35 km/h wind. The usual answer is a group chat and a spreadsheet. ...

September 28, 2026 · 10 min · zolty
A design review of a small typed decision model A design review of a small typed decision model

Reviewing Jev: a decision model that is cheaper, not smarter

TL;DR Jev is TypeSafe AI’s first “System One” model, public since 2026-09-15. You send it a state object and a few typed questions, and it returns probabilities in 70 to 500 ms. I wrote a design doc for using it in my own tooling, and then rewrote it twice in two days because the first draft was wrong in ways I only noticed by rereading the sources. Nothing is built. There is no client, no table, no spike, no measured number of mine in this post. This is a review of a third-party product before I adopt it. Every accuracy and cost figure below belongs to the vendor or to a third party, and I label it that way. ...

September 25, 2026 · 11 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
A modelled repair queue with tickets, parts, and labour A modelled repair queue with tickets, parts, and labour

The repair queue is four states and a no-go rule

TL;DR An unmodelled repair backlog becomes a pile of dusty things you feel bad about. Ask me how I know. Four ticket states: open, blocked, done, abandoned. blocked and abandoned are the two that do the work. abandoned is a feature. A queue you can’t quit is a queue that grows forever, and “I’ll get to it” is not a state. Parts are modelled as purchases and uses separately, because you buy a bag of ten capacitors and use two. Otherwise your cost-per-repair is nonsense. Labour gets logged per worker with an hourly rate, and that number flows all the way back into what I’m willing to bid on a lot. The no-go rule does more for profitability than any repair skill I have: if parts and time exceed roughly 60% of realistic resale, or resale is under about $120, it doesn’t get a ticket. Other people work this queue. Four access roles keep them on inventory and tickets and away from anything financial. Why a pile forms Buying surplus means buying broken things, on purpose, because broken is cheap and some broken things are trivially fixable. A cassette deck with a perished belt is a $4 part and twenty minutes. A workstation that won’t POST is often one reseated stick of RAM. ...

August 25, 2026 · 10 min · zolty
Listing drafts, barcode labels, and printed pack slips Listing drafts, barcode labels, and printed pack slips

Drafts, labels, and pack slips: automating everything except the publish button

TL;DR The work between “I own this thing” and “money arrived” is mostly paperwork and printing, and it’s where the hours actually go. Listings get built through the marketplace API, not by driving a browser — structured item specifics as JSON beat fighting a typeahead widget every time. Drafts are created automatically. Publishing is always manual. Publish is a money-moving, buyer-facing, irreversible action; it gets a human every time. Barcode labels render from the inventory record itself: ZPL → PDF → print job to a thermal printer, triggered from the item’s page. No separate label app, no retyping serials. Pack slips print on a schedule, and the job verifies the printer’s page counter moved before marking the slip printed. Optimistic “it printed” flags are how orders ship without paperwork. Anything bulk or financial — repricing, printing five-plus labels — sits behind an explicit confirmation. Cheap friction in exactly the right places. The boring half is the expensive half Sourcing gets all the attention. I’ve written about the funnel that finds lots and the data model that tracks the units. Both were interesting problems. ...

August 21, 2026 · 9 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
A funnel from surplus auction listings to a ranked shortlist A funnel from surplus auction listings to a ranked shortlist

Two dozen crawlers and a shortlist: how I stopped browsing surplus auctions

TL;DR Government and institutional surplus auctions are a firehose: thousands of live lots, most of them badly photographed, vaguely titled, and worthless. Browsing them by hand doesn’t scale and — worse — it’s biased. You click the pretty photos, and the pretty photos are the ones everyone else clicked. The fix is a funnel: ~24 scheduled crawlers feeding a single Postgres table, photos and paperwork in object storage, an hourly evaluation pass, and one ranked board. The judgement layer (reading the goods out of photos, estimating what they’re worth) runs as a small model pipeline with a second pass that checks the first. I’m deliberately light on its internals here. The output isn’t a decision. It’s a shortlist with reasons attached — and a “this is wrong” button that puts the lot back in the queue. The single highest-value change in three months was raising how many photos get looked at. The label is often in photo nine. None of this is my job. It’s the hobby that pays for the hobby, which is the only reason it’s allowed to be this over-engineered. Why I built a data pipeline for a weekend habit Let’s be honest about the proportions here. I built two dozen scheduled crawlers, an object store, an hourly evaluation pass and a ranked dashboard, in order to buy second-hand equipment on the internet slightly better than I did before. ...

August 7, 2026 · 11 min · zolty
Three domain-specific MCP servers Three domain-specific MCP servers

Building domain-specific MCP servers: three I actually use

TL;DR Three production MCP servers I run: a docs/wiki wrapper, a personal-finance aggregator, and an inventory-ops tool for a side project. Type hints are your schema. The FastMCP SDK extracts JSON Schema from Python docstrings and type annotations — you almost never hand-write schemas. Flatten nested APIs. If the upstream API returns nested JSON, unwrap it in the MCP tool and return clean markdown or a simple dict — agents prefer predictable structures. Reads always-on, writes gated. Use environment variables (MCP_WRITE=1) or similar to gate mutation tools; put mutations behind a validated service layer and audit every call. Log to stderr, not stdout. Stdio protocol uses stdout for JSON-RPC; anything else breaks the connection. The lineup I’ve written three MCP servers from scratch over the last few months. This is the cookbook — concrete patterns, not theory. If you haven’t read the intro to writing MCP servers, start there; I won’t re-explain the anatomy. ...

July 28, 2026 · 10 min · zolty
An autonomy ladder for coding agents An autonomy ladder for coding agents

The autonomy ladder in practice: letting agents commit, then merge

TL;DR Five rungs: Rung 0 (read-only) → Rung 1 (draft MRs) → Rung 2 (commit on /llm fix) → Rung 3 (auto-merge patches) → Rung 4 (exceptions-only). Promotion is earned, not promised — ~30 days at Rung 1 + <10% reverts → Rung 2; ~60 days at Rung 2 + <5% reverts → Rung 3. Infra agents cap at Rung 2 — Kubernetes, Terraform, secrets, cert-manager always need human approval to merge; blast radius is too high. Demotion is immediate on test deletion, secret leak, scope creep, or any deploy needing manual rollback. Non-negotiable gates (deterministic, run at the CI runner) block everything: no deleted tests, no secrets, no medium+ CVEs, destructive commands on allowlist only. From concept to operational I wrote about the trust ladder for agents in May — the philosophy of supervised → monitored → trusted → autonomous, and why you can’t just flip a switch. That post is the why; this one is the how. I’m going to walk through the exact rungs, the promotion criteria that actually work, the demotion triggers, and the CI rules that make it safe. ...

July 24, 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.