refactor(sse): extract combo target resolution into combo/targetResolution.ts - #8592
diegosouzapw merged 11 commits into
Conversation
dff0969 to
789c07c
Compare
|
Hi @MumuTW — thanks for this Block K decomposition of One mandatory pre-merge item: the
Approving as |
789c07c to
b41e3ae
Compare
CI fix pushed (
|
…ude.ts Pure move, no behaviour change. First of ~7 PRs decomposing the combo.ts god-file (diegosouzapw#3501). handleComboChat evaluates a series of dispatch branches before it ever reaches target resolution or the sequential attempt loop. None of them iterate targets in priority order or need the failover/retry/credential gate machinery that follows, so they move to a leaf: - context-cache pin routing (Fix diegosouzapw#679), including the pinIsDurablyUnhealthy / isPinnedModelDurablyUnhealthy health gate - fusion panel dispatch + the diegosouzapw#6455 misconfiguration warn - pipeline chaining - nested combo-ref execute-mode runtime-unit dispatch Only the chaos and round-robin hand-offs stay inline (11 and 13 lines); extracting those would be pure indirection. open-sse/services/combo.ts 3642 -> 3341 (-301) open-sse/services/combo/dispatchPrelude.ts: 619 (under the 800 cap) Each helper keeps the fall-through protocol the inline blocks had: return a Response to OWN the request, return null to fall through. A flipped null/Response would silently bypass the whole combo strategy, so the new tests pin both directions for every branch. combo.ts re-exports pinIsDurablyUnhealthy so combo-pin-health-gate.test.ts keeps resolving. The leaf takes handleComboChat as a `runCombo` parameter instead of importing it, so combo/ keeps zero back-edges into combo.ts. Complexity-neutral: the first cut added +3 violations (two max-lines-per-function, one complexity) inside the new leaf, so evaluatePinnedResponse, orderRuntimeUnits, recordRuntimeUnitStickySuccess and buildBaseOptions were split out. check:complexity now measures 2169 and check:cognitive-complexity 956 — identical to the pristine base.
…n testing An adversarial mutation audit of the suite added in the previous commit found it guarded the fall-through protocol well but asserted almost nothing about what the helpers do once they OWN the request. 5 of 12 seeded mutations survived. Worst case: deleting the pinned-model dispatch call outright left all 12 tests green. Three holes, now closed (8 tests -> 20): Hole A — the honored-pin path had zero coverage. Both existing pin tests DROP the pin, so the dispatch, the 200-but-empty quality gate, the [408, 429, 500, 502, 503, 504] failover list and the catch(pinErr) branch were unguarded — exactly the logic the 2026-06-21 / 2026-06-22 incident comments call load-bearing. Adds five tests over a seeded healthy provider connection so the pin is actually honored. Hole B — orderRuntimeUnits was only ever driven with `priority`, which is a no-op through it. Four of five strategy branches could be deleted with nothing failing. Adds round-robin rotation and weighted sticky ordering tests. Hole C — recordRuntimeUnitStickySuccess never did anything under test: both its guards need weighted/round-robin, so an early return changed nothing. Covered by the new sticky-batch test. Verified by re-running the mutations rather than assuming: all 7 that previously survived (delete-pin-dispatch, serve-despite-failed-quality, never-fail-over-on-transient, rr-counter-not-advanced, rotation-removed, weighted-sticky-skipped, sticky-recording-no-op) are now killed. The first sticky-batch test I wrote was itself vacuous — asserting "same unit twice" holds equally when the recording helper is stubbed out, since nothing advances the counter either. It now asserts the batch runs out and rotation resumes on the third dispatch, which is what actually distinguishes the two. Also restores API_KEY_SECRET in test.after; it was set at module load and never put back, inconsistent with the DATA_DIR handling beside it.
The combo sub-check of check:known-symbols asserts every canonical routing
strategy has a real dispatch branch. It scanned a hardcoded file list and
matched only `strategy === "..."`, so the prelude extraction tripped it twice:
[combo] 2 estratégia(s) canônica(s) sem branch de despacho em combo.ts:
✗ fusion
✗ pipeline
Both branches are still wired — they just moved to combo/dispatchPrelude.ts and
took the early-return guard form `if (strategy !== "fusion") return null;` that
extracting a branch into a `tryXDispatch()` leaf naturally produces.
Two changes, both extending existing precedent (the list already carries the
Block J leaves for the same reason):
- register combo/dispatchPrelude.ts in comboDispatchFiles
- widen the extractor to `strategy [!=]== "..."` so the inverted guard counts
Loose `==`/`!=` stay rejected, and no `handledNotCanonical` fallout: the gate
now reports 20 canonical strategies, all 20 via despacho.
check:mutation-test-coverage --strict failed once the known-symbols fix let
Fast Quality Gates advance to it:
✗ 2 covering unit test(s) across 2 module(s) are missing from
stryker.conf.json tap.testFiles
open-sse/services/combo/comboStructure.ts
open-sse/services/combo/rrState.ts
The new tests/unit/combo-dispatch-prelude.test.ts exercises both modules, and
both are already in stryker's mutate list, so without the registration its
mutant kills would not have counted toward the nightly mutation gate.
Note (unchanged, still out of scope): combo/dispatchPrelude.ts itself is not in
stryker's `mutate` list. Adding it would widen the nightly mutation surface,
which is a separate call from fixing this drift.
…ution.ts
Pure move, no behaviour change. Lifts the target-resolution stage of
handleComboChat — everything between the dispatch prelude and the attempt
loop — into a new leaf, open-sse/services/combo/targetResolution.ts.
Moved verbatim: provider-wildcard expansion, weighted step-group resolution
+ sticky-weighted eligibility, request-tag routing, the known-context-overflow
early return, the smart/pipeline-enabled auto dispatch, auto-strategy
ordering, per-strategy ordering, cache-strategy affinity, session stickiness,
eval scores, request-compatibility + context-requirement filters, task-aware
reordering, prompt-cache affinity, and the priority-strategy pre-screen.
The three early exits become an { earlyResponse } result so the host decides
to return them (same pattern as resolveAutoStrategyOrder). The values the
attempt loop still reads — orderedTargets, stickyWeightedLimit,
getWeightedStepKeyForTarget, the session-stickiness result and preScreenMap —
are returned instead of closed over. Loop config (maxRetries, retryDelayMs,
fallbackDelayMs, maxSetRetries, setRetryDelayMs) stays in combo.ts.
buildAutoCandidates is dependency-injected because it lives in combo.ts, so
the leaf keeps zero back-edges into its host.
combo.ts 3640 -> 3321 lines; new leaf 484 lines (under the 800 cap).
Part of the diegosouzapw#3501 god-file decomposition campaign.
…bo.ts file-size baseline Follow-up to the target-resolution extraction: the moved region landed as one 311-line function, which converted inline code inside the (already-violating) handleComboChat into a NEW separately-counted violating function — check:complexity 2169 -> 2171 and check:cognitive-complexity 956 -> 957. Split resolveComboTargetPipeline along its natural stage boundaries into 14 helpers (wildcard expansion, weighted eviction/eligibility/sticky-key/selection, step-key mapper, context-overflow response, pool-size log, smart-pipeline dispatch and its fall-through logger, strategy ordering, continuity filters, task-aware ordering, prompt-cache enablement/first-target protection/affinity stage). Each stage takes the previous stage's output and returns the next; still a pure move. The leaf now contributes ZERO complexity, max-lines-per-function and cognitive-complexity violations. Both ratchets are back at base 4053e23 values: check:complexity 2169, check:cognitive-complexity 956. (Both still print RED against their frozen ceilings 2130/951 — pre-existing base-red per diegosouzapw#8580.) Also ratchets ONLY the open-sse/services/combo.ts entry in config/quality/file-size-baseline.json from 3642 to 3322, with a justification note in the file's existing style. No sweep of unrelated entries.
Rebased onto refactor/combo-dispatch-prelude. Keep both leaves in check-known-symbols. Regenerate file-size baseline; sync agent skills.
…etResolution extract Stacking targetResolution onto the dispatchPrelude tip dropped the diegosouzapw#8488/diegosouzapw#8494 compatFilterFailOpen wiring: hard capability filters emptied the pool into a generic 404 no_executable_targets, and fail-open never re-admitted the pool. Restore describeCapabilityFilterExhaustion earlyResponse in applyContinuityFilters and the matching round-robin path, then rebank the file-size baseline for tip growth the incomplete prior rebank missed.
c127e3d to
4780d8e
Compare
…ouzapw#8254 type This branch predates diegosouzapw#8254, which renamed the recordModelLockoutFailure option `exactCooldownVerified` -> `exactCooldownIsUpstreamReset` and changed combo.ts's predicate from `lockoutHintVerified` (diegosouzapw#8393's `lockoutHintMs > 0`) to `lockoutHintMs > mlSettings.baseCooldownMs`. Rebasing onto the current tip brought the renamed type without updating these two call sites, so typecheck:core failed with TS2353 at both. Restores the base expression verbatim rather than re-wiring `lockoutHintVerified` under the new name. The base predicate is the correct one: selectLockoutCooldownMs returns the parsed hint ONLY when `lockoutHintMs > baseCooldownMs`, and otherwise returns 0 or a synthetic baseCooldownMs — so `lockoutHintMs > 0` would mark a synthetic cooldown as an upstream reset and let it bypass the diegosouzapw#7940 maxCooldownMs cap, which is the bug diegosouzapw#8254 fixed.
4780d8e to
d7c1e3f
Compare
check-known-symbols.ts: the combo dispatch file list gained combo/dispatchPrelude.ts on the release side (diegosouzapw#8582, same extraction series) while this branch adds combo/targetResolution.ts. Both entries are needed — each extraction moved a set of `strategy === "..."` dispatch checks out of combo.ts, and the gate reads all of them to derive the handled-strategy set. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
… pools (diegosouzapw#8786) Strict contextFilterMode excluded every target whose context limit was missing from the capability catalog, so otherwise-executable combos returned 404 no_executable_targets. Restore unknown-context targets when no known-good survivor remains, surface context_requirements_exhausted from targetResolution, and keep the empty-pool payload in pinRecovery after the diegosouzapw#8592 split.
… pools (#8786) (#8798) Strict contextFilterMode excluded every target whose context limit was missing from the capability catalog, so otherwise-executable combos returned 404 no_executable_targets. Restore unknown-context targets when no known-good survivor remains, surface context_requirements_exhausted from targetResolution, and keep the empty-pool payload in pinRecovery after the #8592 split.
…ution.ts (diegosouzapw#8592) * refactor(sse): extract combo dispatch prelude into combo/dispatchPrelude.ts Pure move, no behaviour change. First of ~7 PRs decomposing the combo.ts god-file (diegosouzapw#3501). handleComboChat evaluates a series of dispatch branches before it ever reaches target resolution or the sequential attempt loop. None of them iterate targets in priority order or need the failover/retry/credential gate machinery that follows, so they move to a leaf: - context-cache pin routing (Fix diegosouzapw#679), including the pinIsDurablyUnhealthy / isPinnedModelDurablyUnhealthy health gate - fusion panel dispatch + the diegosouzapw#6455 misconfiguration warn - pipeline chaining - nested combo-ref execute-mode runtime-unit dispatch Only the chaos and round-robin hand-offs stay inline (11 and 13 lines); extracting those would be pure indirection. open-sse/services/combo.ts 3642 -> 3341 (-301) open-sse/services/combo/dispatchPrelude.ts: 619 (under the 800 cap) Each helper keeps the fall-through protocol the inline blocks had: return a Response to OWN the request, return null to fall through. A flipped null/Response would silently bypass the whole combo strategy, so the new tests pin both directions for every branch. combo.ts re-exports pinIsDurablyUnhealthy so combo-pin-health-gate.test.ts keeps resolving. The leaf takes handleComboChat as a `runCombo` parameter instead of importing it, so combo/ keeps zero back-edges into combo.ts. Complexity-neutral: the first cut added +3 violations (two max-lines-per-function, one complexity) inside the new leaf, so evaluatePinnedResponse, orderRuntimeUnits, recordRuntimeUnitStickySuccess and buildBaseOptions were split out. check:complexity now measures 2169 and check:cognitive-complexity 956 — identical to the pristine base. * test(sse): close the dispatch-prelude coverage holes found by mutation testing An adversarial mutation audit of the suite added in the previous commit found it guarded the fall-through protocol well but asserted almost nothing about what the helpers do once they OWN the request. 5 of 12 seeded mutations survived. Worst case: deleting the pinned-model dispatch call outright left all 12 tests green. Three holes, now closed (8 tests -> 20): Hole A — the honored-pin path had zero coverage. Both existing pin tests DROP the pin, so the dispatch, the 200-but-empty quality gate, the [408, 429, 500, 502, 503, 504] failover list and the catch(pinErr) branch were unguarded — exactly the logic the 2026-06-21 / 2026-06-22 incident comments call load-bearing. Adds five tests over a seeded healthy provider connection so the pin is actually honored. Hole B — orderRuntimeUnits was only ever driven with `priority`, which is a no-op through it. Four of five strategy branches could be deleted with nothing failing. Adds round-robin rotation and weighted sticky ordering tests. Hole C — recordRuntimeUnitStickySuccess never did anything under test: both its guards need weighted/round-robin, so an early return changed nothing. Covered by the new sticky-batch test. Verified by re-running the mutations rather than assuming: all 7 that previously survived (delete-pin-dispatch, serve-despite-failed-quality, never-fail-over-on-transient, rr-counter-not-advanced, rotation-removed, weighted-sticky-skipped, sticky-recording-no-op) are now killed. The first sticky-batch test I wrote was itself vacuous — asserting "same unit twice" holds equally when the recording helper is stubbed out, since nothing advances the counter either. It now asserts the batch runs out and rotation resumes on the third dispatch, which is what actually distinguishes the two. Also restores API_KEY_SECRET in test.after; it was set at module load and never put back, inconsistent with the DATA_DIR handling beside it. * fix(ci): teach known-symbols gate the relocated fusion/pipeline dispatch The combo sub-check of check:known-symbols asserts every canonical routing strategy has a real dispatch branch. It scanned a hardcoded file list and matched only `strategy === "..."`, so the prelude extraction tripped it twice: [combo] 2 estratégia(s) canônica(s) sem branch de despacho em combo.ts: ✗ fusion ✗ pipeline Both branches are still wired — they just moved to combo/dispatchPrelude.ts and took the early-return guard form `if (strategy !== "fusion") return null;` that extracting a branch into a `tryXDispatch()` leaf naturally produces. Two changes, both extending existing precedent (the list already carries the Block J leaves for the same reason): - register combo/dispatchPrelude.ts in comboDispatchFiles - widen the extractor to `strategy [!=]== "..."` so the inverted guard counts Loose `==`/`!=` stay rejected, and no `handledNotCanonical` fallout: the gate now reports 20 canonical strategies, all 20 via despacho. * chore(ci): register combo-dispatch-prelude test in stryker tap.testFiles check:mutation-test-coverage --strict failed once the known-symbols fix let Fast Quality Gates advance to it: ✗ 2 covering unit test(s) across 2 module(s) are missing from stryker.conf.json tap.testFiles open-sse/services/combo/comboStructure.ts open-sse/services/combo/rrState.ts The new tests/unit/combo-dispatch-prelude.test.ts exercises both modules, and both are already in stryker's mutate list, so without the registration its mutant kills would not have counted toward the nightly mutation gate. Note (unchanged, still out of scope): combo/dispatchPrelude.ts itself is not in stryker's `mutate` list. Adding it would widen the nightly mutation surface, which is a separate call from fixing this drift. * docs(changelog): add fragment for diegosouzapw#8582 combo dispatch prelude * refactor(sse): extract combo target resolution into combo/targetResolution.ts Pure move, no behaviour change. Lifts the target-resolution stage of handleComboChat — everything between the dispatch prelude and the attempt loop — into a new leaf, open-sse/services/combo/targetResolution.ts. Moved verbatim: provider-wildcard expansion, weighted step-group resolution + sticky-weighted eligibility, request-tag routing, the known-context-overflow early return, the smart/pipeline-enabled auto dispatch, auto-strategy ordering, per-strategy ordering, cache-strategy affinity, session stickiness, eval scores, request-compatibility + context-requirement filters, task-aware reordering, prompt-cache affinity, and the priority-strategy pre-screen. The three early exits become an { earlyResponse } result so the host decides to return them (same pattern as resolveAutoStrategyOrder). The values the attempt loop still reads — orderedTargets, stickyWeightedLimit, getWeightedStepKeyForTarget, the session-stickiness result and preScreenMap — are returned instead of closed over. Loop config (maxRetries, retryDelayMs, fallbackDelayMs, maxSetRetries, setRetryDelayMs) stays in combo.ts. buildAutoCandidates is dependency-injected because it lives in combo.ts, so the leaf keeps zero back-edges into its host. combo.ts 3640 -> 3321 lines; new leaf 484 lines (under the 800 cap). Part of the diegosouzapw#3501 god-file decomposition campaign. * refactor(sse): split targetResolution into stage helpers, ratchet combo.ts file-size baseline Follow-up to the target-resolution extraction: the moved region landed as one 311-line function, which converted inline code inside the (already-violating) handleComboChat into a NEW separately-counted violating function — check:complexity 2169 -> 2171 and check:cognitive-complexity 956 -> 957. Split resolveComboTargetPipeline along its natural stage boundaries into 14 helpers (wildcard expansion, weighted eviction/eligibility/sticky-key/selection, step-key mapper, context-overflow response, pool-size log, smart-pipeline dispatch and its fall-through logger, strategy ordering, continuity filters, task-aware ordering, prompt-cache enablement/first-target protection/affinity stage). Each stage takes the previous stage's output and returns the next; still a pure move. The leaf now contributes ZERO complexity, max-lines-per-function and cognitive-complexity violations. Both ratchets are back at base d408a20 values: check:complexity 2169, check:cognitive-complexity 956. (Both still print RED against their frozen ceilings 2130/951 — pre-existing base-red per diegosouzapw#8580.) Also ratchets ONLY the open-sse/services/combo.ts entry in config/quality/file-size-baseline.json from 3642 to 3322, with a justification note in the file's existing style. No sweep of unrelated entries. * chore: stack targetResolution on dispatchPrelude tip, rebank + skills Rebased onto refactor/combo-dispatch-prelude. Keep both leaves in check-known-symbols. Regenerate file-size baseline; sync agent skills. * fix(sse): restore diegosouzapw#8494 capability fail-closed after targetResolution extract Stacking targetResolution onto the dispatchPrelude tip dropped the diegosouzapw#8488/diegosouzapw#8494 compatFilterFailOpen wiring: hard capability filters emptied the pool into a generic 404 no_executable_targets, and fail-open never re-admitted the pool. Restore describeCapabilityFilterExhaustion earlyResponse in applyContinuityFilters and the matching round-robin path, then rebank the file-size baseline for tip growth the incomplete prior rebank missed. * fix(sse): realign model-lockout cooldown options with the post-diegosouzapw#8254 type This branch predates diegosouzapw#8254, which renamed the recordModelLockoutFailure option `exactCooldownVerified` -> `exactCooldownIsUpstreamReset` and changed combo.ts's predicate from `lockoutHintVerified` (diegosouzapw#8393's `lockoutHintMs > 0`) to `lockoutHintMs > mlSettings.baseCooldownMs`. Rebasing onto the current tip brought the renamed type without updating these two call sites, so typecheck:core failed with TS2353 at both. Restores the base expression verbatim rather than re-wiring `lockoutHintVerified` under the new name. The base predicate is the correct one: selectLockoutCooldownMs returns the parsed hint ONLY when `lockoutHintMs > baseCooldownMs`, and otherwise returns 0 or a synthetic baseCooldownMs — so `lockoutHintMs > 0` would mark a synthetic cooldown as an upstream reset and let it bypass the diegosouzapw#7940 maxCooldownMs cap, which is the bug diegosouzapw#8254 fixed. --------- Co-authored-by: MumuTW <johnsxn.us@gmail.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
… pools (diegosouzapw#8786) (diegosouzapw#8798) Strict contextFilterMode excluded every target whose context limit was missing from the capability catalog, so otherwise-executable combos returned 404 no_executable_targets. Restore unknown-context targets when no known-good survivor remains, surface context_requirements_exhausted from targetResolution, and keep the empty-pool payload in pinRecovery after the diegosouzapw#8592 split.
…ution.ts (diegosouzapw#8592) * refactor(sse): extract combo dispatch prelude into combo/dispatchPrelude.ts Pure move, no behaviour change. First of ~7 PRs decomposing the combo.ts god-file (diegosouzapw#3501). handleComboChat evaluates a series of dispatch branches before it ever reaches target resolution or the sequential attempt loop. None of them iterate targets in priority order or need the failover/retry/credential gate machinery that follows, so they move to a leaf: - context-cache pin routing (Fix diegosouzapw#679), including the pinIsDurablyUnhealthy / isPinnedModelDurablyUnhealthy health gate - fusion panel dispatch + the diegosouzapw#6455 misconfiguration warn - pipeline chaining - nested combo-ref execute-mode runtime-unit dispatch Only the chaos and round-robin hand-offs stay inline (11 and 13 lines); extracting those would be pure indirection. open-sse/services/combo.ts 3642 -> 3341 (-301) open-sse/services/combo/dispatchPrelude.ts: 619 (under the 800 cap) Each helper keeps the fall-through protocol the inline blocks had: return a Response to OWN the request, return null to fall through. A flipped null/Response would silently bypass the whole combo strategy, so the new tests pin both directions for every branch. combo.ts re-exports pinIsDurablyUnhealthy so combo-pin-health-gate.test.ts keeps resolving. The leaf takes handleComboChat as a `runCombo` parameter instead of importing it, so combo/ keeps zero back-edges into combo.ts. Complexity-neutral: the first cut added +3 violations (two max-lines-per-function, one complexity) inside the new leaf, so evaluatePinnedResponse, orderRuntimeUnits, recordRuntimeUnitStickySuccess and buildBaseOptions were split out. check:complexity now measures 2169 and check:cognitive-complexity 956 — identical to the pristine base. * test(sse): close the dispatch-prelude coverage holes found by mutation testing An adversarial mutation audit of the suite added in the previous commit found it guarded the fall-through protocol well but asserted almost nothing about what the helpers do once they OWN the request. 5 of 12 seeded mutations survived. Worst case: deleting the pinned-model dispatch call outright left all 12 tests green. Three holes, now closed (8 tests -> 20): Hole A — the honored-pin path had zero coverage. Both existing pin tests DROP the pin, so the dispatch, the 200-but-empty quality gate, the [408, 429, 500, 502, 503, 504] failover list and the catch(pinErr) branch were unguarded — exactly the logic the 2026-06-21 / 2026-06-22 incident comments call load-bearing. Adds five tests over a seeded healthy provider connection so the pin is actually honored. Hole B — orderRuntimeUnits was only ever driven with `priority`, which is a no-op through it. Four of five strategy branches could be deleted with nothing failing. Adds round-robin rotation and weighted sticky ordering tests. Hole C — recordRuntimeUnitStickySuccess never did anything under test: both its guards need weighted/round-robin, so an early return changed nothing. Covered by the new sticky-batch test. Verified by re-running the mutations rather than assuming: all 7 that previously survived (delete-pin-dispatch, serve-despite-failed-quality, never-fail-over-on-transient, rr-counter-not-advanced, rotation-removed, weighted-sticky-skipped, sticky-recording-no-op) are now killed. The first sticky-batch test I wrote was itself vacuous — asserting "same unit twice" holds equally when the recording helper is stubbed out, since nothing advances the counter either. It now asserts the batch runs out and rotation resumes on the third dispatch, which is what actually distinguishes the two. Also restores API_KEY_SECRET in test.after; it was set at module load and never put back, inconsistent with the DATA_DIR handling beside it. * fix(ci): teach known-symbols gate the relocated fusion/pipeline dispatch The combo sub-check of check:known-symbols asserts every canonical routing strategy has a real dispatch branch. It scanned a hardcoded file list and matched only `strategy === "..."`, so the prelude extraction tripped it twice: [combo] 2 estratégia(s) canônica(s) sem branch de despacho em combo.ts: ✗ fusion ✗ pipeline Both branches are still wired — they just moved to combo/dispatchPrelude.ts and took the early-return guard form `if (strategy !== "fusion") return null;` that extracting a branch into a `tryXDispatch()` leaf naturally produces. Two changes, both extending existing precedent (the list already carries the Block J leaves for the same reason): - register combo/dispatchPrelude.ts in comboDispatchFiles - widen the extractor to `strategy [!=]== "..."` so the inverted guard counts Loose `==`/`!=` stay rejected, and no `handledNotCanonical` fallout: the gate now reports 20 canonical strategies, all 20 via despacho. * chore(ci): register combo-dispatch-prelude test in stryker tap.testFiles check:mutation-test-coverage --strict failed once the known-symbols fix let Fast Quality Gates advance to it: ✗ 2 covering unit test(s) across 2 module(s) are missing from stryker.conf.json tap.testFiles open-sse/services/combo/comboStructure.ts open-sse/services/combo/rrState.ts The new tests/unit/combo-dispatch-prelude.test.ts exercises both modules, and both are already in stryker's mutate list, so without the registration its mutant kills would not have counted toward the nightly mutation gate. Note (unchanged, still out of scope): combo/dispatchPrelude.ts itself is not in stryker's `mutate` list. Adding it would widen the nightly mutation surface, which is a separate call from fixing this drift. * docs(changelog): add fragment for diegosouzapw#8582 combo dispatch prelude * refactor(sse): extract combo target resolution into combo/targetResolution.ts Pure move, no behaviour change. Lifts the target-resolution stage of handleComboChat — everything between the dispatch prelude and the attempt loop — into a new leaf, open-sse/services/combo/targetResolution.ts. Moved verbatim: provider-wildcard expansion, weighted step-group resolution + sticky-weighted eligibility, request-tag routing, the known-context-overflow early return, the smart/pipeline-enabled auto dispatch, auto-strategy ordering, per-strategy ordering, cache-strategy affinity, session stickiness, eval scores, request-compatibility + context-requirement filters, task-aware reordering, prompt-cache affinity, and the priority-strategy pre-screen. The three early exits become an { earlyResponse } result so the host decides to return them (same pattern as resolveAutoStrategyOrder). The values the attempt loop still reads — orderedTargets, stickyWeightedLimit, getWeightedStepKeyForTarget, the session-stickiness result and preScreenMap — are returned instead of closed over. Loop config (maxRetries, retryDelayMs, fallbackDelayMs, maxSetRetries, setRetryDelayMs) stays in combo.ts. buildAutoCandidates is dependency-injected because it lives in combo.ts, so the leaf keeps zero back-edges into its host. combo.ts 3640 -> 3321 lines; new leaf 484 lines (under the 800 cap). Part of the diegosouzapw#3501 god-file decomposition campaign. * refactor(sse): split targetResolution into stage helpers, ratchet combo.ts file-size baseline Follow-up to the target-resolution extraction: the moved region landed as one 311-line function, which converted inline code inside the (already-violating) handleComboChat into a NEW separately-counted violating function — check:complexity 2169 -> 2171 and check:cognitive-complexity 956 -> 957. Split resolveComboTargetPipeline along its natural stage boundaries into 14 helpers (wildcard expansion, weighted eviction/eligibility/sticky-key/selection, step-key mapper, context-overflow response, pool-size log, smart-pipeline dispatch and its fall-through logger, strategy ordering, continuity filters, task-aware ordering, prompt-cache enablement/first-target protection/affinity stage). Each stage takes the previous stage's output and returns the next; still a pure move. The leaf now contributes ZERO complexity, max-lines-per-function and cognitive-complexity violations. Both ratchets are back at base 54b4bf1 values: check:complexity 2169, check:cognitive-complexity 956. (Both still print RED against their frozen ceilings 2130/951 — pre-existing base-red per diegosouzapw#8580.) Also ratchets ONLY the open-sse/services/combo.ts entry in config/quality/file-size-baseline.json from 3642 to 3322, with a justification note in the file's existing style. No sweep of unrelated entries. * chore: stack targetResolution on dispatchPrelude tip, rebank + skills Rebased onto refactor/combo-dispatch-prelude. Keep both leaves in check-known-symbols. Regenerate file-size baseline; sync agent skills. * fix(sse): restore diegosouzapw#8494 capability fail-closed after targetResolution extract Stacking targetResolution onto the dispatchPrelude tip dropped the diegosouzapw#8488/diegosouzapw#8494 compatFilterFailOpen wiring: hard capability filters emptied the pool into a generic 404 no_executable_targets, and fail-open never re-admitted the pool. Restore describeCapabilityFilterExhaustion earlyResponse in applyContinuityFilters and the matching round-robin path, then rebank the file-size baseline for tip growth the incomplete prior rebank missed. * fix(sse): realign model-lockout cooldown options with the post-diegosouzapw#8254 type This branch predates diegosouzapw#8254, which renamed the recordModelLockoutFailure option `exactCooldownVerified` -> `exactCooldownIsUpstreamReset` and changed combo.ts's predicate from `lockoutHintVerified` (diegosouzapw#8393's `lockoutHintMs > 0`) to `lockoutHintMs > mlSettings.baseCooldownMs`. Rebasing onto the current tip brought the renamed type without updating these two call sites, so typecheck:core failed with TS2353 at both. Restores the base expression verbatim rather than re-wiring `lockoutHintVerified` under the new name. The base predicate is the correct one: selectLockoutCooldownMs returns the parsed hint ONLY when `lockoutHintMs > baseCooldownMs`, and otherwise returns 0 or a synthetic baseCooldownMs — so `lockoutHintMs > 0` would mark a synthetic cooldown as an upstream reset and let it bypass the diegosouzapw#7940 maxCooldownMs cap, which is the bug diegosouzapw#8254 fixed. --------- Co-authored-by: MumuTW <johnsxn.us@gmail.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
… pools (diegosouzapw#8786) (diegosouzapw#8798) Strict contextFilterMode excluded every target whose context limit was missing from the capability catalog, so otherwise-executable combos returned 404 no_executable_targets. Restore unknown-context targets when no known-good survivor remains, surface context_requirements_exhausted from targetResolution, and keep the empty-pool payload in pinRecovery after the diegosouzapw#8592 split.
PR 2 of ~7 in the #3501 god-file decomposition campaign, shrinking
open-sse/services/combo.tstoward the 800-line file-size cap.What this is
A pure move. No behaviour change. The target-resolution stage of
handleComboChat— everything between the dispatch prelude and the attempt loop — moves verbatim into a new leaf,open-sse/services/combo/targetResolution.ts, entry pointresolveComboTargetPipeline().Log message wording, log levels, and the order of side effects and awaits are preserved exactly, quirks included. Nothing was "improved" on the way out.
Line counts
open-sse/services/combo.tsopen-sse/services/combo/targetResolution.tstests/unit/combo-target-resolution-split.test.tsComplexity — the leaf contributes ZERO violations
A pure move must be complexity-neutral. The first pass was not: it landed the region as one 311-line function, which turned inline code inside the (already-violating)
handleComboChatinto a new separately-counted violating function.4053e2314check:complexitycheck:cognitive-complexityFixed by splitting
resolveComboTargetPipelinealong its natural stage boundaries into 14 helpers — wildcard expansion; weighted sticky eviction / eligibility / sticky-key / selection; step-key mapper; context-overflow response; pool-size log; smart-pipeline dispatch and its fall-through logger; strategy ordering; continuity filters; task-aware ordering; prompt-cache enablement, first-target protection and affinity stage. Each stage takes the previous stage's output and returns the next.Per-file check on the new leaf reports 0 violations across all three rules:
Sub-blocks extracted
In original order, all verbatim:
resolveWeightedTargets— including theisTargetSelectableForWeightedpredicateapplyRequestTagRoutinggetKnownContextOverflow400 early returnlog.infolineshandlePipelineCombo/buildPipelineResponse, with all three fall-through branchesresolveAutoStrategyOrder) and, for every other strategy,applyStrategyOrderingshouldProtectOriginalFirstprotectionpreScreenTargets, priority strategy only)Sub-blocks deliberately SKIPPED
maxRetries,retryDelayMs,fallbackDelayMs,maxSetRetries,setRetryDelayMs(base lines 1096–1100). These are loop configuration, not target resolution, and every one is read downstream by the attempt loop. Left incombo.ts.quotaCutoffResetWindowConfigandcomboAttemptOrder, immediately past the region ceiling — same reason: attempt-loop state.Nothing else in the region was skipped; no sub-block turned out to be un-extractable.
Values that escape the region
Every identifier the region declares was grepped forward from the attempt loop. Five are still read downstream, so they are returned rather than recomputed:
orderedTargets— the stage's net productstickyWeightedLimitandgetWeightedStepKeyForTarget— the sticky-weighted success write-back.messageHashis read on both the success and failure pathspreScreenMap— read per attemptThe three early exits (known-context overflow, pipeline dispatch, auto-strategy
unavailableResponse) become an{ earlyResponse }result the host returns, following the existingresolveAutoStrategyOrderprecedent in the same directory.buildAutoCandidateslives incombo.ts, so it is dependency-injected — thecombo/directory keeps zero back-edges and stays acyclic.Also removed from
combo.ts: 16 imports that went dead with the move (verified per-symbol).Verification
npm run typecheck:corenpm run lintnpm run check:cyclesOK— but its default roots do NOT includeopen-sse/services, so this proves nothing here; see the cycles note belownode scripts/check/check-file-size.mjsOK — 183 arquivos congeladosnpm run check:complexitynpm run check:cognitive-complexitynode --import tsx/esm --test tests/unit/combo-target-resolution-split.test.tsnode --import tsx/esm --test tests/unit/combo-routing-engine.test.tsnode --import tsx/esm --test tests/unit/*combo*.test.ts tests/unit/combo/*.test.ts(157 files)npm run test:vitestnpm run test:unit(full)The 5 full-suite failures are pre-existing on base and unrelated to this change. Four are in
tests/unit/antigravity-oauth-postexchange-nonblocking.test.ts(OAuthpostExchangetimeout tests) — verified by running that file on a detached checkout of base4053e2314, where the same four fail identically. The fifth (db-usageanalytics-split.test.ts) passes in isolation; parallel-run flake.CI gate fix (
check:known-symbols)First CI run failed
Fast Quality Gates:Real, and caused by this PR — though no dispatch was lost. The gate's combo sub-check
scans a hardcoded file list, and the
strategy === "priority"latency pre-screen movedinto
combo/targetResolution.tswith the rest of the region.Fix is one entry: register
combo/targetResolution.tsincomboDispatchFiles, followingthe precedent already in that list (the Block J decomposition added its leaves for exactly
this reason). Gate now reports 20 canonical strategies, all 20 via despacho;
tests/unit/check-known-symbols.test.tsstays green at 35/35.Base-red: the
Unit Tests fast-path (2/4)jobAlso red, and not this PR. Two tests in
tests/unit/serial/combo-quota-share-cooldown-wait-timing.test.ts(expected 200 → got 429;expected 429 → got 403). Reproduced byte-identical on a pristine detached worktree at the
base
4053e2314. Pre-existing.Baseline changes — read this, it was not forgotten
Exactly one entry was ratcheted:
open-sse/services/combo.tsinconfig/quality/file-size-baseline.json, 3642 → 3322, with a justification note in the file's existing note style.check-file-size.mjs --updatewas deliberately not used — it sweeps ~74 unrelated files. The diff on that file is 2 insertions, 1 deletion.No complexity or cognitive-complexity baseline was touched.
combo.tskey to 3341. That is the one and only merge conflict between the two PRs. Whoever rebases second must set it to the combined figure, not simply take the lower of the two. Measured on the actual merged tree:combo.tsmerges to 3017 lines bywc -l, which is 3018 gate units —check-file-sizecountssplit("\n").length, one higher thanwc -l. 3018 is the number that belongs in the baseline entry.Incidental formatting
prettier --writeoncombo.tsalso reformatted one pre-existing 103-char line at base line 2927 (computeCompatRejectedTargets(...), insidehandleRoundRobinCombo) into 5 lines. It is the only non-move hunk in the diff. That line already failedprettier --checkon base, so reverting it would leave the file rejecting the formatter — it is kept deliberately.Tests
Repo rule #8 — production code changed, so tests ship in the same PR. As a pure move, the existing 157-file
tests/unit/*combo*suite is the real regression guard (all green above).tests/unit/combo-target-resolution-split.test.tsadds the new leaf's own direct coverage: the exported entry point, priority-path target ordering, the shape handed back to the attempt loop, the empty-pool case, and the known-context-overflowearlyResponse(400 +context_length_exceeded+ zero attempts).Stacking
Stacked alongside #8582 (
refactor/combo-dispatch-prelude), which touches a disjoint region of the same file (pinned-model routing, fusion, pipeline, nested combo-ref execute mode, round-robin delegation; base lines 283–286, 603–667, 727–1094). This PR's first body hunk starts at base line 1102.git merge-tree --write-treeof the two branches auto-mergesopen-sse/services/combo.tscleanly — including the import block, where both PRs delete imports their own region no longer needs. The sole conflict is thefile-size-baseline.jsonkey noted above.On cycles
check:cyclesis cited green above, but its default roots aresrc/shared/components,src/lib/db,src/lib/compliance,open-sse/translator,open-sse/mcp-server—open-sse/servicesis not among them, so a green run says nothing about this directory. (Caught by an adversarial review of #8582; the same correction applies here.)Scoped explicitly and compared against a pristine detached worktree at the base commit,
open-sse/servicescarries a large pre-existing cyclic cluster that already containscombo.ts. This PR's leaf imports members of that cluster, so it joins it. That is not a new back-edge: no file undercombo/importscombo.ts— the leaf takesbuildAutoCandidatesby dependency injection — and no previously-acyclic pair became cyclic. Untangling the cluster belongs with the later phases of #3501.