Skip to content

feat(codex): add a setting that turns prompt-time recall off - #2453

Merged
hireview001 merged 1 commit into
oceanbase:masterfrom
Fengzdadi:feat/codex-prompt-recall-setting
Nov 5, 2026
Merged

hireview001 merged 1 commit into
oceanbase:masterfrom
Fengzdadi:feat/codex-prompt-recall-setting

Conversation

@Fengzdadi

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

Part of #2250 (step 1 of the plan). Gateway design: RFC #2199.

Rationale for this change

When Codex's model traffic goes through the gateway, the gateway prepares and injects prepared context for each turn. Core's before_send check (_require_bound_markers in src/powercontext/builtin/runtime/context_check.py) rejects any BEGIN_POWERCONTEXT_PREPARED_CONTEXT_V1 or BEGIN_POWERCONTEXT_PREPARED_TEXT_V1 block that the gateway did not declare. The Codex plugin's UserPromptSubmit hook adds such a block on every prompt. So with the plugin installed, every turn through the gateway fails: the check returns 422 context-binding-required, and the gateway answers 503 gate_paused. This happens even with PC_GATEWAY_INJECTION=off (see the table in #2250).

Only the host's configuration knows, before a turn starts, where its model request will go. So the signal is a plugin setting, not a Server-wide owner flag. A Server-wide flag would also silence hosts the gateway does not carry, such as Claude Code.

What changes are included in this PR?

  • integrations/codex/plugins/powercontext/settings.py: new field prompt_recall: bool = True, read from POWERCONTEXT_CODEX_PROMPT_RECALL, next to capture_prompts.

  • integrations/codex/plugins/powercontext/hooks/recall.py:

    • With prompt_recall false, the hook skips the /v1/context/prepare call and returns no additionalContext.
    • Prompt capture still follows capture_prompts, and it still resolves the Scope it needs.
    • When both settings are off, the hook returns before resolving the Scope, so it sends no request at all. This is the second branch, next to the one around the prepare call: capture alone still needs the Scope, so only the both-off case can return that early.
    • No new log event: the hook stays quiet when recall is off, as it already does when capture is off.
  • Unchanged, each under its own setting:

    • SessionStart: the bootstrap and the session binding. Its BOOTSTRAP_CONTEXT_V1 marker is not one the check covers.
    • PreToolUse: MCP Scope binding.
    • Stop (token savings): it reads statistics for the Scope the plugin resolves and does not depend on a prepare in the same turn. With recall off, it counts the gateway's preparations only when PC_GATEWAY_DEFAULT_SCOPE is that same Scope.
    • Diagnostics.
  • Docs:

  • Tests: two new Codex hook characterization cases in tests/fixtures/hook_characterization/codex.json:

    • prompt-recall-can-be-disabled: the hook sends Scope resolution and the capture. It sends no prepare and writes nothing to stdout.
    • prompt-recall-and-capture-can-be-disabled: no request, no output.

    The existing cases are byte-identical: the fixture diff adds 117 lines and deletes none, and claude-code.json is unchanged.

scripts/integration_manifest.py still finds the prepare, capture and flush routes in recall.py, so the hook inventory and integration-manifest-docs-check are unchanged.

What changes once the gateway owns prompt-time context

These come from the gateway, not from this PR, and the docs state them. They are gateway follow-ups and do not block this setting.

  • Scope. The gateway prepares and captures only in PC_GATEWAY_DEFAULT_SCOPE (gateway/crates/pc-gateway-app/src/main.rs). It does not use the Scope the plugin binds for the Session or workspace. The first gateway follow-up in feat(integrations): keep host plugins thin and make them work behind the gateway #2250 covers this: resolve the Scope from the Session binding.
  • Bootstrap dedup. The plugin's prepare sends bootstrap_receipt_id, so the Runtime leaves out Memory the bootstrap package already injected. The gateway never sends one; gateway/ has no reference to it. With bootstrap on and recall off, that Memory can appear again in the gateway's context.
  • The hook still reads and clears its local receipt when only recall is off. When recall and capture are both off, the hook leaves the receipt in place. Either way, nothing reaches the Server: the receipt feeds only the plugin's own prepare, and the Server deletes old receipts itself.

Overlap with #2317

#2317 (open) also changes hooks/recall.py, both codex.md docs and codex.json. A trial merge of this branch with #2317's head c77d5c4 is clean. On the merged tree, tests/test_hook_characterization.py and tests/codex_plugin pass (448 passed, 4 skipped). Whichever PR lands second will merge upstream/master and resolve any overlap.

Are there any user-facing changes?

Yes, opt-in only:

  • New setting: POWERCONTEXT_CODEX_PROMPT_RECALL, default true.
  • Without the setting, the plugin behaves exactly as before.
  • With false, the Codex plugin sends no preparation request and injects no prepared context.
  • Settings, local state and workspace keys keep their names and paths.
  • No Server API change, no persisted-format change.

How was this change tested?

  • uv run pytest tests/test_hook_characterization.py tests/codex_plugin tests/test_integration_manifest*.py: 465 passed, 4 skipped.

  • make check: passed (prek hooks, ruff, ty, manifest tests).

  • make unit-test: 7756 passed, 366 skipped, 17 failed. None of the failing tests touch the Codex plugin:

    • 9 in tests/test_source_management.py, which read /proc/<pid>/stat, a Linux-only path, on macOS;
    • 4 in tests/test_ci_installation_roles.py, which install wheels into fresh environments;
    • 4 spawn tests in tests/builtin/runtime/test_artifact_processing.py, which failed under parallel load and passed when rerun on their own.
  • Real host: Codex CLI 0.166.0-alpha.5.1, unmodified (bundled with Codex.app, macOS).

    • Setup:
      • Each Codex home was isolated. The plugin was installed from this branch with codex plugin marketplace add and codex plugin add.
      • The hooks were trusted once through the "Hooks need review" dialog. --dangerously-bypass-hook-trust was not used.
      • The Server was built from this branch, with enforced auth and enforcing context protection.
      • The gateway was the Rust gateway in the combined deployment (PC_GATEWAY_CORE_UDS). The gateway/ tree is unchanged since 9c8f061.
      • The model was a local recording upstream.
      • Codex reached the gateway through a recording proxy, which also served as the plugin's Server URL. A recording tap sat between the gateway and Core.
    • Two shims are still needed until bug(inference): an unmodified Codex 0.145 cannot complete a turn through an enforcing Server #2420 is fixed:
      • The proxy adds "max_output_tokens":4096 to Codex's /gateway/v1/responses bodies. Without it, Core stops Codex at 409 output_limit_required.
      • The upstream returns no encrypted reasoning.
    • Each scenario sent two messages in one thread (codex exec --json, then codex exec resume --last).
    Plugin settings Gateway Prepares per message, plugin / gateway Prepared blocks in each upstream request Result Sources per message
    PROMPT_RECALL=false, CAPTURE_PROMPTS=false PC_GATEWAY_SEDIMENT=on 0 / 1 1, the gateway's PREPARED_TEXT_V1 block in the user message Both turns complete; before_send and before_release allow 1, the gateway's gw-turn-*
    PROMPT_RECALL=false, capture default sediment off 0 / 1 1, the gateway's block Both turns complete 1, the plugin's codex-user-prompt:*
    defaults sediment off 1 / 1 none reach the upstream Every check returns 422; the Server log names context-binding-required. The gateway answers 503 gate_paused. Codex sends 30 requests per message, and the turn fails. 1, the plugin's
    • With recall and capture both off, the prompt hook sent nothing: the proxy recorded no plugin request between the SessionStart requests and Codex's model request.
    • In each completed turn, the Stop hook still resolved the Scope and loaded statistics. Codex ran no Stop hook for the failed turns of the default row.
    • In each before_send check, the gateway declared one prepared context, and the carrier held exactly that block.
    • The default row reproduces the failure in feat(integrations): keep host plugins thin and make them work behind the gateway #2250.
    • Only Codex and the Responses route were run. The payloads were real Codex requests; the upstream was recorded, not a provider.

AI usage statement

This PR was prepared with Claude Code (Claude Opus 5.5). It made the change, the fixtures and the docs, ran the checks, and ran the real Codex host behind the gateway. I reviewed the change and the results.

Copilot AI balanced review requested due to automatic review settings November 3, 2026 17:27

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@hidb4ai hidb4ai 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.

Reviewed commit 9d1ea0c42574447afd9227008161a70d8cf650b1. I found no blocking code defect in the prompt-recall opt-out.

Merge readiness is still blocked by code-seekdb (3.14, 0): the job exceeded its 30-minute limit during unified-migration tests. CI result failed because that dependency was cancelled. The diff does not change the migration or workflow paths.

Please rerun the timed-out job, addressing its execution budget if the timeout repeats, and get the CI gate green before merging. Rerunning only the aggregate gate will not rerun the missing validation.

POWERCONTEXT_CODEX_PROMPT_RECALL=false makes the UserPromptSubmit hook send
no preparation request and inject no context, for Codex setups whose model
traffic goes through the gateway, which prepares and injects each turn.
Prompt capture keeps its own setting; with both off the hook sends nothing.

Part of oceanbase#2250.
@Fengzdadi
Fengzdadi force-pushed the feat/codex-prompt-recall-setting branch from 9d1ea0c to b518bcd Compare November 4, 2026 04:32
Copilot AI balanced review requested due to automatic review settings November 4, 2026 04:32

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@hidb4ai hidb4ai 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.

Re-reviewed b518bcdcce48f2935c09a67404a23ef461272479. The prompt-recall patch is unchanged after rebasing, and I found no blocking code defect. CI still has two validation blockers:

  • code-seekdb (3.11, 0): Test unified migrations with embedded seekdb exceeded its 25-minute step limit. The overall job now allows 60 minutes, so the previous job-budget increase has not completed this validation.
  • Candidate macos-arm64: downloading email_validator-2.3.0-py3-none-any.whl from files.pythonhosted.org failed with curl exit 35 (Recv failure: Operation timed out). The subsequent candidate build/consumption and bundled launcher/Ops checks were skipped.

Please rerun both failed jobs and get the CI gate green. If the migration step times out again, address its execution budget or runtime. These failures are in paths untouched by this PR; rerunning only the aggregate gate will not complete the missing checks.

@Fengzdadi

Copy link
Copy Markdown
Contributor Author

Thanks. Both failures are outside this PR: the migration step hits the 25-minute step limit on most PRs since #2304, and #2458 splits it into three shards; the macOS job is a PyPI download timeout. I can't re-run jobs here, so I'll rebase once #2458 lands, which re-runs both.

@hidb4ai hidb4ai 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.

LGTM

@hireview001 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@hireview001
hireview001 merged commit 8d01851 into oceanbase:master Nov 5, 2026
101 of 104 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.5.1 v1.5.1 release tracking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants