Skip to content

[Bug]: combo defaultEffortMode=force does not override declared none/minimal sentinels #5989

Closed as not planned

Description

@RHODIZSECURITY

Summary

A combo configured with defaultEffort: high and defaultEffortMode: force can still forward a caller-supplied reasoning.effort: none unchanged.

Root cause: concreteComboRequestBody() decides whether force mode may override the caller using isCodexReasoningEffort(). That predicate intentionally covers only the ranked low..ultra ladder. none and minimal are valid declared request sentinels handled by isDeclaredReasoningEffort(), so force mode silently excludes them.

Reproduction

  1. Configure a combo with defaultEffort: high and defaultEffortMode: force.
  2. Include a concrete target that advertises a known reasoning ladder containing high but rejects none on the wire (Meta Muse is one live example).
  3. Send a request through the combo with reasoning: { effort: "none" }.
  4. Before the fix, inspect the concrete child body: the target still receives reasoning.effort=none instead of the forced combo default.
  5. The live Meta Muse target returns HTTP 400 because none is unsupported.

Minimal unit reproduction: concreteComboRequestBody({ reasoning: { effort: "none" } }, target, "high", ["minimal","low","medium","high","xhigh","max"], "strict", "force") should produce reasoning.effort === "high".

Live impact

Claude Code Auto's safety side-query classifier can send reasoning.effort=none. In a failover combo whose healthy target is Meta Muse, Muse rejects the leaked value with HTTP 400. Claude Code then treats its Auto classifier as unavailable and fail-closes Bash.

After the fix, the same live logical request changed from HTTP 400 to HTTP 200 and the concrete Muse child wire effort is high.

Proposed fix

Use isDeclaredReasoningEffort() for the caller-side force-mode check while continuing to require the configured combo default itself to be a ranked isCodexReasoningEffort() value.

For unknown target ladders, strict mode remains conservative and preserves the caller effort because the proxy cannot prove that the forced default is supported; the PR includes an explicit regression test for that boundary.

Validation

  • focused combo + reasoning suites: 122 pass / 0 fail / 490 assertions
  • TypeScript typecheck: PASS
  • live OpenCodex 2.66.9 RHODIZ deployment: caller none on the forced-high combo -> HTTP 200; concrete Muse child wire effort -> high

Activity

  1. github-actions commented on Sep 26, 2026

    @github-actions
    Contributor

    Issue closed: insufficient detail

    Thanks for taking the time to submit this issue. It was closed automatically because the structured report is missing information needed to evaluate or reproduce it.

    • Reproduction is empty.

    Please edit the issue to add:

    • List the exact steps to reproduce the problem.

    See the Contributing guide for details.

    Once the report passes the automated checks, it will be reopened automatically unless a maintainer has changed its state.

  2. RHODIZSECURITY commented on Sep 26, 2026

    @RHODIZSECURITY
    ContributorAuthor

    Reproduction details added after the initial report was auto-closed:

    1. Configure a combo with defaultEffort: high and defaultEffortMode: force.
    2. Route to a target with a known ladder containing high but rejecting none on the wire (Meta Muse is one live example).
    3. Send reasoning: { effort: "none" } through the combo.
    4. Before the fix, concreteComboRequestBody() preserves none because the force check uses isCodexReasoningEffort() and excludes the valid declared sentinels none/minimal.
    5. The concrete Muse request fails HTTP 400. After switching the caller-side force check to isDeclaredReasoningEffort(), the child wire uses high and the same live request succeeds.

    Unknown target capability intentionally remains conservative in strict mode; PR #5990 now has an explicit regression for that boundary.

    Current focused validation: 122 pass / 0 fail / 490 assertions, typecheck PASS. Live RHODIZ validation changed the classifier path from HTTP 400 to HTTP 200.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions