Skip to content

feat(combo): quota-weighted routing — skip empty accounts, draw by leftover - #12789

Merged
diegosouzapw merged 27 commits into
diegosouzapw:release/v3.8.51from
HouMinXi:feat/quota-weighted-routing
Sep 7, 2026
Merged

diegosouzapw merged 27 commits into
diegosouzapw:release/v3.8.51from
HouMinXi:feat/quota-weighted-routing

Conversation

@HouMinXi

@HouMinXi HouMinXi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds combo strategy quota-weighted.

Same-model account pools (Antigravity Gemini is the case that bit us) currently have two bad fits: p2c scores by model latency, so ten identical modelStr rows look the same and empty accounts stay in the draw; reset-aware always takes the current snapshot leader, so concurrent workers pile onto one connection until the next refresh. This strategy drops accounts that are actually empty, then draws the first target in proportion to reset-aware leftover.

p2c / reset-aware / headroom / quota-share are unchanged. New enum value, dashboard can pick it.

Changes

  • getResetAwareRemainingPercent next to the existing reset-aware score.
  • expandTargetsByQuotaAwareConnections takes an optional skipExhaustionFilter. The 99% hard filter would wipe the 1% soft-floor pool before the orderer can use it.
  • orderTargetsByQuotaWeighted: circuit-open and remaining=0 stay out; draw from pool A (remaining > floor, default 1%); if A is empty, draw from B; tail is leftover descending. All-zero weights fall back to uniform.
  • Wired through routing constants, combo schema (quotaWeightedFloorPercent), dispatch, prompt-cache protect-first (otherwise the cache restack moves the weighted first target), dashboard fallback copy, 42 locales.
  • Regression pin: quota-weighted is not in group-B connection-aware expansion. It expands itself; putting it in B double-expands.

Does not flip any production combo. After merge, set the combo to quota-weighted and disableSessionStickiness=true. Leave stickiness on and the first hit is pinned for the rest of the session, which is the opposite of a spread. The dashboard tips say this; the strategy does not change the global default.

Test plan

  • tests/unit/combo/quota-weighted-strategy.test.ts (weighted draw, A/B split, skip-exhaustion expand, uniform fallback)
  • tests/unit/combo/connection-aware-expansion.test.ts T0a/T0b: quota-weighted stays out of group B
  • Injection: add quota-weighted to CONNECTION_AWARE_EXPANSION_GROUP → T0a+T0b fail; restore → pass
  • GitHub CI. Tip currently fails Fast Quality Gates / some unit shards on files this PR does not touch (same fingerprints as other open PRs on release/v3.8.51). Not fixing those here.

Notes

Base: release/v3.8.51 @ 9d1a896c6.

@HouMinXi

HouMinXi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Local review of the product commits (the four feat(combo) SHAs; the follow-up T0a pin is test-only).

I stopped the run after two L1 cycles because the finding fingerprints did not overlap. That is oscillation, not a clean 3-cycle pass. I went through the ten candidates against the spec and the source instead of letting it burn another round.

None of them is a product defect:

  • acc > r is the half-open interval the tests pin (r=40, [40,20] → index 1). Changing it to >= would fail that case.
  • Pool B cannot land in the tail twice: first pick from A means B is only appended; first pick from B means the unused-B list is empty.
  • Missing try/catch around the orderer is the same shape as reset-aware. An empty pool has to return [] so the combo 404s; falling back to the original list would put hard-empty accounts back in rotation.
  • toFixed(6) rounding remaining <5e-7 to 0 does not fire on the percentUsed path we actually score.
  • Exporting _pickWeightedIndexForTests matches the existing _setSecureRandomFloatSource hook.

Wiring I checked myself (not from the review output): new strategy sits after headroom in the constants/dispatch tables; expander 5th arg defaults off and this strategy passes skipExhaustionFilter: true; remaining === 0 is the hard empty; floor is Number.isFinite, default 1; not in the group-B expansion set; shouldProtectOriginalFirst includes it; production combos are untouched.

