Skip to content

aggregator(snapshot-records): recordsFromProviderUsage never range-checks used_percent — a Codex window at -20% or 1e9% is appended as measured pct history against its own limit:100 #2568

Closed

Description

@dezren39

Problem

recordsFromProviderUsage (pm-stack/aggregator/src/snapshot-records.ts:236-279) derives Codex quota snapshot records from the keeper's provider-usage snapshot JSON, but the window check is only typeof win.used_percent === "number" (snapshot-records.ts:261). Any finite number — negative, over 100, or absurd — becomes a pct record stamped with limit: 100:

  • used_percent: -20 → record {serviceId: "openai:codex", metricId: "7-day", value: -20, unit: "pct", limit: 100, source: "api"}
  • used_percent: 150 → value 150 against its own stated limit of 100
  • used_percent: 1e9 → value 1000000000

Evidence (verified this run, main 86b7d01)

Laptop Bun probe importing the real snapshot-records.ts + snapshots.ts (md5-matched to this repo's main; probe at laptop ~/git/fleet/probe-2026-10-29-laneB-codexpct/):

  • used_percent=-20 → 7-day record value -20, isSnapshotRecord(record) === true
  • used_percent=150 → value 150, passes the store gate
  • used_percent=1e9 → value 1000000000, passes the store gate
  • used_percent=42 (control) → value 42, passes

The append path offers no second line of defence: scripts/append-usage-snapshots.ts (--from-provider-usage mode) filters derived records through isSnapshotRecord (src/snapshots.ts:295-310), which checks only that a numeric value is finite — no pct range check (the limit/resetsAt side of that gap is #2483). The records then enter the append-only snapshot store via the outbox/collector, and monthly compaction (snapshots.ts rollups) preserves each series' min/max indefinitely — a single glitched keeper read (the Codex usage endpoint is an external API whose values this code does not control) permanently poisons the openai:codex history series and every chart/pace consumer that assumes a pct value sits within its own record's limit (cf. #2346, where validator-passing pct records throw in projectPace).

The sibling writers in the same file validate their domains (window seconds must match a known window exactly or the window is skipped); only the percent value itself is unchecked. Note the contrast inside the same function: a window with an unrecognised limit_window_seconds is dropped silently rather than recorded under a guessed metric — the percent deserves the same fail-closed treatment.

Why it matters

Snapshot history is append-only and treated as measured fact (source: "api"). A negative or 1e9-percent record is not a display glitch that ages out: compaction keeps it as the series extremum forever, and it misstates Codex usage in the quota history the stewardship cycle reads.

Acceptance criteria

  • recordsFromProviderUsage emits a Codex window record only when used_percent is a finite number in [0, 100]; out-of-range windows are skipped (the rest of the snapshot's records are still derived).
  • test/snapshot-writers.test.ts gains cases: negative, >100, and non-finite-shaped used_percent values produce no Codex record for that window, while the other window and the sessions record are unaffected.
  • Existing snapshot-writers tests stay green (the 12% / 3% happy path at test lines ~184-185 unchanged).

Dedup

Checked this run against open + recently closed issues: #2553 is the Turso writer's negative counts (different writer/function), #2264 is snapshot-records' shape-only dates (different field), #2483 is the store gate's limit/resetsAt checks, #2346 is the downstream projectPace throw. No existing issue covers recordsFromProviderUsage's percent range.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/lowNice to have; do when convenient
    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 B, new-issue-creator-code-b)
    • Session / ID: cron new-issue-creator-code-b, run 2026-10-30T05:01Z
    • Found via: filed this run
    • Task: Triage the used_percent range gap in recordsFromProviderUsage — pin the [0,100] gate (skip the window vs clamp), 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-29-laneB-codexpct/
  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. 3 remaining items

  5. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: repo-worker-code (OpenChamber scheduled lane)
    • Session / ID: ses_e5e698512fcffee2cHNEyF6r1J / queue task 3819
    • Outcome: Triage complete on main 199d9f2e222c17826533dc3d9320aebd88aab533; the out-of-range positive-window defect is reproduced and the stale negative-store claim was corrected.
    • Follow-up: Issue aggregator(snapshot-records): skip Codex used_percent outside finite [0,100] #3460 queued as small_fix task 5391; it skips invalid windows without clamping and preserves sessions/valid companion windows.
    • Validation: bun test test/snapshot-writers.test.ts passed (17 tests, 56 assertions); current out-of-range behavior documented on the issue.
  6. added
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    on Nov 1, 2026
  7. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triaged — already done, closing with evidence

    • Agent: queue worker (repo-worker-code), lane code-i
    • Session / ID: queue task 7932
    • Found via: repo-health-code-pilot claim (coverage pass 2026-11-03)
    • Prior triage: this issue was already triaged 2026-11-01 by task 3819, which routed the fix as issue aggregator(snapshot-records): skip Codex used_percent outside finite [0,100] #3460 / queue task 5391. This coverage task re-verified the chain end to end instead of re-triaging.

    Evidence the acceptance criteria are met at current main:

    • pm-stack/aggregator/src/snapshot-records.ts:300-303 — recordsFromProviderUsage now skips a window unless used_percent is a finite number in [0, 100] (typeof + Number.isFinite + < 0 / > 100 checks), exactly the fail-closed skip the issue asked for; other windows and the sessions record are unaffected.
    • Regression tests in pm-stack/aggregator/test/snapshot-writers.test.ts: negative (-1, line ~223), >100 (100.01, line ~242) and non-finite (Infinity, line ~243) windows produce no Codex record; boundary 0 and 100 (lines ~260-261) and the original 12% / 3% happy path (lines ~200-201) still emit.
    • Fix delivered by PR fix(aggregator): validate Codex usage percentages #4217 "fix(aggregator): validate Codex usage percentages", merged 2026-11-02T06:42Z; follow-up issue aggregator(snapshot-records): skip Codex used_percent outside finite [0,100] #3460 is CLOSED.

    Closing #2568 as completed — the work it asked for is landed and tested on main.

  8. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 3, 2026
  9. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 30 (priority/low). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

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/lowNice to have; do when convenientqueue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobstatus/needs-triageNot yet triaged; needs a first looktask/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