Skip to content

aggregator(quota-extract): a lowercase or non-month reset month silently discards a pool's whole read — monthDayTime's case-sensitive MONTHS lookup throws on its case-insensitive regex captures, and out-of-range days roll over into invented reset dates #2196

Closed

Description

@dezren39

aggregator(quota-extract): a lowercase or non-month reset month silently discards a pool's whole read — and an out-of-range day rolls over into an invented reset date

Summary

monthDayTime in pm-stack/aggregator/src/quota-extract.ts converts a quota-view reset read like resets Oct 30, 18:01 UTC into an ISO timestamp. Its callers capture the month with case-insensitive regexes (/resets (\w{3}) (\d{1,2}), (\d{2}:\d{2}) UTC/i), but monthDayTime looks the capture up in the case-sensitive MONTHS table. A lowercase (oct) or all-caps (OCT) month — or any other 3-letter word the \w{3} capture accepts (Foo) — makes MONTHS[mon] undefined, so Date.UTC(year, undefined, …) is NaN and d.toISOString() throws RangeError: Invalid time value. extractQuotaPools catches the throw per pool and treats the extractor as if it had matched nothing: the pool's entire read is discarded — including the usage percentage the extractor had already matched — and the pool renders with its shell "No current read" note.

Separately, an out-of-range day silently rolls over instead of failing: resets Oct 39, 18:01 UTC produces a reset of 2026-11-08 — an invented date, the exact outcome the module header says the extractors must never produce ("renders with its identity and a 'no current read' note instead of vanishing or inventing numbers").

Evidence (verified this run, main 318ca58)

Probe: real extractQuotaPools under Bun, minimal ChatGPT section, now = 2026-10-29T12:00:00Z:

reset line in the quota view Codex pool result
7-day 42% used; resets Oct 30, 18:01 UTC window: 42%, resetsAt 2026-10-30T18:01:00.000Z ✅
7-day 42% used; resets oct 30, 18:01 UTC no windows, "No current read" shell ❌
7-day 42% used; resets OCT 30, 18:01 UTC no windows, "No current read" shell ❌
7-day 42% used; resets Foo 30, 18:01 UTC no windows, "No current read" shell ❌
7-day 42% used; resets Oct 39, 18:01 UTC window: 42%, resetsAt 2026-11-08T18:01:00.000Z (rolled over) ❌

File:line (main 318ca58):

  • pm-stack/aggregator/src/quota-extract.ts:17-30 — MONTHS table, title-case keys only.
  • pm-stack/aggregator/src/quota-extract.ts:33-40 — monthDayTime: MONTHS[mon] unchecked; Date.UTC(...) with undefined month → NaN; d.toISOString() throws. No day-range check, so Date.UTC rolls day 39 into the next month.
  • pm-stack/aggregator/src/quota-extract.ts:113 — extractCodex reset regex /resets (\w{3}) (\d{1,2}), (\d{2}:\d{2}) UTC/i.
  • pm-stack/aggregator/src/quota-extract.ts:168 — goWorkspace reset regex (same shape, /i) — affects both OpenCode Go pools.
  • pm-stack/aggregator/src/quota-extract.ts:203 — extractCommandCode "period ends" regex (same shape, /i).
  • pm-stack/aggregator/src/quota-extract.ts:351-358 — extractQuotaPools wraps each extractor in try/catch and maps a throw to raw = null (the shell pool), so the failure is silent and total for that pool.

Why it matters

The quota view is a human-written document; month case in a dated read is exactly the kind of variation the fail-soft design is meant to absorb. Today one lowercase oct in the Codex or OpenCode Go section makes a healthy, current read vanish from the hub with no log line and no diagnosable difference from "the pattern stopped matching" — and a typo'd day invents a reset date the stewardship rotation decisions then trust. Four pools share the helper (Codex, OpenCode Go Personal, OpenCode Go Lab, Command Code), so one fix covers all of them.

Acceptance criteria

  • Triage pins the rule for monthDayTime (expected: normalize the captured month case-insensitively against MONTHS; an unrecognized month token or an out-of-range day yields a null reset for that window — the matched percentage read is kept — rather than throwing or rolling over).
  • Regression tests in pm-stack/aggregator/test/quota.test.ts (or the extractor's test home): oct/OCT resets produce the same resetsAt as Oct; a non-month token and day 39 produce the window with resetsAt: null; title-case behavior unchanged.
  • All four monthDayTime call sites (Codex, both Go workspaces, Command Code) covered by the fix.
  • No change to the per-pool try/catch in extractQuotaPools semantics for genuinely unmatched sections (shell note still renders).

Filed by new-issue-creator-code-b (lane B), run 2026-10-29 03:50 CDT. Dedup: #1204 (extractor health reporting) is observability, not this correctness bug; #1626 (closed) was reset pairing by paragraph. No open or closed issue covers monthDayTime/MONTHS.

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/normalOrdinary priority (the default)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. added
    queue/queuedHas a queued task in the Supabase agent queue
    effort/xsTrivial: expected under 30 minutes of work
    app/aggregatorPackage/app: aggregator (pm-stack in this repo)
    on Oct 29, 2026
  4. dezren39 commented on Oct 29, 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-29 03:50 CDT
    • Found via: filed this run
    • Task: Filed after verifying on main 318ca58 with the real extractQuotaPools under Bun (sandbox probe /tmp/probe-laneB-quota): "resets oct 30"/"OCT 30"/"Foo 30" discard the Codex pool's whole matched read (case-sensitive MONTHS lookup throws on the /i regex capture; extractQuotaPools' try/catch maps the throw to the shell pool), and "Oct 39" rolls over to an invented 2026-11-08 reset. Enqueued as a triage packet for the repo-health-code-pilot queue.
    • Expectation: effort/xs — triage pins the monthDayTime rule (expected: case-insensitive month match; unknown month / out-of-range day → resetsAt null, percentage read kept), then routes a small_fix.
    • Links: evidence body in goal hidden_files/issue-code-quotaextract-monthday-body.md; probe /tmp/probe-laneB-quota/probe.ts
  5. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  6. 45 remaining items

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

    @dezren39
    MemberAuthor

    Task 5221 parked for 20 minutes because the canonical PR #4135 test check is still QUEUED (started 2026-11-02T02:50Z; fresh check 2026-11-02T05:40Z). CodeRabbit is neutral and Codex review on head bfc1e0b has no findings. The targeted quota test passed in a prior checkpoint (26 tests, 0 failures); no code changed in this claim.

    Resume: after CI completes, freshly verify issue #2196 and PR #4135, inspect the diff and mergeability, and merge only if all required checks are green. PR: #4135 · branch: queue/5221-2196-monthdaytime-normalize-month-case.

  9. 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
  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor
  11. added a commit that references this issue on Nov 2, 2026
  12. 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