diff --git a/apps/web-roo-code/src/app/evals/methodology/methodology-content.tsx b/apps/web-roo-code/src/app/evals/methodology/methodology-content.tsx
index 852356c6c8..6bca818da1 100644
--- a/apps/web-roo-code/src/app/evals/methodology/methodology-content.tsx
+++ b/apps/web-roo-code/src/app/evals/methodology/methodology-content.tsx
@@ -147,7 +147,9 @@ export function MethodologyContent() {
Evals
/
-
+
Recommendations
/
@@ -183,7 +185,7 @@ export function MethodologyContent() {
variants={fadeUpVariants}
className="mt-10 flex flex-wrap items-center gap-5">
View recommendations
@@ -500,7 +502,7 @@ export function MethodologyContent() {
Links
diff --git a/apps/web-roo-code/src/app/evals/recommendations/[roleId]/compare/page.tsx b/apps/web-roo-code/src/app/evals/recommendations/[roleId]/compare/page.tsx
new file mode 100644
index 0000000000..24ceb8946c
--- /dev/null
+++ b/apps/web-roo-code/src/app/evals/recommendations/[roleId]/compare/page.tsx
@@ -0,0 +1,2 @@
+export { generateMetadata } from "../../../workers/[roleId]/compare/page"
+export { default } from "../../../workers/[roleId]/compare/page"
diff --git a/apps/web-roo-code/src/app/evals/recommendations/[roleId]/page.tsx b/apps/web-roo-code/src/app/evals/recommendations/[roleId]/page.tsx
new file mode 100644
index 0000000000..e40887c5bf
--- /dev/null
+++ b/apps/web-roo-code/src/app/evals/recommendations/[roleId]/page.tsx
@@ -0,0 +1,2 @@
+export { generateMetadata } from "../../workers/[roleId]/page"
+export { default } from "../../workers/[roleId]/page"
diff --git a/apps/web-roo-code/src/app/evals/recommendations/page.tsx b/apps/web-roo-code/src/app/evals/recommendations/page.tsx
new file mode 100644
index 0000000000..6bf7b6c5e8
--- /dev/null
+++ b/apps/web-roo-code/src/app/evals/recommendations/page.tsx
@@ -0,0 +1,94 @@
+import type { Metadata } from "next"
+import { Fraunces, IBM_Plex_Sans } from "next/font/google"
+
+import { SEO } from "@/lib/seo"
+import { ogImageUrl } from "@/lib/og"
+import { getEngineerRoles, getAllRecommendations } from "@/lib/mock-recommendations"
+
+import { WorkersContent } from "../workers/workers-content"
+
+// ── SEO Metadata ────────────────────────────────────────────────────────────
+
+const TITLE = "Build with Roo Code Cloud | Roo Code Evals"
+const DESCRIPTION =
+ "Outcome-first, eval-backed recommendations for shipping production code. Start from your objective and pick a tradeoff."
+const OG_DESCRIPTION = "Outcome-first recommendations for shipping production code"
+const PATH = "/evals/recommendations"
+
+const display = Fraunces({ subsets: ["latin"], variable: "--font-display" })
+const body = IBM_Plex_Sans({ subsets: ["latin"], weight: ["400", "500", "600"], variable: "--font-body" })
+
+export const metadata: Metadata = {
+ title: TITLE,
+ description: DESCRIPTION,
+ alternates: {
+ canonical: `${SEO.url}${PATH}`,
+ },
+ openGraph: {
+ title: TITLE,
+ description: DESCRIPTION,
+ url: `${SEO.url}${PATH}`,
+ siteName: SEO.name,
+ images: [
+ {
+ url: ogImageUrl(TITLE, OG_DESCRIPTION),
+ width: 1200,
+ height: 630,
+ alt: TITLE,
+ },
+ ],
+ locale: SEO.locale,
+ type: "website",
+ },
+ twitter: {
+ card: SEO.twitterCard,
+ title: TITLE,
+ description: DESCRIPTION,
+ images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
+ },
+ keywords: [
+ ...SEO.keywords,
+ "AI coding",
+ "coding agents",
+ "roo code cloud",
+ "model recommendations",
+ "coding evals",
+ "model comparison",
+ "shipping code",
+ "prototype",
+ ],
+}
+
+// ── Page Component ──────────────────────────────────────────────────────────
+
+export default function RecommendationsPage() {
+ const roles = getEngineerRoles()
+ const recommendations = getAllRecommendations()
+
+ // Aggregate totals
+ const totalEvalRuns = recommendations.reduce((sum, recommendation) => sum + recommendation.totalEvalRuns, 0)
+ const totalExercises = recommendations.reduce((sum, recommendation) => sum + recommendation.totalExercises, 0)
+ const uniqueModels = new Set(
+ recommendations.flatMap((recommendation) => recommendation.allCandidates.map((candidate) => candidate.modelId)),
+ )
+ const totalModels = uniqueModels.size
+
+ const lastUpdated = recommendations
+ .map((r) => r.lastUpdated)
+ .sort()
+ .pop()
+
+ return (
+
+
+
+ )
+}
diff --git a/apps/web-roo-code/src/app/evals/workers-v2/page.tsx b/apps/web-roo-code/src/app/evals/workers-v2/page.tsx
index e8559e09ca..4f067eb276 100644
--- a/apps/web-roo-code/src/app/evals/workers-v2/page.tsx
+++ b/apps/web-roo-code/src/app/evals/workers-v2/page.tsx
@@ -8,5 +8,5 @@ type PageProps = {
export default async function WorkersV2Page({ searchParams }: PageProps) {
const sp = (await searchParams) ?? {}
- permanentRedirect(`/evals/workers${buildQueryString(sp)}`)
+ permanentRedirect(`/evals/recommendations${buildQueryString(sp)}`)
}
diff --git a/apps/web-roo-code/src/app/evals/workers/[roleId]/candidates-content.tsx b/apps/web-roo-code/src/app/evals/workers/[roleId]/candidates-content.tsx
index 0b1105650f..cda3c52c6e 100644
--- a/apps/web-roo-code/src/app/evals/workers/[roleId]/candidates-content.tsx
+++ b/apps/web-roo-code/src/app/evals/workers/[roleId]/candidates-content.tsx
@@ -660,7 +660,7 @@ export function CandidatesContent({
totalExercises,
lastUpdated,
cloudUrls,
- workersRootPath = "/evals/workers",
+ workersRootPath = "/evals/recommendations",
}: CandidatesContentProps) {
const searchParams = useSearchParams()
const theme = ROLE_THEMES[roleId] ?? DEFAULT_THEME
diff --git a/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/comparison-chart.tsx b/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/comparison-chart.tsx
index 8c005cdbd5..186ef34c77 100644
--- a/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/comparison-chart.tsx
+++ b/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/comparison-chart.tsx
@@ -441,7 +441,7 @@ export function ComparisonChart({
recommendation,
role,
roleId,
- workersRootPath = "/evals/workers",
+ workersRootPath = "/evals/recommendations",
}: ComparisonChartProps) {
const searchParams = useSearchParams()
const { allCandidates } = recommendation
diff --git a/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/page.tsx b/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/page.tsx
index 9d03aa9cca..ac16a21b56 100644
--- a/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/page.tsx
+++ b/apps/web-roo-code/src/app/evals/workers/[roleId]/compare/page.tsx
@@ -25,7 +25,7 @@ export async function generateMetadata({ params }: PageProps): Promise
const title = `Compare Models — ${role.name} | Roo Code Evals`
const description = `Interactive comparison of AI models for the ${role.name} setup. Compare composite score, success rate, cost efficiency, and speed.`
const ogDescription = `Compare Models — ${role.name}`
- const path = `/evals/workers/${roleId}/compare`
+ const path = `/evals/recommendations/${roleId}/compare`
return {
title,
@@ -82,7 +82,7 @@ export default async function CompareModelsPage({ params }: PageProps) {
recommendation={recommendation}
role={recommendation.role}
roleId={roleId}
- workersRootPath="/evals/workers"
+ workersRootPath="/evals/recommendations"
/>
)
}
diff --git a/apps/web-roo-code/src/app/evals/workers/[roleId]/page.tsx b/apps/web-roo-code/src/app/evals/workers/[roleId]/page.tsx
index 54ca16d0d2..c408f5098c 100644
--- a/apps/web-roo-code/src/app/evals/workers/[roleId]/page.tsx
+++ b/apps/web-roo-code/src/app/evals/workers/[roleId]/page.tsx
@@ -26,7 +26,7 @@ export async function generateMetadata({ params }: PageProps): Promise
const title = `${role.name} — Recommended Models | Roo Code Evals`
const description = `Eval-backed recommendations for ${role.name}. Compare models by success rate, cost, and speed across 5 languages.`
const ogDescription = `${role.name} — Recommended Models`
- const path = `/evals/workers/${roleId}`
+ const path = `/evals/recommendations/${roleId}`
return {
title,
@@ -100,7 +100,7 @@ export default async function RoleCandidatesPage({ params }: PageProps) {
totalExercises={totalExercises}
lastUpdated={lastUpdated}
cloudUrls={cloudUrls}
- workersRootPath="/evals/workers"
+ workersRootPath="/evals/recommendations"
/>
)
}
diff --git a/apps/web-roo-code/src/app/evals/workers/workers-content.tsx b/apps/web-roo-code/src/app/evals/workers/workers-content.tsx
index 9189190120..21d8646257 100644
--- a/apps/web-roo-code/src/app/evals/workers/workers-content.tsx
+++ b/apps/web-roo-code/src/app/evals/workers/workers-content.tsx
@@ -202,7 +202,7 @@ export function WorkersContent({
totalExercises: _totalExercises,
totalModels: _totalModels,
lastUpdated,
- workersRootPath = "/evals/workers",
+ workersRootPath = "/evals/recommendations",
}: WorkersContentProps) {
const enableOutcomeLayer = ENABLE_OUTCOME_LAYER
const router = useRouter()
@@ -214,6 +214,10 @@ export function WorkersContent({
if (!outcome) return null
return isEvalOutcomeId(outcome) ? outcome : null
}, [searchParams])
+ const effectiveOutcomeId = useMemo(() => {
+ if (selectedOutcomeId) return selectedOutcomeId
+ return EVAL_OUTCOMES[0]?.id ?? null
+ }, [selectedOutcomeId])
const selectedMode = useMemo((): EvalOptimizationMode => {
const mode = searchParams.get("mode")
@@ -255,18 +259,27 @@ export function WorkersContent({
const roleById = useMemo(() => new Map(roles.map((r) => [r.id, r])), [roles])
const selectedOutcome = useMemo(() => {
- if (!selectedOutcomeId) return null
- return EVAL_OUTCOMES.find((o) => o.id === selectedOutcomeId) ?? null
- }, [selectedOutcomeId])
+ if (!effectiveOutcomeId) return null
+ return EVAL_OUTCOMES.find((o) => o.id === effectiveOutcomeId) ?? null
+ }, [effectiveOutcomeId])
const setupQuery = useMemo(() => {
- if (!selectedOutcomeId) return ""
+ if (!effectiveOutcomeId) return ""
const params = new URLSearchParams()
- params.set("outcome", selectedOutcomeId)
+ params.set("outcome", effectiveOutcomeId)
params.set("mode", selectedMode)
const query = params.toString()
return query ? `?${query}` : ""
- }, [selectedOutcomeId, selectedMode])
+ }, [effectiveOutcomeId, selectedMode])
+
+ const profileViewQuery = useMemo(() => {
+ if (!effectiveOutcomeId) return ""
+ const params = new URLSearchParams()
+ params.set("outcome", effectiveOutcomeId)
+ params.set("mode", selectedMode)
+ params.set("view", "profile")
+ return `?${params.toString()}`
+ }, [effectiveOutcomeId, selectedMode])
const isProfileView = useMemo(() => {
return searchParams.get("view") === "profile"
@@ -277,22 +290,22 @@ export function WorkersContent({
"A default setup built from our eval signals. It’s a baseline, not a guarantee."
const profileHowItWorks = selectedOutcome?.builderProfile?.howItWorks ?? selectedOutcome?.whyItWorks ?? []
const objectiveDefaultModel = useMemo(() => {
- if (!selectedOutcomeId) return null
- return pickObjectiveDefaultModelV1(selectedOutcomeId, selectedMode)
- }, [selectedOutcomeId, selectedMode])
+ if (!effectiveOutcomeId) return null
+ return pickObjectiveDefaultModelV1(effectiveOutcomeId, selectedMode)
+ }, [effectiveOutcomeId, selectedMode])
const objectiveDefaultModelLabel = useMemo(() => {
if (!objectiveDefaultModel?.modelId) return "—"
return formatModelIdForUi(objectiveDefaultModel.modelId)
}, [objectiveDefaultModel])
const examplePrompt = selectedOutcome?.builderProfile?.examplePrompt ?? ""
const cloudSetupHref = useMemo(() => {
- if (!selectedOutcomeId) return "/cloud-agents/setup"
+ if (!effectiveOutcomeId) return "/cloud-agents/setup"
const params = new URLSearchParams()
- params.set("outcome", selectedOutcomeId)
+ params.set("outcome", effectiveOutcomeId)
params.set("mode", selectedMode)
if (examplePrompt) params.set("prompt", examplePrompt)
return `/cloud-agents/setup?${params.toString()}`
- }, [examplePrompt, selectedMode, selectedOutcomeId])
+ }, [examplePrompt, selectedMode, effectiveOutcomeId])
const profileCapabilities = useMemo(() => {
if (!selectedOutcome) return []
@@ -741,7 +754,7 @@ export function WorkersContent({
{EVAL_OUTCOMES.map((outcome) => {
const Icon = outcome.icon
- const isSelected = outcome.id === selectedOutcomeId
+ const isSelected = outcome.id === effectiveOutcomeId
return (
Learn more / customize