fix(resilience): clear the combo LKGP pin only when it names the failed target - #12235
Conversation
c28dca9 to
ba597b6
Compare
|
Rebuilt onto current Why this is a re-application rather than a rebaseThe branch was 217 commits behind, and
Substance unchanged. Target-scoped pin: still always cleared. Combo-level pin: cleared only when it actually names the failed target's provider, because it records whichever provider last succeeded, which need not be the one failing now. Under Verification
Two base-branch things worth knowing, neither mine1. I had to commit with
So the entry is already stale and any commit staging that file trips it. Plain 2. Two CI gates are red on |
…ed target
Re-applied onto current release/v3.8.51. The branch was 217 commits behind
and dispatchWithCooldownRetry / handleRoundRobinCombo have since moved out
of combo.ts, so this is a re-application, not a rebase: the definition is
still in combo.ts, and the 14 call sites now live in
combo/executeTargetAttempt.ts (4), combo/executeTargetGates.ts (5) and
combo/roundRobinCombo.ts (5). clearStaleLKGP is dependency-injected via
attemptLoopTypes.ts, so that signature takes the new parameter too.
Unchanged in substance. The target-scoped pin is still always cleared; the
combo-level pin is cleared only when it actually names the failed target's
provider, because it records whichever provider last *succeeded* and that
need not be the one failing now. Under `auto` the pin is a scoring input
rather than a hoist, so clearing unconditionally discarded a preference for
a healthy provider every time an unrelated target was skipped. Omitting
`failed` keeps the old behaviour for callers with no target in scope.
4 regression tests pass, including "a pin naming a healthy provider
survives another target being skipped"
mutation: clear the combo pin unconditionally (the pre-fix behaviour)
-> only that test fails, 3 pass
248 tests pass across tests/unit/lkgp*, tests/unit/combo/*
eslint clean on all five changed files (exit 0, no suppressions flag)
91fe648 to
25c7b20
Compare
|
Rebased onto current Checked it was still needed before doing the work. #12425 landed "clear LKGP pins on delete" and the const promises: Promise<void>[] = [clearLKGP(comboName, comboId || comboName)];Those tests cover when the pin is cleared; this PR is about which pin. The target-scoped The conflict was one hunk, and base's side won on the part that wasn't mine. VerificationMutation-checked on the new base rather than trusting the old run: reverting the guard to clear unconditionally fails exactly the follow-up case and leaves the three original ones green — which is the shape you'd want, since it shows the change is additive to #11911's contract rather than altering it. One pre-existing note: |
|
Solid, evidence-driven narrowing on top of #12013 — the measured before/after |
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
7ba0835 to
e25a18a
Compare
|
Both done, and the answer to your question is "not gotten to yet" — not deliberate.
All 14 call sites that have a failed target in scope now pass it — 4 in Changelog fragment added at One thing I want to flag, because it nearly became a silent bugRebasing onto the So Seven positional parameters with an VerificationMutation-checked on the new base rather than trusting the old run:
Both rows fail only the new case and leave base's three #11911 cases plus all five #13614 cases green — which is the shape you'd want, since it shows this narrows the behaviour without altering the contract either of those pinned. |
e25a18a to
7ba0835
Compare
|
@diegosouzapw — same as on #13673: I force-pushed over your commit here earlier today and have restored it. Head is Answering your actual question, which still stands: I did the rest of that work before realising I was overwriting you, and I've not pushed it — it would clobber your commit a second time, and which way this goes is your call, not mine. What it contains, if you want it:
Tell me which you'd prefer — I push that on top, you take it from a fresh PR, or you'd rather do it yourself — and I'll follow that. I'm not touching this branch again without you saying so. |
The conflict was structural, not textual: the base extracted
clearStaleLKGP out of combo.ts into combo/staleLkgpClear.ts, gave it a
promise contract ("routing callers ignore it, tests await it") and a
positional test seam. This branch had grown a `failed` parameter in the
same slot that seam now occupies.
Took the base's structure wholesale — combo.ts stays a re-export — and
moved the pin-scoping into staleLkgpClear.ts, keeping its promise
contract and its richer warn payload. `failed` moves to slot 7 so the
seam keeps the position tests/unit/combo/stale-lkgp-clear-13614.test.ts
passes it in; all 14 call sites updated.
Also closes a coverage hole this branch always had: nothing exercised
the sibling-connection rule, so matching on provider alone survived
mutation. Two connections of the same provider are independent targets,
and one failing says nothing about the other.
|
Merged Merged rather than rebased on purpose: the branch tip was your commit, and a rebase would have rewritten it. The conflict was structural, not textual. So I took the base's structure wholesale rather than either side of the hunk: The rewrite also closed a hole this branch always had. Mutation-testing after the move, one mutant survived: replacing the connection check with Nothing exercised the sibling-connection rule, so "same provider, different connection" was unguarded — two connections of one provider are independent targets, and one failing says nothing about the other. Added a test for both directions (conn-B failing leaves a conn-A pin alone; conn-A failing clears it). That mutant and its over-strict opposite now both die.
|
|
Answering the call-site question, and thanks for the changelog fragment — you'd already added it before I got back to this. Every call site passes the failed target, including All 14 call sites, at that commit and now: and So why is the parameter optional at all? Not because any current caller skips it — it's that the omitted-arg path has to keep the old unconditional behaviour for a caller that genuinely has no target in scope, so adding the argument could never be a silent behaviour change for code I hadn't looked at. With every caller now passing it, that path is effectively dead today and exists as a compatibility floor rather than an intentional exemption. Happy to make it required instead if you'd rather the type enforce it — that would be a one-line change plus the signature. One thing that did change since your review: the branch conflicted after the merge wave, and I resolved it by merging The re-port also closed a hole this branch always had: mutation-testing after the move, replacing the connection check with |
…Clear clearStaleLKGP's optional `failed` reached clearPins as FailedTarget | undefined while the parameter is FailedTarget (null-able, not undefined) — typecheck:core on the merged tree flagged it; pass `failed ?? null`. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
|
Thanks @abhisheksharma2411 — merging via the release merge-train. Validated in local merge-train (mt-train8e) on the devbox @ train tip b38c9340f092c8852ecfa7f6e0f9592fd6f7fc35 with the 26 sibling PRs of the owner-approved file-size rebaseline batch: typecheck:core, file-size (rebaselined entry _rebaseline_2026_09_18_merge_train_8_frozen_growth), complexity, cognitive-complexity, changelog-integrity green; changed-area node:test 722/722; vitest 479/482 where the 3 reds were 5s/20s timeouts under devbox load 18 and pass when re-run alone on the same train tip (flake class). Merged --admin per merge-gates §7. |
0f5f83c
into
diegosouzapw:release/v3.8.51
…ed target (diegosouzapw#12235) * fix(resilience): clear the combo LKGP pin only when it names the failed target Re-applied onto current release/v3.8.51. The branch was 217 commits behind and dispatchWithCooldownRetry / handleRoundRobinCombo have since moved out of combo.ts, so this is a re-application, not a rebase: the definition is still in combo.ts, and the 14 call sites now live in combo/executeTargetAttempt.ts (4), combo/executeTargetGates.ts (5) and combo/roundRobinCombo.ts (5). clearStaleLKGP is dependency-injected via attemptLoopTypes.ts, so that signature takes the new parameter too. Unchanged in substance. The target-scoped pin is still always cleared; the combo-level pin is cleared only when it actually names the failed target's provider, because it records whichever provider last *succeeded* and that need not be the one failing now. Under `auto` the pin is a scoring input rather than a hoist, so clearing unconditionally discarded a preference for a healthy provider every time an unrelated target was skipped. Omitting `failed` keeps the old behaviour for callers with no target in scope. 4 regression tests pass, including "a pin naming a healthy provider survives another target being skipped" mutation: clear the combo pin unconditionally (the pre-fix behaviour) -> only that test fails, 3 pass 248 tests pass across tests/unit/lkgp*, tests/unit/combo/* eslint clean on all five changed files (exit 0, no suppressions flag) * docs(changelog): add fragment for the LKGP pin scope fix Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: abhisheksharma2411 <abhisheksharma2411@users.noreply.github.com>
Summary
Follow-up to #12013 (thanks @HouMinXi — the clearing itself is right, this only narrows which pin gets cleared).
#12013 clears the persisted LKGP pin from 13 call sites so a dead provider stops being re-pinned. None of them look at which provider the pin names, and the combo-level pin records whichever provider last succeeded — not necessarily the one failing now.
Under
lkgpthis is harmless:applyStrategyOrderinghoists the pinned target to index 0, so the pinned provider is always the first thing tried, and by the time anything else fails the pin either already refreshed or legitimately went stale.Under
autoit is not.resolveAutoStrategyreads the pin intolastKnownGoodProviderand feeds it to candidate scoring rather than hoisting it, so the pinned provider is not necessarily tried first. Skipping an unrelated target — a connection cooldown, a quota cutoff, an unavailable model — then discards a preference for a provider that never failed.Measured on
63e4afa32, same combo and same skip, only the strategy differing:In all three,
felois healthy and returns 200; the target that gets skipped isopencode.This clears the combo-level pin only when it names the failed target's provider, and — when both sides carry one — its connection, so a sibling connection failing doesn't invalidate the pinned one. The target-scoped
executionKeypin is still cleared unconditionally; that one is unambiguously about the target that just failed.failedis optional, so a caller without a target in scope keeps the previous unconditional behaviour.Related Issues
Validation
npm run lint— 21 problems onopen-sse/services/combo.ts, identical count on the base; this adds none63e4afa32); focused checks rerun afterwardThe new test failed on
63e4afa32before the fix.The three tests #12013 added are the guard against over-narrowing, and they're what makes this safe — they pin the same provider that gets skipped, so they must keep passing. Mutation results:
So the guard can be neither too loose nor too tight without something going red.
npx tsc --noEmitreports 0 errors for the touched files on the base and 0 with this change.Tests Added Or Updated
tests/unit/lkgp-stale-pin-exhaustion-11911.test.ts— 1 added: underauto, a pin naming healthyfelosurvivesopencodebeing skipped. Added to fix(resilience): clear persisted LKGP pin on target exhaustion and skip (#11911) #12013's own file so the LKGP behaviour stays described in one place.Coverage Notes
open-sse/services/combo.tsis the only production file changed, and both sides of the new branch are covered — the pin naming the failed provider (#12013's three tests) and naming a different one (the new test). Coverage does not move down in any touched file.Reviewer Notes
target, which they already had in scope; the change at each is one argument.connectionId. That deliberately errs toward clearing, so it stays closer to fix(resilience): clear persisted LKGP pin on target exhaustion and skip (#11911) #12013's behaviour where the data is ambiguous.