-
Stefy Lanza (nextime / spora ) authored
Two gaps I left open last night, both about the ledger telling the truth. **A pod that went away without a pool noticing was never billed to anyone.** A pool closes its own pods' bills — `_terminate` for one it retires, `forget` for one RunPod says is gone — and neither runs for a pod only the REGISTRY knew about: one orphaned by a restart, or terminated from the console or straight through the RunPod API. Observed: pod lgdgfs749chkib, killed by hand, never appeared in the ledger while two pods that went through coderai did. `close_bill_from_registry()` now computes the hours from the registry's own `created` — the only record of when a pod started once no handle exists — and records them against the pool it belonged to. Both reaper paths use it, so the "terminated as stale" and "vanished from the account" cases cannot drift apart, and a pod a pool still holds is deliberately skipped: `forget` ledgers that one, and counting it here as well would double-count every pod RunPod reclaims. **`record_spend` dropped the `reaped` flag it was being handed.** It keeps a deliberate allow-list, which is right — the file is persisted and pruned for every pod — but the list has to carry what distinguishes one kind of charge from another. A pod the reaper killed looked in the file exactly like one a pool retired on purpose. `reaped` and `reason` are kept now. Writing the test for the first fix found a third thing, and it was mine: v0.3.56 made a stale registry entry stop counting as "known" so the reaper could kill the pod it named — but the reconcile was only ever asked about known ids, so that same change stopped stale entries being RECONCILED. An entry for a pod that no longer exists was then never pruned and its hours never attributed. The reaper now reconciles against the whole registry; `known` keeps its one job, which is deciding what is shielded from reaping. 9 tests in tests/test_ledger_out_of_band.py, including the double-count guard and the pruning regression. test_runpod_client_scale's source anchor moves with the change and says why. Co-Authored-By:
Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PxYHWbCAjjjD7A1rhFKtaW
d8984250