Skip to content

fix(selfhost): route single-provider AI through the name-aware router (#1610) - #1611

Merged
JSONbored merged 1 commit into
mainfrom
fix/selfhost-single-provider-model-route
Jun 27, 2026
Merged

fix(selfhost): route single-provider AI through the name-aware router (#1610)#1611
JSONbored merged 1 commit into
mainfrom
fix/selfhost-single-provider-model-route

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Single-provider self-host reviews (AI_PROVIDER=claude-code) failed 100% with an opaque claude_code_exit_1. Root cause: resolveAiReviewerPlan builds the single-provider plan as reviewers: [{ model: "claude-code" }] — the reviewer "model" is the router address (the provider name), matching the 2-provider convention. But createSelfHostAi returned the bare provider for a single-provider setup, bypassing routeProviders's name-stripping. So createClaudeCodeAi.run("claude-code", …) ran resolveModel(undefined, "claude-code", "claude-sonnet-4-6"), which returns "claude-code" verbatim (it only strips @cf/ ids) → claude --model claude-code404 (is_error:true, api_error_status:404, "There's an issue with the selected model (claude-code)"), exit 1, three times, then ai_review_provider_exhausted.

Verified in-container: claude --model claude-code ⇒ exit 1 / 404; claude --model claude-sonnet-4-6 (what the router yields) ⇒ exit 0. The 2-provider path already worked because routeProviders strips the name (its test documents exactly this: "the router never passes the provider NAME through as a model id").

Fix: createSelfHostAi wraps a single provider in routeProviders too, so the provider-name address resolves to the provider's own default model. One-line behavior change (delete the length === 1 bare-return) plus a regression test that pins it. No public behavior, schema, binding, or migration change.

Closes #1610.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked an issue, or this is small enough that the summary explains why an issue is not needed.

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally; codecov/patch requires ≥97% coverage of the lines AND branches you changed (aim for 98%+ on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • No artifact regeneration needed: backend-only, no API/OpenAPI/schema change (ui:openapi), no wrangler.jsonc binding/var change (cf-typegen), no DB change (migration).

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. — N/A: no auth/CORS/session change; this is AI-provider routing.
  • API/OpenAPI/MCP behavior is updated and tested where needed. — N/A: no API/MCP surface change.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. — N/A: no UI change.
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots. — N/A: no visible UI change.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. — N/A.

Notes

  • This is the single-provider counterpart of the existing routeProviders name-stripping that already covers the 2-provider/dual-review path; it makes env.AI uniformly name-aware so a reviewer-plan address never reaches a provider as a literal --model.

…#1610)

A single-provider self-host (e.g. AI_PROVIDER=claude-code) failed every AI
review with an opaque claude_code_exit_1. resolveAiReviewerPlan addresses the
lone reviewer by provider name ({ model: "claude-code" }) — the same router
address convention as the 2-provider plan — but createSelfHostAi returned the
BARE provider for a single provider, bypassing routeProviders's name-stripping.
So createClaudeCodeAi ran resolveModel(undefined, "claude-code", ...), which
passes "claude-code" through verbatim (it only strips @cf/ ids), producing
`claude --model claude-code` → 404 → exit 1, three attempts, then exhausted.

Wrap a single provider in routeProviders too, so the provider-name address
resolves to the provider's own default model. Regression test pins that a
single-provider env.AI.run(<providerName>) sends the provider default, never
the literal provider name as a model id.
@dosubot dosubot Bot added the size:S label Jun 27, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored JSONbored self-assigned this Jun 27, 2026
@JSONbored JSONbored added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jun 27, 2026
@JSONbored JSONbored moved this from Todo to In progress in gittensory - v1 roadmap Jun 27, 2026
@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.51%. Comparing base (427eb35) to head (5c327db).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1611      +/-   ##
==========================================
- Coverage   95.51%   95.51%   -0.01%     
==========================================
  Files         204      204              
  Lines       22042    22041       -1     
  Branches     7964     7963       -1     
==========================================
- Hits        21053    21052       -1     
  Misses        413      413              
  Partials      576      576              
Files with missing lines Coverage Δ
src/selfhost/ai.ts 99.44% <ø> (-0.01%) ⬇️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored
JSONbored merged commit 2ba452e into main Jun 27, 2026
28 of 30 checks passed
@JSONbored
JSONbored deleted the fix/selfhost-single-provider-model-route branch June 27, 2026 17:55
@github-project-automation github-project-automation Bot moved this from In progress to Done in gittensory - v1 roadmap Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

Self-host single-provider reviews fail 404: provider name passed as claude --model claude-code

1 participant