fix(proxy): bypass engages through installation excluded_models - #797
Conversation
The subscription usage-bypass gate refused to engage when the requested
model was on the installation's excluded_models list, even though the
bypass toggle ('strict pass-through until low') is an explicit opt-in
that overrides this policy exclusion. The effect: an org with bypass
turned on but a stale/broad excluded_models list saw Claude subscription
turns kicked out of the gate and routed to an OSS model paid by credits
instead of served on the caller's own subscription.
The fix splits the exclusion set carried on router.Request into two
layers:
- ExcludedModels — the full union (installation policy + request-time
safety filters), used by the scorer unchanged.
- SafetyExcludedModels (new) — only the hard, request-time constraints
a model cannot physically satisfy (context-window overflow,
gemini-unsigned-history). The bypass gate consults this set so a
customer who opted into pass-through is served on their own
subscription for models they merely deprioritized via excluded_models,
while a model that literally can't accept the request (overflow) still
blocks bypass.
Wired at both dispatch sites: ProxyMessages (Anthropic) and
ProxyOpenAIChatCompletion (OpenAI). The Gemini path and route-preview
correctly leave the new field nil — neither engages the bypass gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @steventohme's task —— View job
Posted 6 committable suggestions on 4 files. All advisory — won't block merge. |
workweave-bot
left a comment
There was a problem hiding this comment.
Advisory only — comment-length nits. Won't block merge.
What T-Rex did
Reviews (1): Last reviewed commit: "fix(proxy): bypass engages through insta..." | Re-trigger Greptile |
Cursor Bugbot caught a real gap in the first commit: SafetyExcludedModels was built from the routing-path overflow/gemini lists, but those filters seed the installation excluded_models set as their base and skip any model already in it. So a requested model that is BOTH policy-excluded AND over the context window never landed in SafetyExcludedModels — usage bypass engaged and sent the turn on the subscription, where the same overflow 400s. Replace safetyExcludedSet (which reused the routing lists) with Service.safetyExcludedModels, which re-runs both filters against an EMPTY base so overflow / gemini-unsigned are evaluated for every available model regardless of policy exclusion. Wired at both dispatch sites. Adds TestSafetyExcludedModels_CatchesPolicyExcludedOverflow, which exercises the real wiring (not a hand-built set) and asserts the routing list misses the both-excluded model while the safety set catches it. Also applies review-bot comment-concision passes across the changed files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @steventohme's task —— View job
Posted 4 committable suggestions on 4 files. All advisory — won't block merge. |
workweave-bot
left a comment
There was a problem hiding this comment.
Advisory only — comment-length nits. Won't block merge.
…t concision Addresses PR review feedback: - greptile P1 (defensive): runTurnLoop re-verifies an over-conservative context-window pin exclusion and, when the pin actually fits, prunes it from req.ExcludedModels. Also delete it from req.SafetyExcludedModels so a cleared context exclusion can't linger and refuse usage bypass for the pinned model. In practice the two are computed from the same estimate/window so the model is already absent — this makes the invariant explicit and robust if the fit formulas ever drift. delete on a nil map is a safe no-op. - Comment-concision passes (workweave-bot) across service.go, usage_bypass.go, and two test files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @steventohme's task —— View job
Posted 1 committable suggestion on |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f5b2550. Configure here.
workweave-bot
left a comment
There was a problem hiding this comment.
Advisory only — comment-length nit. Won't block merge.
workweave-bot
left a comment
There was a problem hiding this comment.
Advisory only — comment-length nit. Won't block merge.
Cursor Bugbot (medium) caught a real regression from this PR: the maxed-out loop-breaking guard in runTurnLoop adds the saturated model only to req.ExcludedModels. Before this PR the bypass gate checked ExcludedModels, so a re-request of the maxed subscription model was refused and forced to route, breaking the degenerate auto-continue loop. After the gate switched to SafetyExcludedModels, the maxed model was no longer barred — bypass re-engaged and reopened the loop on the subscription. Fix: both maxed-out guard blocks (active pin + HMM history) now also add the saturated model to req.SafetyExcludedModels via a new addToSet helper (copy-on-write; nil-safe). The maxed exclusion is a hard loop-breaking constraint, same class as context-overflow / gemini-unsigned, so it belongs in the set the bypass gate honors — unlike the installation excluded_models policy, which bypass may still override. Adds TestUsageBypass_MaxedOutModel_EngagesRouting: a bypass-enabled session whose pin maxed out last turn must fall through to the scorer on re-request, not re-bypass the saturated model. Also trims the pin-re-verify SafetyExcludedModels comment per review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @steventohme's task —— View job Claude finished @steventohme's task —— View job
Posted 3 committable suggestions on 2 files. All advisory — won't block merge. |
workweave-bot
left a comment
There was a problem hiding this comment.
Advisory only — comment-length nits. Won't block merge.
Comment-concision passes (workweave-bot) on addToSet, the maxed-out bypass guard, and TestUsageBypass_MaxedOutModel_EngagesRouting. No logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @steventohme's task —— View job
Posted 3 committable suggestions on 3 files. All advisory — won't block merge. |
workweave-bot
left a comment
There was a problem hiding this comment.
Advisory only — comment-length nits. Won't block merge.
Comment-concision passes (workweave-bot) on two test docs and the SafetyExcludedModels field comment. No logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @steventohme's task —— View job Claude finished @steventohme's task —— View job
No blocks worth flagging — all remaining 3+ line comment blocks are at or within the ~5-line target, and each explains a genuinely non-obvious WHY (empty-base re-run rationale, bypass gate distinction, loop-breaking constraint). No new suggestions posted. |

Problem
The subscription usage-bypass gate (the "strict pass-through until low" feature) refused to engage whenever the requested model was on the installation
excluded_modelslist. An org that opted into bypass but also had a stale/broadexcluded_modelspolicy saw every Claude turn kicked out of the gate and routed to a substituted OSS model paid by credits instead of served on the caller's own subscription — the opposite of what the toggle promises.usageBypassEngagedcheckedreq.ExcludedModels, which is the union of the customer'sexcluded_modelspolicy plus hard request-time safety filters (context-overflow, gemini-unsigned-history). The customer's policy list was leaking into the pass-through decision.Fix
Split the model-exclusion set carried on
router.Request:ExcludedModels(unchanged) — the full union of installation policy exclusions + request-time safety filters. The scorer continues to honor all of it.SafetyExcludedModels(new) — only the request-time safety filters. The bypass gate consults this set, so a customer who opted into pass-through is served on their own subscription for models they merely deprioritized viaexcluded_models, while a model that literally can't accept the request (context overflow) still blocks bypass.Wired at both dispatch sites:
ProxyMessages(Anthropic) andProxyOpenAIChatCompletion(OpenAI). The Gemini path and route-preview correctly leave the new field nil — neither engages the bypass gate.Verified
wv mr tc— typecheck cleanwv mr t— all proxy and router tests pass, including:TestUsageBypassEngaged_SafetyExclusionBlocks_PolicyExclusionDoesNot— 3 subtests: policy exclusion bypasses, safety exclusion still blocks, and the load-bearing both-excluded case (a model on the deny list that ALSO overflows the window stays blocked, since it would 400 on the subscription too).TestUsageBypass_InstallationExcludedModel_StillBypasses(renamed fromTestUsageBypass_ExcludedModel_EngagesRouting, asserts the new contract).TestUsageBypass_ExcludedProvider_EngagesRouting— provider exclusion is a separate check, untouched).go vet ./...cleangofmt -lcleanApplies universally to every installation with bypass enabled — no per-org config change.