← Logs

What claude -p taught me the expensive way

Building kantan meant driving the claude CLI headlessly with -p, for hours, across plans, approvals, implementations and retries. The CLI is well documented for interactive use. Headless, at length, it has behaviours you only discover when a run does something odd and a bill arrives. Each of these became a small probe script in the repo, so the finding can be re-checked when the CLI changes rather than remembered.

1. A question looks exactly like “done”

In -p mode there’s no interactive question tool. The agent asks in prose — “should I also migrate the old records?” — and the process exits with subtype: success. A question and a completion are indistinguishable by message type.

So never infer completion from exit status. kantan expects a sentinel at the end of a finished turn; when it’s missing, the run is parked as needs you, and your reply goes back to the session that asked.

2. --max-budget-usd bounds one process, not your run

The budget flag applies to a single process, and cost resets on every --resume. A run that pauses at a gate and resumes three times has had its ceiling silently restart three times. kantan tracks the total itself and passes ceiling − spent on each resume.

3. --resume can take a different --effort

…and keeps the context. That’s what makes a useful retry policy possible: when the same failure repeats, raise the effort on the same conversation instead of starting over and paying to rebuild everything the agent already understood.

4. Switching models mid-conversation is a trap

It cold-starts the prompt cache and drops thinking blocks the new model can’t read. It looks like a cheap way to plan with one model and build with another; in practice you pay to re-read everything and lose the reasoning. kantan gives implementation a fresh conversation instead, which is cheaper and cleaner.

5. Hook events flood the stream

Expect around twenty system/hook_* events before the first real one. If your run record logs everything verbatim, it’s mostly noise. Plan for them: collapse them, and make sure an event type a future CLI adds still shows up — as raw JSON if need be — rather than disappearing.

The principle

When you build on a tool you don’t control, write down what it actually does as executable probes, not as notes. Notes go stale silently. A probe fails loudly the day the behaviour changes.