One purchase lot fanning out into many inventory items One purchase lot fanning out into many inventory items

One lot, forty units: the cost-basis problem nobody warns you about

TL;DR You buy a lot. You sell units. That asymmetry is the whole data-modelling problem, and spreadsheets lose to it immediately. The spine is four tables: purchase_lot → inventory_item → listing → sale, with join tables where the relationships are genuinely many-to-many. Cost gets allocated from the lot down to units, and the allocation locks once the first unit sells. Retroactively re-allocating cost after a sale is how you accidentally rewrite last quarter’s profit. A unit walks a status machine: sourced → processing → drafted → listed → sold → shipped → reconciled, with dead and personal as the two honest escape hatches. Selling quantity-N on one listing is N sales, not one sale with a quantity column. I modelled it the wrong way first and spent August unpicking it. Packing several units into one sellable bundle, and splitting a bundle back into units, both need to conserve cost exactly. Getting that wrong is silent and it shows up as fake margin. The mismatch Here’s the transaction that starts everything: I pay $340 for a pallet described as Assorted Networking and Computer Equipment. One line item, one payment, one date. ...

August 14, 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
Fetching secrets into CI without leaking them Fetching secrets into CI without leaking them

Secrets without leaks: a Bitwarden CLI into CI and agent permissions

TL;DR Secrets leak through echo, debug tracing, logs, process args, and env-var inheritance — guard the flow, not just storage Fetch from Bitwarden once, assign to a shell variable, reference the variable — never inline the raw value Verify with first-3/last-3: ${VAR:0:3}...${VAR: -3} — confirm you have the right token without exposing it GitLab masked vars hide values in logs (8+ chars); protected vars only expose on protected branches — use both for production secrets Agent permission rules prefer command substitution inside the allowed command, not as an env var Rotate tokens immediately; verify before use; pin your bw CLI version The leak surface — where secrets escape I used to think storing secrets in Bitwarden (or Vault, HashiCorp, whatever) meant they were safe. I was wrong. ...

August 4, 2026 · 8 min · zolty
Scoring Pokémon card lots against sold comps Scoring Pokémon card lots against sold comps

Scoring Pokémon card lots against eBay sold comps

TL;DR Active eBay listings are asking prices, not realized prices—they lie. Use SOLD comps (~90-day history) to anchor your math. Card identity is everything: name + set + edition + variant + grade. “Charizard” is not a price; “Base Set Unlimited Charizard, PSA 9” is. Sell-through velocity (sales per week) predicts how long your capital sits locked up and is the variable most buyers ignore. Expected value = (median sold price × (1 − fee rate)) − shipping − grading. Apply a 30% margin of safety before you bid. Confidence gates protect you: skip lots where you can’t find ≥3 grade-matched comps or where the price spread exceeds ~6× the median. Buying Cards Rewards Discipline I’ve overpaid for Pokémon lots I “knew” were steals. The problem was never the cards—it was that I was bidding on nostalgia and gut feeling, not math. ...

July 31, 2026 · 9 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
Background monitors watching an automated cluster Background monitors watching an automated cluster

Background agents that babysit the cluster: drift, test-deletion, and cost sentinels

TL;DR Deterministic guardrails run first. PreToolUse regex blocks destructive commands (kubectl delete, terraform destroy, DROP TABLE, rm -rf) before the LLM is consulted. LLM safety is probabilistic; regex is not. Goal-drift detector compares initial objective vs final commit via embedding cosine similarity; alerts if divergence > 40%. Test-deletion sentinel hard-fails CI if tests are deleted without a [test-refactor] tag—you can’t hallucinate past the regex. Cost-spike alerts monitor 24h rolling spend at 80% (warn) and 100% (critical), plus a circuit breaker that opens after 3 failed runs. Action audit flags when the execution plan diverged wildly from the initial strategy—catches scope creep before it becomes expensive. The Problem: Autonomy Without a Leash Last year I started automating routine cluster work—dependency upgrades, CI improvements, minor bug fixes—by running Claude in the background with Git/Terraform access. The first three months were great. The fourth month I woke up to a $180 cloud bill. ...

July 21, 2026 · 8 min · zolty
Auditing repository history against ecosystem adoption timelines Auditing repository history against ecosystem adoption timelines

I audited four months of my own repos to see what I was actually early on

TL;DR I ran an audit over my repos going back to March: pull the first commit that introduced each agentic pattern, then go find out when that pattern actually became common practice in the wider ecosystem. The results were humbling in a useful way. Four things I quietly considered myself early on were catch-up, in one case by two years. Four other things turned out to be genuinely ahead of documented practice. The interesting part is what those four have in common, and it is not what I expected. ...

July 19, 2026 · 8 min · zolty
Routing a diff to a second vendor's model for adversarial review before merge Routing a diff to a second vendor's model for adversarial review before merge

The merge gate is a rival model: four days of Gemini deleting my code

TL;DR Before I merge anything where being wrong would be silent, I paste it into a live Gemini session and ask that model to attack it. Not “review this,” which gets you a rubber stamp with bullet points. Attack it. Over four days in April, that gate killed a performance optimization 88 minutes after it shipped, found two engine bugs that meant every historical number in my trading leaderboard was inflated, and identified five separate ways my “out-of-sample” evaluator was reading the future. Claude wrote all of that code. Claude also reviewed all of that code. Claude caught none of it. The useful property is not that Gemini is smarter, it is that Gemini did not write the thing and has no investment in the design being correct. ...

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