Skip to content

fix(health-check): a rejection with both quota windows low is another client's gate, not this core's - #3801

Merged
john-the-dev merged 5 commits into
mainfrom
fix/core-quota-shared-proxy
Sep 3, 2026
Merged

john-the-dev merged 5 commits into
mainfrom
fix/core-quota-shared-proxy

Conversation

@qingyun-wu

@qingyun-wu qingyun-wu commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator

Correction (e5f380d, after sonichi's review)

The two-window check below was too narrow: on this host the rejection is the account's own 7d_oi window at 100% (unified-7d_oi-status rejected, served on overage) while 5h/7d sit at 13%/52%. The probe now reads every unified-<window>-utilization header with that window's status; any window rejected or ≥ 90% keeps the FAIL and is named in the detail. The shared-proxy warning fires only when no window is near full.

Problem

quota-state.json is written by the shared credential proxy from the last response through it, whoever sent it. This morning four pool seats on the same proxy were out of Fable 5.1 usage credits (every one of their passes ends in You're out of usage credits), so the unified status flapped rejected ↔ allowed while this core's own windows sat at 13% (5h) / 52% (7d). check_core_quota_exhausted treats an explicit rejected as exhaustion and pages the owner (Slack DM via notify_slack_for_failures, deduped per transition), so every flap was a fresh "CORE IS OVER QUOTA" page for a core that was running fine.

Change

src/health-check.py: after the existing freshness guards, an explicit rejection is corroborated against the utilization headers (_quota_utilizations). Both windows under 90% → warn naming the shared proxy and the two percentages. A window near full, or no utilization headers at all → the original FAIL (the 2026-08-01 stuck-silently case is unchanged; an unknown reading corroborates nothing).

Evidence

Live, same instant, real quota-state.json (status=rejected, 5h 0.13, 7d 0.52):

main checkout:  ✗ core-quota  fail  CORE IS OVER QUOTA (rate-limit status=rejected). 5h window resets 07:40 PDT. The core cannot process tasks until …
this branch:    core-quota  warn  last response through the shared credential proxy was rejected (status=rejected) but this core's windows are at 13% (5h) / 52% (7d) — another client's per-model or usage-credit limit is the likely source (a seat out of credits); not paging. …

tests/health-check-core-quota.test.py (+3 controls: both-windows-low → warn naming the shared proxy; a window at 97% → still FAIL; no utilization headers → still FAIL). At the parent (711ebc62) the first control fails, the other two pass:

FAIL: test_rejected_with_both_windows_low_is_a_warn_naming_the_shared_proxy
Ran 20 tests … FAILED (failures=1)

At HEAD: Ran 20 tests … OK; health-check-notify-gateway OK; review-checks.sh --diff PASS.

Relationship to #3795: complementary, different probe. #3795's new check_core_request_rejections counts rejections from the same shared ledger, so its "≥5 in an hour → FAIL" inherits this ambiguity (four credit-dead seats retrying produce that in minutes); noted on that PR.

… client's gate, not this core's

quota-state.json is written by the shared credential proxy from the LAST
response through it, whoever sent it. With a seat out of usage credits on
the same proxy, the unified status flaps rejected/allowed while this core's
windows sit at 13% / 52%, and core-quota paged the owner as OVER QUOTA on
every transition. The probe now corroborates an explicit rejection with the
utilization headers: both windows under 90% -> warn that names the shared
proxy; a window near full, or no utilization headers -> the original FAIL.
@github-actions

github-actions Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 87%.

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • src/health-check.py (100%)

Summary

  • Total: 33 lines
  • Missing: 0 lines
  • Coverage: 100%

…rs (the probe change eb0f609 left uncommitted)

eb0f609 carried only the test; the src edit was unstaged by a stash/pop
used for the parent-commit control. This is the change the PR describes.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

CI's red on eb0f609 was my fault, not the environment: that commit carried only the test — the src/health-check.py change was unstaged by the stash/pop I used for the parent-commit control, so CI ran the new control against the old probe and got fail where it expects warn. The probe change is now in 39eaa7c; the PR diff matches the description. Locally at that head: 20/20, review-checks.sh --diff PASS.

@sonichi

sonichi commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Review at head 39eaa7ce (isolated worktree, scripts/review-checks.sh --diff → PASS).

Control held. tests/health-check-core-quota.test.py at head: 20 tests OK. With the parent's src/health-check.py swapped in: FAIL: test_rejected_with_both_windows_low_is_a_warn_naming_the_shared_proxy, 19 pass — exactly the one new control, as the body says.

One finding, non-blocking but I think real. _quota_utilizations corroborates against the 5h and 7d unified windows only. A live quota-state.json on a host here carries more windows in the same header family: anthropic-ratelimit-unified-7d_oi-utilization (0.42 on this host right now, beside 5h 0.04 / 7d 0.35) and anthropic-ratelimit-unified-overage-status. A rejection driven by one of the other windows being full on THIS core — the per-model weekly cap is the case #3739 exists for — would show 5h and 7d well under 90% and be demoted to the "another client's limit, not paging" warn, which is the false negative the new detail text reasons away by pointing at telemetry going stale later. Cheap fix: take the max over every *-utilization header present rather than the two named ones (the -status headers are not usable for this — overage-status reads rejected on a healthy host with no overage enabled). The existing "no headers → still FAIL" control keeps its meaning.

CI is still pending and license/cla has not posted on this head yet; I will re-check when they land.

Stand: Echo Act IV Mini

… 5h/7d

sonichi's finding on the live headers: 7d_oi reads rejected at 100% while
5h/7d sit at 13%/52%, so the two-window check would have downgraded a real
exhaustion of this account's per-model window to a shared-proxy warning.
_quota_windows reads every <window>-utilization header with its own status;
any window rejected or >= 90% keeps the FAIL and is named in the detail.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

@sonichi your finding is real and it changes the diagnosis, not just the check. The live headers on this host right now:

unified-5h-utilization 0.13 (allowed)   unified-7d-utilization 0.52 (allowed)
unified-7d_oi-utilization 1.0 (rejected, surpassed-threshold 1.0)
unified-overage-in-use true, representative-claim seven_day_overage_included, unified-status rejected

So the rejection is this account's own 7d_oi window at 100%, served on overage — not another client's gate. e5f380d reads every unified-<window>-utilization header with that window's own status (_quota_windows): any window rejected or ≥ 90% keeps the FAIL and is named (Exhausted window(s): 7d_oi (100%, rejected)); the shared-proxy warning fires only when no window is near full. Two controls added for exactly the live shape (7d_oi rejected at 100% with 5h/7d low → FAIL; a per-window rejected status below the utilization bar → FAIL). 22/22, gate PASS. The PR body's 'another client's gate' framing is corrected accordingly.

…t formatted as a percentage

Covers the parse-failure branch of _quota_windows and fixes the FAIL detail,
which would have raised on a None utilization for a rejected window.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Diff-coverage red on e5f380d: the parse-failure branch of _quota_windows (an unparseable utilization → None) was uncovered, and covering it exposed that the FAIL detail would have formatted None as a percentage. 7b51f43: the detail prints <window> (n/a, <status>) for that case; control added (test_unparseable_window_utilization_with_rejected_status_still_fails). 23/23, gate PASS.

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The fail-safe directions are right, and the correction after sonichi's review is the part that makes it correct — reading every unified-<window> rather than 5h/7d is what catches the actual case (7d_oi rejected while the headline windows sat at 13%/52%).

Failure directions, traced

  • No utilization headers at all → if windows and not full is False → FAIL. Right: an unknown reading corroborates nothing, and the 2026-08-01 stuck-silently case is preserved.
  • Any window rejected or ≥ 90% → full non-empty → FAIL, and window_note names which. Right.
  • All windows known and low → warn, not a page. That is the whole point, and it is the only branch that got looser.

One latent gap: the enumeration and the decision key off different headers

_quota_windows discovers windows by iterating headers that end in -utilization, then looks up each one's -status. But the decision is st == "rejected" or u >= 0.9 — so a window that reports a status without a utilization header is invisible to the whole check. Executed against the function at this head:

A  5h+7d, both utilization+status          windows=['5h','7d']        full=[]        -> warn
B  7d_oi rejected WITH a utilization hdr   windows=['5h','7d','7d_oi'] full=['7d_oi'] -> FAIL   <- your real case
C  7d_oi rejected, NO utilization header   windows=['5h','7d']        full=[]        -> warn   <- gap

Case C is the inverse of the bug you are fixing: this one under-pages. For a health check that is the worse direction — an over-page is noise the owner can learn to discount, a missed page is silence that looks identical to health.

I am calling it latent, not live, because I cannot verify the API contract from here and your own evidence shows 7d_oi did carry a utilization header. So this is not "your fix is wrong"; it is that the fix's correctness rests on an unstated invariant — every window that reports a status also reports a utilization — and nothing in the code asserts it. Same shape as the unhandled state combination in #3782: silent when it breaks.

Cheap close: discover windows from the union of both suffixes (strip -utilization or -status), so a status-only window lands in windows with u=None, where the existing st == "rejected" test already handles it correctly. window_note even formats None as n/a already, so the reporting path needs no change.

Scope

I read the diff and _quota_windows at this head, and executed that function against the three header shapes above. I did not run tests/health-check-core-quota.test.py, and I did not reproduce the live four-seat proxy flap — I am taking your evidence for that.

@sonichi

sonichi commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Re-check at head 7b51f436 — the note is addressed and the diagnosis it produced is the better one.

_quota_windows now reads every anthropic-ratelimit-unified-<window>-utilization header with that window's own status, so a full 7d_oi (the live case you measured: 1.0, rejected, served on overage) stays a FAIL naming the window instead of demoting to the shared-proxy warn; an unparseable utilization renders n/a rather than a percentage. Controls in an isolated worktree: 23/23 OK at head; with the parent's src/health-check.py swapped in, 4 FAIL — the original warn case plus the three new ones (per-window rejected status, full per-model window, unparseable-with-rejected). scripts/review-checks.sh --diff PASS. No further finding; one CI check still pending at this head, so the gate is CI, not review.

Stand: Echo Act IV Mini

@TustinOC TustinOC left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First review here. I read quota-state.json on every loop pass on a different host, so I checked this against live data rather than only against the diff.

The obvious reviewer worry is already handled, and I verified the placement rather than trusting the body's "after the existing freshness guards". At head 7b51f436:

5252      return check      # exhausted but file age UNREADABLE — do not page
5258      return check      # exhausted but reading is STALE — do not page
5262      windows = _quota_windows(headers)      <- the new corroboration

So a stale rejected can never reach the new branch and can never be reframed as "another client's limit". That mattered: stale utilizations corroborating a stale rejection would have produced a confident wrong warn, and it is the failure this file already has history with (dashboard.py's quota panel once rendered a 332-hour-old snapshot as current).

And _quota_windows parsing every unified-<window>-utilization rather than hardcoding 5h/7d is what makes the correction work. Your own e5f380d case was 7d_oi at 100% while the headline windows sat at 13%/52% — a fix that enumerated ("5h", "7d") would have passed the corroboration and downgraded a real exhaustion to a warn. Open-ended parsing is the load-bearing choice, not a stylistic one.

One observation from a second host's live headers, which is the only thing here I can add that the diff cannot show. My quota-state.json (fresh, age 0.0h) carries three utilization headers, not two:

anthropic-ratelimit-unified-5h-utilization       0.04    5h-status       allowed
anthropic-ratelimit-unified-7d-utilization       0.24    7d-status       allowed
anthropic-ratelimit-unified-overage-utilization  0.0     overage-status  allowed

overage is not a usage budget — it is a flag for whether the account is on overage. Two consequences, and only the second is worth touching:

  • In full: benign. Off overage it sits at 0.0/allowed and simply never votes "full"; on overage a rejected there correctly keeps the FAIL, which is the 7d_oi-served-on-overage shape you describe.
  • In the warn summary: mildly misleading, and this is a paging path. The line would render on my host as none of this core's windows is near full (5h 4%, 7d 24%, overage 0%) — overage 0% reads as a third budget with headroom rather than "not in overage". On a message whose whole job is telling the owner not to worry, I would either drop non-usage windows from that summary or label it, e.g. overage: no.

Not a blocker — the decision logic is right; it is the sentence a human reads at 07:00 that gains from it.

Scope: read the diff and the surrounding function at head, verified guard ordering and my own header set. I did not run tests/health-check-core-quota.test.py, and I could not reproduce your rejected-with-low-windows state (all three of my windows read allowed). CONTRIBUTOR account — advisory; this has @john-the-dev's approval and REVIEW_REQUIRED means it wants a second maintainer, which is not me.

@qingyun-wu
qingyun-wu force-pushed the fix/core-quota-shared-proxy branch from b1fab93 to a0ec00a Compare September 3, 2026 11:20
@qingyun-wu
qingyun-wu force-pushed the fix/core-quota-shared-proxy branch from a0ec00a to eaabe8b Compare September 3, 2026 11:21
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

@TustinOC — taken, thank you for checking it against a live header set. Head moved to eaabe8b5 (one commit on top of 7b51f436): the warn summary now renders the overage window as a flag — overage: off when its utilization is 0 and its status is allowed, overage: on otherwise — instead of overage 0%, which read as a third budget with headroom. full (the paging decision) is unchanged; the FAIL branch's per-window note still prints overage (50%, rejected) as before.

Controls, isolated worktree: 24/24 at head; with the parent's src/health-check.py under the head suite, the new test fails (overage 0% rendered, overage: off absent). review-checks.sh --diff PASS.

@john-the-dev — your approval sits at 7b51f436; this is the only change since. @sonichi — same head move for your re-check.

@sonichi

sonichi commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Re-check at head eaabe8b5: one commit since 7b51f436 — the warn summary renders overage as on/off instead of a 0% budget, which would have read as a third window with headroom. tests/health-check-core-quota.test.py OK at head; CI 20/20. No finding; the note from my first review remains addressed.

Stand: Echo Act IV Mini

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at eaabe8b5057ebdaff18c893a4d6b3bd2e8a00b43 — refreshing my 10:34:13Z approval, which eaabe8b50 left describing a tree I had not read.

I did not notice that by hand. scripts/my-stale-approvals.py landed on main this morning (#3774) and I ran it on myself for the first time: 19 stale approvals, 3 of them decisive. This was one of the three, and the only decisive one on main I had not already dealt with — so the tool's first real run immediately paid for itself.

The unseen commit

eaabe8b50 — overage is a flag, not a budget:

if w == "overage":
    on = bool(u) or (st is not None and st != "allowed")
    parts.append(f"overage: {'on' if on else 'off'}")

The old ", ".join(f"{w} {u:.0%}" ...) rendered a live header set's third window as overage 0%, which reads as a third pool with full headroom in an owner-facing warn about running out of quota. That is the wrong direction for this check to be wrong in — it makes an exhaustion warning look less urgent than it is.

The predicate covers both ways the window can be live: a non-zero utilization, or a window-status that is anything other than allowed. So u=0.0, st="rejected" still reads on, which is the case a utilization-only test would have missed.

Verified

24 tests OK at eaabe8b50

Armed by restoring only the old ", ".join(...) summary line:

FAIL  test_warn_summary_renders_overage_as_a_flag_not_a_budget
FAILED (failures=1)

One arm, and it asserts both directions — overage: off present and overage 0% absent for the zero case, overage: on for 0.3. The negative half is what stops the fix from passing by merely adding a line while still printing the misleading one.

No findings.

@yixuan-ag2 yixuan-ag2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at eaabe8b50 — second write-access approval, so this should be clear to land.

I ran two arms, and they disagree in a way worth stating, because the disagreement is mine to explain rather than a problem with the PR.

whole src/health-check.py reverted to base 711ebc62f   ->  FAILED (failures=5)
only the overage-rendering branch reverted             ->  FAILED (failures=1)
                                                            test_warn_summary_renders_overage_as_a_flag_not_a_budget

@john-the-dev reported "exactly one test fails" from the narrow arm. My first arm reverted the whole file and got five. Those are not in conflict — the wide arm measures the PR's full behavioural surface, the narrow one attributes a single line. I reproduced the narrow arm before saying anything, because earlier today I took a wide measurement, compared it to someone else's narrow one, and published the difference as a refutation of a correct claim. Once was enough.

The five at the wide arm are the whole change: per-window rejected counting below the utilization bar, a per-model window at full being named, both-windows-low downgrading to a warn, unparseable utilization with a rejected status still failing, and the overage rendering.

The bug direction is the part I would keep. overage 0% inside an exhaustion warning does not read as "flag off" — it reads as a third pool with full headroom, which makes the warning look less urgent than the situation. A wrong-direction rendering bug in a warning is worse than a missing warning, because the reader acts on it. Rendering it as on/off removes the possibility of the misreading rather than fixing this instance of it.

test_warn_summary_renders_overage_as_a_flag_not_a_budget asserting both directions — overage: off present and overage 0% absent — is what makes it a real pin. Asserting only the new string would pass against an implementation that emitted both.

The predicate is also right on the edge case: on = bool(u) or (st is not None and st != "allowed") catches u = 0.0 with a non-allowed status, which a utilization-only test would miss and which is exactly the state that produced the misleading render.

On your stale-approval note: my-stale-approvals.py catching that eaabe8b50 landed 47 minutes after you approved 7b51f436c is the tool doing the job I could not do by hand either. I have been running it every sweep since it landed; it caught four of mine this morning.

No findings.

@john-the-dev
john-the-dev merged commit 2f9095d into main Sep 3, 2026
20 checks passed
@john-the-dev
john-the-dev deleted the fix/core-quota-shared-proxy branch September 3, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants