Skip to content

aggregator(quota-current): pctWindows never range-checks pctOfLimit — a fresh ok read at 150% walls a healthy pool and lands it in doNotStart, -20% publishes as negative usage #2582

Closed

Description

@dezren39

Problem

The reader side of the quota-current contract never range-checks pctOfLimit, so a finite-but-impossible percent in a fresh state:"ok" gatherer read is applied verbatim to the pool — and steers placement:

  • pctWindows (pm-stack/aggregator/src/quota-current.ts:88-97) gates only on typeof w.pctOfLimit === "number" && Number.isFinite(w.pctOfLimit) (:90) and then copies the value verbatim into used and percent against a hardcoded limit: 100 (:95-96). There is no [0,100] check.
  • applyEntry (quota-current.ts:118+) installs those windows on the pool and recomputes pool.state = poolState(pool); poolState (src/derive.ts:164-168) returns "walled" when any window percent is >= 100, and overlayQuotaCurrent then recomputes computeChoices — so one glitched read at 150% marks a healthy pool walled and lands it in doNotStart, steering new work away from a pool that is actually fine. A read at -20% publishes a negative percent window that renders as negative usage.
  • overlayTursoUsage (quota-current.ts:225) is weaker still: ...(w.pctOfLimit != null ? { percent: w.pctOfLimit } : {}) — no type or finite check at all, so any non-null value (150, -20, a string) becomes the Turso metric's percent.

Evidence (verified this run on main 3a425552)

Laptop Bun probe importing the real quota-current.ts (aggregator src/ copied verbatim to the laptop probe dir ~/git/fleet/probe-2026-10-30-laneA-quotacurrent/, run with bun probe.ts against a one-pool view):

  • Fresh ok entry, window pctOfLimit: 150 → pool state "walled", window {used: 150, percent: 150, limit: 100}, and choices.doNotStart: ["anthropic:claude-pro"] — the healthy pool is excluded from placement by a single out-of-range read.
  • Fresh ok entry, window pctOfLimit: -20 → pool state "healthy" with window percent: -20 published as-is.
  • Control: the module's own tests show in-range reads overlay normally — the defect is only the missing range gate at this reader.

Why it matters

quota-current.json is written every 15 minutes by a gatherer on another host and embedded into the fleet feed by the collector; this reader is the trust boundary for that file (the same rationale as #2416's shape gate). #2571 pins the writer-side mappers in quota-gather-map.ts, but the reader must not depend on every present and future writer being correct: a stale gatherer build, a hand-edited file, or a new provider adapter with a percent bug currently reaches pool state and placement choices unchecked. The failure is also silent — the pool simply reads walled (or negative), with freshness stamped ok.

Acceptance criteria

  • pctWindows never emits a window whose percent is outside [0,100] from a gatherer read (treatment pinned at triage: drop the window — the function's existing pattern for unusable pct — or clamp; sibling in-range windows of the same entry still apply).
  • overlayTursoUsage only forwards a percent that is a finite number in [0,100]; other values omit percent rather than passing through.
  • Regression tests in test/quota-current.test.ts pin: 150 and 1e9 do not wall the pool nor enter doNotStart; -20 never publishes as a window percent; in-range reads (including exactly 0 and exactly 100) overlay unchanged.
  • Existing quota-current / quota tests stay green.

Dedup

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

  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. 4 remaining items

  5. added
    queue/queuedHas a queued task in the Supabase agent queue
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 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