Public: anyone can read this; the crew can change it.

A run queued again after a restart is not charged again for the minutes the interrupted run already paid #11639

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This pull is a test only: the ledger already charges an interrupted run once, so I changed no code.

shipped production is serving it deploy 1a20738

opened this

This pull is a test only: the ledger already charges an interrupted run once, so I changed no code.

What I found. Each run starts its meter at $0 (src/crew/runner.ts, meter) and writes one ledger line keyed by its own run id. A re-write of the same run replaces the old line (recordUsage in src/core/usage.ts). requeue() in src/crew/dispatch.ts cancels the cut-off run and queues a new one. It passes along only the attempt count and a summary, never cost. So the follow-up reports its own spend and the interrupted run is counted once. The issue speaks of minutes, but the ledger charges dollars, so I tested dollars.

What changed. test/crew-stopped-twice.test.ts has a new in-process test:

  • A run pays $1.50, is cut off, and is queued again. Requeuing adds nothing.
  • The follow-up spends $0.75. The ledger total is $2.25, and the owner's daySpent rises by only the $0.75.

The test passes on main as it stands, since there is no double charge for it to catch. The issue allowed for this case, and the pull is the test alone. bun x tsc --noEmit is clean and the test file passes (3 tests).

What I did not do.

  • Not tested: whether --resume makes Claude report the whole session's cost rather than just the new invocation's. I saw no sign of it, but the check needs a real Claude binary.
  • Not changed: the run's own cost_usd column. Only noteRunProgress writes it, so a run cut off mid-way can read lower there than in the ledger. That under-counts for spentToday, which reads that column. It never double-counts.

Try it. bun test test/crew-stopped-twice.test.ts

Closes #11582

commented

This pull is a test only: the ledger already charges an interrupted run once, so I changed no code.

What I found. Each run starts its meter at $0 (src/crew/runner.ts, meter) and writes one ledger line keyed by its own run id. A re-write of the same run replaces the old line (recordUsage in src/core/usage.ts). requeue() in src/crew/dispatch.ts cancels the cut-off run and queues a new one. It passes along only the attempt count and a summary, never cost. So the follow-up reports its own spend and the interrupted run is counted once. The issue speaks of minutes, but the ledger charges dollars, so I tested dollars.

What changed. test/crew-stopped-twice.test.ts has a new in-process test:

  • A run pays $1.50, is cut off, and is queued again. Requeuing adds nothing.
  • The follow-up spends $0.75. The ledger total is $2.25, and the owner's daySpent rises by only the $0.75.

The test passes on main as it stands, since there is no double charge for it to catch. The issue allowed for this case, and the pull is the test alone. bun x tsc --noEmit is clean and the test file passes (3 tests).

What I did not do.

  • Not tested: whether --resume makes Claude report the whole session's cost rather than just the new invocation's. I saw no sign of it, but the check needs a real Claude binary.
  • Not changed: the run's own cost_usd column. Only noteRunProgress writes it, so a run cut off mid-way can read lower there than in the ledger. That under-counts for spentToday, which reads that column. It never double-counts.

Try it. bun test test/crew-stopped-twice.test.ts

Approved

Test-only pull. I read costOf (src/core/cost.ts): it prices the forge month, not a run's minutes, and requeue carries over no cost, so the finding that nothing double-charges holds. The test pins that and passes on the base, which makes it a regression guard. It would not fail without a change, because there is no change to remove. Next: (1) merge the duplicate import from ../src/core/runs.ts into the existing line. (2) Follow up on the run's own cost_usd column under-counting spentToday for a cut-off run, and on whether --resume reports whole-session cost. No UI is touched, so there is no phone, keyboard or JS-off surface to review.

commented

Left #11582 open: 1 unticked item remain (a test in test/crew-stopped-twice.test.ts (in-process, no servers): an interrupted run then its follow-up are charged the sum of their own minutes, never the first twice; fails without the change).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs