sanitizeGroupMessagesDispatchFields forces AllowMessagesDispatch=false for
every non-openai platform including composite, and the gate checked only the
group platform — so composite requests resolved to grok/CN targets were
rejected 403 on /v1/messages and /v1/messages/count_tokens before reaching
the composite target whitelist, leaving the CN rollout unusable for
Claude-protocol clients.
- allowOpenAICompatibleMessagesDispatch: composite groups resolved to a
grok/CN target get the same exemption as the standalone platforms;
openai-resolved and unresolved targets keep requiring the switch
- resolveOpenAIMessagesDispatchMappedModel: skip the group-level dispatch
model mapping (openai-specific gpt defaults) for composite grok/CN
targets — model rewriting stays with account-level model_mapping
- Responses WebSocket composite whitelist stays openai+grok: CN accounts
cannot pass the WSv2 ingress transport filter and the WS HTTP bridge
has no Responses conversion for them, so admitting CN targets only
turns a clear policy rejection into a misleading no-available-account
- widen the responses/input_tokens and messages/count_tokens composite
gates to the shared openai-compatible text whitelist so CN targets get
the same local-estimate token counting as generation
- restore the Claude default-model fallback for standalone CN groups
(admin candidates and custom models list) while keeping composite
listings limited to CN account mapping keys
- refresh the scheduler bulk-rebuild comment and bucket capacity hints
for the 8-platform canonical set
P2-2/P2-4/P2-5 from review:
- validateLinkedAccount/revalidateLinkedAccount now check
monitorAccountQuotaCapability after the platform match, blocking combos
that would permanently error at runtime: deepseek coding and
custom-domain kimi coding (no quota endpoint), zhipu payg (no balance
endpoint), anthropic/openai API-Key accounts (usage query requires
oauth; anthropic setup-token still allowed via local estimation).
gemini/grok/antigravity stay permissive. Quota mode surfaces
CHANNEL_MONITOR_ACCOUNT_NOT_SUPPORTABLE on edit; probe mode silently
unbinds (same as platform mismatch).
- applyMonitorUpdate re-runs validateCheckMode on the effective
provider+check_mode whenever either field is patched, so a
provider-only update can no longer persist antigravity+probe (the
check is conditioned on provider/check_mode presence so legacy illegal
rows can still be renamed/disabled).
- normalizeMonitorPrimaryModel only substitutes the "quota" placeholder
for pure quota mode; quota_probe with an empty model now fails with
MissingPrimaryModel instead of probing model="quota". The quota branch
also moves ahead of the grok default, so grok+quota now gets "quota"
rather than "grok-4.5" (review L-item).
Tests: capability matrix (15 cases incl. anti-over-blocking rows),
linked-account wiring, revalidate quota/probe split, provider-only
update bypass + legacy-row rename, quota_probe missing-model create,
normalize matrix.
P2-1/P2-3 from review:
- fetchCNQuota: credential-invalid now judged by StatusCode 401/403
(aligned with fetchCNBalance) instead of `!Success && !CredentialValid` —
CN quota service only sets CredentialValid=true on the success path, so
500/429/zhipu business errors were all misclassified as failed instead
of error.
- fetchCNBalance: snapshot carries new BalanceLow flag computed with the
scheduler's exact criterion (`!Available || allCNBalancesBelowThreshold`)
against Gateway.CNProviders.BalanceThreshold (ctor now takes cfg; wire
regenerated). quotaDegradedHint reports "balance low" instead of the old
`<=0` check, so an account already paused by the scheduler (balance 5 /
threshold 10) no longer shows green in the monitor.
- threshold helper falls back to viper default 0.5 for nil/<=0 config to
avoid a zero-threshold regression where balance=0 stops alerting.
Tests: CN quota status-code matrix (rewrites the test that cemented the
old behavior), balance-low matrix (below-threshold / unavailable /
multi-currency healthy), threshold-from-config; PayG stubs now set
Available explicitly (zero-value trap).
- ChannelsView platformOrder now includes the three CN provider
platforms so channel pricing can be configured for them; composite
group expansion/attachment stays limited to the five main platforms
(matches backend isConcreteRequestPlatform and composite-routes
target_platform validation)
- SyncPricingModels maps kimi->moonshot, zhipu->zhipu,
deepseek->deepseek; also fixes gemini mapping to "google" which
matched zero catalog entries (provider key is "gemini")
- Add CN platform colors to channel pricing model tag classes
- Replace ApplyCodexCanonicalIdentity with CodexCanonicalAuthIdentity /
ApplyCodexCanonicalAuthIdentity: the credential face (auth.openai.com
token exchange / refresh / PAT whoami) now sends the originator +
canonical User-Agent pair and no version header, matching codex-rs
default_headers(); the version gate (#3901) only exists on the
/backend-api/codex inference face. whoami keeps its original header
shape (originator + UA) with the canonical UA source.
- Token exchange and refresh send the full pair instead of a bare UA,
eliminating the half-identity (UA without originator) combination no
real client ever emits.
- Codex models manifest: the Version header now follows the client's
own client_version when it is valid and >= the upstream floor (same
source as the query param, restoring the pre-refactor consistency),
falling back to the canonical version otherwise; the query param
keeps its verbatim passthrough contract.
- Drop the now-unreferenced openAICodexProbeVersion constant and its
vacuous consistency assertions; probes resolve their version through
resolveCodexOutboundIdentity at runtime.
The struct field alone never reached the wire: the raw passthrough
pipeline is covered by enableMixedGeminiToolInvocations (#5711), but
TransformClaudeToGeminiWithOptions builds GeminiToolConfig from scratch
and never set the flag, so gemini-* models entering through the Claude
format gateway could still hit the upstream 400 from issue #5709.
- Set IncludeServerSideToolInvocations=true when the built tool
declarations mix functionDeclarations with googleSearch, matching the
raw-path injection semantics.
- Replace the marshal-roundtrip-only test with behavior tests that
drive TransformClaudeToGeminiWithOptions: mixed tools set the flag,
function-only and web-search-only requests leave it unset.
- user_repo.create(): keep the TxFromContext fast path, but restore
tolerance for dbent.ErrTxStarted in the self-owned-transaction branch.
ent's Client.Tx only inspects the driver type, so a repository built
from a tx-bound client (client-injected transactions, e.g. the
integration fixture testEntTx + tx.Client()) hits ErrTxStarted; reuse
that client instead of failing. Fixes the two red integration tests in
allowed_groups_contract_integration_test.go.
- createUserAndClaimInvitation: roll back via defer (matching the OAuth
registration precedent) so a panic inside the transaction cannot leak
the connection.
- settingRepoStub: guard call counters and state with a mutex; the new
concurrency regression test exercises it from multiple goroutines and
the unsynchronized counters were flagged by -race.
Reduce filtered admin usage statistics from four scans to one GROUPING SETS query so every breakdown shares the exact same filters. Add concurrent expression indexes for requested and upstream model filters on large usage_logs tables.