Skip to content

feat(usage): redeem GLM Coding Plan Reset Cards from Provider Limits - #12754

Merged
diegosouzapw merged 8 commits into
cheaper-inference:release/v3.8.51from
insoln:feat/glm-reset-card
Sep 17, 2026
Merged

diegosouzapw merged 8 commits into
cheaper-inference:release/v3.8.51from
insoln:feat/glm-reset-card

Conversation

@insoln

@insoln insoln commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds listing and redemption of z.ai/GLM Coding Plan Reset Cards (5-hour and weekly windows) from the Provider Limits dashboard, for providers glm, glm-cn, glmt, and zai.
  • New wire layer open-sse/services/usage/glmResetCards.ts (list GET /api/biz/customer-package-reset/list?targetType=PERSONAL, redeem POST .../use) with Bearer auth, bigmodel-organization/bigmodel-project team headers, and 15 s AbortSignal.timeout per request. International host api.z.ai, China host open.bigmodel.cn.
  • Fail-closed envelope validation: success === true plus numeric code ∈ {0, 200}; list responses additionally require both fiveHourResets and weekResets to be arrays, so a truncated data: {} fails closed instead of parsing as an authoritative zero. Known code 1001 maps to 401. Public error messages are static or sanitized — no payload or credential text ever reaches a response body.
  • Card availability: rejects consumed|redeeming|redeemed|used|expired|unavailable statuses, available:false, and boolean consumed/redeemed flags. Timestamps parse both ISO and z.ai's timezone-less YYYY-MM-DD HH:mm:ss (as UTC, with component validation). Parseably expired cards are dropped, unknown expiries are kept, and usable cards sort earliest-expiry-first with unknown last.
  • Redemption semantics: a validated /use success is the irreversible commit boundary. A later quota-refresh failure returns { outcome: "reset", refreshPending: true } instead of HTTP 500 — committed success is never reported as a server error.
  • Process-local idempotency keyed by connectionId + normalized idempotency key and bound to the selected card: concurrent identical requests coalesce (including duplicates that arrive while the wire operation is already in flight), committed results replay for a short TTL, and a transport failure after card selection retains the selected card so a retry reissues the exact same /use body and requestId without relisting. Reusing one key for a different card returns 409 idempotency_key_conflict. Bounded to 500 entries / 10-minute TTL. This is process-local — it does not survive a restart and does not claim exactly-once semantics.
  • Proxy and lease semantics: list/use (and the post-commit refresh) run in the connection's proxy context when one is configured, and in an explicit direct context when none is — an ambient proxy context is never inherited. An unreachable or failed proxy fails closed with typed errors (proxy_unreachable 503, proxy_family_unavailable/proxy_request_failed 502, relay_timeout 504) and static messages, never falling back to direct. Each list/use wire operation is fenced by a short synthetic exclusive-connection lease (60 s, released in finally); while it is active the connection cannot simultaneously hold a real exclusive lease. A lease-conflict 409 does not discard a retained ambiguous-retry attempt.
  • getGlmUsage() requests the auxiliary card list only when the account has a resettable window (session or weekly quota), and emits bankedResetCredits on success. The count distinguishes authoritative zero (empty API key or confirmed empty list) from unknown (failed/truncated response → null); an unknown preserves the previously cached count, an authoritative zero clears it.
  • UI: zai is dispatched through the GLM-family parsing/order path; getResetCreditEndpoint() is exhaustive (Codex → its route, the four GLM aliases → the GLM route, anything else → no mutation endpoint). GLM cards are labeled by window ("5-hour window" / "weekly window"), never "Full reset". GLM-specific i18n keys with safe English fallbacks (only en.json edited; legacy Codex-specific es/pl strings remain valid on the Codex path). The seven new GLM keys are also mirrored into pt-BR.json and vi.json (3acf3d01a) — the locale-parity gates (i18n-pt-br fix: i18n drift — pt-BR (and other locales) missing/untranslated keys after update (194 missing, ~1792 untranslated) #6695, i18n-vi-completeness) require full key parity and were failing when only en.json was edited. The browser idempotency key persists across picker reopen after an ambiguous failure. Codex behavior and copy are unchanged. opencode-go keeps GLM-family parsing but has no redemption endpoint.

Related Issues

Validation

  • Change type: provider / routing / UI / i18n
  • Focused tests and category gates from the golden path
  • npm run lint
  • Reconciled with the current active release base; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR
  • SonarQube is temporarily opt-in while the private project has no quota; it is not a PR gate.

Commands run locally (worktree at 307ea7c97, base upstream/release/v3.8.51):

  • node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/glm-reset-cards.test.ts — 23/23 pass
  • Same runner over glm-reset-card-route, glm-reset-card-cache, hard-session-lease-bypass-inventory, provider-limits-ui, glm-team-quota — 63/63 pass
  • npm run test:unit — pass except documented inherited base-red failures (🔴 Release branch not green: release/v3.8.51 #12732) and unrelated sanitizer/stream tests; no GLM reset-card failures
  • npm run test:vitest — 463/465; the 2 failures are autoCombo/provider-family-combos and autoCombo/tieredRotation, which this PR does not touch (no files under open-sse/services/combo* or MCP changed) — inherited
  • npm run typecheck:core — clean
  • npm run typecheck:noimplicit:core — clean
  • npm run lint — 0 errors (stale-suppressions note is inherited)
  • npm run lint:json — pass
  • npm run quality:scan:fast — pass
  • npx prettier --check on touched files, git diff --check — clean
  • i18n fix (head 3acf3d01a): i18n-pt-br 3/3, i18n-vi-completeness 8/8, i18n-translation-ratio-gate 4/4, lint:json, Prettier — all pass. CI on 3acf3d01a confirms: the four prior PR-introduced locale-parity failures are fixed, zero new test failures appeared; the remaining red checks are byte-identical inherited base-red failures (🔴 Release branch not green: release/v3.8.51 #12732) present on both heads.

Tests Added Or Updated

  • tests/unit/glm-reset-cards.test.ts — full backend behavior: proxied/direct/ambient-context routing for list and redemption, dead-proxy fail-closed with typed 503 and no topology leakage, synthetic lease fencing + post-operation release, coalescing of concurrent and lease-window duplicates, ambiguous-transport retry with identical body/requestId, idempotency-key conflict, committed-success-with-failed-refresh, authoritative-zero vs unknown count, provider validation, availability/expiry/order parsing
  • tests/unit/glm-reset-card-route.test.ts — API route: auth-before-parsing, schema rejection, typed status/code propagation, sanitized 500, envelope-401 mapping, malformed upstream → 502
  • tests/unit/glm-reset-card-cache.test.ts — count merge semantics (unknown preserves cache, authoritative zero clears)
  • tests/unit/glm-team-quota.test.ts — getGlmUsage() integration (no request without resettable window, one with, empty-key, zero, transient unknown)
  • tests/unit/provider-limits-ui.test.ts — GLM-family ordering, modal copy/window labels, committed-refresh fallback, i18n keys with fallbacks, preserved Codex behavior
  • tests/unit/hard-session-lease-bypass-inventory.test.ts — new class-B entry for src/lib/usage/glmResetCards.ts (one connection lookup, classified beside codexResetCredits.ts)

Coverage Notes

  • Backend: glm-reset-cards.test.ts (23 tests), glm-reset-card-route.test.ts, glm-reset-card-cache.test.ts, glm-team-quota.test.ts.
  • UI: provider-limits-ui.test.ts (ordering, copy, fallback behavior, i18n).
  • No touched file's coverage decreased relative to its pre-PR state.

Reviewer Notes

  • ⚠️ base-red inherited: 🔴 Release branch not green: release/v3.8.51 #12732 — the untouched open-sse/executors/glm.ts TS2554 typecheck failure and the malformed unrelated changelog.d/fixes/reset-aware-model-family.md are present on the base branch and are not addressed here (base-red fixes belong in their own freeze-gated PR).
  • The redemption is irreversible; all retry/idempotency logic is process-local and bounded. After a process restart, a client retry with the same idempotency key relists and selects again — this is documented behavior, not durable exactly-once.
  • No migrations, no feature flags. New API surface: GET/POST /api/usage/glm-reset-card (management-authenticated, Zod-validated, sanitized errors).

z.ai sells Reset Cards that clear an exhausted GLM coding-plan window (5-hour or
weekly) ahead of its natural rollover, but OmniRoute only ever read the passive
nextResetTime, so redeeming one meant leaving the dashboard.

Add the wire layer for z.ai's two reset endpoints
(/api/biz/customer-package-reset/list and /use), which authenticate with the same
Bearer API key as /api/monitor/usage/quota/limit and report failures inside an
HTTP-200 envelope, so callers must inspect success/code rather than the status line.

The banked count rides along with the quota poll - only for keys that actually
report a resettable window, and strictly best-effort so a card-less account or a
transient failure still renders its quotas. The existing reset-credit card, picker
and confirmation flow, until now gated to Codex, now also drive glm/glm-cn/glmt/zai
through the new /api/usage/glm-reset-card route, reusing z.ai's requestId as the
idempotency key so a retry cannot burn two cards.
@insoln
insoln requested a review from diegosouzapw as a code owner September 4, 2026 23:16
… duplicates

- Acquire a synthetic 60s exclusive-connection lease around each list/use
  wire operation; release in finally so a competing lease can acquire
  immediately after success or failure.
- Coalesce same-key duplicates that arrive after lease acquisition by
  checking the in-flight attempt before loading the connection.
- Run the post-commit quota refresh outside the lease (redemption is
  already committed; the refresh is auxiliary and failure-tolerant).
- Do not discard a retained ambiguous attempt on a lease-conflict 409.
- Harden transport error mapping: static messages for proxy transport
  failures, keep explicit direct routing for unproxied connections
  through list, use, and refresh.
@diegosouzapw

Copy link
Copy Markdown
Collaborator

This is a thorough, well-engineered PR — the fail-closed envelope validation, the
idempotency/lease design (especially retaining the selected card across a transport failure
so a retry reissues the exact same /use body), and the "committed success is never
reported as a server error" semantics are all exactly right for an action that spends a real,
irreversible upstream resource. 45/45 tests pass locally, fully mocked. No code blockers
found. One optional suggestion before flipping this on for real users: a one-time live
validation against a z.ai account with an actual reset card available would be a nice
addition to the evidence trail (similar to what #13623 in this same batch included), given
the nature of the action — but the test suite's mutation coverage already gives solid
confidence in the logic itself.

# Conflicts:
#	src/app/(dashboard)/dashboard/usage/components/ProviderLimits/quotaParsing.ts
#	src/app/(dashboard)/dashboard/usage/components/ProviderLimits/useCodexResetCreditRedemption.ts
#	src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx
#	tests/unit/hard-session-lease-bypass-inventory.test.ts
@diegosouzapw
diegosouzapw merged commit 241e63b into cheaper-inference:release/v3.8.51 Sep 17, 2026
9 of 16 checks passed
diegosouzapw added a commit that referenced this pull request Sep 18, 2026
…14079)

The gate fails on the pure release/v3.8.51 tip (3 files above the frozen
baseline), so every PR against the release is born red on it. None of the
three is a PR defect — they are drift from merged work:

- src/lib/usage/glmResetCards.ts: entered the gate's scope when #12754 added
  the route that imports it. runWithProxyContext is an untyped async helper
  (Promise<any>), so runWithConnectionFetch<T> could not return T. Every call
  site passes an async callback and awaits it — declare that contract:
  fn: () => Promise<T> → Promise<T>.
- src/sse/handlers/chat.ts: handleSingleModelChat had no return annotation, so
  runWithTransientBackendRetry<T extends ResponseLike> fell back to the
  constraint and the value could no longer feed withSessionHeader(Response).
  Annotated Promise<Response> (every return path builds a Response).
- src/app/api/internal/codex-responses-ws/route.ts: the bridge helpers return
  either { error: Response } or a payload, but as unannotated object-literal
  unions TypeScript synthesised error?: undefined on the success member, and
  every "error" in x guard stopped narrowing (TS2339 x5 on the destructure).
  Explicit return types keep the discriminant real; the ApiKeyMetadata alias now
  points at the policy shape (the wider one both sources are assignable to),
  which clears the TS2740 self-mismatch; logger.warn → log.warn (the module
  logger factory has no .warn).

check-api-typecheck.mjs: OK — 283 errors, baseline ratcheted DOWN from 294
(codex-responses-ws 7→4 TS2339, TS2740 1→0; combos/test and keys/[id] 1→0).
typecheck:core clean; 43/43 unit tests in the touched areas green.
diegosouzapw added a commit that referenced this pull request Sep 18, 2026
…the write, drain the 09-18 base-reds (#14101)

* fix(quality): drain the 09-18 base-reds, part 1 — thinking gate parity, inventory, webpack externals

Reproduced on the clean tip 7cc454d before touching anything.

Five of the failures trace to one commit, #12905 (b7192b7): it gated
thinking-block emission on `requestedThinking === true` in the streaming
translator, while its own non-streaming path documents `undefined` as the
legacy caller shape that keeps "always a thinking block". The two paths
disagreed on the same input, and the streaming side also synthesized the
reasoning into a TEXT block for that legacy shape. chatCore always resolves a
boolean, so production never sends `undefined` — but every direct caller and
the older #5786 suites do. Aligned the streaming gate to the documented
tri-state: `false` suppresses, `true` and `undefined` relay, and the fix-B text
synthesis fires only on an explicit opt-out. The #12905 test that asserted
suppression used a bare createState() (`undefined`) to mean "client did not
request thinking"; it now passes `requestedThinking: false`, which is what that
sentence resolves to in production. The whole thinking family — dsml, adapter,
translator, non-stream parity, #13620, #5786, markdown boundary — is 77/77.

#12864 added requestRejectedFailure.ts with a getProviderConnectionById read
that seeds the refusal streak across restarts; inventoried as a connection
state read next to the family-cooldown site it resembles.

#13909 made machineToken.ts import ./dataPaths; the isolated webpack compile has
no repo tree, so it joins the sibling externals.

The free-tier budget card SVG was one wave behind again (482 -> 491 models).

Refs #13866

* fix(sse): restore maxQueueDepth=0 as unbounded, sanitize refusals at the write, drain the rest

Part 2 of the 09-18 base-red drain. Two of the remaining failures were not
stale tests but production defects the tests had caught.

#12911 taught accountSemaphore to read `maxQueueSize: 0` as "reject when the
slot is busy", which is what its Codex WS lease wants. But chatCore forwards
`resilienceSettings.requestQueue.maxQueueDepth` into that option, and that
setting's documented default since #6593 is `0 = disabled`. Under default
settings every request that found its account slot occupied was answered
429 "Semaphore queue full (0)" instead of waiting — the managed-lease routing
test saw exactly that. `0` (and any non-positive value) is unbounded again;
the lease gets an explicit `failFast` option and its four tests stay green, so
the #12911 behaviour is preserved where it was meant to apply. A contract test
pins the #6593 semantics on the semaphore itself.

#12864 moved two providerFailure persistence branches out of chatCore into
requestRejectedFailure.ts and the sanitization did not travel with them: three
`lastError` writes stored the message as received. The only caller already
hands in the projected persistentMessage, so nothing leaks today, but a
persistence branch must be safe at its own write (docs/security/
ERROR_SANITIZATION.md) rather than trust whoever calls it. The module now
sanitizes on entry, and the public-boundary guard — which caught this by
counting sanitized writes in chatCore and coming up two short — covers the
extracted module too, verified by mutating one write back to raw.

The rest are tests that had fallen behind legitimate changes:

- #12905 inserted `requestedThinking` as the 14th positional argument of
  createSSETransformStreamWithLogger; two tests passed customToolNames or the
  buffer budget at their old positions. Both production callers were already
  correct.
- #12754 added a per-connection reset-card fetch after the quota fetch; the
  spacing test now marks a chunk at the quota request only.
- #13910 renamed `error` to `errorMetadata` in the timeout classification; the
  probe matches the identifier with a backreference and still fails when
  BodyTimeoutError is removed from both sites.

Refs #13866

* fix(test): pin the opt-out thinking cases to requestedThinking=false; keep acquireMany under the complexity ceiling

The #12905 gate-restore suite encoded 'requestedThinking absent' as opt-out, the
same undefined-means-false shape its non-streaming twin documents the other way
and that the two-month-old #5786 suites contradict. The three opt-out cases now
set the flag explicitly, which is what chatCore resolves for an opted-out
client; the two opt-in cases already did. Both suites pass together (27/27).

The failFast branch pushed acquireMany over the complexity ceiling it already
sat on; the admission policy (fail-fast / bounded / unbounded queue) moves to
findQueueRejection() and the new-code ratchet is back at its base.

Refs #13866

* fix(test): suspend the #14110 redaction assertion inline; refresh the budget card

The 57 commits merged since the previous validation moved two things.

#13295 changed how an unknown-root path with an ambiguous tail is answered:
where `Provider failed at /custom/internal secret directory` used to become
`Provider failed at <path>` it now ships verbatim. The #12506 boundary guard
caught it. Two candidate fixes were tried and each breaks one of the two live
contracts — #12506's fail-closed swallow, or #13144's rule that a route in
prose must survive — so the choice is the owner's (#14110). The one contested
assertion is suspended inline with the exact line and the issue; the other
nine stay active. The isolated-child harness requires tests == pass, which is
why it is a comment and not a todo.

The free-tier budget card was one wave behind again (491 -> 489 models).

Refs #13866, #14110

* fix(providers): type the TinyCMS DOM stub global as a loose record

#13957 typed the mock global as `typeof globalThis & Record<string, unknown>`.
The api-route typecheck loads lib.dom, so that intersection carries the real
Window / HTMLCanvasElement / document signatures — every stub assignment fails
against a DOM constructor, and `delete g.window` narrows the object to
`never` (13 diagnostics, the API Route Typecheck base-red on the tip). The
function exists to overwrite those globals with stubs; it is now typed as the
plain record it manipulates. 29/29 tinycms tests unchanged.

Refs #13866

* fix(test): pin the last opt-out thinking sibling to requestedThinking=false

translator-reasoning-gate-502-repro is the third #12905 test that encoded a bare
state as opt-out; the previous sweep matched files by glob and missed it. The
family is now enumerated by grep on requestedThinking (7 files) plus the two
pre-#12905 suites: 83/83 together.

Refs #13866
@diegosouzapw diegosouzapw mentioned this pull request Sep 21, 2026
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…heaper-inference#12754)

* feat(usage): redeem GLM Coding Plan Reset Cards from Provider Limits

z.ai sells Reset Cards that clear an exhausted GLM coding-plan window (5-hour or
weekly) ahead of its natural rollover, but OmniRoute only ever read the passive
nextResetTime, so redeeming one meant leaving the dashboard.

Add the wire layer for z.ai's two reset endpoints
(/api/biz/customer-package-reset/list and /use), which authenticate with the same
Bearer API key as /api/monitor/usage/quota/limit and report failures inside an
HTTP-200 envelope, so callers must inspect success/code rather than the status line.

The banked count rides along with the quota poll - only for keys that actually
report a resettable window, and strictly best-effort so a card-less account or a
transient failure still renders its quotas. The existing reset-credit card, picker
and confirmation flow, until now gated to Codex, now also drive glm/glm-cn/glmt/zai
through the new /api/usage/glm-reset-card route, reusing z.ai's requestId as the
idempotency key so a retry cannot burn two cards.

* test(usage): cover GLM reset-card edge cases

* test(dashboard): require GLM reset-card copy

* fix(usage): harden GLM reset-card redemption

* fix(usage): treat missing GLM key as empty

* fix(usage): fence GLM reset-card operations and coalesce lease-window duplicates

- Acquire a synthetic 60s exclusive-connection lease around each list/use
  wire operation; release in finally so a competing lease can acquire
  immediately after success or failure.
- Coalesce same-key duplicates that arrive after lease acquisition by
  checking the in-flight attempt before loading the connection.
- Run the post-commit quota refresh outside the lease (redemption is
  already committed; the refresh is auxiliary and failure-tolerant).
- Do not discard a retained ambiguous attempt on a lease-conflict 409.
- Harden transport error mapping: static messages for proxy transport
  failures, keep explicit direct routing for unproxied connections
  through list, use, and refresh.

* fix(i18n): sync GLM reset-card keys to pt-BR and vi locales

---------

Co-authored-by: insoln <is@careerum.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…heaper-inference#14079)

The gate fails on the pure release/v3.8.51 tip (3 files above the frozen
baseline), so every PR against the release is born red on it. None of the
three is a PR defect — they are drift from merged work:

- src/lib/usage/glmResetCards.ts: entered the gate's scope when cheaper-inference#12754 added
  the route that imports it. runWithProxyContext is an untyped async helper
  (Promise<any>), so runWithConnectionFetch<T> could not return T. Every call
  site passes an async callback and awaits it — declare that contract:
  fn: () => Promise<T> → Promise<T>.
- src/sse/handlers/chat.ts: handleSingleModelChat had no return annotation, so
  runWithTransientBackendRetry<T extends ResponseLike> fell back to the
  constraint and the value could no longer feed withSessionHeader(Response).
  Annotated Promise<Response> (every return path builds a Response).
- src/app/api/internal/codex-responses-ws/route.ts: the bridge helpers return
  either { error: Response } or a payload, but as unannotated object-literal
  unions TypeScript synthesised error?: undefined on the success member, and
  every "error" in x guard stopped narrowing (TS2339 x5 on the destructure).
  Explicit return types keep the discriminant real; the ApiKeyMetadata alias now
  points at the policy shape (the wider one both sources are assignable to),
  which clears the TS2740 self-mismatch; logger.warn → log.warn (the module
  logger factory has no .warn).

check-api-typecheck.mjs: OK — 283 errors, baseline ratcheted DOWN from 294
(codex-responses-ws 7→4 TS2339, TS2740 1→0; combos/test and keys/[id] 1→0).
typecheck:core clean; 43/43 unit tests in the touched areas green.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…the write, drain the 09-18 base-reds (cheaper-inference#14101)

* fix(quality): drain the 09-18 base-reds, part 1 — thinking gate parity, inventory, webpack externals

Reproduced on the clean tip 7784784 before touching anything.

Five of the failures trace to one commit, cheaper-inference#12905 (8b5f4dd): it gated
thinking-block emission on `requestedThinking === true` in the streaming
translator, while its own non-streaming path documents `undefined` as the
legacy caller shape that keeps "always a thinking block". The two paths
disagreed on the same input, and the streaming side also synthesized the
reasoning into a TEXT block for that legacy shape. chatCore always resolves a
boolean, so production never sends `undefined` — but every direct caller and
the older cheaper-inference#5786 suites do. Aligned the streaming gate to the documented
tri-state: `false` suppresses, `true` and `undefined` relay, and the fix-B text
synthesis fires only on an explicit opt-out. The cheaper-inference#12905 test that asserted
suppression used a bare createState() (`undefined`) to mean "client did not
request thinking"; it now passes `requestedThinking: false`, which is what that
sentence resolves to in production. The whole thinking family — dsml, adapter,
translator, non-stream parity, cheaper-inference#13620, cheaper-inference#5786, markdown boundary — is 77/77.

cheaper-inference#12864 added requestRejectedFailure.ts with a getProviderConnectionById read
that seeds the refusal streak across restarts; inventoried as a connection
state read next to the family-cooldown site it resembles.

cheaper-inference#13909 made machineToken.ts import ./dataPaths; the isolated webpack compile has
no repo tree, so it joins the sibling externals.

The free-tier budget card SVG was one wave behind again (482 -> 491 models).

Refs cheaper-inference#13866

* fix(sse): restore maxQueueDepth=0 as unbounded, sanitize refusals at the write, drain the rest

Part 2 of the 09-18 base-red drain. Two of the remaining failures were not
stale tests but production defects the tests had caught.

cheaper-inference#12911 taught accountSemaphore to read `maxQueueSize: 0` as "reject when the
slot is busy", which is what its Codex WS lease wants. But chatCore forwards
`resilienceSettings.requestQueue.maxQueueDepth` into that option, and that
setting's documented default since cheaper-inference#6593 is `0 = disabled`. Under default
settings every request that found its account slot occupied was answered
429 "Semaphore queue full (0)" instead of waiting — the managed-lease routing
test saw exactly that. `0` (and any non-positive value) is unbounded again;
the lease gets an explicit `failFast` option and its four tests stay green, so
the cheaper-inference#12911 behaviour is preserved where it was meant to apply. A contract test
pins the cheaper-inference#6593 semantics on the semaphore itself.

cheaper-inference#12864 moved two providerFailure persistence branches out of chatCore into
requestRejectedFailure.ts and the sanitization did not travel with them: three
`lastError` writes stored the message as received. The only caller already
hands in the projected persistentMessage, so nothing leaks today, but a
persistence branch must be safe at its own write (docs/security/
ERROR_SANITIZATION.md) rather than trust whoever calls it. The module now
sanitizes on entry, and the public-boundary guard — which caught this by
counting sanitized writes in chatCore and coming up two short — covers the
extracted module too, verified by mutating one write back to raw.

The rest are tests that had fallen behind legitimate changes:

- cheaper-inference#12905 inserted `requestedThinking` as the 14th positional argument of
  createSSETransformStreamWithLogger; two tests passed customToolNames or the
  buffer budget at their old positions. Both production callers were already
  correct.
- cheaper-inference#12754 added a per-connection reset-card fetch after the quota fetch; the
  spacing test now marks a chunk at the quota request only.
- cheaper-inference#13910 renamed `error` to `errorMetadata` in the timeout classification; the
  probe matches the identifier with a backreference and still fails when
  BodyTimeoutError is removed from both sites.

Refs cheaper-inference#13866

* fix(test): pin the opt-out thinking cases to requestedThinking=false; keep acquireMany under the complexity ceiling

The cheaper-inference#12905 gate-restore suite encoded 'requestedThinking absent' as opt-out, the
same undefined-means-false shape its non-streaming twin documents the other way
and that the two-month-old cheaper-inference#5786 suites contradict. The three opt-out cases now
set the flag explicitly, which is what chatCore resolves for an opted-out
client; the two opt-in cases already did. Both suites pass together (27/27).

The failFast branch pushed acquireMany over the complexity ceiling it already
sat on; the admission policy (fail-fast / bounded / unbounded queue) moves to
findQueueRejection() and the new-code ratchet is back at its base.

Refs cheaper-inference#13866

* fix(test): suspend the cheaper-inference#14110 redaction assertion inline; refresh the budget card

The 57 commits merged since the previous validation moved two things.

cheaper-inference#13295 changed how an unknown-root path with an ambiguous tail is answered:
where `Provider failed at /custom/internal secret directory` used to become
`Provider failed at <path>` it now ships verbatim. The cheaper-inference#12506 boundary guard
caught it. Two candidate fixes were tried and each breaks one of the two live
contracts — cheaper-inference#12506's fail-closed swallow, or cheaper-inference#13144's rule that a route in
prose must survive — so the choice is the owner's (cheaper-inference#14110). The one contested
assertion is suspended inline with the exact line and the issue; the other
nine stay active. The isolated-child harness requires tests == pass, which is
why it is a comment and not a todo.

The free-tier budget card was one wave behind again (491 -> 489 models).

Refs cheaper-inference#13866, cheaper-inference#14110

* fix(providers): type the TinyCMS DOM stub global as a loose record

cheaper-inference#13957 typed the mock global as `typeof globalThis & Record<string, unknown>`.
The api-route typecheck loads lib.dom, so that intersection carries the real
Window / HTMLCanvasElement / document signatures — every stub assignment fails
against a DOM constructor, and `delete g.window` narrows the object to
`never` (13 diagnostics, the API Route Typecheck base-red on the tip). The
function exists to overwrite those globals with stubs; it is now typed as the
plain record it manipulates. 29/29 tinycms tests unchanged.

Refs cheaper-inference#13866

* fix(test): pin the last opt-out thinking sibling to requestedThinking=false

translator-reasoning-gate-502-repro is the third cheaper-inference#12905 test that encoded a bare
state as opt-out; the previous sweep matched files by glob and missed it. The
family is now enumerated by grep on requestedThinking (7 files) plus the two
pre-cheaper-inference#12905 suites: 83/83 together.

Refs cheaper-inference#13866
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.

2 participants