Skip to content

meta-exporter: parseBillingBanner accepts fractional minor units — 1050.5 exports as $10.505 of free credits instead of failing closed on shape drift #2510

Closed

Description

@dezren39

Problem

parseBillingBanner (pm-stack/meta-exporter/meta-quota-export.mjs:99-111) converts the billing banner's remaining_minor_units to USD with only a type/finiteness/sign check:

if (typeof minor !== "number" || !Number.isFinite(minor) || minor < 0) throw …
return minor / 100;

Minor units are integer cents by definition — a fractional value is shape drift, and this exporter's parsers are documented to fail closed on shape drift ("Throws on any shape drift — fail-closed, never partial zeros", parseSubscriptionQuota header). But a fractional or absurd value passes here and is exported in a status: "ok" document as freeCreditsUsd:

Verified against the real module (main 7d1b081, node probe):

  • remaining_minor_units: 1050.5 → freeCreditsUsd = 10.505 (a half-cent that cannot exist)
  • remaining_minor_units: 0.1 → 0.001
  • remaining_minor_units: 1e15 → 10000000000000 ($10 trillion of "free credits")
  • Controls behave: integer 14857 → 148.57; negative and string values throw as intended.

Why it matters

freeCreditsUsd lands in the export document the aggregator's quota plane reads as the Meta card's measured free-credit balance. A drifted banner payload (API change, partial response, wrong field) currently produces a precise-looking but impossible balance instead of the exporter's unparseable (exit 6) failure document — the failure mode every sibling parser in this file is built to produce.

Related, distinct: #2460 (subscription-quota timestamps) and #2461 (parsePaygBalance truncation) are different functions in this file; neither covers the banner parser. #1301 is the null-resets_at diagnosis in parseSubscriptionQuota.

Acceptance criteria

  • parseBillingBanner throws unparseable (exit 6) when remaining_minor_units is not an integer (Number.isInteger), and when it exceeds a stated plausible bound if triage chooses to add one.
  • Regression tests in meta-quota-export.test.mjs: fractional (1050.5, 0.1) throws; the existing integer/currency/missing cases still behave as today.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    effort/xsTrivial: expected under 30 minutes of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Oct 30, 2026
  2. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: filed

    • Agent: new-issue-creator job (new-issue-creator-code-b)
    • Session / ID: cron new-issue-creator-code-b run 2026-10-30T02:07Z
    • Found via: filed this run
    • Task: Triage parseBillingBanner's missing integer check on remaining_minor_units: fractional minor units export as fractional-cent free credits in a status:ok document instead of the exporter's unparseable failure.
    • Expectation: effort xs — pin the integer (and any bound) check, add regression tests, then route a small_fix packet.
    • Links: probe hidden_files/probe-2026-10-29-laneB-billingbanner/probe.mjs; issue body file hidden_files/issue-code-billingbanner-minor-body.md
  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  4. 40 remaining items

  5. added
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 2, 2026
  6. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  8. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 2, 2026
  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions