Repository navigation
Release v1.8.0
Summary
allocate assumed one price, untaxed, in dollars. None of that holds for a buyer outside the US, and each gap pushed arithmetic back onto the person the command exists to spare.
$ workstats allocate -p Ada --sub claude=2@1866 --sub codex=3@1992 \
--currency NOK --vat 25 --month 2026-08
2026-08 · 5 subscriptions · 12,135 kr billed incl. 25% tax · basis: output tokens
month family subs plan/mo project pool share owed
─────────────────────────────────────────────────────────────────────────────────
2026-08 claude 2 2,333 kr 65.5M 147.5M 44.4% 2,072 kr
2026-08 openai 3 2,490 kr 45.8M 112.1M 40.8% 3,048 kr
─────────────────────────────────────────────────────────────────────────────────
ATTRIBUTABLE 42.2% 5,121 krTax is money that left the account. Vendors advertise ex-tax prices, so a Norwegian buyer paying 25% MVA was under-reporting the pool by a fifth.
Two vendors rarely cost the same. Anthropic prices in dollars and lets a card convert them; OpenAI sets a local price. Previously the only way to express that was to run the two pools separately and add them by hand — which defeats the point of the command.
No exchange rate is applied. --currency labels and formats; it converts nothing. workstats makes no network calls outside workstats update, and a rate compiled into a binary goes stale without saying so — the worst failure mode for a number about to be invoiced. State what you were charged and the arithmetic stays yours.
Added
--vat PERCENTadds the consumption tax applied at checkout, and the tax-inclusive unit price now appears on each row so a claim can be checked line by line against a bank statement.--currency CODEstates which currency--priceis in and formats amounts to match — symbols where unambiguous, ISO code otherwise. No conversion is performed.--sub PLAN=N@PRICEprices one vendor separately from the rest, for the common case of two vendors billing the same buyer different amounts.
Notes for review
- A share is a usage ratio and cannot move when only a price does. A test pins that, so a pricing change can never quietly restate what the work was.
Plan { count, price }replaces the bare count inAllocationOptions; a vendor given no@PRICEfalls back to the run's--price, which is also tested.- Two display bugs found while running real data: a literal
$in the cross-check that rendered$ 5,121 kr, and the ATTRIBUTABLE row sitting one column out once the plan-price column was added. Both fixed. - 286 tests pass (4 new). Clippy, rustfmt, MSRV 1.88 and rustdoc all clean locally.