Skip to content

fix(tests): repair 4 pre-existing test failures on main - #1002

Open
seonghobae wants to merge 48 commits into
mainfrom
fix/orchestrated-responses-stream-and-spend-analytics-20260901
Open

fix(tests): repair 4 pre-existing test failures on main#1002
seonghobae wants to merge 48 commits into
mainfrom
fix/orchestrated-responses-stream-and-spend-analytics-20260901

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Repair the pre-existing Responses/spend/test failures that README PR #994 exposed after its runner admission repair made the full suite execute.

  • keep orchestrator/free on the integrated single-worker auto-route contract while non-free virtual models retain conduct-path assertions;
  • isolate optional model-judge calls from worker-only usage fixtures;
  • pin repository-owned Tests/Fuzz/Security jobs to ubuntu-24.04 rather than the starved floating selector;
  • preserve provider-reported judge usage at the transport boundary with explicit provider-vs-synthetic provenance and immutable usage snapshots;
  • accept both Chat Completions (prompt_tokens / completion_tokens) and Responses (input_tokens / output_tokens) token-counter families without converting authoritative all-zero provider evidence into estimated spend.

Causal repair lineage

The hosted full suite first exposed worker-accounting fixtures whose evidence scope was polluted by the optional fast-mlsirm judge call. Those tests now suppress only that optional judge path while retaining the actual worker provider call and its usage assertions.

A subsequent valid review showed that an all-zero usage mapping was ambiguous: the same value shape can be a real provider measurement or synthetic mock/fast-mlsirm zero-fill. The source repair therefore records served_usage_source at the transport boundary, follows the agent that actually served after failover, snapshots mutable provider usage, treats provider-reported zero as measured, and leaves synthetic/unknown zero unmeasured.

A later exact-head review correctly found that the zero-usage validator only recognized legacy Chat Completions fields. The current source and regression suite now accept complete non-negative Responses counters (input_tokens, output_tokens, total_tokens) as the same authoritative usage family. Positive aggregate fallback uses the same aliases. The focused regression explicitly proves provider-reported all-zero Responses usage survives accounting.

Temporary self-modifying repair workflows used during the source repair have been removed from the branch. Superseded temp-writer review findings are resolved only because those workflow files no longer exist.

Why this owns README #994's blocker

README PR #994 did not create these accounting/test defects; its runner repair exposed them. The causal production/test fixes therefore live here. #994 must converge onto protected main only after this owner repair integrates and then reacquire exact-head README checks/reviews on the new base.

Current authority

Exact current head: 8f9fd87283fc6998fc590d3ab7ea3c6007db3bdb on recorded protected base main@4d143601c2904a28e95d091b261c0a15e9a4f283.

Current review-thread inventory is resolved. Fresh exact-head Tests, Security, Fuzz, SAST Semgrep, and Security Scan runs are queued and therefore non-passing. No predecessor-head workflow result transfers. Merge only if this unchanged head becomes terminal-clean and then-live review/thread/branch governance permits the ordinary protected path.

Scope

Tests, repository-owned CI runner admission, and the accounting source repair required to make their asserted evidence truthful. No security threshold, release, deployment, credential, or governance gate is weakened.

Summary by CodeRabbit

  • 개선 사항

    • 모델 판정 결과의 토큰 사용량이 제공자 보고값과 합성값을 구분해 기록됩니다.
    • 제공자가 보고한 토큰 사용량이 0인 경우에도 유효한 측정값으로 보존됩니다.
    • 출처가 불명확하거나 테스트용으로 생성된 사용량은 실제 사용량으로 기록되지 않습니다.
    • 사용량 및 추적 정보가 변경되지 않는 스냅샷으로 보존됩니다.
    • 다양한 채팅 및 응답 형식의 토큰 사용량 처리가 보강되었습니다.
  • 품질 개선

    • 자동화된 보안·퍼징·품질 검사 환경이 Ubuntu 24.04로 고정되었습니다.
    • 비동기 임베딩 배치 처리의 완료 확인이 안정화되었습니다.

…routing change; fix 1 impossible usage_source assertion

Root causes (all test-only fixes; no production code changed):

1-3. tests/test_orchestrated_responses_stream.py: commit 9173923
   ("Keep orchestrator/free on the auto route path") intentionally removed
   FREE_MODEL from would_route()'s conduct-eligible set, so orchestrator/free
   now unconditionally takes the single-worker route path instead of the
   thinker/worker/verifier/synthesizer conduct workflow, regardless of
   _needs_workflow(). That commit already added its own passing regression
   tests (test_chat_orchestration_mode_http_honesty.py,
   test_routing_eval.py) locking in the new behavior, but left 3 tests in
   this file asserting the old conduct-path shape for orchestrator/free:

   - test_virtual_models_stream_openai_reasoning_summaries[orchestrator/free]:
     expected a 4-stage reasoning summary; route now emits only one
     ("Executing the selected approach."), and run["mode"] is "route" not
     "conduct". Verified against the pre-9173923b revision that the test
     passed there, confirming the routing change is what broke it.
   - test_http_virtual_responses_preserves_message_array_and_sampling_controls:
     asserted the caller's [system, user, assistant] messages at slice
     [1:4], which only holds when conduct prepends a per-stage system
     instruction at index 0. route_once forwards the original messages
     unchanged, so they start at index 0 -- confirmed by direct
     instrumentation of client.chat().
   - test_stream_failure_emits_terminal_responses_event: patched
     orchestrator.conduct to raise, but orchestrator/free's stream now
     never calls conduct, so the failure injection no longer fired.
     Repointed the mock to stream_route (the method actually invoked on
     this path); the generic Exception handler's redaction/response.failed
     behavior in _stream_orchestrated_response is unchanged.

4. tests/test_spend_analytics.py::test_exact_output_without_prompt_usage_is_explicitly_unavailable:
   asserted usage_source == "mixed", which is structurally unreachable for
   this fixture. _step_output_tokens() only returns "reported" when a step
   carries a usage dict with a valid completion/output token count, and the
   single agent here uses the mock:// transport, which ModelClient.chat()
   never populates with usage (self._local.usage stays None on that path).
   Every conduct-stage and judge step therefore falls back to the exact
   tokenizer, so the bucket is homogeneously "tokenizer" -- "mixed" would
   require at least one genuinely provider-reported step, which this
   offline fixture can never produce. Confirmed by direct inspection of
   _step_output_tokens and by exercising the fixture with a working
   fast-mlsirm judge mocked in (still all-tokenizer). This was wrong from
   the test's introduction in b2a2607 (#975) and unrelated to any later
   commit.

The tests/test_psychometric_routing.py::test_fast_mlsirm_fit_uses_judge_acceptance_item_for_context_score
failure (ModuleNotFoundError: fast_mlsirm, gated on python_full_version >=
3.12) is confirmed environment-scoped, not touched here.

Verification (Python 3.11.15 venv, pinned hash-locked install per
CLAUDE.md/AGENTS.md; fast-mlsirm's private-repo tarball could not be
fetched through this sandbox's egress proxy, so numpy/fast_mlsirm and
tests/test_psychometric_routing.py are unavailable here -- a sandbox
limitation, not a code issue):

- python -m pytest tests -q --continue-on-collection-errors:
  3300 passed, 2 skipped (docker CLI unavailable; optional mcp/_token_packer
  deps not installed), 1 collection error (the known numpy/fast_mlsirm gap
  above). All 4 target tests now pass; no other regressions.
- coverage run -m pytest tests -q --ignore=tests/test_psychometric_routing.py:
  3300 passed, 2 skipped, 0 failed. coverage report: 95% (gaps are the same
  fast-mlsirm/numpy-gated branches, e.g. psychometric_routing.py at 50%;
  pre-existing and unrelated to this change -- production code was not
  modified).
- interrogate: RESULT PASSED (minimum: 100.0%, actual: 100.0%).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review Change Stack

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 74d3e08d-d289-428c-bc94-d445fbc72c6b

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: e0903695-49fd-4bf7-ae9f-6c203d6d3135

📥 Commits

Reviewing files that changed from the base of the PR and between 02b61e4 and 65f5a03.

📒 Files selected for processing (1)
  • tests/test_provider_embedding_batch_backend.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Judge 사용량의 출처와 snapshot 처리를 명확히 하고, provider 및 mock usage의 회계 규칙과 테스트를 갱신했습니다. GitHub Actions 작업의 실행 러너를 ubuntu-24.04로 고정했습니다.

Changes

Judge 사용량 회계

Layer / File(s) Summary
사용량 캡처 및 출처 분류
contextual_orchestrator/orchestrator.py
_FastMLSIJudgeAdapter가 usage snapshot과 served_usage_source를 기록합니다. Provider와 mock 출처를 구분합니다.
Judge 사용량 증거 판정
contextual_orchestrator/orchestrator.py, tests/test_model_judge.py
Chat 및 Responses token 형식을 검증합니다. Adapter usage를 우선 사용하고, 조건에 맞는 집계 usage를 fallback으로 사용합니다.
사용량 출처 회귀 검증
tests/test_model_judge_usage_provenance_regression.py
Provider의 전부 0인 usage, mock usage, 단일 usage 읽기, usage snapshot 보존을 검증합니다.
Provider 및 spend 회계 테스트 격리
tests/test_provider_usage_capture.py, tests/test_spend_analytics.py, tests/test_batch_optimizer.py, tests/test_provider_embedding_batch_backend.py
선택적 fast-mlsirm judge를 비활성화하고 worker usage 및 tokenizer usage 계약을 독립적으로 검증합니다. 비동기 embedding 작업의 완료 대기를 명시합니다.

CI 실행 환경 고정

Layer / File(s) Summary
GitHub Actions 러너 고정
.github/workflows/ci.yml, .github/workflows/fuzz.yml, .github/workflows/security.yml
pytest, 벤치마크, 퍼징, CodeQL 및 Python 공급망 작업의 runner를 ubuntu-24.04로 변경합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🔵 Low · up to 65f5a

The embedding-batch test synchronization change is approved, but an earlier workflow configuration concern remains unresolved and should be addressed before relying on that automation.

Sequence Diagram(s)

sequenceDiagram
  participant FastMLSIJudgeAdapter
  participant ProviderOrMockTransport
  participant TaskOrchestrator
  FastMLSIJudgeAdapter->>ProviderOrMockTransport: Judge 요청
  ProviderOrMockTransport-->>FastMLSIJudgeAdapter: 응답과 usage 반환
  FastMLSIJudgeAdapter->>FastMLSIJudgeAdapter: usage snapshot과 출처 기록
  FastMLSIJudgeAdapter-->>TaskOrchestrator: judge 결과와 accounting fields 반환
  TaskOrchestrator->>TaskOrchestrator: judge_usage 우선 판정
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 PR의 주요 목적이었던 기존 테스트 4건의 실패 수정과 직접 관련되며, 간결하고 명확합니다.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 30 functions across 7 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/orchestrated-responses-stream-and-spend-analytics-20260901

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

_judge_adapter_accounting_fields() used a plain truthiness check on
served_usage, so a non-empty-but-all-zero usage dict (which fast-mlsirm
produces when it aggregates a missing trace) was treated as genuine
reported spend, disagreeing with the already-guarded sibling check a
few lines above for the identical issue (Devin review on #961).

Add a shared _usage_has_positive_evidence() helper and apply it
consistently at both call sites so a completed-but-unmeasured judge
call stays honestly attributed as unmeasured rather than fabricated
as reported-zero spend.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Pushed fd9aac52: an additional, complementary fix on top of f3ef3dcd/25ec4823.

Devin's finding that test_exact_output_without_prompt_usage_is_explicitly_unavailable depends on whether the optional fast-mlsirm package is installed traced back to a real production bug, not just a test-isolation gap: _judge_adapter_accounting_fields() used a plain truthiness check (if judge_adapter.served_usage:) on the usage dict. fast-mlsirm aggregates a missing trace usage into a non-empty, all-zero mapping ({"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}), which is truthy in Python yet carries no real measurement — so that call site fabricated it as "reported" spend, disagreeing with the sibling call site a few lines above, which was already guarded against the identical shape (Devin review on #961).

Fix: a shared _usage_has_positive_evidence() helper, applied consistently at both call sites, so a completed-but-unmeasured judge call stays honestly attributed as unmeasured rather than counted as reported-zero spend.

Added two regression tests to tests/test_model_judge.py (RED against the reverted fix, GREEN with it restored):

  • test_judge_adapter_zero_aggregate_served_usage_is_not_treated_as_reported
  • test_judge_adapter_positive_served_usage_is_still_reported

This is independent of and additive to the test-isolation fix already on this branch — both fixes fix different halves of the same underlying usage_source divergence. Full local suite: 3302 passed, 2 skipped (the 1 collection error is numpy missing in this sandbox's venv, unrelated — test_psychometric_routing.py isn't touched by this change). The six directly related test files (test_batch_optimizer.py, test_model_judge.py, test_nim_benchmark.py, test_orchestrator_client_boundaries.py, test_orchestrator_dispatch_boundaries.py, test_spend_analytics.py) all pass on the combined head: 233 passed.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

seonghobae and others added 5 commits September 2, 2026 05:04
Manually completes the fix the "One-shot usage provenance repair"
workflow (added in 934fdb0) was written to apply, since that
workflow has been stuck queued on this org's saturated Actions fleet
since it was added (first on ubuntu-24.04, then macos-15 in
9556fc8 -- neither has run) and the underlying finding is
merge-blocking per review. Applies the exact same patch the
workflow's embedded script specifies, plus two fixes the workflow
itself did not cover:

- complete()'s served_usage_source resolution looks up the actually-
  served agent via self.orchestrator._agent(served_id) to classify
  provenance; when _invoke fails over to (or a test stands in) an
  agent outside the orchestrator's own candidate pool, that lookup
  raises KeyError. Catch it and fall back to unknown provenance
  (None, already the documented "unmeasured" case) instead of losing
  the call's accounting entirely.
- tests/test_model_judge_usage_provenance_regression.py's ModelAgent
  fixtures used "judge-agent"/hyphenated ids, which fail this repo's
  two-word snake_case object-name convention
  (contextual_orchestrator.conventions.require_object_name) at
  construction. Renamed to "judge_agent".
- tests/test_model_judge.py::test_fast_mlsirm_path_is_used_when_available
  asserted judge_usage against fast-mlsirm's result.usage aggregate;
  the new logic intentionally prefers the adapter's own transport-
  boundary served_usage capture when both carry evidence, so updated
  the expectation to the served_usage value that test's own mocked
  _invoke call actually returns.

Removes the now-fulfilled one-shot workflow file per this repo's
self-modifying-workflow convention (delete once its purpose is
achieved) and cancels both of its stuck queued runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…m-and-spend-analytics-20260901' into fix/orchestrated-responses-stream-and-spend-analytics-20260901

# Conflicts:
#	.github/workflows/_temp_usage_provenance_repair.yml
devin-ai-integration[bot]

This comment was marked as resolved.

claude and others added 4 commits September 1, 2026 20:28
…m-and-spend-analytics-20260901' into fix/orchestrated-responses-stream-and-spend-analytics-20260901

# Conflicts:
#	contextual_orchestrator/orchestrator.py
@seonghobae
seonghobae enabled auto-merge (squash) September 1, 2026 23:04
@opencode-agent
opencode-agent Bot disabled auto-merge September 2, 2026 00:23

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 new potential issues.

Devin Review

Comment thread contextual_orchestrator/orchestrator.py Outdated
Comment thread .github/workflows/source-fix-1002-batch-usage-fixture.yml Outdated
Comment thread scripts/source_fix_1002_batch_usage_fixture.py Outdated
Comment thread .github/workflows/source-fix-1002-batch-usage-fixture.yml Outdated
Adopt protected-main's already-merged Responses streaming contract from #1005 and preserve PR #1002's accounting/CI repairs plus its active exact-head test-fixture repair lane. The temporary merge-intent markers are removed in this merge commit.
@seonghobae seonghobae added the bug Something isn't working label Sep 2, 2026 — with ChatGPT Codex Connector
@seonghobae
seonghobae enabled auto-merge September 2, 2026 03:15
@opencode-agent
opencode-agent Bot disabled auto-merge September 2, 2026 09:17
CodeRabbit flagged 52.17% docstring coverage (12/23 functions) against
this PR's diff, below its 80% threshold. This repo's own `interrogate`
gate already passed at 100% throughout (it excludes tests/ and
private/semiprivate functions), so no production code was undocumented
-- the gap was entirely in touched test functions and their nested
helper classes, which CodeRabbit's own scanner does count.

Added 11 concise, accurate docstrings across the exact 5 files/23
functions CodeRabbit analyzed (verified: 12/23 = 52.1739...% matches
their reported 52.17% precisely once nested defs inside touched test
functions are included):

- tests/test_model_judge_usage_provenance_regression.py:
  _ChangingUsageResponse.__init__, _ChangingUsageResponse.get, _adapter
- tests/test_model_judge.py: test_fast_mlsirm_path_is_used_when_available
  and its nested _FakeJudge.__init__, _FakeJudge.judge, _Criterion.__init__
- tests/test_batch_optimizer.py: test_batch_route_persists_runs_with_usage
- tests/test_provider_usage_capture.py: test_reported_usage_preferred_and_labeled,
  test_reported_prompt_tokens_surface_in_totals
- tests/test_spend_analytics.py:
  test_exact_output_without_prompt_usage_is_explicitly_unavailable

Separately verified CodeRabbit's "Merge Risk: Moderate" usage-accounting
and repair-workflow findings against the PR's actual current head and
found both already resolved by earlier commits on this branch (the
review was submitted against commit 4351a04, a mid-repair state):
Responses-API input_tokens/output_tokens aliases were already added to
both _usage_has_positive_evidence/_usage_is_reported_token_mapping
(6eafe23), and all _temp_usage_*_repair.yml workflow files were
already deleted (ebb087b, 06e6369, and others) once their one-shot
purpose was fulfilled -- confirmed no such files exist on this head.
No production code change needed for either.

Full suite verified clean: 3316 passed, 2 skipped, 1 failed (the
known, environment-scoped fast_mlsirm ModuleNotFoundError; not a
regression). interrogate: RESULT PASSED (100.0%).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

CodeRabbit follow-up: verified all three findings; docstrings fixed, two already stale, one flagged for owner decision

Re-checked against the PR's actual current head, since CodeRabbit's review (up to 4351a) was submitted against a mid-repair commit, several commits before this branch's current tip.

Docstring coverage (fixed, pushed in 2f325e8e). CodeRabbit's 52.17% (12/23) is a real number from its own diff-scoped scanner, not this repo's interrogate gate (which already passed 100% throughout — it excludes tests/ and private/semiprivate functions, so the gap was entirely in touched test functions and nested test-double classes that CodeRabbit's scanner does count). Verified the exact function set (12/23 = 52.1739...%, matching CodeRabbit's number precisely once nested defs inside touched test functions are included) and added 11 concise docstrings across the 5 flagged files. Full suite re-verified clean afterward: 3316 passed, 2 skipped, 1 failed (known environment-scoped fast_mlsirm ModuleNotFoundError, not a regression).

"Drops valid Responses usage" (already resolved, no action needed). _usage_has_positive_evidence and _usage_is_reported_token_mapping (contextual_orchestrator/orchestrator.py) already fall back to Responses-style input_tokens/output_tokens aliases (usage.get("prompt_tokens", usage.get("input_tokens")) etc.) — landed in commit 6eafe232 ("accept Responses usage counters in both accounting helpers"), well before the current head. tests/test_model_judge_usage_provenance_regression.py::test_provider_reported_responses_zero_usage_remains_reported exercises exactly this and passes.

"Non-parseable repair workflow" (already resolved, no action needed). .github/workflows/_temp_usage_alias_repair.yml and _temp_usage_counter_family_repair.yml (and their siblings _temp_usage_provenance_repair.yml, _temp_usage_alias_repair_v3.yml) do not exist on the current head — confirmed by listing .github/workflows/ at this branch's tip. They were one-shot RED→fix→GREEN→remove-itself workflows per this repo's convention; their fixes were applied by hand once the workflow runners themselves failed to materialize jobs, then the files were deleted in commits 06e6369e, dfc04d89, bd8f8f87, 2f5f84ec, ebb087b1. Nothing to fix in the current diff.

Adapter reuse lifecycle — documenting for explicit owner attention, not fixing here.

_FastMLSIJudgeAdapter (contextual_orchestrator/orchestrator.py) tracks one provider call's evidence in single-slot mutable instance attributes: served_agent_id, served_model, served_usage, served_usage_source, served_output. Each call to complete() / complete_structured() overwrites these — it does not accumulate them. _model_judge_verification() constructs a fresh adapter per verification call (orchestrator.py:~8160), so there is no cross-request reuse issue.

The latent issue is reuse within a single verification call. fast_mlsirm.llm_judge.ContextualOrchestratorJudge.judge() calls the adapter's complete()/complete_structured() exactly once when category_method resolves to "direct" — the only path this repo's sole call site (orchestrator.py, fast_judge.judge(task=..., answer=..., criteria=(...)), no category_count passed) ever exercises. But judge()'s binary_threshold / cumulative_threshold methods (fast_mlsirm/llm_judge.py, _binary_threshold_judgments) call self._complete(...) once per (criterion × threshold) pair — up to MAX_BINARY_THRESHOLD_CALLS (64) — against the same adapter instance, optionally concurrently via a ThreadPoolExecutor (_binary_threshold_concurrency) whenever the client exposes local_concurrency > 1.

If that path were ever exercised, each complete_structured() call would clobber the adapter's served_usage/served_agent_id/etc. from the prior call, so _judge_adapter_accounting_fields() would only ever see the last provider call's usage — silently losing every earlier call's spend — and under the thread-pool path it would be a genuine data race (concurrent writers mutating the same instance's attributes).

Verified this is dormant, not currently firing: category_count is never passed to judge() anywhere in contextual_orchestrator/ or tests/ today (grep -rn "category_count" contextual_orchestrator/ tests/test_model_judge*.py → no matches), so _binary_threshold_judgments is unreachable from this codebase as it stands. Not blocking this PR's merge on its own.

This needs an explicit owner decision on intended adapter lifecycle before category_count/binary-threshold judging is ever wired up here, e.g.:

  • forbid reuse — raise if complete()/complete_structured() is called more than once per adapter instance, forcing one adapter per provider call; or
  • make the adapter accumulate a list of served calls instead of overwriting a single slot, with _judge_adapter_accounting_fields() summing/reporting all of them; or
  • require any future category_count-based judge path to construct a fresh adapter per boundary call rather than sharing one.

Flagging precisely so it isn't lost, per this session's standing loop directive.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4


Generated by Claude Code

@seonghobae seonghobae added priority: high status: needs-review Open pull request requiring current-head review or checks type: bug Defect or incorrect behavior labels Sep 2, 2026 — with ChatGPT Codex Connector
seonghobae pushed a commit that referenced this pull request Sep 2, 2026
…fast-mlsirm judge

test_exact_output_without_prompt_usage_is_explicitly_unavailable failed on
protected main (212ff43, unmodified) and reproduced identically here:
AssertionError: assert 'tokenizer' == 'mixed'. The optional fast-mlsirm
judge integration is environment-dependent (present/absent changes whether
a second judge step contributes a different usage source, which is what
made "mixed" true in whatever environment originally authored this
assertion) -- this environment has no fast_mlsirm installed, so only the
single tokenizer-sourced worker step exists and the correct, deterministic
usage_source is "tokenizer".

Port PR #1002's fix for this same test (verified there): patch
_resolve_fast_mlsirm_components to return None so the assertion is pinned
to the raw-output tokenizer-fallback contract this test actually owns,
independent of whether the optional dependency happens to be installed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX

Copy link
Copy Markdown
Contributor Author

Rebased onto current main + noema-review root cause

Merge. Branch was behind on the well-known stale base 8839081.... Fetched fresh, re-verified live head (2f325e8e...) and mergeable_state: behind matched, confirmed no concurrent edits (last commit ~10h old), then ran git fetch origin main && git merge origin/main --no-edit. The merge was conflict-free (ort auto-merged cleanly) — this PR's own diff (9 files: orchestrator.py, CI workflow pins, 5 accounting/spend-analytics test files) doesn't touch CHANGELOG.md or docs/product-technical-gap-baseline.md, so neither of those usual conflict points was in play here. The one file both sides touched, .github/workflows/fuzz.yml, merged correctly: this PR's ubuntu-24.04 pin and main's new "Fuzz governed rater observation parser" step both survived. Pushed non-force: 2f325e8e..cef6214f.

Verification on the merged head (python -m pip install --require-hashes -r requirements.lock && python -m pip install --no-deps -e .):

  • PR's own claimed scope (test_batch_optimizer.py, test_model_judge.py, test_model_judge_usage_provenance_regression.py, test_provider_usage_capture.py, test_spend_analytics.py): 82/82 passed. The "4 pre-existing test failures" this PR fixes are confirmed fixed on the merged head.
  • Full suite (python -m pytest tests -q): 3360 passed, 2 skipped, 2 failed in 844.98s. Both failures are unrelated to this PR and to the merge — confirmed via git diff origin/main -- <file> returning empty for both, i.e. byte-identical to main and untouched by either this PR's diff or the merge resolution:
    • test_admin_contract.py::test_model_group_mutations_refresh_audit_eventsNameError: name 'json' is not defined (pre-existing missing import json in that test file on main, unrelated to this PR).
    • test_psychometric_routing.py::test_fast_mlsirm_fit_uses_judge_acceptance_item_for_context_scoreModuleNotFoundError: No module named 'fast_mlsirm' (that dependency is gated python_full_version >= "3.12" in requirements.lock/pyproject.toml; this verification ran on Python 3.11, so it's an environment gap, not a regression).

Neither failure is a regression introduced by this merge.

noema-review root cause (infra-side, matching #993/#976/#984/#972). The failed run (https://github.com/ContextualWisdomLab/contextual-orchestrator/actions/runs/33613811994/job/100194921923) shows the reviewer-model line-citation-drift pattern: the LLM reviewer cited contextual_orchestrator/orchestrator.py:406 (RIGHT side) on its first pass and :382 on its one permitted bounded-repair pass, but neither is an exact changed-side line in the diff — the evidence validator's nearest-line search landed on 409-413 and 385-389 respectively. Both the original review and its repair attempt were rejected for the same reason (... which is not an exact changed-side line), so the job errored with Noema model-output repair remained invalid and no verdict was ever posted. This is a review-infrastructure line-binding issue, not a defect in this PR's diff.

Ready for a fresh noema-review run on the new head cef6214f.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Diagnosis: pre-39a4348e admin-contract staleness, not a defect in this PR's own diff

The "Full unit and contract suite" required check was failing with:

FAILED tests/test_admin_contract.py::test_model_group_mutations_refresh_audit_events - NameError: name 'json' is not defined. Did you forget to import 'json'
1 failed, 3361 passed, 2 skipped in 729.47s

Root cause: this branch's merge-base with main was 212ff437 (the commit that introduced the missing-import json / extraction bug in tests/test_admin_contract.py::test_model_group_mutations_refresh_audit_events). The fix for that shared bug landed on main as 39a4348e (#1035), but this branch predates it, so it inherited the stale failing test. PR #1002's own diff (Responses/spend accounting test fixes, README-related) does not touch test_admin_contract.py — confirmed via git diff 212ff437...cef6214f -- tests/ showing only test_batch_optimizer.py, test_model_judge.py, test_model_judge_usage_provenance_regression.py, test_provider_usage_capture.py, test_spend_analytics.py.

The separately-failing CodeQL analysis check is the known, already-tracked org-wide "default setup enabled" infra issue being fixed centrally in .github (#1750/#1788) — left untouched here.

What was done

  1. Verified no concurrent activity on fix/orchestrated-responses-stream-and-spend-analytics-20260901 (head unchanged at cef6214f from updated_at: 2026-09-02T19:28:23Z through the merge and both pre-push re-checks).
  2. git fetch origin main && git merge --no-edit in a scratch worktree — clean merge, no conflicts. Diff of the resulting merge commit vs. origin/main is exactly 418 insertions(+), 40 deletions(-) — matching this PR's own stated diff stat precisely, confirming the merge is base-main + this PR's changes and nothing else.
  3. Verification (offline sandbox venv seeded from local uv cache to work around this session's egress policy on raw GitHub archive downloads — CI's own hosted runner is unaffected and re-ran everything fresh post-push):
    • tests/test_admin_contract.py alone: 3 passed (previously 1 failed).
    • PR's own affected accounting/spend files (test_batch_optimizer.py, test_model_judge.py, test_model_judge_usage_provenance_regression.py, test_provider_usage_capture.py, test_spend_analytics.py): 82 passed.
    • Test files touched by the merged-in main commits (test_hourly_opencode_loop_contract.py, test_web_search.py, fuzz/test_fuzz_properties.py): 59 passed.
    • Full suite (python -m pytest -q, matching CI's uv run --locked --extra api --extra db --extra queue --group dev python -m pytest -q): 3401 passed, 2 skipped, 0 failed in 772.74s (0:12:52).
  4. Pushed the merge commit non-force: cef6214f..02b61e41 (fast-forward). Re-checked remote branch head immediately before pushing — unchanged.

CI's hosted "Full unit and contract suite" will re-run on the new head 02b61e41 and is expected to go green now that 39a4348e is included.


_Generated by Claude Code


Generated by Claude Code

…tions

Ports the identical fix from #1044 (not yet merged) into this PR's head,
per the standing PR-governance rule to port the same change now rather
than wait on a separate PR to merge.

PR #1002's "Full unit and contract suite" check failed on its current
head with exactly one failure, unrelated to this PR's own diff:

  FAILED tests/test_provider_embedding_batch_backend.py::test_unknown_tokenizer_byte_bound_never_becomes_recorded_usage[한글🙂é] - KeyError: 'total_tokens'
  1 failed, 3400 passed, 2 skipped

Root cause (from #1044): both
test_unknown_tokenizer_uses_authoritative_provider_usage and
test_unknown_tokenizer_byte_bound_never_becomes_recorded_usage call
complete_embeddings_batch() on a provider-backed (non-mock) embedding
agent without wait_timeout. That backend completes asynchronously in a
background ThreadPoolExecutor thread, so without wait_timeout the
calling thread can read the document before the job finishes, hitting
the not-is_complete early-return branch that omits total_tokens
entirely. Under CI load this triggers intermittently; the failing
Unicode parametrize case is incidental, not causal.

Test-only change, no production code touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Ported test-only race fix from #1044 (not yet merged)

Diagnosis. This PR's "Full unit and contract suite" check failed on head 02b61e4 with exactly one failure, unrelated to this PR's own diff:

FAILED tests/test_provider_embedding_batch_backend.py::test_unknown_tokenizer_byte_bound_never_becomes_recorded_usage[한글🙂é] - KeyError: 'total_tokens'
1 failed, 3400 passed, 2 skipped

This is the exact race condition already root-caused and fixed in #1044 (fix/embedding-batch-wait-timeout-race), currently open and unmerged: test_unknown_tokenizer_uses_authoritative_provider_usage and test_unknown_tokenizer_byte_bound_never_becomes_recorded_usage call complete_embeddings_batch() on a provider-backed (non-mock) embedding agent without wait_timeout. That backend completes asynchronously in a background ThreadPoolExecutor thread, so the calling thread can read the document before the job finishes, hitting the not-is_complete early-return branch that omits total_tokens entirely. Under CI load this triggers intermittently; the specific failing Unicode parametrize case is incidental, not causal.

Per the standing PR-governance rule (port the same change into the affected PR now rather than wait on the origin PR to merge — it no-ops once the base carries it), I ported #1044's identical diff directly onto this PR's head rather than waiting on #1044.

Change applied (byte-identical to #1044's diff, verified via matching pre/post blob hashes 0eb661f..ec44918): added wait_timeout=1 to both complete_embeddings_batch(...) calls in tests/test_provider_embedding_batch_backend.py, plus the same explanatory comment and the same added assert document["status"] == "completed" in the second test. Test-only, no production code touched.

Verification:

  • tests/test_provider_embedding_batch_backend.py (all 20 tests): passing, repeated 10x with zero flakes.
  • pytest -k "embedding or token_counting or cost_router": 261 passed, 1 skipped.
  • Full suite (pytest tests -q, Python 3.11 sandbox venv against requirements.lock): 1 failed, 3400 passed, 2 skipped in 743.11s. The sole failure — test_psychometric_routing.py::test_fast_mlsirm_fit_uses_judge_acceptance_item_for_context_score (ModuleNotFoundError: No module named 'fast_mlsirm') — is the same pre-existing sandbox-only artifact fix(tests): wait for provider embedding batch completion before assertions #1044's own verification documented (this environment's git proxy can't fetch the native fast-mlsirm wheel), unrelated to this change. The originally reported target failure is gone; the embedding batch tests now pass cleanly.
  • interrogate: not applicable — tests/ is excluded from the 100%-docstring gate in pyproject.toml, and no new functions were added.

Freshness: confirmed PR head unchanged (02b61e41b074f6f2f675b0d7f3b7c9e4f02e0168, updated_at static) immediately before pushing.

Pushed non-force as a fast-forward: 02b61e4..65f5a03 → new head 65f5a0397d5e8dc958b00e3a20b2c2999750b8af.


🤖 Generated with Claude Code


Generated by Claude Code

claude and others added 2 commits September 3, 2026 22:25
Devin review on PR #1002 (thread PRRT_kwDOTB3CTs6eVM7K,
contextual_orchestrator/orchestrator.py lines ~414-419): after
TaskOrchestrator._invoke returns, _FastMLSIJudgeAdapter.complete()
re-resolved the serving agent via self.orchestrator._agent(served_id) --
a fresh scan of the *live*, mutable TaskOrchestrator.candidates list -- to
classify served_usage_source (provider_reported vs synthetic_mock).

Verified the race is real, not theoretical: every pool-mutation API
(add/patch/remove/promote/demote candidate, lines ~6018-6265) reassigns
self.candidates to a new list rather than mutating it in place, and no
lock guards it. build_server() (server.py) wires one TaskOrchestrator
instance into every request Handler closure, served by
ThreadingHTTPServer, so an admin-API pool mutation on one request thread
can race an in-flight judge call's _invoke on another thread against the
same orchestrator instance. served_model already avoided this (captured
directly from _invoke's own return tuple), but served_usage_source did
not -- so a same-id pool replacement landing between the provider call
completing and this lookup could relabel a genuine provider-reported
all-zero usage as synthetic (spend silently dropped as unmeasured), or
relabel synthetic mock/fast-mlsirm zero-fill as provider-reported (fake
measured spend recorded) -- exactly Devin's description.

Fix: TaskOrchestrator._invoke gains an optional on_success callback,
invoked with the exact (frozen) ModelAgent that served the winning call,
at both success points -- the sequential failover loop and the immediate
endpoint race -- before _invoke returns. Because ModelAgent is a frozen
dataclass and candidates/race_members are call-local snapshots immune to
a later self.candidates reassignment, this reference can never be
retroactively altered by a concurrent pool mutation, unlike a post-hoc
self._agent(served_id) lookup. _FastMLSIJudgeAdapter.complete() now
prefers this atomically-captured agent for usage-source classification,
falling back to the previous self._agent(served_id) lookup only when
on_success was never invoked (a custom _invoke test double that ignores
the new kwarg entirely) -- preserving compatibility for exactly the test
doubles Devin's own suggested direction called out. _invoke's return
type and all of its other callers/test doubles are unchanged.

Added two regression tests to
tests/test_model_judge_usage_provenance_regression.py reproducing the
race in both directions Devin asked for: a ModelClient.chat() override
mutates orchestrator.candidates (same id, different base_url) as a side
effect of serving the call, simulating a concurrent admin request.
Confirmed RED against unmodified orchestrator.py (provider-to-mock:
served_usage_source flips to synthetic_mock and judge_usage is dropped;
mock-to-provider: flips to provider_reported and synthetic zero usage is
recorded as measured), then GREEN with this fix. Full suite: 3402
passed, 2 skipped (unrelated), 1 deselected (fast-mlsirm needs Python
>=3.12, this venv is 3.11) -- 0 failed. interrogate on the touched
production file: 100%.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high status: needs-review Open pull request requiring current-head review or checks type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants