Python
Python is where the rules become code.
The agents I run that touch money are written in Python. So are the tools that plan my week, build my sites, and check my own work. This is the shelf.
Projects
Python 3.13 · FastAPI · Claude
Merchant agent harness
A hardened harness over Anthropic's reference commerce agents, vendored at a pinned commit and never edited. The harness adds caps, an audit chain, approvals, stores, and a hold on travel bookings. A FastAPI service hosts the agents, and adapters connect real stacks: Shopify, Mews, Eventbrite, and a robotics merchant. Live on Railway.
Claude Agent SDK
Chief-of-staff agent
A single-loop agent with memory, scripts, plan mode, slash commands, subagents, and two hooks: a guard before every tool call and an audit after it. The house rules live in the hook, not the prompt.
Planning
Board and daily plan
A git-backed tracker: project sync, board generation, an overnight digest, and a daily-plan heuristic that reads the board and prod health and picks the day's deliverables.
Publishing
Static site builders
Small generators with no framework: this site, the glossary and review pages for NANTUCKET, a PDF redline tool for contracts, and the renderers behind the Field Notes cards.
By the numbers
Merchant agent harness, October 2026. Own code only, not the vendored reference.
How I write it
Money is an int.
Minor units, always. No float ever touches a price, a fee, or a balance.
Unmeasured is None.
Not zero, not a mean, not a default. If a metric was not observed, the field says so and the caller has to handle it.
Typed, linted, tested, pinned.
Type hints throughout, ruff for lint and format, pytest with the reference's fake client so unit tests need no API key, and every third-party dependency pinned by commit or version.
Extend, never monkey-patch.
Upstream code is vendored read-only. Everything I add is a subclass or an extension, so an upstream update is a diff, not an archaeology dig.
# The shape of every tool in the harness.
def place_order(session: Session, draft_id: str) -> OrderResult:
draft = session.stores.drafts.get(draft_id) # server-issued id
cap.check(session, draft.total_minor) # cap on resulting state
approval.require(session, "place_order", draft) # human on the decision
with session.serialized(): # one write at a time
order = session.stores.orders.commit(draft)
audit.append(session, "place_order", order.id) # chained record
return OrderResult(order_id=order.id, total_minor=order.total_minor)
Notebook
Short entries on what I learned building the above. Newest first.
- Oct 2026
Pinned upstream at a commit and vendored it as plain files. Editable installs are convenient until a tool rewrites a file you did not own.
- Sep 2026
Moved every safety rule out of prompt strings and into a pre-tool guard hook. The prompt got shorter and the agent got harder to talk out of its rules.
- Sep 2026
Replaced every "or 0" on a metric with None and a schema that allows it. Three dashboards stopped lying the same afternoon.