Skip to content

Release v1.8.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 19:51
8b0bce1

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 kr

Tax 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 PERCENT adds 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 CODE states which currency --price is in and formats amounts to match — symbols where unambiguous, ISO code otherwise. No conversion is performed.
  • --sub PLAN=N@PRICE prices 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 in AllocationOptions; a vendor given no @PRICE falls 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.