Skip to content

Review only: HOUSE-79 our fork's changes on upstream v8.0.44 - #26

Closed
ericlitman wants to merge 1 commit into
review/house79/fork-delta-basefrom
review/house79/fork-delta
Closed

ericlitman wants to merge 1 commit into
review/house79/fork-delta-basefrom
review/house79/fork-delta

Conversation

@ericlitman

@ericlitman ericlitman commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Review only. Do not merge. This is the review of our own changes in #13, the merge of upstream v8.0.44 into the fork. That PR's own diff (966 files, almost all upstream) is too large for one review round. Upstream's code is out of scope: our goal is to validate our changes.

  • The head tree is identical to Merge upstream v8.0.44 into the fork (HOUSE-79) #13's head tree (c88d701f). The base is that tree with only our changed production files restored to upstream v8.0.44. So this diff is our fork's delta on the new base, and the surrounding code is upstream v8.0.44.
  • What our delta does:
    • adds the soonest-reset selector, quota observations and timed usage reads, and per-account quota reserves;
    • injects the Terra model;
    • applies the dependency patches;
    • carries the conflict resolutions from merging v8.0.44, in conductor_selection.go, conductor_lifecycle.go, config_v8.go, service_config.go and codex_executor_request.go;
    • fixes reserve windows so they survive upstream's quota restore (conductor_lifecycle.go).
  • These are left out of the diff: our tests (they pass in CI on this tree), the e2e suite, go.sum, docs and workflows.
  • Report defects in our changes, including places where they break against the upstream v8.0.44 code around them. Do not report defects in upstream code that our changes do not touch.

Linear: HOUSE-79

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@mergify

mergify Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@unfret

unfret Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🛑 10 blocking findings

🟡 Medium · Newer quota observations can be overwritten by stale refreshes

🟡 Medium · In-flight polls can overwrite newer quota evidence

🟡 Medium · Bind usage results to the credential registration

🟡 Medium · Do not resurrect explicitly inactive quota windows

🟡 Medium · Idle Claude 5h window permanently trips quota reserve

🟡 Medium · Version persisted quota observations by observation time

🟡 Medium · Decode compressed Codex usage responses

🟡 Medium · Normalize provider names before scheduling usage reads

🟡 Medium · Preserve resolved-model availability in soonest-reset

🟡 Medium · Keep a 100% reserve enforced after quota resets

Walkthrough

This review-only PR isolates the fork's own changes on top of upstream v8.0.44, so they can be checked apart from the 966-file upstream merge in #13. It adds a `soonest-reset` routing strategy that prefers the account whose weekly quota resets first. It also adds a background job that reads Claude and Codex usage endpoints every 5 minutes, and per-account `quota-reserves` that hold back a percentage of an account's quota. Quota observations and reserve windows are saved alongside cooldown state and kept through auth updates. The PR also injects the `gpt-6.1-terra` Codex model, bumps `klauspost/compress`, and carries the merge conflict resolutions. Tests are excluded from this diff; the author reports they pass in CI on this tree.

Area What changes Review notes
Soonest-reset selector New selector ranks candidates by weekly reset time taken from Claude and Codex rate-limit signals. It skips accounts with an exhausted window whose reset is still ahead. Unknown Claude resets sort first and unknown Codex resets sort last. Preserve resolved-model availability in soonest-reset
Background usage reads Service polls `/api/oauth/usage` for Claude and `wham/usage` for Codex on each OAuth credential. It uses CLI-shaped headers and a 30s timeout per read, maps the response into rate-limit headers, and records the result through `ObserveQuotaHeaders`. In-flight polls can overwrite newer quota evidence
Bind usage results to the credential registration
Decode compressed Codex usage responses
Normalize provider names before scheduling usage reads
Quota reserves New `routing.quota-reserves` config, matched by provider and email, excludes an account once any tracked window reaches 100 minus the reserve percent. It disables the scheduler fast path and is rejected when home mode is enabled. Idle Claude 5h window permanently trips quota reserve
Keep a 100% reserve enforced after quota resets
Do not resurrect explicitly inactive quota windows
Observation persistence and merge Quota signals and reserve windows are written into cooldown state records, including `observed`-only records, and restored on load. Auth updates and upstream quota restore now carry `ReserveWindows` forward. Newer quota observations can be overwritten by stale refreshes
Version persisted quota observations by observation time
Other fork patches Injects the Terra model into the Codex catalog and treats Anthropic `thread_not_found` as a request fault. Also wraps gitstore missing-object errors, bumps `klauspost/compress`, and adds `.mergify.yml`.
Since the last review
  • New: Newer quota observations can be overwritten by stale refreshes
  • New: In-flight polls can overwrite newer quota evidence
  • New: Bind usage results to the credential registration
  • New: Do not resurrect explicitly inactive quota windows
  • New: Idle Claude 5h window permanently trips quota reserve
  • New: Version persisted quota observations by observation time
  • New: Decode compressed Codex usage responses
  • New: Normalize provider names before scheduling usage reads
  • New: Preserve resolved-model availability in soonest-reset
  • New: Keep a 100% reserve enforced after quota resets
Repair prompt

Read the Unfret check run for mobilyze-llc/CLIProxyAPI#26 at head e43ac725b05d21b032e0eee21775ed60b7151bc8 (run panel:e43ac725b05d21b032e0eee21775ed60b7151bc8:LpjlL_kSYND:n6pYYCxOCro, check). Use the check's App id and GET /repos/mobilyze-llc/CLIProxyAPI/check-runs/122413334877; its output.text contains the structured result. Compare it with the current code and head, verify that each listed mandatory finding still applies, repair only those that do, honor the repository constraints, and validate the changes. Leave advisories and accepted risks alone. A manually resolved thread is not a waiver.

Details
Lane / stage Model Effort Outcome Duration
broad-gpt-5.6-sol-xhigh / review openai/gpt-5.6-sol → gpt-5.6-sol · primary xhigh succeeded 9 min 55 s
broad-opus-5.5-max / review anthropic/claude-opus-5-5 → claude-opus-5-5 · primary max succeeded 17 min 41 s
focal-gpt-5.6-sol-xhigh copy / review openai/gpt-6.1-sol → gpt-6.1-sol · primary xhigh succeeded 14 min 42 s
focal-gpt-5.6-sol-xhigh copy 2 / review openai/gpt-6.1-sol → gpt-6.1-sol · primary xhigh succeeded 17 min 11 s
judge / judge openai/gpt-6-astra → gpt-6-astra · primary high succeeded 2 min 3 s

First review of this pull request.
4 reviewers finished.
Commit e43ac72.
Check run.
Run: GET /unfret/run/panel:e43ac725b05d21b032e0eee21775ed60b7151bc8:LpjlL_kSYND:n6pYYCxOCro.

@unfret review · @unfret status · @unfret help

@unfret unfret Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 10 blocking findings
🟡 Medium · Newer quota observations can be overwritten by stale refreshes
🟡 Medium · In-flight polls can overwrite newer quota evidence
🟡 Medium · Bind usage results to the credential registration
🟡 Medium · Do not resurrect explicitly inactive quota windows
🟡 Medium · Idle Claude 5h window permanently trips quota reserve
🟡 Medium · Version persisted quota observations by observation time
🟡 Medium · Decode compressed Codex usage responses
🟡 Medium · Normalize provider names before scheduling usage reads
🟡 Medium · Preserve resolved-model availability in soonest-reset
🟡 Medium · Keep a 100% reserve enforced after quota resets
Check

Comment on lines +413 to +414
} else if auth.Quota.ObservedAt.IsZero() {
auth.Quota = mergeQuotaObservation(auth.Quota, existing.Quota)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Newer quota observations can be overwritten by stale refreshes

A refresh/update can carry an older non-zero Quota.ObservedAt while a concurrent usage read has already written a newer observation to the registered auth. Because this branch merges the existing quota only when the incoming timestamp is zero, the stale update replaces the newer Signals and ReserveWindows, causing soonest-reset or quota-reserve routing to use stale data until another observation arrives.

}
m.mu.Lock()
auth := m.auths[authID]
observed := auth != nil && auth.Quota.observeHeaders(auth.Provider, headers, time.Now(), true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · In-flight polls can overwrite newer quota evidence

A usage read can capture 9% usage, then finish after an inference response records 95%. This line timestamps the older reading at completion, replacing the newer Signals and ReserveWindows. With a 20% reserve, the credential becomes selectable again despite the newer evidence exceeding the 80% threshold. Poll commits need a freshness check against observations received since the read started.

log.Warnf("quota usage read failed for auth %s: %v", authID, errRead)
return
}
s.coreManager.ObserveQuotaHeaders(authID, observed)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Bind usage results to the credential registration

If a credential is removed and re-registered under the same ID for another account while ReadQuotaUsage is pending, this call attaches the old account's response to the replacement. A low reading can therefore admit a previously unobserved protected account and bypass its reserve. The commit does not validate the RegistrationEpoch or CredentialVersion of the credential used for the read. Also reported for this defect: - Reject usage reads from superseded credentials (sdk/cliproxy/service_quota.go:70): If an auth is removed and re-registered under the same ID while ReadQuotaUsage is in flight, this call applies the old account's response to the replacement credential. A delayed 9%-used response can overwrite the replacement's 95%-used observation and admit it despite a 20% reserve. Unlike MarkResult, this path does not check the captured registration epoch or credential version before committing.

