Skip to content

fix(responses): reject unscoped reasoning replay - #1474

Merged
Wibias merged 1 commit into
lidge-jun:devfrom
luvs01:codex/fix-global-reasoning-replay-cache-leak
Aug 11, 2026
Merged

fix(responses): reject unscoped reasoning replay#1474
Wibias merged 1 commit into
lidge-jun:devfrom
luvs01:codex/fix-global-reasoning-replay-cache-leak

Conversation

@luvs01

@luvs01 luvs01 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • require an explicit client thread scope before the in-process reasoning replay cache can read or write raw reasoning
  • remove the process-wide "global" fallback from the bridge, OpenAI chat adapter, Responses core, image loop, and web-search loop
  • keep legitimate same-thread DeepSeek reasoning replay and placeholder behavior while preventing unscoped cross-request call-ID collisions
  • add stream, batch, cross-request, thread-isolation, and production call-site regression coverage

Root cause and impact

Provider-generated tool call IDs such as call_1 are not globally unique. The existing optional scope still fell back to a process-wide namespace whenever x-codex-parent-thread-id was absent, so an unrelated unscoped continuation could recover another request's cached raw reasoning for up to the cache TTL and forward it as reasoning_content.

There is no safe stable conversation identity for generic unscoped clients. This change therefore fails closed on cache read/write when the explicit client thread scope is absent. Preserve-listed DeepSeek models continue to use the existing minimal placeholder on a cache miss, so an unscoped request remains valid without replaying unrelated reasoning.

Refs #950.

Verification

  • Bun 1.4.0-canary.1: 95 focused tests passed, 0 failed, 261 assertions
  • Bun 1.3.14: 95 focused tests passed, 0 failed, 261 assertions
  • Bun 1.4.0-canary.1: TypeScript 7.0.2 tsc --noEmit passed
  • Bun 1.3.14: TypeScript 7.0.2 tsc --noEmit passed
  • bun run privacy:scan passed
  • git diff --check passed
  • two independent read-only production/test reviews found no remaining P0-P3 findings

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. No user-facing configuration or workflow changes.
  • Security-sensitive changes were reviewed for secrets, auth boundaries, unsafe defaults, and privacy.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • Bug Fixes
    • Improved reasoning replay isolation between conversations.
    • Prevented unscoped or empty replay data from being stored or retrieved.
    • Reduced the risk of replay collisions across streaming, image, web-search, and standard responses.
  • Tests
    • Added coverage for scoped replay behavior, conversation isolation, and unscoped requests.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6976a293-b5aa-4948-bca4-f19be946fd27

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change removes the shared "global" reasoning replay scope. Replay entries now require a client-thread scope, and tests cover scoped isolation and unscoped behavior across response, adapter, image, and web-search paths.

Changes

Reasoning replay scope isolation

Layer / File(s) Summary
Require conversation-scoped cache access
src/responses/reasoning-replay-cache.ts
At lines 37, 46–51, and 92, cache keys require a scope. Missing scopes, invalid IDs, and empty reasoning are ignored.
Propagate client thread scopes
src/adapters/openai-chat.ts, src/bridge.ts, src/server/responses/core.ts, src/images/loop.ts, src/web-search/loop.ts
At the changed lines, response bridges and provider loops pass _clientThreadId directly instead of using "global".
Validate scoped and unscoped replay
tests/bridge-raw-reasoning-hidden.test.ts, tests/bridge-reasoning-replay-batch.test.ts, tests/deepseek-reasoning-replay-gaps.test.ts, tests/images/loop-reasoning-replay.test.ts, tests/reasoning-replay-scope-source.test.ts, tests/web-search.test.ts
Tests cover explicit scopes, shared call IDs across threads, unscoped producers and consumers, and scope propagation across provider paths.

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

Possibly related PRs

Suggested labels: review-ready

Suggested reviewers: wibias, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: rejecting unscoped reasoning replay in Responses.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 11, 2026
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently. If no CodeRabbit review appears, comment @coderabbitai review to request one.
Maintainers: @lidge-jun @Ingwannu @Wibias

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

Reviewed the replay-cache privacy change. I did not find an additional blocker in this head. Removing the implicit process-wide global namespace at every read/write call site and making unscoped cache access a no-op closes the demonstrated cross-request call-id collision without inventing a replacement identity.

I am not approving this head because it is currently not mergeable against the latest dev. Please rebase/resolve conflicts and request a short re-review of the integrated cache call sites.

@Wibias
Wibias force-pushed the codex/fix-global-reasoning-replay-cache-leak branch from 4e22f98 to 1ecb5a7 Compare August 11, 2026 19:35
@Wibias
Wibias marked this pull request as ready for review August 11, 2026 19:42
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] I will review PR #1474. I am replying to the latest comment only. The earlier comment chain is unavailable to me.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. PR #1474 is marked ready for review. The current commit was already reviewed.

You are interacting with an AI system.

@Wibias
Wibias merged commit 4b135d2 into lidge-jun:dev Aug 11, 2026
33 of 38 checks passed

Wibias commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Thanks @luvs01 for tightening this up. This is useful because provider-generated tool call IDs are not globally unique, so removing the unscoped global reasoning replay namespace prevents reasoning from one conversation being recovered by an unrelated request with the same call ID. Keeping replay only when an explicit thread scope exists gives us the compatibility benefit without the cross-request privacy risk. Merged, thank you!

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

Labels

bug Something isn't working review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants