Skip to content

[REVIEW ONLY — DO NOT MERGE] fix(providerroutes): degraded stitch must not emit the version segment twice - #9

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

[REVIEW ONLY — DO NOT MERGE] fix(providerroutes): degraded stitch must not emit the version segment twice#9
damonleelcx merged 1 commit into
review-base-2/providerroutes-stitch-dedupfrom
review/providerroutes-stitch-dedup

Conversation

@damonleelcx

Copy link
Copy Markdown

🔴 DRAFT ON PURPOSE — do not mark ready, do not merge.
This commit is already on develop-v1.0.5 (18eca55). GitHub refuses a PR against develop-v1.0.5"No commits between develop-v1.0.5 and review/…" — because a PR proposes commits the base lacks, and the base already has this one. The base here is therefore a snapshot at its parent (a2e8936), purely so the diff is readable and commentable.
Merging accomplishes nothing (it would move two throwaway branches). An earlier version of this PR was merged unread within two minutes, which is why this one is a draft. Anything review turns up lands as a new commit and a normal PR against 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)

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

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 marked this pull request as ready for review August 15, 2026 09:52
@damonleelcx
damonleelcx merged commit 1d2cf74 into review-base-2/providerroutes-stitch-dedup Aug 15, 2026
48 of 56 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