Comment on lines +105 to +106
if current, exists := merged[name]; !exists || !window.ObservedAt.Before(current.ObservedAt) {
merged[name] = window

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Do not resurrect explicitly inactive quota windows

A refresh can snapshot a secondary window at 95%, then a newer zero-minute placeholder deletes that window before the refresh completes. This merge unconditionally accepts the older source entry because the target key is absent. MergeRefreshedAuth thus resurrects the inactive window and incorrectly blocks the now-weekly-only credential until another explicit deletion or the old reset. Also reported for this defect: - Preserve inactive-window removals during quota merges (sdk/cliproxy/auth/quota_reserve_history.go:105-106): An explicit Codex Window-Minutes: 0 observation deletes the corresponding reserve window, but this merge treats its absence as permission to restore any older source value. If a token refresh started before that observation, MergeRefreshedAuth resurrects the deleted window from its snapshot. For example, an obsolete 95%-used secondary window again excludes an otherwise eligible weekly-only credential until the obsolete reset expires.

Comment on lines +68 to +69
} else if next.UsedPercent == 0 && !rejected {
next.UsageTrusted = false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Idle Claude 5h window permanently trips quota reserve

Scenario: a Claude OAuth credential is listed in routing.quota-reserves (any percent > 0) and has no prior reserve history. That happens after a restart with the default save-cooldown-status:false, or for a newly added credential. The account is idle, so the startup usage read returns five_hour {utilization: 0, resets_at: null}. What happens: ParseClaudeUsageHeaders emits only the 5h Utilization header. observeReserveWindows reaches this else-if with no previous entry and stores the 5h window with UsageTrusted=false and a zero ResetAt. quotaReserveReached (quota_reserve.go:33-37) does not skip zero-reset windows and returns true for any untrusted window. The credential is therefore dropped from every pickNextLegacy, pickNextMixedLegacy and pickViaBuiltinScheduler candidate list. Why it never clears: excluded credentials get no inference, so the proxy never starts their 5h window. Every later read still reports 0 with no reset, and line 59 (exists && !rejected && (... || next.UsedPercent == 0) → continue) discards it. The block lifts only if someone uses the account outside the proxy. Result: an account with its full quota is never routed to, and requests fail with auth_not_found if it is the only candidate. The same account with a persisted, past-reset history entry is allowed, so the outcome depends on persistence rather than actual usage.

Model: model,
Status: cooldownStateStatusObserved,
Quota: QuotaState{ObservedAt: observation.ObservedAt, Signals: observation.Signals, ReserveWindows: observation.ReserveWindows},
UpdatedAt: updatedAt,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Version persisted quota observations by observation time

Usage polls advance quota observations without advancing auth/model UpdatedAt, but observation-only records use that older timestamp. With two instances sharing PostgreSQL, a request on one instance can advance the stored record beyond the other instance's timestamp; subsequent fresh polls on the latter are silently rejected by the store's timestamp-guarded upsert. Restart then restores the older quota. The existing-record attachment branch at lines 741–744 likewise leaves the persistence version unchanged.

Comment on lines +43 to +47
body, errRead := io.ReadAll(resp.Body)
if errRead != nil {
return nil, fmt.Errorf("read codex usage response: %w", errRead)
}
return helps.ParseCodexUsageHeaders(body), nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Decode compressed Codex usage responses

The identity helper forwards credential-specific Accept-Encoding headers. When an operator configures gzip or another supported encoding, net/http does not automatically decompress the explicitly requested encoding. Unlike the inference request path, this method reads and parses the compressed bytes directly. A compressed 200 response consequently returns nil observation headers without an error, leaving quota routing stale or a previously unobserved reserved credential permanently excluded.

if !ok || auth.Disabled || auth.AuthKind() != coreauth.AuthKindOAuth || !auth.HasValidAccessToken(time.Now()) {
return
}
if auth.Provider != "codex" && auth.Provider != "claude" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Normalize provider names before scheduling usage reads

Provider dispatch elsewhere accepts case-insensitive provider names, but this exact comparison skips an otherwise valid OAuth auth whose Provider is, for example, "Claude" or "Codex". Quota-reserve matching is case-insensitive, so such an auth with no prior windows is excluded while this reader permanently refuses to populate those windows, resulting in auth_not_found for requests using that credential.

func (s *SoonestResetSelector) Pick(ctx context.Context, provider, model string, opts cliproxyexecutor.Options, auths []*Auth) (*Auth, error) {
_ = opts
now := time.Now()
available, err := getSelectorAvailableAuths(ctx, auths, provider, model, now)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Preserve resolved-model availability in soonest-reset

Without session affinity, the manager checks each credential's resolved upstream model, but this call checks availability again using the client-visible alias: SoonestResetSelector is not given the prevalidated-candidates context. Retargeting alias A to healthy model B while A retains a cooldown consequently rejects otherwise eligible credentials with a model_cooldown 429.

Comment on lines +33 to +34
if !window.ResetAt.IsZero() && !window.ResetAt.After(now) {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Keep a 100% reserve enforced after quota resets

Validation accepts a 100% reserve, which leaves no quota available to proxy requests. Once every recorded window's reset passes, however, this continue skips every threshold check and quotaReserveReached returns false. Both Claude and Codex credentials can then be selected and consume their newly reset quota despite the full reserve, until another usable observation arrives.

@ericlitman

Copy link
Copy Markdown
Contributor Author

Review complete. Its head tree is identical to PR #13's, which merged as 81ed1aa and deployed to studio2 as v8.0.44-mob.1. The 10 findings are adjudicated in the HOUSE-79 records; none is material for our deployment.

@ericlitman ericlitman closed this Oct 27, 2026
@ericlitman
ericlitman deleted the review/house79/fork-delta branch October 27, 2026 21:08
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.

1 participant