Skip to content

aggregator(quota-gather-map): entryFromResult and entryFromFileExport never range-check percent — a window at -20% or 1e9% publishes as live ok data and appends as pct history against limit:100 #2571

Closed

Description

@dezren39

Problem

pm-stack/aggregator/src/quota-gather-map.ts's shared scalar gate is finite-only, and neither quota-current mapper range-checks the percent it publishes:

  • num() (quota-gather-map.ts:38) is typeof v === "number" && Number.isFinite(v) — no range.
  • entryFromResult's pushWindow (:197-211) copies num(raw?.usedPercent) verbatim into both value and pctOfLimit, with the entry stamped state: "ok" for a healthy provider result.
  • entryFromFileExport (:137-151) does the same with num(w.usedPct) for the Meta file-export document, and its labelWindow (:154-168) accepts any finite USD amount, rendering a negative freeCreditsUsd as the label "$-42.50".

Downstream, nothing range-checks the percent either:

  • recordsFromEntries (quota-gather-map.ts:268+) takes any non-null pctOfLimit as a pct snapshot record stamped limit: 100 — and its pool-level collapse keeps the max pct per key, so one glitched 1e9 reading beats every sane credential's reading for that tick.
  • isSnapshotRecord (snapshots.ts:295-310) is finite-only for numeric values, so the out-of-range records pass the store gate and enter the append-only snapshot store, where rollup min/max preserves the extremum.
  • parseQuotaCurrent (quota-current.ts:90-96) is also finite-only, converting pctOfLimit into the pool window's used/percent served on /quota.

Evidence (verified this run, main 627cbc9)

Laptop Bun probe importing the real quota-gather-map.ts + snapshots.ts (file md5 86fa3547e81f325f9211dfe1c4577894, identical in the sandbox clone at 627cbc9 and the laptop clone; probe at ~/git/fleet/probe-2026-10-30-laneA-gatherpct/):

  • entryFromResult with usedPercent: -20 → entry state:"ok", window value:-20, pctOfLimit:-20; recordsFromEntries emits a pct record value:-20, limit:100 and isSnapshotRecord returns true.
  • Same for usedPercent: 150 → record value:150; and usedPercent: 1e9 → record value:1000000000, gate true.
  • entryFromFileExport with windows usedPct: 150 / usedPct: -20 and freeCreditsUsd: -42.5 → entry state:"ok", both out-of-range windows published, plus a free_credits label window valueLabel:"$-42.50"; recordsFromEntries emits pct records 150 and -20, both passing the store gate.
  • Control: usedPercent: 42 → window and record 42, as expected.

No existing test pins the out-of-range acceptance (no range cases in test/quota-gather-map.test.ts).

Why it matters

These two mappers are the gatherer's front door for every provider-module result and every laptop file-export: one glitched or drifting upstream read (a provider returning a fraction instead of a percent, a scaled value, a negative placeholder) is published as live, measured, ok quota data, mis-states pool headroom/wall decisions on /quota, and is then permanently appended to the snapshot history as a pct point against limit:100 — the same poisoned-history failure already filed for the sibling writers (#2553 Turso, #2568 provider-usage), through a different, uncovered pair of entry points.

Dedup

Acceptance criteria

  • Triage pins the treatment for an out-of-[0,100] percent in both mappers (skip that window is the file's own pattern for unusable windows — entryFromFileExport already skips a window whose pct is null — but pin it explicitly), and pins whether a negative freeCreditsUsd/paygBalanceUsd label is skipped or the entry degrades.
  • After the fix: entryFromResult and entryFromFileExport never publish a window with pctOfLimit outside [0,100] as state:"ok" measured data, and recordsFromEntries therefore cannot append an out-of-range pct record from these entries.
  • test/quota-gather-map.test.ts covers negative, >100, and absurd (1e9) usedPercent and usedPct cases, with the sibling in-range windows of the same result/document unaffected, plus the negative-USD label case per the pinned treatment.
  • Existing quota-gather-map / quota-current tests stay green.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    app/aggregatorPackage/app: aggregator (pm-stack in this repo)
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 30, 2026
  2. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (lane A, new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a, run 2026-10-30T05:15Z
    • Found via: filed this run
    • Task: Triage the percent range gap in entryFromResult/entryFromFileExport (quota-gather-map.ts) — pin the [0,100] treatment for both mappers and the negative-USD label, then route a small_fix packet
    • Expectation: effort/xs — done when triage confirms the gate shape and a fix packet exists
    • Links: evidence in the issue body; probe on the laptop at ~/git/fleet/probe-2026-10-30-laneA-gatherpct/
  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. 14 remaining items

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

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  7. added a commit that references this issue on Nov 2, 2026
  8. 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/aggregatorPackage/app: aggregator (pm-stack in this repo)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/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions