Skip to content

fix(responses): bind reasoning replay to route identity - #1505

Merged
Wibias merged 1 commit into
lidge-jun:devfrom
luvs01:agent/bind-reasoning-replay-identity
Aug 12, 2026
Merged

fix(responses): bind reasoning replay to route identity#1505
Wibias merged 1 commit into
lidge-jun:devfrom
luvs01:agent/bind-reasoning-replay-identity

Conversation

@luvs01

@luvs01 luvs01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • bind process-local raw-reasoning replay to the exact client thread, configured provider destination, wire adapter, final model, and physical credential identity
  • share one mutable replay-scope holder across parsed-request copies and already-created bridges so account/key rotation changes the namespace before the next cache read or write
  • fail closed for missing, keyless, or header-only credential identities; OAuth replay is bound to the existing credential slot and exact token generation
  • represent destinations and credential-bearing material only with process-local HMAC identities; raw URLs, tokens, API keys, account-slot IDs, generations, and authorization headers are never stored in replay keys
  • clear an already-bound holder when a later route cannot establish a complete identity, preventing stale-namespace writes

Root cause and impact

#1474 removed the process-wide fallback and required an explicit conversation scope. That prevents unrelated unscoped requests from sharing replay entries, but a single conversation can still move between providers or physical credentials during combo, pool, OAuth, image, or web-search recovery. Provider-generated call IDs are not globally unique, so a later target could reuse the same thread and call ID and receive the earlier target's raw reasoning.

This follow-up keeps replay process-local and bounded, but keys each entry to the active route and physical credential identity. Same-generation, same-target continuation remains compatible. A provider, model, destination, account/key, or OAuth token-generation change receives the existing safe placeholder or cache-miss behavior instead of another target's reasoning.

The final patch uses the existing OAuth access snapshot (accountId plus credential generation) and does not change the OAuth authentication module's public snapshot surface.

Follow-up to #1474.

Verification

  • Bun 1.4.0-canary.1 after the original rebase: 99 focused tests passed, 0 failed, 292 assertions
  • Bun 1.3.14 after the original rebase: 99 focused tests passed, 0 failed, 292 assertions
  • OAuth scope reduction on Bun 1.4.0-canary.1: 15 focused tests passed, 0 failed, 78 assertions
  • OAuth scope reduction on Bun 1.3.14: 14 focused tests passed; one existing concurrent xAI test exceeded the default 5 s watchdog at 5.017 s and passed 1/1 with a 30 s timeout (5.988 s)
  • invalid-holder regression on the final head: 5/5 and 33 assertions passed on both Bun 1.4.0-canary.1 and Bun 1.3.14
  • final latest-dev head: TypeScript 7.0.2 tsc --noEmit passed on both Bun versions
  • focused physical key-rotation E2E passed on Bun 1.4 and Bun 1.3.14
  • bun run privacy:scan passed
  • git diff --check passed
  • latest-dev rebase preserved the exact validated patch-id while retaining the adjacent passive-production core changes
  • Codex Security diff scan reviewed all changed production surfaces and found 0 reportable candidates
  • independent read-only production, test, and OAuth-reduction reviews found no remaining P0-P3 findings
  • exact-head Codex forward credential-boundary regressions on Bun 1.4 and Bun 1.3.14 — 7/7 passed with 60 assertions on each runtime
  • exact-head TypeScript 7.0.2, privacy scan, and git diff --check — passed
  • independent validation accepted the effective-bearer/pool-slot/generation HMAC binding with no remaining P0-P2 security gap

Focused affected-surface tests, dual-runtime typecheck, privacy, diff, and independent review satisfy the local CI gate. Unrelated environment/runtime timing is recorded separately and is not attributed to this change; the same GitHub CI workload is not duplicated locally.

Checklist

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

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 across providers, destinations, models, adapters, and credentials.
    • Prevented replay when required identity information is missing or invalid.
    • Ensured credential rotation, refresh, and failover cannot reuse reasoning from a previous credential.
  • Documentation

    • Documented safeguards and conditions governing reasoning replay.
  • Tests

    • Added coverage for identity isolation, credential changes, failover, invalid scopes, and streaming or batch responses.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1dd0ba2c-58cd-44b4-bf40-1f0213da3f14

📥 Commits

Reviewing files that changed from the base of the PR and between 4d725bd and 93bf9a3.

📒 Files selected for processing (5)
  • src/responses/reasoning-replay-cache.ts
  • src/server/responses/core.ts
  • structure/04_transports-and-sidecars.md
  • tests/reasoning-replay-identity.test.ts
  • tests/reasoning-replay-scope-source.test.ts

📝 Walkthrough

Walkthrough

Reasoning replay caching now uses structured scopes bound to provider, destination, adapter, model, and credential identities. Request routing rebinds scopes after authentication and transport changes. Bridges and adapters propagate the structured scope, with tests covering isolation and failover behavior.

Changes

Identity-bound reasoning replay

Layer / File(s) Summary
Scope contract and cache identity
src/types.ts, src/responses/reasoning-replay-cache.ts, structure/04_transports-and-sidecars.md
Adds structured replay identity and scope types. Cache keys use validated HMAC-derived destination and credential identities. Invalid or incomplete scopes fail closed.
Server scope binding and rotation
src/server/responses/core.ts
Creates scopes during route resolution and rebinds them after OAuth changes, Codex account changes, transport rotation, adapter replacement, and key failover.
Bridge and adapter scope propagation
src/bridge.ts, src/server/responses/core.ts, src/adapters/openai-chat.ts, src/images/loop.ts, src/web-search/loop.ts
Passes _reasoningReplayScope to streaming and JSON bridges and uses it for OpenAI Chat, image, and web-search replay paths.
Replay scope validation coverage
tests/*reasoning-replay*, tests/server-key-failover-e2e.test.ts, tests/images/loop-reasoning-replay.test.ts, tests/web-search.test.ts
Updates fixtures to structured scopes and tests provider, destination, model, credential, rotation, fail-closed, and failover behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ResponsesCore
  participant ProviderRoute
  participant ResponsesBridge
  participant ReasoningReplayCache

  Client->>ResponsesCore: submit request
  ResponsesCore->>ProviderRoute: resolve provider and credential
  ProviderRoute-->>ResponsesCore: return route identity
  ResponsesCore->>ReasoningReplayCache: bind replay scope
  ResponsesCore->>ResponsesBridge: pass structured replay scope
  ResponsesBridge->>ReasoningReplayCache: store or retrieve reasoning
  ProviderRoute->>ResponsesCore: rotate credential or transport
  ResponsesCore->>ReasoningReplayCache: rebind scope before retry
Loading

Possibly related PRs

Suggested labels: review-ready

Suggested reviewers: ingwannu, lidge-jun, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: binding reasoning replay to route identity.
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 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 github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/oauth/index.ts.

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

github-actions Bot commented Aug 12, 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 has been marked 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 notified: @lidge-jun @Ingwannu @Wibias

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/reasoning-replay-identity.test.ts`:
- Around line 69-82: Import bindReasoningReplayScope and add coverage in the
identity replay tests for binding a valid holder with undefined: verify
holder.current is cleared, then confirm subsequent writes do not overwrite
reasoning stored under the previous identity. Exercise the delete scope.current
behavior while preserving the existing fail-closed assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: acd6db84-973f-4ddb-ab68-1e78ac7af7a6

📥 Commits

Reviewing files that changed from the base of the PR and between b8a2e51 and 2eab82c.

📒 Files selected for processing (18)
  • src/adapters/openai-chat.ts
  • src/bridge.ts
  • src/images/loop.ts
  • src/oauth/index.ts
  • src/responses/reasoning-replay-cache.ts
  • src/server/responses/core.ts
  • src/types.ts
  • src/web-search/loop.ts
  • structure/04_transports-and-sidecars.md
  • 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-identity.test.ts
  • tests/reasoning-replay-robustness.test.ts
  • tests/reasoning-replay-scope-source.test.ts
  • tests/server-key-failover-e2e.test.ts
  • tests/web-search.test.ts

Comment thread tests/reasoning-replay-identity.test.ts
@luvs01
luvs01 force-pushed the agent/bind-reasoning-replay-identity branch from 2eab82c to ca953dd Compare August 12, 2026 02:21
@github-actions github-actions Bot removed the intake: hygiene-blocked Deterministic PR hygiene checks failed label Aug 12, 2026
@luvs01
luvs01 force-pushed the agent/bind-reasoning-replay-identity branch from ca953dd to 199b7d6 Compare August 12, 2026 02:55
@Ingwannu

Copy link
Copy Markdown
Owner

Maintainer review on exact head 199b7d6241804795ca6924add5988f2dd6c0d6f8 against current dev@37139818d2d3fe7b033e958ccf87080e4be2b4b7.

The underlying cross-route replay problem is real, and this head applies the retention boundary at the right identity level: client thread, provider name, opaque destination identity, wire adapter, final model, and physical credential identity. API keys, OAuth slot/generation material, account ids, authorization headers, and configured URL paths are represented only by process-local HMACs and are neither logged nor persisted. Incomplete identities clear the mutable holder and fail closed, while key rotation, OAuth refresh, and Codex alternate-account selection rebind before the next replay read/write.

I also checked the recovery branches. API-key 429 failover and Codex alternate-account retry rebind to the replacement credential. OAuth refresh changes the generation-bound namespace. Anthropic's separate multi-account pool currently cannot provide the same stable credential-generation tuple, so it remains deliberately unscoped/fail-closed rather than reusing another account's reasoning, consistent with the documented limitation.

Exact-head validation under a 2-CPU/8-GB cap:

  • replay identity, cache bounds, bridge propagation, DeepSeek continuation, image/web-search, and key-failover tests: 95 passed, 0 failed;
  • bun run typecheck: passed;
  • bun run privacy:scan: passed;
  • git diff --check: passed.

I found no additional code blocker in the reviewed surface. I am leaving this as a draft and not approving or merging until the author completes the local-CI/ready checklist and exact-head required CI is available.

@luvs01
luvs01 force-pushed the agent/bind-reasoning-replay-identity branch from 199b7d6 to 4d725bd Compare August 12, 2026 04:13
@github-actions
github-actions Bot marked this pull request as ready for review August 12, 2026 04:15
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The readiness gate is complete for head 4d725bd2f4b495a31abb46e195f4950c190038b2.

The prior maintainer review covered a different head SHA. I will review the current head before any approval decision.


🧠 Learnings used
Learnt from: Wibias
Repo: lidge-jun/opencodex PR: 986
File: .github/workflows/enforce-pr-target.yml:631-636
Timestamp: 2026-08-04T06:41:30.089Z
Learning: For the PR readiness gate in `.github/workflows/enforce-pr-target.yml`, the required scope is to bind contributor checklist completion and maintainer notification to the exact PR head SHA, then reset them after a new head commit. Maintainer approval staleness is separate from this gate and should be enforced by GitHub branch protection, such as `dismiss-stale-reviews` or `require_last_push_approval`, when configured for `dev`.
⚠️ 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 12, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The readiness gate is complete for head 4d725bd2f4b495a31abb46e195f4950c190038b2.

I will review the current head. The prior maintainer review covered a different head SHA.


🧠 Learnings used
Learnt from: Wibias
Repo: lidge-jun/opencodex PR: 986
File: .github/workflows/enforce-pr-target.yml:631-636
Timestamp: 2026-08-04T06:41:30.089Z
Learning: For the PR readiness gate in `.github/workflows/enforce-pr-target.yml`, the required scope is to bind contributor checklist completion and maintainer notification to the exact PR head SHA, then reset them after a new head commit. Maintainer approval staleness is separate from this gate and should be enforced by GitHub branch protection, such as `dismiss-stale-reviews` or `require_last_push_approval`, when configured for `dev`.
⚠️ 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 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/responses/core.ts`:
- Around line 281-286: Update the forward branch around
reasoningReplayCredentialIdentity so pool and main-pool contexts derive Codex
identity from accountId plus writerGeneration, while direct forward contexts use
the effective forwarded credential headers. Pass the effective
chatgpt-account-id as an override, keeping all credential material inside
reasoningReplayCredentialIdentity so only its HMAC output becomes the replay
key. Add regression coverage for newer generations, differing forwarded
Authorization values with one account ID, and alternate pool accounts sharing a
ChatGPT account ID.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8564192a-d523-4b17-b85b-9c72db242785

📥 Commits

Reviewing files that changed from the base of the PR and between 2eab82c and 4d725bd.

📒 Files selected for processing (5)
  • src/responses/reasoning-replay-cache.ts
  • src/server/responses/core.ts
  • structure/04_transports-and-sidecars.md
  • tests/reasoning-replay-identity.test.ts
  • tests/reasoning-replay-scope-source.test.ts

Comment thread src/server/responses/core.ts Outdated

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

Review on current head 4d725bd2f4b495a31abb46e195f4950c190038b2.

There is one blocking credential-isolation bug in the new replay scope. In the forward-auth branch, pool/main-pool identity is derived from chatgptAccountId only, while direct-forward identity ignores the effective forwarded Authorization value and hashes provider.headers instead. That means a token generation change, or two physical pool credentials that share one ChatGPT account id, can keep the same replay namespace. Under a same-thread/call-id collision, raw reasoning from the previous physical credential can therefore still be reused.

Please bind pool/main-pool replay identity to the physical slot and generation, e.g. accountId plus writerGeneration, and bind direct-forward identity to the effective forwarded credential headers including Authorization and chatgpt-account-id. Keep the raw material inside the existing HMAC helper. Add regressions for generation rotation, distinct pool accounts sharing a ChatGPT account id, and distinct forwarded bearer tokens sharing an account id.

Separately, this head is now 10 commits behind current dev@70d2e175 after #1464 merged, and the exact-head Cross-platform CI / React Doctor runs are action_required with zero jobs. Rebase onto current dev and obtain an executed exact-head CI signal after fixing the credential identity.

@luvs01
luvs01 force-pushed the agent/bind-reasoning-replay-identity branch from 4d725bd to 93bf9a3 Compare August 12, 2026 05:11
@github-actions
github-actions Bot marked this pull request as draft August 12, 2026 05:11
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The readiness gate is complete for head 4d725bd2f4b495a31abb46e195f4950c190038b2.

I will review the current head. This uses the exact-head readiness-gate requirement from the repository guidance.


🧠 Learnings used
Learnt from: Wibias
Repo: lidge-jun/opencodex PR: 986
File: .github/workflows/enforce-pr-target.yml:631-636
Timestamp: 2026-08-04T06:41:30.089Z
Learning: For the PR readiness gate in `.github/workflows/enforce-pr-target.yml`, the required scope is to bind contributor checklist completion and maintainer notification to the exact PR head SHA, then reset them after a new head commit. Maintainer approval staleness is separate from this gate and should be enforced by GitHub branch protection, such as `dismiss-stale-reviews` or `require_last_push_approval`, when configured for `dev`.
⚠️ 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.

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

Re-review on current head 93bf9a376eb607a5167f35c475f5a7e5bef2e037.

The prior P1 credential-isolation blocker is fixed. Pool/main-pool replay identity is now bound to the effective bearer plus the physical accountId, credential generation, and writerGeneration; direct-forward identity uses the effective forwarded Authorization and chatgpt-account-id. The raw credential material stays inside the process-local HMAC identity. The focused regressions cover distinct forwarded bearers under one ChatGPT account, alternate pool slots sharing one ChatGPT account, and generation/writer-generation rotation. The server key-failover E2E also verifies that a 429 rotation cannot replay the previous physical key's raw reasoning.

I also checked the rebinding points: initial route binding uses the resolved effective forward headers/auth context, pool retry binds before rebuilding the retry request, and key/OAuth rotations replace or clear the shared mutable holder before subsequent replay reads/writes. I did not find a new code-level credential-boundary blocker.

The rebase gate is now satisfied: this head is one commit ahead of and zero commits behind current dev@d667367. The remaining blocker is exact-head CI: Cross-platform CI and React Doctor are both action_required with zero jobs executed. Please obtain an executed exact-head CI signal, then request re-review.

@Wibias Wibias added the maintainer-sponsored Maintainer sponsors this change to an auth, workflow, release, or dependency surface label Aug 12, 2026
@github-actions
github-actions Bot marked this pull request as ready for review August 12, 2026 05:24
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The readiness gate is complete for head 93bf9a376eb607a5167f35c475f5a7e5bef2e037.

The prior review references a different head SHA. I will review the current head before an approval decision.


🧠 Learnings used
Learnt from: Wibias
Repo: lidge-jun/opencodex PR: 986
File: .github/workflows/enforce-pr-target.yml:631-636
Timestamp: 2026-08-04T06:41:30.089Z
Learning: For the PR readiness gate in `.github/workflows/enforce-pr-target.yml`, the required scope is to bind contributor checklist completion and maintainer notification to the exact PR head SHA, then reset them after a new head commit. Maintainer approval staleness is separate from this gate and should be enforced by GitHub branch protection, such as `dismiss-stale-reviews` or `require_last_push_approval`, when configured for `dev`.
⚠️ 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.

@Wibias
Wibias merged commit 4a299fa into lidge-jun:dev Aug 12, 2026
35 of 37 checks passed

Wibias commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Thank you @luvs01 for pushing this through and for addressing the credential-isolation review feedback.

This is useful because raw reasoning replay is now scoped to the exact active route and physical credential identity instead of only the client thread. Provider/model/destination changes, key rotation, OAuth generation changes, and Codex pool failover now move to a different replay namespace, which prevents reasoning from one credential or upstream target from being reused by another. The process-local HMAC identities also keep raw tokens, API keys, account-slot IDs, generations, and upstream destinations out of replay keys while still preserving same-target continuation behavior.

@Wibias Wibias mentioned this pull request Aug 12, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working maintainer-sponsored Maintainer sponsors this change to an auth, workflow, release, or dependency surface review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants