fix(review-policy): fill the sidecar catalog round-robin across credential accounts - #1939
Conversation
build_zdr_prioritized_catalog sorted eligible routes by (cost, ZDR, provider, model) and filled the bounded catalog in that order, taking up to account_cap per account. With the sidecar's ORCHESTRATOR_CATALOG_ ACCOUNT_CAP=8 and ORCHESTRATOR_CATALOG_LIMIT=12 the fill took 8 nvidia_nim + 4 nvidia_nim_sub and stopped before the alphabetically last account: noema-review run 33969842312 admitted 62 free routes across three accounts (free_account_diversity 3) and served 12 NVIDIA routes, of which runtime preflight kept 2, so a stalled NVIDIA endpoint had no other account to fail over to (contextual-orchestrator#1045). Keep the sort; group the sorted rows by (cost, ZDR) tier and fill each tier round-robin across provider accounts until limit, honouring account_cap. Tier order, cap, limit and discovery-order independence are unchanged; the same input now yields 4 + 4 + 4. The launcher's evidence_only filter (#1476) is not the cause on the current pin (2e414d15 includes contextual-orchestrator#949), so that PR stays a complementary hardening. Tests: three new cases (interleave within tier; ZDR tier still first; exhausted accounts hand turns over) were RED against the old loop (3 failed) and are GREEN now. Gate on this tree: 2896 passed, coverage 100%, interrogate 100%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthrough
Changes카탈로그 계정 분산
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to Catalog entries now rotate among credential accounts within each cost/ZDR tier, improving failover diversity without changing tier priority, account caps, or catalog limits. The covered selection behavior is ready to merge. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
|
Merge-order note (host 1, measured with |
|
Independent verification (lane:jan session) of head Negative control reproduced. Your three new tests: 3 failed against Differential fuzz, 400 random discovery reports (0–14 free routes per provider across
One observation, not a defect of this PR. When the report contains duplicate On the sidecar cap default: your reasoning holds — with the interleaved fill the cap no longer decides diversity, and two accounts at cap 8 fill 12 where cap 4 leaves four empty. I withdraw it from this PR's scope. The remaining wrinkle is only that Generated by Claude Code |
|
Merged as Verified on the head tree Authorization, cited at the act from the standing directive's chicken-and-egg clause for workflow issues that create the 60-job ceiling: a ⑥, written before observation: count only sidecar runs created after this merge (pre-merge runs execute the old policy from their creation-time tarball). Discriminators, in order: the sidecar log's |
Rebinds workflow_sha so Strix/Noema runs pick up #1939's round-robin catalog fill; the previous head's reviews failed on the single-upstream gateway stall that #1939 removes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Rebinds workflow_sha so the required review runs pick up #1939's round-robin catalog fill; the previous head's Noema run failed on the single-upstream gateway stall that #1939 removes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Rebinds workflow_sha so the required review runs pick up #1939's round-robin catalog fill; the previous head's Strix and Noema runs failed on the single-upstream gateway stall that #1939 removes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…letion Rebinds workflow_sha so the required review runs pick up #1939's round-robin catalog fill; the previous head's Strix and Noema runs failed on the single-upstream gateway stall that #1939 removes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…-merge push, not a re-run Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
|
First post-merge observation (host 1), discriminator ⑥ as written before the merge. Runs on
Reading: the selection change did exactly what the tests say (8+4+0 → 4+4+4, ZDR tier untouched). New constraint surfaced: two of the four OpenRouter routes were rejected with 429 at preflight. OpenRouter's free tier is rate-limited per key (and per day without credits), and every sidecar boot probes its OpenRouter routes with a real completion, so under org-wide concurrent CI the preflight itself spends that budget. That is the next lever on the OpenRouter side (probe budget, 429-as-retryable at preflight, or credits) and is a separate decision from this PR. One observation each for noema and strix; the noema job has been re-run (same post-merge |
|
⑥ read at 21:3xZ on every completed
Primary discriminator: confirmed. Pre-merge reports showed 8 + 4 + 0 with OpenRouter absent; post-merge OpenRouter appears at parity with each NVIDIA account, and readiness reached 6/12 on one run (was 2/12). The tier reading is consistent with the ZDR table (OpenRouter ahead or level, never behind). Lagging signal, n=2: 1 success, 1 failure — the failure is still a gateway 502, and at 3122 s it is the longest stall observed today (pre-merge range 180–2174 s). A plausible reading is that a wider candidate set lengthens the serial failover walk before the gateway gives up, which is contextual-orchestrator#1045's mechanism (per-process breaker, no persisted state), not this PR's. Strix post-merge: 1 success / 1 failure / 1 cancelled. Recovery for heads whose reviews ran on the old sidecar is one push merging |
|
One post-#1939 counter-example, with the catalog change confirmed and the stall not.
So the round-robin fill works — OpenRouter routes are in the pool — but the request still sat on one NVIDIA upstream for 52 minutes and came back 502 without the gateway leaving that upstream. The residual is failover on a stalled upstream (no response at all), not on a fast error, which is the CO gateway layer (ContextualWisdomLab/contextual-orchestrator#1045 / the item-4 stall), not the sidecar cap. "Fixed for every new run" should read "pool composition fixed; stall→502 still reproduces on new runs". The 57-minute slot this run held is the same slot cost as before. |
New review runs bind workflow_sha at creation time, so the 13:40:58Z runs that failed executed the pre-#1939 sidecar catalog. This merge creates a fresh event so the current round-robin catalog is used, and clears mergeable_state=behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Correction to my counter-example above — the gateway did fail over; I misread From the pinned orchestrator source (peer reading, So the two layers are: #1939 fixed pool composition (4/4/4 confirmed), and the residual cost is per-hop 274 s × pool size on a bad day — a wider pool makes one total failure longer, not shorter, until a first-byte/response-start timeout exists at the policy layer (an owner decision under the no-caller-wall-clock-cap policy) and per-attempt outcomes are logged at INFO and shipped with the noema/strix artifacts (today they are DEBUG-only in the sidecar stderr, which is why no job log shows attempt durations — the exact gap behind the "why 900 s / why failed" complaint). One unverified detail: whether serving uses the 6 |
…rcuit events (#1945) * fix(sidecar): let the stream sanitizer pass orchestrator route and circuit events Every provider_*/circuit_* line from the orchestrator was folded into omitted_unstructured_lines, so even the provider_exhausted WARNING that fires today never reached an artifact. Admit those templates field by field against bounded charsets, cut provider_attempt_failed before its free-text error_message, and accept both the default and the sidecar formatter log prefixes (keeping the timestamp for durations). Companion to #1943 and #1944. Refs #1935, #1939 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(sidecar): accept float circuit counters and pin the real formatter output The orchestrator's circuit counters are floats (failures 0.0 += 1.0, circuit_reset_seconds 30.0), so the lines that reach stderr say failures=2.0 / reset_seconds=30.0; the integer-only pattern rejected both circuit_failure and circuit_opened. Found by rendering the templates through the sidecar's logging.Formatter, which the new test now does for all ten. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…erdict phase fails (#1944) A failed noema-review run left artifacts=0, so a 3122 s walk across six ready routes ending in HTTP 502 (run 33981136873) was diagnosable only from the caller's one-line summary. Ship the sanitized sidecar stderr and the preflight report on failure, using the same pinned upload-artifact and the same file Strix already publishes in strix-reports. Refs #1935, #1939 Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Summary
The review sidecar (
noema-review,strix,opencode-review) served a NVIDIA-onlyorchestrator/freecatalog on 2026-09-05 although discovery had admitted free routes from three credential accounts.build_zdr_prioritized_catalogsorted eligible routes by(cost, ZDR, provider, model)and then filled the bounded catalog in that order, taking up toaccount_capper account: with the sidecar'sORCHESTRATOR_CATALOG_ACCOUNT_CAP=8andORCHESTRATOR_CATALOG_LIMIT=12, the fill took 8nvidia_nim+ 4nvidia_nim_suband stopped before the alphabetically last account (openrouter) was reached. Runtime preflight then kept 2 of those 12 routes, so a stalled NVIDIA endpoint had no other account to fail over to — thenoema-review502 class tracked incontextual-orchestrator#1045.This PR keeps the sort and fills each
(cost, ZDR)tier round-robin across accounts instead. Same input now yields 4 + 4 + 4. Tier order (free before priced, ZDR before non-ZDR),account_cap,limit, and the discovery-order-independence contract are unchanged; only which rows occupy the bounded slots changes.Evidence
noema-reviewrun33969842312, job101327666732, sidecar output after##[endgroup]:Why the launcher's
evidence_onlyfilter (#1476) is not the cause on the current pin:contextual_orchestrator_review_sidecar.sh:17pins2e414d15, which descends fromcontextual-orchestrator#949(git merge-base --is-ancestor 8cd99f13 2e414d15→ yes); in that pin the OpenRouterProviderModelSourcesets noevidence_only(model_discovery.py:411-418), the only model-levelevidence_only=Trueisopencode_gonon-chat (:1398-1401), and:1667ORs twoFalses. OpenRouter rows reach the catalog builder; the selection dropped them.#1476remains a valid hardening against a regressed pin and is complementary. Cross-referenced on#1476(claim comment) andcontextual-orchestrator#1045.Why not lower the cap instead: with round-robin the cap no longer decides diversity (three accounts → 4/4/4 at cap 8 or 4), but with two accounts cap 8 fills all 12 slots (6/6) where cap 4 would leave four empty.
#1468's purpose for the cap (no single credential owns the catalog) holds either way, so the sidecar default is untouched.Changes
scripts/ci/contextual_orchestrator_review_policy.py: module-level_route_tier(row, zdr_endpoints)returning(cost rank, ZDR rank), shared by the sort key;build_zdr_prioritized_cataloggroups the sorted rows by tier (itertools.groupby) and fills each tier round-robin acrossprovider_accounts untillimit, honouringaccount_cap.tests/test_contextual_orchestrator_review_policy.py:test_build_catalog_interleaves_accounts_within_a_tier(8/8/8 in, limit 12, cap 8 → first three picks one per account, 4/4/4),test_build_catalog_interleaving_keeps_zdr_tier_first(an attested route still ranks above every unattested one),test_build_catalog_interleaving_skips_exhausted_accounts(5/1/2 in →a b o a o a a a).CHANGELOG.md: entry with the run-log numbers.Verification
3 failed; after the change — passing.build_zdr_prioritized_catalog(review_policy,review_live_discovery_contract,bytez_catalog_integration,free_credential_admission,review_runtime_preflight): 123 passed.test_build_catalog_applies_account_cap(cap 2 → 2/2/2) andtest_build_auto_catalog_order_is_independent_of_discovery_orderstill hold, as they must.coverage run -m pytest tests,coverage report,interrogate): see the commit message for the exact counts.Developer experience
One helper and one loop; the sort key reads as
(*tier, provider, model)and the fill states its own invariant in a comment with the measured numbers. Priorities are still unique and descending in pick order.User experience
A review that admits several credential accounts is served from all of them, so one stalled provider endpoint no longer turns into a held runner and a 502 to the reviewer. No configuration change is needed by any repository.
🤖 Generated with Claude Code
Summary by CodeRabbit
변경 사항
문서