Skip to content

fix(responses): repair legacy dotted call names on replay (#5095) - #5101

Merged
lidge-jun merged 1 commit into
devfrom
codex/5095-replay-legacy-dotted-name-repair
Sep 19, 2026
Merged

lidge-jun merged 1 commit into
devfrom
codex/5095-replay-legacy-dotted-name-repair

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

Stacked on #5100, which stops new damage. This is the recovery half, and it is the part of #5095 that actually hurts.

Codex stores what it received. A conversation that already contains a default.-prefixed function_call name is refused on every later request that replays it:

Invalid 'input[877].name': string does not match pattern '^[a-zA-Z0-9_-]+$'.

So the task cannot be compacted or continued, and upgrading OpenCodex does not reach it. The reporter hit this in a side chat opened from an 11h43m parent task, on a plain OpenAI model (GPT-5.6 Luna), replaying history a routed Command Code Muse turn had damaged.

repairLegacyDottedToolCallNames repairs the replayed item on the way out. It is applied in buildRequest beside backfillWebSearchQueries — the existing seam for exactly this class of stored-history repair — and again in src/server/responses/compact.ts, which forwards the caller's body directly without passing through the adapter. It runs before the canonical-destination split, because the destination that refused the request was plain OpenAI.

What resolves is bounded on purpose. Only replayed input items are eligible; the caller's tool catalog is never rewritten.

replayed name catalog outcome
default.view_image, default.apply_patch code mode (exec only) → view_image, apply_patch via CODE_MODE_HELPER_WIRE_NAMES
default.view_image declares bare view_image → view_image (suffix names exactly one declared tool)
default.lookup lookup declared in additional_tools → lookup
default.lookup not declared, not a helper untouched
default.view_image two namespaces each declaring view_image untouched (ambiguous)
default.view_image a declared default namespace untouched (real identity)
default.reporting declared top-level any untouched (real identity)
default.view.image any untouched (suffix still invalid)
item carrying namespace: "mcp__x" any untouched (not the flattened shape)
view image, view_image!, other.view_image any untouched

There is deliberately no "strip everything before the first dot" rule. A legitimate tool name may contain a dot in another provider's vocabulary, and a replayed item names a call that already happened, which is the worst possible place to guess. default. is one literal prefix: the one invented namespace this proxy is known to have emitted, already the subject of #3402, #4176 and #4412.

Anything unresolved is left exactly as it is. The upstream still refuses it, which is the pre-existing outcome rather than a new failure mode.

call_id is untouched, so a repaired call still pairs with its stored function_call_output, and a body with nothing to repair is returned by reference.

Files:

  • src/responses/legacy-dotted-tool-name-repair.ts — the repair, reading the request's own catalog through collectResponsesToolGroups.
  • src/types/tools.ts, src/types.ts — CODE_MODE_HELPER_WIRE_NAMES, a membership view of the helper tuple that already exists there, so the bounded vocabulary cannot drift from the list normalizeDeclaredToolName reads.
  • src/adapters/openai-responses/passthrough.ts, src/server/responses/compact.ts — the two forward seams.
  • structure/transports/responses.md — records the boundary and why it is bounded.

Verification

No local suite was run. This lane is static-analysis plus hosted CI only; verification is by source reasoning and the hosted matrix on the exact head.

New regression file tests/responses/responses-legacy-dotted-tool-name-repair.test.ts covers every row of the table above, plus: the whole helper vocabulary resolving without being declared, call_id pairing surviving the rewrite, a mixed replay where the undamaged items come back by reference and only the damaged one is rewritten, an undamaged history returned by reference, a body with no input array, and the catalog staying reference-identical across a repair.

Registered in both scripts/test-layout/layout.json and tests/fixtures/test-layout-expected.json. No entry in tests/fixtures/file-size-baseline.json covers any file this PR touches, so no cap moves.

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.

No authorization decision is affected. This rewrites a name in a replayed historical item on the outbound wire; it grants nothing, and the only names it can produce are a declared tool or one of the five code-mode helper spellings the proxy itself owns. The undeclared-tool guard on the response side is untouched.

Refs #5095

Summary by CodeRabbit

  • Bug Fixes

    • Fixed replayed Responses requests that could fail when tool-call names used a legacy default. prefix.
    • Applies the correction to standard and compact request flows while preserving valid, ambiguous, or explicitly namespaced tool names.
    • Supports recognized code-mode helper tools and maintains tool-call pairing information.
  • Tests

    • Added coverage for legacy name repair, catalog-based resolution, compact forwarding, and fail-closed scenarios.
  • Documentation

    • Documented the legacy tool-name repair behavior for Responses requests.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 19, 2026 05:16
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 9954db0e-62fe-4948-8e69-df0d1e0d53f0

📥 Commits

Reviewing files that changed from the base of the PR and between 04761a1 and 23fcfc7.

📒 Files selected for processing (9)
  • scripts/test-layout/layout.json
  • src/adapters/openai-responses/passthrough.ts
  • src/responses/legacy-dotted-tool-name-repair.ts
  • src/server/responses/compact.ts
  • src/types.ts
  • src/types/tools.ts
  • structure/transports/responses.md
  • tests/fixtures/test-layout-expected.json
  • tests/responses/responses-legacy-dotted-tool-name-repair.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The change adds bounded repair for replayed default.-prefixed tool-call names. Standard and compact Responses forwarding apply the repair before dispatch. Tests, documentation, and test-layout fixtures cover the new behavior.

Changes

Responses tool-name repair

Layer / File(s) Summary
Repair contracts and bounded resolution
src/responses/legacy-dotted-tool-name-repair.ts, src/types/tools.ts, src/types.ts
The new helper reads declared tool identities, repairs only uniquely resolvable suffixes or recognized code-mode helper names, and preserves ambiguous, declared, invalid, or explicitly namespaced items.
Standard and compact forwarding integration
src/adapters/openai-responses/passthrough.ts, src/server/responses/compact.ts
Both request paths apply repairLegacyDottedToolCallNames before forwarding the request body.
Validation and documented behavior
tests/responses/responses-legacy-dotted-tool-name-repair.test.ts, structure/transports/responses.md, scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
Tests cover repair and fail-closed cases. Documentation and test-layout fixtures describe and register the new behavior.

Priority: ⬇️ Low

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

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ResponsesAdapter
  participant repairLegacyDottedToolCallNames
  participant ToolCatalog
  participant ResponsesEndpoint
  Client->>ResponsesAdapter: submit replayed request
  ResponsesAdapter->>repairLegacyDottedToolCallNames: pass request body
  repairLegacyDottedToolCallNames->>ToolCatalog: inspect declared tools
  ToolCatalog-->>repairLegacyDottedToolCallNames: return tool declarations
  repairLegacyDottedToolCallNames-->>ResponsesAdapter: return repaired or original body
  ResponsesAdapter->>ResponsesEndpoint: forward request
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 6 files. (3 skipped: 3… 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: repairing legacy dotted tool-call names during replay in the Responses path.
Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 6 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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 Sep 19, 2026
Base automatically changed from codex/5095-emit-schema-valid-tool-names to dev September 19, 2026 06:22
@lidge-jun
lidge-jun changed the base branch from codex/5095-emit-schema-valid-tool-names to dev September 19, 2026 06:22
Stopping the emission is only half the report. Codex stores what it received, so
a conversation that already contains a default.-prefixed function_call name is
refused on EVERY later request that replays it:

  Invalid 'input[877].name': string does not match pattern '^[a-zA-Z0-9_-]+$'.

The task cannot be compacted or continued, and upgrading does not reach it. That
is the part of the report that actually hurts: a side chat opened from an 11h43m
parent hit it on a plain OpenAI model, from history a routed provider had
damaged.

repairLegacyDottedToolCallNames repairs the replayed item on the way out, in
buildRequest beside backfillWebSearchQueries and again in compact.ts, which
forwards the caller's body directly. It runs before the canonical-destination
split, because the destination that refused the request was plain OpenAI.

What resolves is bounded on purpose:

- a dotted spelling the caller's own catalog declares is a real tool identity and
  is never stripped; rewriting it would change which tool the history says was
  called;
- a suffix claimed by two declared identities is ambiguous and is left alone;
- a suffix naming exactly one declared tool resolves to it;
- otherwise, only the code-mode helper spellings in CODE_MODE_HELPER_WIRE_NAMES
  resolve, because a code-mode catalog never declares them -- they exist solely
  as nested tools.* helpers inside exec, so a recorded call under one of those
  names can only be a provider echoing the helper.

There is deliberately no "strip everything before the first dot" rule. A
legitimate tool name may contain a dot in another provider's vocabulary, and a
replayed item names a call that already happened, which is the worst possible
place to guess. Anything unresolved is left exactly as it is; the upstream still
refuses it, which is the pre-existing outcome rather than a new one.

Only replayed input items are eligible. The caller's tool catalog is never
rewritten, call_id is untouched so a repaired call still pairs with its stored
output, and a body with nothing to repair is returned by reference.

No local suite was run.

Refs #5095
@lidge-jun
lidge-jun force-pushed the codex/5095-replay-legacy-dotted-name-repair branch from 8125f52 to 23fcfc7 Compare September 19, 2026 06:22
@lidge-jun
lidge-jun merged commit 4e45e17 into dev Sep 19, 2026
31 checks passed
@lidge-jun
lidge-jun deleted the codex/5095-replay-legacy-dotted-name-repair branch September 19, 2026 07:07
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.

1 participant