The T0a assertion (quota-weighted stays out of group B) and the stale "20 strategies" comment are in 151c2c5bf on this PR. I injected quota-weighted into CONNECTION_AWARE_EXPANSION_GROUP locally: T0a+T0b failed; restore passed.

HOLD, 0 confirmed product defects. CI on this base currently fails Fast Quality Gates / some unit shards on files this PR does not touch — same fingerprints as the other open PRs on release/v3.8.51.

@HouMinXi

HouMinXi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Head is now 8ca90705f.

This commit keeps session stickiness on. New conversations draw leftover / (1 + in-flight). An existing conversation stays on its account until that account is empty, then rebinds. The in-flight slot is reserved on the post-stickiness [0], not during strategy ordering, so a pin cannot charge the wrong account.

Technical review of the three logic files on this SHA ran four cycles and did not converge (fingerprints kept changing). I stopped there and checked the surviving claims against the tree:

  • Reserving during orderTargetsByQuotaWeighted would book the draw winner. Stickiness and prompt-cache then move [0]. The tests require quotaShareRelease === null at the ordering step (quota-weighted-strategy.test.ts). The gap between draw and reserve is the cost of booking the account that actually goes out.
  • Empty connectionId does not share a counter. getInflight("") and incrementInflight("") are no-ops (quotaShareInflight.ts).
  • Reusing quotaShareRelease is so handleComboChat's existing finally (combo.ts) still releases the slot. Renaming is a follow-up, not a product bug.
  • A leaked slot cannot grow forever: 4096 leases per connection and a 120s lease.
  • Failover keeping the original slot elevated until settle is the same shape as quota-share (fix(quota-share): the in-flight slot is reserved and never released — no production caller of decrementInflight #11371). Releasing mid-retry would free a busy account for a concurrent draw.

Product defects from this SHA: 0. Tests: 27/27 on the new file, 110/110 on the sibling combo suites.

I did not change production onmi-gemini3.6. Cutover is a separate ops step: set strategy to quota-weighted, leave disableSessionStickiness off.

HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 6, 2026
Cherry-pick of diegosouzapw#12789 onto the deploy recut conflicted here.
quota-weighted needs to skip the 99% exhaustion filter so the B
pool can still draw leftover accounts. Accept the optional 5th
argument; default keeps extra-usage cutoff behaviour.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 6, 2026
Cherry-pick of diegosouzapw#12789 onto the deploy recut conflicted here.
quota-weighted needs to skip the 99% exhaustion filter so the B
pool can still draw leftover accounts. Accept the optional 5th
argument; default keeps extra-usage cutoff behaviour.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi
HouMinXi force-pushed the feat/quota-weighted-routing branch from 793ce63 to 7a58217 Compare September 6, 2026 08:13
@HouMinXi

HouMinXi commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto release/v3.8.51 tip f9a1cc8a9 (#12682 / #12691 / #12834).

793ce63b4 → 7a582173c. range-diff 11 commits =. GPG G. No file overlap with the three landed commits.

@HouMinXi

HouMinXi commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Looked at da86900c3 (same-tick inflight reserve) on the three logic files in that commit.

No code change from this pass. The TOCTOU fix stands. The lock is the two-pipeline race test: it fails if the same-tick incrementInflight is skipped. Full suite on this SHA: 34 quota-weighted + 34 quota-share.

Notes that came up, and why they do not need a patch:

  1. Leak on the dispatchSmartPipeline early return (targetResolution.ts:748). Wrong order. That return is at 744, before orderByStrategy at 750, so the quota-weighted reserve has not happened yet. The helper also only returns a response when strategy === "auto". quota-weighted never takes this path.

  2. Empty connectionId incrementing inflight. The orderer only increments when winnerId is non-empty, and incrementInflight returns on a falsy id.

  3. Release closure captures [0] rather than the internal winner. Today the orderer returns the pick as [0], so the ids match. A later reorder after the pick would have to keep them in sync. Not a bug here.

  4. else if (!quotaShareRelease && finalId) looks unused for quota-weighted. It is not. The orderer skips both increment and release when the drawn target has an empty connectionId. Stickiness / prompt-cache can still put a real id in [0]. That branch reserves the account that will actually be dispatched. It is also gated on strategy === "quota-weighted", so it cannot invent a slot for p2c / reset-aware.

  5. Idempotent released flag copied in three places. Same three-line guard as quota-share. Optional extract, not a defect.

  6. quota-share does not transfer the slot when stickiness moves [0]. True of the existing quota-share path. Out of scope here. The comment at 775–776 is on purpose: transferring that slot would double-count or steal a reservation quota-share already owns. Happy to take that as a follow-up.

Soft-floor routing needs remaining% from the same session/weekly windows
as scoreResetAwareQuota. Missing snapshots stay at 100 so they are not
treated as empty; limitReached is 0. Float dust from 1-percentUsed is
rounded to 6 decimals so 30% is 30, not 30.000000000000004.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
quota-weighted needs remaining <=1% in the B pool. The dashboard kick
(DEFAULT_QUOTA_THRESHOLD_PERCENT=99) would empty that pool for agy.
Fifth arg skipExhaustionFilter defaults off so reset-aware / headroom
and connection-aware expansion stay the same.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Hard-empty (remaining 0 / limitReached) is dropped. Remaining above the
floor is pool A; (0, floor] is B and only used when A is empty. First
pick is a weighted draw over reset-aware scores; the unused selected
pool plus B form the failover tail.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Adds the public strategy value, HANDLED entry, applyStrategyOrdering
branch, Zod floor, default 1%, prompt-cache first-slot protection,
i18n keys in all 42 locales, and dashboard fallback copy. page.tsx
own-growth 5018->5032 is the two FALLBACK maps.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
T0a/T0b now fail if quota-weighted is added to
CONNECTION_AWARE_EXPANSION_GROUP_B. That strategy expands itself;
sitting in group B would double-expand when the opt-in switch is on.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Number(null) and Number("") both coerce to 0, so a combo whose config
carried the key as null, or a dashboard field left empty, silently turned
the soft floor off. That put a 0.5% account in the first slot ahead of a
healthy one, which is the exact case the floor exists to prevent.

Only a real number, or a non-empty numeric string, may move the floor now.
Everything else takes the documented default of 1.
getResetAwareRemainingPercent had copied the five lines that turn a quota
snapshot into per-window remaining fractions from scoreResetAwareQuota. Two
copies of the same fallback rules would eventually drift, and the strategy
depends on both agreeing: an account is placed in a pool by its leftover and
ranked inside that pool by its score.

The weighted draw helper also loses its ForTests suffix. It sits on the
production hot path and was only ever exported so the half-open boundary
could be pinned directly; the name should say what it is.
… tests

Move the fragment out of the illegal changelog.d/feat/ directory into
features/12789-quota-weighted-routing.md so merge-integrity accepts it,
and list tests/unit/combo/quota-weighted-strategy.test.ts in
stryker.conf.json tap.testFiles so FQG mutation counts its kills.

Does not register reset-aware-request-scope-12600 or rewrite
fixes/reset-aware-model-family.md; those are tip inherited.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Keep session stickiness on. New conversations draw leftover / (1+inflight);
an existing conversation stays on its account until that account is empty.
Reserve the post-stickiness [0] so a pin cannot charge the wrong account.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Cherry-pick onto the deploy recut conflicted with diegosouzapw#12803 at the
isQuotaExhaustedForRequest call. Keep skipExhaustionFilter and pass
connection.providerSpecificData as an optional 4th argument so a
later extra-usage PR can honor blockExtraUsage=false without
rewriting this site. The argument is unused on this branch.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Cover the onmi-gemini3.6 production shape that the original suite missed:
disableSessionStickiness re-draws past a leftover sticky pin, ten equal
agy accounts dilute the first pick by in-flight, three hard-empty of ten
never win, and a Claude-empty weekly window does not drop a Gemini
account after convertUsageToQuotaInfo.

Injection: dropping the inflight divisor made the ten-account draw fail
as expected. Production code is unchanged.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Two in-process pipelines could both see inflight=0 because the orderer
drew, then yielded through stickiness/cache, and only then reserved.
Move incrementInflight into the same synchronous turn as the pick.
applyStrategyOrdering hands back the release; the pipeline transfers
the slot if stickiness later moves [0].

Tests: concurrent Promise.all pipelines, orderer half-open boundary,
floor=0 leftover as first pick, p2c still keeps hard-empty accounts,
family:gemini fetch scope.

Injection: dropping the same-turn increment made the race test fail
(both pipelines landed on the same idle account).

Signed-off-by: Minxi Hou <houminxi@gmail.com>
quota-share reserved inside selectQuotaShareTarget. Stickiness /
prompt-cache could then promote a different account while the drawn
connection kept the slot. Mirror the quota-weighted transfer. Empty-id
fallback stays quota-weighted only so a no-op quota-share release is
never double-counted.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi
HouMinXi force-pushed the feat/quota-weighted-routing branch from 8cef05c to 75be3b0 Compare September 7, 2026 02:37
HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 7, 2026
Cherry-pick of diegosouzapw#12789 onto the deploy recut conflicted here.
quota-weighted needs to skip the 99% exhaustion filter so the B
pool can still draw leftover accounts. Accept the optional 5th
argument; default keeps extra-usage cutoff behaviour.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi

HouMinXi commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto upstream/release/v3.8.51 b345c7f6c (#12870 OpenCode v2 plugin).

  • 8cef05c5684 → 75be3b0df1d
  • range-diff identity (eq=13); all commits GPG G
  • 13 unique commit(s) ahead, 0 behind
  • --force-with-lease to HouMinXi:feat/quota-weighted-routing

diegosouzapw and others added 5 commits September 7, 2026 08:35
…ibution (diegosouzapw#12772)

* chore(ci): guard commit identity in pre-commit to stop author misattribution

Two windows of commits in this checkout were signed with the wrong identity,
both caused by an identity override left behind by an automated session:
2026-08-13..26 (name "Xiangzhe" + @backryun's e-mail, 237 commits) and
2026-08-29..09-02 (name "Markus Hartung" + the maintainer's e-mail, 59 commits).
The .mailmap repairs the record after the fact; this gate stops the next window.

The gate is opt-in per machine via omniroute.expectedName / expectedEmail — with
no config it exits 0, so contributors who clone the repo are never affected. It
blocks three things: a committer that is not this machine's identity (which is
what BOTH windows looked like — in August neither the name nor the e-mail was
the maintainer's, so checking only their e-mail would have missed it), an author
carrying the maintainer's e-mail under someone else's name, and any address
listed in omniroute.legacyEmail.

Crediting a contributor with `git commit --author="Name <their@email>"` keeps
working, since the rule targets the committer and the maintainer's own address.

* test(ci): isolate the identity gate's test from the ambient git config

The "stays inert when the machine has not opted in" case read the real
global config, so on a machine that HAS opted in (omniroute.expectedEmail
set — the maintainer's own boxes, where this gate matters most) the gate
correctly refused a synthetic contributor identity and the test failed.
It only passed on a clean CI runner.

Neutralising GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM makes the opt-in state
come solely from what the test injects, so the suite is deterministic on
both an opted-in and a clean machine.
…2770)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

O `65536` era o 16º posicional de um helper com 15 parâmetros — `TS2554` vivo no tip (`open-sse/executors/glm.ts:244`, confirmado aqui antes do board). O teste de guarda de aridade é o que impede a reincidência: ele checa a assinatura do helper e o call site, não o comportamento, que é exatamente onde o erro morava.

Obrigado por isolar isso do diegosouzapw#12711 em vez de deixar o `glm.ts` viajar junto com pin/combo-split/moonshot.
…iegosouzapw#12711)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

Além do bug do toast, esta PR foi a que derrubou os três base-reds vivos do tip: o fragmento `changelog.d/fixes/reset-aware-model-family.md` sem o `- ` inicial, o registro do `tests/unit/reset-aware-request-scope-12600.test.ts` no `stryker.conf.json` e o `TS2554` do glm. O `check-changelog-integrity` voltou a passar aqui por causa dela.

O diagnóstico do MouseEvent é o que dá o valor: `onConfirm` chegava como handler de clique nativo e `handleBatchDeleteConfirm` tratava qualquer primeiro argumento truthy como callback. O cinto (`typeof`) e o suspensório (o wrap no ConfirmModal) juntos estão certos — só um dos dois deixaria a porta aberta para o próximo caller.
)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

O `nodeMap` lido do closure de `RuntimePageClient` por uma função de nível de módulo é uma bomba-relógio silenciosa: só explode quando um monitor entra em error/exhausted/alerting, e o teste existente só alimentava listas vazias. Tirar o arquivo do exclude do vitest vale tanto quanto o fix — confirmei aqui que `tests/unit/ui/runtime-page-client.test.tsx` agora roda na `test:vitest:ui` e passa.

A anotação sobre o "内部服务器错误" ser o catálogo RSC da página, e não o crash, poupou o próximo a caçar fantasma.
…diegosouzapw#12733)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

Available 0% com Voucher 100% e Cash 100% não é estado de carteira que exista — foi o sinal certo para puxar o fio. Tratar leftover como booleano só para Available e cravar 100% nos outros dois buckets é o tipo de defeito que passa despercebido enquanto a conta tem saldo.

Os dois testes cobrem os dois lados: o produtor e o caminho até `getQuotaRemainingPercentage` com `isCredits` + CNY.
HouMinXi and others added 9 commits September 7, 2026 08:57
…egosouzapw#12767)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

A separação entre o que é do service worker e o que é do Caddy está certa e é o que torna a PR mergeável: o `respondWith` em navegação é defeito nosso, o `Alt-Svc` mentindo h3 é config de proxy reverso e não tem o que fazer aqui.

O `/dashboardfoo` casando com `startsWith("/dashboard")` é um achado à parte, e o bump de cache v2→v3 é o que faz o worker antigo sair do ar nos clientes que já estão presos.
…gets (diegosouzapw#12475) (diegosouzapw#12926)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

A busca do sufixo mais longo primeiro (`-xhigh` antes de `-high`) é o detalhe que faz a herança funcionar em vez de quase-funcionar. Manter `getResolvedModelContextOverride` fora do escopo, com o teste existente registrando que aquele caminho continua sem herança, deixa a fronteira explícita.
… member (diegosouzapw#12899)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

Regressão de v3.8.50 vinda do diegosouzapw#9057, com o sintoma mais enganoso possível: `attempted: 0`. A política já tinha admitido o combo e a checagem era refeita em cada membro interno.

Manter o filtro por prefixo de provider e o `disableNonPublicModels` intactos é o que impede o short-circuit de virar um buraco na allow-list.
…ts (diegosouzapw#12805)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

A decodificação dos campos aninhados 10/20/30 do `GetRemainingResets` ao vivo (último commit) é o que separa isto de um palpite sobre o formato do frame. Mostrar zero em vez de esconder a linha é a escolha certa: crédito zerado é informação, ausência de linha é ambiguidade.
…apw#12803)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

`blockExtraUsage: false` significa "pode usar crédito extra", nunca "esconda a conta antes de despachar" — a conta saía da rota justamente quando o crédito extra existia para ser usado. Os 32/32 cobrem os quatro pontos onde a mesma decisão era tomada, e revalidei após o merge da base (32/32 de novo).

Nota de integração: o seu `isQuotaExhaustedForRequest` colidiu com o placeholder `_providerSpecificData` do diegosouzapw#12789 na worktree combinada. Ficou a sua implementação, que é a que de fato usa o parâmetro. A base foi mergeada na branch para resolver o `file-size-baseline.json` (aditivo, JSON revalidado).
…iegosouzapw#12697)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes. Após o merge da base nesta branch, os 11/11 do `combo-pin-implicit-allowlist` foram revalidados.

A distinção entre pin de step de combo e pin forçado por header (`x-omniroute-connection`) é o que salva a PR de virar uma restrição ampla demais — o header continua permitindo fallback para conexões irmãs, o step não.

Apontar que o `a11930ec4` para a rotação dentro do `handleSingleModel` mas não popula `allowedConnectionIds` no resolve foi a peça que explicou por que os dois são complementares e não redundantes. Sem isso a PR pareceria duplicar um gate que já existia.
…atalog (diegosouzapw#12597) (diegosouzapw#12934)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

A assimetria era exatamente o defeito: o REST do picker já mesclava `customModels`, o despacho não, e o operador via o modelo na tela e tomava 400 na inferência. O overlay só de campos definidos é o detalhe que impede uma escrita esparsa do picker de apagar metadata de capacidade que veio do sync.

Nota de integração: este arquivo colidiu com o diegosouzapw#12866, que extraiu o mesmo bloco para `loadConnectionCatalog` e uniu os catálogos irmãos agy/antigravity. Integrei os dois na worktree combinada — a união de irmãos primeiro, o `unionCustomModels` por cima — e a resolução vai junto no merge do diegosouzapw#12866.
…iegosouzapw#12866)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

O ponto que sustenta a PR é o `models.dev` virar overlay de preço em vez de fonte de catálogo. Um catálogo estático que sobrevive à conta já ter listado ids mais novos é o tipo de defeito que só aparece quando o modelo novo é justamente o que se quer usar.

Nota de integração: `activeSyncedCatalog.ts` colidiu com o diegosouzapw#12934 (união dos `customModels` do picker no catálogo de despacho). Como você extraiu o bloco original para `loadConnectionCatalog`, os dois se compõem: a união dos irmãos agy/antigravity primeiro, o `unionCustomModels` por cima. Revalidei com `custom-models-live-catalog-12597`, `live-model-catalog-reconciliation-8926`, `sync-models-degraded-cached-catalog-9683`, `models-dev-catalog-read-gate`, `discovery-class`, `reactive-model-sync` e `l1-oauth-autosync-default` juntos — 48/48 — mais typecheck:core limpo.

Sobre o `autoSync` padrão em Claude/Codex/Copilot com scheduler de 6h: passei isso pelo dono antes de mergear e a decisão foi manter como está.
@diegosouzapw
diegosouzapw merged commit c1b34db into diegosouzapw:release/v3.8.51 Sep 7, 2026
0 of 3 checks passed
@HouMinXi
HouMinXi deleted the feat/quota-weighted-routing branch September 7, 2026 13:27
diegosouzapw added a commit that referenced this pull request Sep 8, 2026
Contadores de docs fora de sincronia com o código, aprovado pelo dono em chat por tocar `AGENTS.md` e `skills/cli-tunnel/SKILL.md` (Hard Rule — superfície de instrução de agente). Nenhuma instrução mudou.

`Docs Gates` acusava 6 drifts STRICT. Dois vieram da minha leva de 16 PRs: migrations 169 → **171** (#12707 trouxe a 173, #12867 a 174) e estratégias de roteamento 19 → **20** (#12789 registrou a `quota-weighted`). Contei os arquivos em vez de confiar na memória: `ls src/lib/db/migrations/*.sql | wc -l` → 171.

Os 41 mirrors de `docs/i18n/*/llm.txt` foram regenerados com `scripts/i18n/sync-llm-mirrors.mjs` — o gate exige cópia exata da raiz.

O outro braço, `check:agent-skills-sync` acusando `GENERATED: + cli-tunnel`, era herdado (o corpo do #12866 já o registrava). O `SKILL.md` commitado documentava `tunnel create [type]`, um argumento que a CLI **não aceita** — conferido em `bin/cli/commands/tunnel.mjs:21`, que declara `.command("create")` puro. Saída do gerador, não escrita à mão.

| gate | antes | depois |
|---|---|---|
| `check:docs-counts` | 6 drifts STRICT | **0** |
| `check:docs-sync` | FAIL — 41 mirrors divergentes | **PASS** |
| `check:agent-skills-sync` | `+ cli-tunnel` | **UNCHANGED: 46 skills** |
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…apw#12803)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

`blockExtraUsage: false` significa "pode usar crédito extra", nunca "esconda a conta antes de despachar" — a conta saía da rota justamente quando o crédito extra existia para ser usado. Os 32/32 cobrem os quatro pontos onde a mesma decisão era tomada, e revalidei após o merge da base (32/32 de novo).

Nota de integração: o seu `isQuotaExhaustedForRequest` colidiu com o placeholder `_providerSpecificData` do diegosouzapw#12789 na worktree combinada. Ficou a sua implementação, que é a que de fato usa o parâmetro. A base foi mergeada na branch para resolver o `file-size-baseline.json` (aditivo, JSON revalidado).
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ftover (diegosouzapw#12789)

Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.

Reservar o sorteio antes do próximo `await` (`2cf74acc`) é a parte não-óbvia e a que mais importa: sem isso dois pipelines no mesmo processo observam `inflight=0` na mesma conta e convergem para ela. O comentário no código explica isso melhor do que o commit message.

**Um ajuste meu na sua branch.** O `tests/unit/combo/quota-weighted-strategy.test.ts` era intermitente — falhava em cerca de 1 a cada 5 execuções, alternando entre `A/B isolation: 7 hard-empty…` e `floor=0 puts 0.5% in the main pool`, sempre com dois pares de mesma faixa trocando de posição. A causa é o helper de fixture:

```ts
const iso = (ms = 86_400_000) => new Date(Date.now() + ms).toISOString();
```

Como `iso()` é chamado a cada invocação do fetcher, dois peers que deveriam empatar recebiam `resetAt` com um milissegundo de diferença sempre que o relógio virava entre as duas chamadas. Pressão de reset entra no score, então esse epsilon quebrava o empate e `sortByScoreThenIndex` nunca chegava ao fallback por índice de inserção.

Fixei a base do relógio uma vez só (`CLOCK_BASE`). Nenhuma asserção foi tocada — as garantias de ordem, tamanho e exclusão continuam idênticas. 10/10 execuções verdes depois, e mais 6/6 após o merge da base nesta branch.

Também mergeei a base para resolver `file-size-baseline.json` (aditivo) e `src/domain/quotaCache.ts`, onde o seu placeholder `_providerSpecificData` cedeu lugar à implementação do diegosouzapw#12803, que usa o parâmetro de fato.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…12970)

Contadores de docs fora de sincronia com o código, aprovado pelo dono em chat por tocar `AGENTS.md` e `skills/cli-tunnel/SKILL.md` (Hard Rule — superfície de instrução de agente). Nenhuma instrução mudou.

`Docs Gates` acusava 6 drifts STRICT. Dois vieram da minha leva de 16 PRs: migrations 169 → **171** (diegosouzapw#12707 trouxe a 173, diegosouzapw#12867 a 174) e estratégias de roteamento 19 → **20** (diegosouzapw#12789 registrou a `quota-weighted`). Contei os arquivos em vez de confiar na memória: `ls src/lib/db/migrations/*.sql | wc -l` → 171.

Os 41 mirrors de `docs/i18n/*/llm.txt` foram regenerados com `scripts/i18n/sync-llm-mirrors.mjs` — o gate exige cópia exata da raiz.

O outro braço, `check:agent-skills-sync` acusando `GENERATED: + cli-tunnel`, era herdado (o corpo do diegosouzapw#12866 já o registrava). O `SKILL.md` commitado documentava `tunnel create [type]`, um argumento que a CLI **não aceita** — conferido em `bin/cli/commands/tunnel.mjs:21`, que declara `.command("create")` puro. Saída do gerador, não escrita à mão.

| gate | antes | depois |
|---|---|---|
| `check:docs-counts` | 6 drifts STRICT | **0** |
| `check:docs-sync` | FAIL — 41 mirrors divergentes | **PASS** |
| `check:agent-skills-sync` | `+ cli-tunnel` | **UNCHANGED: 46 skills** |
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