Skip to content

fix(compact): route combo compact requests through the failover path (rebase of #3109) - #3184

Merged
lidge-jun merged 5 commits into
devfrom
codex/3109-combo-compact-failover
Sep 1, 2026
Merged

fix(compact): route combo compact requests through the failover path (rebase of #3109)#3184
lidge-jun merged 5 commits into
devfrom
codex/3109-combo-compact-failover

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

Maintainer rebase of #3109 onto current dev — all five commits cherry-picked with author credit preserved, no conflicts.

Routes combo compaction requests through the failover path, so a combo whose first target returns 429/5xx during /responses/compact reaches a backup instead of failing the compaction outright.

Review threads — both P1s already resolved on the branch

CHANGES_REQUESTED came from bot reviews against 399726aae0, four commits behind this head. Both substantive findings were fixed by later commits on the same branch, which is why the threads read unresolved rather than addressed:

P1 — "Decode ocx1 after account-gated combo failover." The concern: when a combo starts on an account-gated model, fails over, and a synthetic-compaction backup wins, the code decided whether to decode based on the pre-failover child, so an ocx1: envelope could be returned raw to a v1 caller expecting replacement-history messages.

src/server/responses/compact.ts now keys that decision on the returned prefix rather than the originally selected target:

&& !compactionItems[0]!.encrypted_content.startsWith("ocx1:")

Whichever target actually won, an ocx1: envelope is decoded. "test(compact): cover combo failover and streaming" adds the mixed-target regression the review asked for.

Minor — "Reject ciphertext-free streaming completions." The SSE path could emit { type: "done" } with no compaction payload where the buffered path already rejected the same condition. Fixed by "fix(compact): reject empty native ciphertext", and the guard handles the subtle case the reviewer implied: an empty ocx1: envelope decodes to "" rather than null, so the check is on decoded length, not nullness.

// An empty `ocx1:` envelope decodes to "" rather than null, so length is what matters.
if (decoded === null || decoded.trim().length === 0) {

Verification

Exact head a0f036f87:

  • bun test ./tests/server-combo-failover-e2e.test.ts78 pass, 0 fail, 475 expect() calls
  • bun test ./tests/responses-compaction-routing.test.ts47 pass, 0 fail, 213 expect() calls
  • bun test ./tests/responses-compaction.test.ts25 pass, 0 fail

The failover e2e file is the one that matters here — it also contains the assertion #3175 corrected for the combo effort fix, and both changes coexist cleanly on this head.

Full-suite and typecheck coverage is left to CI on this exact head.

Checklist

  • Targets dev
  • Author credit preserved across all five commits
  • Both outstanding review findings addressed, with the resolving commit and the actual code named
  • Regression coverage for the mixed-target failover case the review requested
  • No credential, auth, workflow, or release-automation surface touched

x3M3x and others added 5 commits September 2, 2026 02:22
When a compact request resolved through a combo, the native-compact fast path
sent the request directly to the picked provider without failover. A 429 or 5xx
from that target surfaced as an exhausted-retry error to the client instead of
advancing to the next combo target.

Skip native compact when route.combo is set so the request falls through to the
synthetic compaction path, which dispatches through handleResponses →
handleComboResponses with full combo failover (cooldown + advanceToNext).

(cherry picked from commit df4c544)
(cherry picked from commit 9582fc3)
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 1, 2026 17:23
@lidge-jun
lidge-jun merged commit afd5b46 into dev Sep 1, 2026
6 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T17:30:00.532831Z a0f036f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@lidge-jun
lidge-jun deleted the codex/3109-combo-compact-failover branch September 1, 2026 17:23
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0f036f875

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2324 to +2326
const responsePayload = isPlainObject(payload.response) ? payload.response : undefined;
const output = Array.isArray(responsePayload?.output) ? responsePayload.output : [];
const compaction = output.find(item => isPlainObject(item) && item.type === "compaction");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Capture compaction output-item events before terminal

When a streaming Responses backend emits the native compaction blob in response.output_item.done and follows it with response.completed whose output is empty—a protocol shape already exercised in tests/server-auth.test.ts—this parser ignores the item because it only searches the terminal snapshot. A combo child using this parsed adapter path therefore emits done without compactionEncryptedContent, causing the bridge to synthesize an empty ocx1: envelope and /responses/compact to return 502 instead of the valid native compaction. Retain compaction items from response.output_item.done as well, with the terminal's non-empty output remaining authoritative.

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants