Skip to content

Deep Scan reducer prompt requires code mode, but reducer workers do not enable code mode #1865

Closed

Description

@jwalkins2017

Summary

The Deep Scan reducer prompt (plugins/codex-security/mcp-app/templates/deep-scan/dedup.md) requires JavaScript code mode. The prompt tells the reducer to use the code-mode load and store functions.

The coordinator starts reducer workers without features.code_mode. The bundled Codex CLI 0.163.0 sets code_mode to off by default. Thus, the code-mode tool is not available to the reducer.

When the tool is not available, the reducer can end its turn without a call to record_codex_security_deep_reduction. The worker runner then does not find result.json and starts a retry. The retry has the same configuration, and the same failure occurs.

The coordinator waits 60 s, 180 s and 540 s (plus up to 30% jitter) between the 4 attempts. During this time, the reducer worker seems to stop. The status shows consecutiveErrors: 0 because the coordinator counts reducer failures in a different counter.

An increase in timeouts or retry counts does not supply the missing tool.

Environment

Observed on npm-v0.6.0 (13dcc6f)
Also on main @ e4b12f8. plugins/codex-security/mcp-app/templates/deep-scan/dedup.md, plugins/codex-security/mcp-app/src/deep-scan/executor.ts, plugins/codex-security/mcp-app/src/deep-scan/worker-runner.ts and plugins/codex-security/mcp-app/src/deep-scan/coordinator.ts have no changes after npm-v0.6.0.
Bundled Codex @openai/codex and @openai/codex-sdk 0.163.0
Scan mode cs scan --mode deep

Codex 0.163.0 with an empty CODEX_HOME shows these defaults:

$ CODEX_HOME=$(mktemp -d) codex features list | grep -E '^code_mode(_host)? '
code_mode                                under development  false
code_mode_host                           stable             true

The code-mode host is on. The code_mode feature is off. Thus, Codex does not give the code-mode tool to the model.

History

No release enables code_mode in production code. The search included all tags from npm-v0.1.0 to npm-v0.6.0. The search included the plugin source, the scripts, the Docker files and the GitHub action.

The prompt requirement started in npm-v0.1.31. In all bundled Codex versions after that release, code_mode is off by default. The data comes from codex features list with an empty CODEX_HOME.

Releases Bundled Codex code_mode default Reducer prompt
0.1.0 – 0.1.30 0.144.6 – 0.155.0 off (0.155.0) One call to get_codex_security_deep_reducer_inputs({}). The prompt does not require code mode.
0.1.31 – 0.2.0 0.156.1, 0.158.0, 0.162.0-alpha.15 off #1025 adds pages. The prompt says "concatenate the fragments inside code mode". The prompt has no load/store helper.
0.3.0 – 0.6.0 0.162.1, 0.163.0 off #1589 adds the mandatory load/store helper. The retry prompt has the same requirement.

Observed behavior

Reducer A (failed on all attempts)

  1. The reducer called get_codex_security_deep_reducer_inputs directly approximately 20 times.
  2. The reducer made 0 calls to record_codex_security_deep_reduction.
  3. At the end of each turn, the model said that code mode was not available.
  4. Validation did not find <artifactDir>/result.json (ENOENT). The runner logged worker_retry_scheduled and waited for the retry delay.
  5. The same failure occurred on all 4 attempts. The coordinator then logged dedup_worker_replaced and started a new reducer.
  6. The status showed consecutiveErrors: 0 during all attempts.

A different reducer agent had the same failure on all 4 attempts.

Reducer B (completed on its third turn)

Reducer B had the same configuration. Its first two turns ended without a submission. On its third turn, it said that code mode was not available. Then it continued with direct tool calls:

  • It called get_codex_security_deep_reducer_inputs directly with pages of approximately 2,000 bytes.
  • It read the findings of the previous aggregate with findingRef.
  • It made the submission arguments and corrected them with the errors from record_codex_security_deep_reduction.

Reducer B made 5 submissions:

# Result Cause
1 Schema rejection Incorrect finding entries. Finding provenance missing.
2 Schema rejection Incorrect finding entries. Finding provenance missing.
3 Identity rejection 1 of 3 previously accepted finding identities kept
4 Identity rejection 2 of 3 previously accepted finding identities kept
5 Accepted 3 of 3 identities kept. 5 findings in the output.

These facts come from Reducer B:

  • The host validates the result. The host does not validate the method. The reducer tools and the validation functions validateRetainedFindings and parseDeepReduction (plugins/codex-security/mcp-app/src/deep-scan/artifact-validation.ts) do not check for code mode. Thus, a reduction without code mode can pass validation. The prompt does not give this procedure.
  • The finding title is not part of the identity. scanFindingIdentity (plugins/codex-security/mcp-app/src/artifact-scan-draft.ts) uses [ruleId, identity.anchor, identity.instance ?? null] when the finding has an explicit identity. Otherwise, it uses [ruleId, locations[0].path, locations[0].startLine, locations[0].endLine ?? null].
  • The identity error does not identify the missing identity. validateRetainedFindings (plugins/codex-security/mcp-app/src/deep-scan/artifact-validation.ts) stops at the first missing identity. The error message is only Deep reduction discarded or changed a previously accepted finding identity. The message does not give the identity or the number of missing identities.

Reducer B completed because the model used a different procedure. The prompt and the tool configuration did not change.

Expected behavior

The reducer prompt and the reducer tool configuration must agree. Do one of these:

  • Enable code mode for the reducer worker, and verify it before the worker starts.
  • Give a reducer procedure in the prompt that does not require code mode.

If the reducer cannot get a required tool, the coordinator must stop with a non-retryable configuration error.

Root cause

1. The prompt requires code mode

plugins/codex-security/mcp-app/templates/deep-scan/dedup.md L9–L40 has these instructions:

  • "concatenate the fragments inside code mode" (L11)
  • "Keep fragments, cursor and completion together in one serializable store value" (L13)
  • A helper that calls load(key), store(key, state) and tools.mcp__cs_artifacts__… (L15–L38)

The prompt does not give a procedure without code mode.

The retry prompt has the same requirement. reducerInputRecoveryInstructions (plugins/codex-security/mcp-app/src/deep-scan/worker-runner.ts) says "assembling each json fragment in code" and "Persist … in one serializable store value".

The tool description has a conditional instruction: "When using code-mode store, …" (plugins/codex-security/mcp-app/src/server/compact-artifact-tools.ts L296).

2. The worker configuration does not enable code mode

CodexWorkerExecutor (plugins/codex-security/mcp-app/src/deep-scan/executor.ts) makes the worker configuration from the runtime configuration and workerSubagentConfig (plugins/codex-security/mcp-app/src/deep-scan/executor.ts). workerSubagentConfig sets only multi_agent_v2 and enable_fanout in features. No code sets features.code_mode.enabled for a worker.

The test assertWorkerSubagentPolicy (plugins/codex-security/mcp-app/tests/test_deep_scan_executor.ts) requires features.code_mode.enabled to be undefined.

Thus, the reducer has code mode only when the Codex configuration of the user enables it.

3. The tests enable code mode

The reducer page tests set code mode directly:

Thus, the tests do not use the configuration that production reducers get.

4. The runner retries a missing submission

5. The status counter does not include reducer failures

Comparison with other prompts and sessions

No session in the repository enables code mode. Other prompts and sessions treat code mode as a user setting that can be on or off. The reducer is the only prompt that requires code mode, does not enable it, and has no fallback.

Prompt or session Prompt requirement Code mode configuration Result without code mode
Deep Scan reducer (plugins/codex-security/mcp-app/templates/deep-scan/dedup.md L9–L40) Required Not set. A test requires the value to stay unset. No submission. 4 attempts with retry delays.
Discovery workers (plugins/codex-security/references/core-scan.md L28) Conditional: "When available, use load/store" Not set The worker continues without code mode.
Reducer tool description (plugins/codex-security/mcp-app/src/server/compact-artifact-tools.ts L296) Conditional: "When using code-mode store" Not applicable The tool works without code mode.
Linear publication through Codex (sdk/typescript/src/publish.ts L858–L861) Required Not set. codex exec starts without a features override (L349–L365). Stop. The prompt says: "If code-mode execution is unavailable or publicationFile cannot be loaded, stop without creating any Linear issues."
Finding duplicate review (sdk/typescript/src/deduplication/codex-review.ts L401–L403) The prompt tells the model not to use code mode (sdk/typescript/src/deduplication/deduplication-prompts.ts L40). Sets code_mode.direct_only_tool_namespaces to keep the validator tools direct if code mode is on Works with or without code mode
Scan comparison (sdk/typescript/src/scan-comparison.ts L627–L628) None Disabled: code_mode: false and code_mode_only: false Not applicable

The reducer eval documentation does not agree with the eval code. evals/deep-reducer/README.md says: "The run uses the caller's normal Codex credentials and configuration". But evals/deep-reducer/run.mts calls runReducerPagingEval, which sets code_mode.enabled=true (plugins/codex-security/mcp-app/tests/support/reducer-paging/deep-reducer-paging.ts L110–L111). Thus, the eval tests the reducer prompt only with code mode enabled.

Workaround

Enable code mode with a Codex override when you start the scan:

codex-security scan . --mode deep --codex features.code_mode.enabled=true

Alternatively, add the setting to the project configuration file:

codex:
  features:
    code_mode:
      enabled: true

The setting goes to the reducer worker through this sequence:

  1. The SDK writes the features table of the scan configuration into the worker_runtime snapshot (sdk/typescript/src/api.ts L1855–L1887). The SDK writes this snapshot for all deep scans.
  2. The executor merges worker_runtime.features into the worker configuration (plugins/codex-security/mcp-app/src/deep-scan/executor.ts L668–L674).
  3. workerSubagentConfig (plugins/codex-security/mcp-app/src/deep-scan/executor.ts) keeps the inherited features.
  4. profileConfigOverrides (plugins/codex-security/scripts/codex_profile.mjs) gives Codex this override: features={code_mode = {enabled = true}, multi_agent_v2 = {enabled = false}}.

Limits of the workaround:

  • codex-security resume has no --codex option. Resume uses the Codex configuration that the scan saved. Thus, you cannot add code mode to a scan that started without it. Start a new scan.
  • The setting applies to all Deep Scan workers and to the parent session, not only to the reducer.
  • In Codex 0.163.0, code_mode has the status "under development".
  • No document tells users to set code_mode. README.md, docs/ and the --codex help text (sdk/typescript/src/cli.ts L397–L398) do not mention it.

To verify the workaround, examine the reducer transcript. The reducer must use code-mode cells with load and store. The reducer must not use many separate calls to get_codex_security_deep_reducer_inputs.

Suggested fix

  1. Make the prompt and the tool configuration agree. Do one of these:
    • (a) Enable code mode for dedup workers in plugins/codex-security/mcp-app/src/deep-scan/executor.ts, for example features.code_mode.enabled=true. Before the worker starts, verify that the effective feature set includes code mode. preflightDeepScanWorkerPermissionProfile (plugins/codex-security/mcp-app/src/deep-scan/permission-profile-preflight.ts) does a similar check for the permission profile. If code mode is not available, stop with a non-retryable configuration error. Note: in Codex 0.163.0, code_mode has the status "under development".
    • (b) Change plugins/codex-security/mcp-app/templates/deep-scan/dedup.md and reducerInputRecoveryInstructions (plugins/codex-security/mcp-app/src/deep-scan/worker-runner.ts) to give a procedure without code mode. Use direct calls with a small maxBytes. Use load/store only when code mode is available. The tool description already uses this condition.
  2. Do not retry when a required tool is missing. Report a configuration error. Do not do 4 attempts with retry delays.
  3. Give more data in the identity error. In validateRetainedFindings (plugins/codex-security/mcp-app/src/deep-scan/artifact-validation.ts), report the missing identity values and the number of missing identities. If you use option (b), put the identity rule in the prompt. Also tell the reducer that the title is not part of the identity. Deep Scan reducer accepts placeholder ("X") finding text, and the degraded aggregate persists across reductions #1853 also proposes this change.
  4. Show reducer failures in the status. Add a reducer failure counter, or add reducer failures to the current counter.
  5. Add a test with the default feature set. Run the reducer page test (plugins/codex-security/mcp-app/tests/support/reducer-paging/) with the default code_mode value of the bundled Codex CLI. Change the assertWorkerSubagentPolicy test (plugins/codex-security/mcp-app/tests/test_deep_scan_executor.ts) to agree with the selected option.

Related issues

#1853 and this issue use the same reducer, the same record_codex_security_deep_reduction tool and the same identity validation. In #1853, the reducer used shell commands to assemble large payloads. Some outputs were truncated. Then the reducer replaced the finding text with X and cited a tool-call size limit.

Without code mode, the model must write all of the aggregate in the arguments of one tool call. With code mode, the model can make the submission object in JavaScript and call tools.mcp__cs_artifacts__record_codex_security_deep_reduction from the code-mode cell. Thus, the submission does not go through the model output, and the output size limit does not apply. The current prompt does not tell the reducer to do this.

This issue does not fix #1853. The host still accepts placeholder text when all identities are present. The fixes in #1853 are still necessary.

Activity

  1. jwalkins2017 commented on Oct 24, 2026

    @jwalkins2017
    Author

    Update: the failing run used Amazon Bedrock, and Codex disables code mode for Bedrock models

    The original report says that code_mode is off by default. That is correct, but it is not the full cause. For most OpenAI models, the Codex model catalog turns on code mode, and the code_mode feature flag has no effect. The failing run used Amazon Bedrock. For Bedrock models, Codex removes the catalog setting. Thus, on Bedrock, code mode depends only on the feature flag.

    Environment (addition)

    Model provider Amazon Bedrock
    Model ID openai.gpt-5.6-sol
    Bundled Codex 0.163.0 (rust-v0.163.0)

    How Codex selects the tool mode

    Codex uses the model's tool_mode from the model catalog. Codex uses the code_mode and code_mode_only feature flags only when tool_mode is not set (codex-rs/core/src/tools/mod.rs L63–L73):

    model_info.tool_mode.unwrap_or_else(|| {
        if features.enabled(Feature::CodeModeOnly) { ToolMode::CodeModeOnly }
        else if features.enabled(Feature::CodeMode) { ToolMode::CodeMode }
        else { ToolMode::Direct }
    })

    In the bundled OpenAI catalog, gpt-5.6-sol has "tool_mode": "code_mode_only" (codex-rs/models-manager/models.json L707). Thus, with the OpenAI provider, the reducer gets code mode with the default feature flags.

    What is different on Bedrock

    1. Bedrock uses its own catalog. static_model_catalog (codex-rs/model-provider/src/amazon_bedrock/catalog.rs L28) makes the openai.gpt-5.6-sol entry (codex-rs/model-provider-info/src/lib.rs L86) from the bundled gpt-5.6-sol entry.
    2. bedrock_model then sets model.tool_mode = None (catalog.rs L155–L168). This applies to all Bedrock GPT models in the catalog, including openai.gpt-6-sol and openai.gpt-6-astra.
    3. The model lookup finds the entry. The lookup uses an exact slug match (codex-rs/models-manager/src/manager.rs L475), and openai.gpt-5.6-sol matches. The cause is not a missing model. The cause is tool_mode: None in the entry that Codex finds.
    4. With tool_mode: None and the default feature flags, the result is ToolMode::Direct. The model does not get exec, wait, load or store.
    5. Codex Security does not set features.code_mode.enabled for workers (see "Root cause" item 2). Thus, the reducer gets only direct tools.

    The Codex change appears to be intentional. It came from openai/codex 2eee483 (#39102). The commit message says that it preserves "Bedrock-specific identifiers, availability, tool, reasoning, and service-tier settings". The test bedrock_models_preserve_source_metadata_with_supported_capabilities expects tool_mode = None (catalog.rs L325, L379). Codex main has the same code. The source does not give the reason.

    Effect on this issue

    • Affected configurations. The reducer fails when the model has no catalog tool_mode and features.code_mode is off. These configurations include all Amazon Bedrock models, gpt-5.5, and model IDs that are not in the catalog. With the OpenAI provider and a code_mode_only model (for example gpt-5.6-sol), the reducer gets code mode.
    • Why the tests pass. The reducer page tests and the eval set features.code_mode.enabled=true. They also use a model that has code mode in the OpenAI catalog. Thus, no test covers a Bedrock reducer.
    • Suggested fix 1(a) needs a change. A check of the feature set is not sufficient, because the catalog tool_mode can enable code mode when the flag is off. A preflight must check the effective tool mode for the worker's provider and model.
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions