Skip to content

fix(providerroutes): degraded stitch must not emit the version segment twice - #8

Merged
damonleelcx merged 1 commit into
review-base/providerroutes-stitch-dedupfrom
review/providerroutes-stitch-dedup
Aug 15, 2026
Merged

fix(providerroutes): degraded stitch must not emit the version segment twice#8
damonleelcx merged 1 commit into
review-base/providerroutes-stitch-dedupfrom
review/providerroutes-stitch-dedup

Conversation

@damonleelcx

Copy link
Copy Markdown

⚠️ Post-hoc review PR. This commit is already on develop-v1.0.5 — it was pushed there directly. To make it reviewable as an isolated diff, the base here is a snapshot branch at its parent (a2e8936) rather than develop-v1.0.5, which already contains it. Do not merge. Review, comment, then both review/* and review-base/* branches can be deleted; any change lands as a new commit on develop-v1.0.5.


The bug

A host absent from provider_routes takes the literal-prepend branch. When the stored base_url already ends in the exact segment the client sends, it was emitted twice:

https://www.cun.ai/v1  +  /v1/chat/completions
  ->  https://www.cun.ai/v1/v1/chat/completions      (upstream 404)

This is not an edge case. The mixed-version manifest carries 19 real vendor endpoints in this shape — vercel ai-gateway, aihubmix, 302.ai, cerebras, fireworks, hunyuan, minimax, mistral, moonshot, sambanova, stepfun, together, z.ai. Those vendors document their base_url with /v1 on it, so the user who pasted the documented URL got a broken route and the one who happened to trim it got a working one.

Same /v1/v1 poison already fixed on the OAuth path (stitchOAuthRequestURL) and for known rows (2026-08-03); this join was the last one left.

What is deliberately NOT changed

The degraded path stays shape-agnostic. Its existing note is explicit, and it stands:

🚫 The DEGRADED path … is untouched on purpose. … a private gateway stored as https://gw.example serving /v1/chat/completions would break the moment we swallowed its /v1. Deciding for a vendor we know is not the same act as deciding for one we do not.

For an unknown vendor we genuinely cannot tell a mount prefix (https://gw.example/proxy serving /proxy/v1/chat/completions) from a complete API root — so we must not decide. This change decides nothing about shape. It only refuses to emit the same segment twice; /v1/v1 is wrong under every reading. The segment still survives — contributed by basePath instead of by reqPath — so the bare-host case that note protects is byte-identical.

trailingVersionSegment shares trimLeadingVersionSegment's digits-only strictness, so /v1beta and /v1abc are never treated as duplicates of /v1.

Behaviour matrix

stored base_url client result
https://www.cun.ai /v1/chat/completions /v1/chat/completions ✅ unchanged
https://gw.example/proxy /v1/chat/completions /proxy/v1/chat/completions ✅ unchanged (mount prefix)
https://relay/api/v3 /v1/chat/completions /api/v3/v1/chat/completions ✅ unchanged (different segment ≠ duplicate)
https://gw.example/v1beta /v1/models /v1beta/v1/models ✅ unchanged (not digits-only)
https://www.cun.ai/v1 /v1/chat/completions /v1/chat/completions 🔧 fixed
https://relay/api/v3 /v3/chat/completions /api/v3/chat/completions 🔧 fixed

Fences

stitch_unknown_host_dedup_test.go — 11 cases. 4 pin the collapse; 6 pin that every other shape stays byte-identical — those carry the weight, they guard the "do not decide for an unknown vendor" rule; 1 pins the strictness helper.

Mutation-verified: removing the guard reproduces www.cun.ai/v1/v1/chat/completions exactly.

Consumers: this changes what aikey-proxy dials for affected credentials. The 19 manifest rows it legitimately moves are registered in the sibling aikey-proxy review PR via stitchAlgorithmChangedRows; the field-behaviour manifest is deliberately left untouched.

Not done

D-17's fail-loud half. provider_routes is an embedded fixed list with no user-extension mechanism, so rejecting unknown hosts would break every third-party relay outright — a product-capability decision needing a relay-registration channel first. Remains 单独立项.

Refs: workflow/CI/bugfix/2026-08-15-degraded-stitch-duplicates-version-segment.md

🤖 Generated with Claude Code

…t twice

A host absent from provider_routes takes the literal-prepend branch, which
deliberately does not strip the client's version segment — for an unknown vendor
we cannot tell a mount prefix (https://gw.example/proxy serving
/proxy/v1/chat/completions) from a complete API root, so we must not decide its
shape. That decision stands and is untouched.

What was still wrong: when the STORED base_url already ends in the exact segment
the client sends, literal-prepend emitted it twice:

    https://www.cun.ai/v1 + /v1/chat/completions
      -> https://www.cun.ai/v1/v1/chat/completions   (upstream 404)

/v1/v1 is wrong under every reading of a vendor's shape, so collapsing it
decides nothing about shape. The segment still survives — it is contributed by
basePath instead of by reqPath — so the bare-host case the existing note
protects is byte-identical.

This is not an edge case: the mixed-version manifest carries 19 real vendor
endpoints in this shape (vercel ai-gateway, aihubmix, 302.ai, cerebras,
fireworks, hunyuan, minimax, mistral, moonshot, sambanova, stepfun, together,
z.ai). Those vendors DOCUMENT their base_url with /v1 on it, so the user who
pasted the documented URL got a broken route and the one who trimmed it got a
working one. Same /v1/v1 poison already fixed on the OAuth path
(stitchOAuthRequestURL) and for known rows (2026-08-03); this join was the last.

trailingVersionSegment shares trimLeadingVersionSegment's digits-only
strictness, so /v1beta and /v1abc are never treated as duplicates of /v1.

Fence: stitch_unknown_host_dedup_test.go (11 cases). 4 pin the collapse; 6 pin
that every other shape stays byte-identical — those carry the weight, they guard
the "do not decide for an unknown vendor" rule. Mutation-verified: removing the
guard reproduces www.cun.ai/v1/v1/chat/completions exactly.

Does NOT implement D-17's fail-loud half — provider_routes has no registration
mechanism, so rejecting unknown hosts would break every third-party relay.
Remains 单独立项; reasoning recorded in the bugfix doc.

Refs: workflow/CI/bugfix/2026-08-15-degraded-stitch-duplicates-version-segment.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@damonleelcx
damonleelcx merged commit 4e47ab7 into review-base/providerroutes-stitch-dedup Aug 15, 2026
36 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant