mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-21 04:59:47 +08:00
Billing was charge-in-arrears: a credit unit was only charged once the downloader had already reported downloading into it, and the first unit only after the first progress report — so a no-credit task still pulled bytes before being blocked, then suspended with no explanation. - Pre-authorize one unit ahead of the bytes pulled: targetUnits = min(ceil(downloaded/unit) + 1, ceil(total/unit)). The downloader never fetches bytes it hasn't paid for, and the cap keeps the lifetime charge at exactly ceil(total/unit) — same total as before, only billed earlier. - Charge the first unit on the transition into 'downloading' (zero bytes), so a task that can't afford a unit is suspended at the gate and pulls nothing. The worker reads the authoritative status from that transition response and does not start downloading when it comes back suspended (progress reports stay pure telemetry; control still flows through the poll). - On suspend, set runtime.message so the UI shows why (insufficient credits). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>