From 310b10cdc2f0580d52e3ac2f4174f9ba274c3a92 Mon Sep 17 00:00:00 2001 From: Michael Preuss Date: Thu, 12 Feb 2026 12:07:14 -0800 Subject: [PATCH] feat: eval-evolution work in progress - recommendations and workers UI --- .../evals/methodology/methodology-content.tsx | 22 +- .../src/app/evals/methodology/page.tsx | 4 +- .../workers-v2/[roleId]/compare/page.tsx | 83 ++ .../app/evals/workers-v2/[roleId]/page.tsx | 100 +++ .../src/app/evals/workers-v2/page.tsx | 91 +++ .../workers/[roleId]/candidates-content.tsx | 51 +- .../[roleId]/compare/comparison-chart.tsx | 71 +- .../evals/workers/[roleId]/compare/page.tsx | 21 +- .../src/app/evals/workers/[roleId]/page.tsx | 10 +- .../src/app/evals/workers/page.tsx | 20 +- .../src/app/evals/workers/workers-content.tsx | 753 ++++++++++++++---- apps/web-roo-code/src/lib/eval-outcomes.ts | 171 ++++ .../src/lib/mock-recommendations.ts | 66 +- 13 files changed, 1207 insertions(+), 256 deletions(-) create mode 100644 apps/web-roo-code/src/app/evals/workers-v2/[roleId]/compare/page.tsx create mode 100644 apps/web-roo-code/src/app/evals/workers-v2/[roleId]/page.tsx create mode 100644 apps/web-roo-code/src/app/evals/workers-v2/page.tsx create mode 100644 apps/web-roo-code/src/lib/eval-outcomes.ts 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 0285be5970..654b1740fe 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 @@ -198,19 +198,19 @@ export function MethodologyContent() { / - Hire an AI Engineer + Build with Roo Code Cloud / - How We Interview + Methodology {/* Heading */} - How We Interview{" "} + How We Run{" "} - AI Models + Evals @@ -262,7 +262,7 @@ export function MethodologyContent() { - The Interview Process + The Eval Process {/* ════════════════════════════════════════════════════════════════ - SECTION 02: THE INTERVIEW SUITE + SECTION 02: THE EVAL SUITE ════════════════════════════════════════════════════════════════ */} - The Interview Suite + The Eval Suite {/* ════════════════════════════════════════════════════════════════ - SECTION 05: RUN YOUR OWN INTERVIEWS + SECTION 05: RUN YOUR OWN EVALS ════════════════════════════════════════════════════════════════ */} - Run Your Own Interviews + Run Your Own Evals - Our evaluation framework is fully open source. Run the exact same interviews on your own + Our evaluation framework is fully open source. Run the exact same evals on your own infrastructure, with your own API keys, against any model. diff --git a/apps/web-roo-code/src/app/evals/methodology/page.tsx b/apps/web-roo-code/src/app/evals/methodology/page.tsx index 8a0960142c..039d7e6fd7 100644 --- a/apps/web-roo-code/src/app/evals/methodology/page.tsx +++ b/apps/web-roo-code/src/app/evals/methodology/page.tsx @@ -7,7 +7,7 @@ import { MethodologyContent } from "./methodology-content" // ── SEO Metadata ──────────────────────────────────────────────────────────── -const TITLE = "How We Interview AI Models | Roo Code Evals" +const TITLE = "Methodology | Roo Code Evals" const DESCRIPTION = "Our methodology for evaluating AI coding models. Transparent, reproducible, evidence-based." const OG_DESCRIPTION = "Our methodology for evaluating AI coding models" const PATH = "/evals/methodology" @@ -46,7 +46,7 @@ export const metadata: Metadata = { "model benchmarking", "coding evals", "methodology", - "interview process", + "evaluation process", "transparent evaluation", ], } diff --git a/apps/web-roo-code/src/app/evals/workers-v2/[roleId]/compare/page.tsx b/apps/web-roo-code/src/app/evals/workers-v2/[roleId]/compare/page.tsx new file mode 100644 index 0000000000..2176eaae88 --- /dev/null +++ b/apps/web-roo-code/src/app/evals/workers-v2/[roleId]/compare/page.tsx @@ -0,0 +1,83 @@ +import { notFound } from "next/navigation" +import type { Metadata } from "next" + +import { SEO } from "@/lib/seo" +import { ogImageUrl } from "@/lib/og" +import { getEngineerRole, getRoleRecommendation } from "@/lib/mock-recommendations" + +import { ComparisonChart } from "../../../workers/[roleId]/compare/comparison-chart" + +type PageProps = { params: Promise<{ roleId: string }> } + +export async function generateMetadata({ params }: PageProps): Promise { + const { roleId } = await params + const role = getEngineerRole(roleId) + + if (!role) { + return { + title: "Role Not Found | Roo Code Evals", + description: "The requested role was not found.", + } + } + + const title = `Compare Models — ${role.name} (V2 Preview) | Roo Code Evals` + const description = `Outcome-first comparison of AI models for ${role.name}. Compare composite score, success rate, cost efficiency, and speed.` + const ogDescription = `Compare Models — ${role.name} (V2 Preview)` + const path = `/evals/workers-v2/${roleId}/compare` + + return { + title, + description, + alternates: { + canonical: `${SEO.url}${path}`, + }, + openGraph: { + title, + description, + url: `${SEO.url}${path}`, + siteName: SEO.name, + images: [ + { + url: ogImageUrl(title, ogDescription), + width: 1200, + height: 630, + alt: title, + }, + ], + locale: SEO.locale, + type: "website", + }, + twitter: { + card: SEO.twitterCard, + title, + description, + images: [ogImageUrl(title, ogDescription)], + }, + keywords: [ + ...SEO.keywords, + "AI coding", + "model comparison", + "coding evals", + role.name.toLowerCase(), + "outcome-first", + ], + } +} + +export default async function WorkersV2ComparePage({ params }: PageProps) { + const { roleId } = await params + const recommendation = getRoleRecommendation(roleId) + + if (!recommendation) { + notFound() + } + + return ( + + ) +} diff --git a/apps/web-roo-code/src/app/evals/workers-v2/[roleId]/page.tsx b/apps/web-roo-code/src/app/evals/workers-v2/[roleId]/page.tsx new file mode 100644 index 0000000000..8afef01a58 --- /dev/null +++ b/apps/web-roo-code/src/app/evals/workers-v2/[roleId]/page.tsx @@ -0,0 +1,100 @@ +import { notFound } from "next/navigation" +import type { Metadata } from "next" + +import { SEO } from "@/lib/seo" +import { ogImageUrl } from "@/lib/og" +import { getRoleRecommendation, getCloudSetupUrl } from "@/lib/mock-recommendations" + +import { CandidatesContent } from "../../workers/[roleId]/candidates-content" + +type PageProps = { params: Promise<{ roleId: string }> } + +export async function generateMetadata({ params }: PageProps): Promise { + const { roleId } = await params + const recommendation = getRoleRecommendation(roleId) + + if (!recommendation) { + return { + title: "Role Not Found | Roo Code Evals", + description: "The requested role was not found.", + } + } + + const { role } = recommendation + const title = `${role.name} — Recommended Models (V2 Preview) | Roo Code Evals` + const description = `Outcome-first recommendations for ${role.name}. Compare models by success rate, cost, and speed across 5 languages.` + const ogDescription = `${role.name} — Recommended Models (V2 Preview)` + const path = `/evals/workers-v2/${roleId}` + + return { + title, + description, + alternates: { + canonical: `${SEO.url}${path}`, + }, + openGraph: { + title, + description, + url: `${SEO.url}${path}`, + siteName: SEO.name, + images: [ + { + url: ogImageUrl(title, ogDescription), + width: 1200, + height: 630, + alt: title, + }, + ], + locale: SEO.locale, + type: "website", + }, + twitter: { + card: SEO.twitterCard, + title, + description, + images: [ogImageUrl(title, ogDescription)], + }, + keywords: [ + ...SEO.keywords, + "AI coding", + "coding agents", + "model recommendations", + "coding evals", + role.name.toLowerCase(), + "outcome-first", + ], + } +} + +export default async function WorkersV2RolePage({ params }: PageProps) { + const { roleId } = await params + const recommendation = getRoleRecommendation(roleId) + + if (!recommendation) { + notFound() + } + + const { role, best, budgetHire, speedHire, allCandidates, totalEvalRuns, totalExercises, lastUpdated } = + recommendation + + const cloudUrls: Record = {} + for (const candidate of allCandidates) { + cloudUrls[candidate.modelId] = getCloudSetupUrl(candidate) + } + + 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 new file mode 100644 index 0000000000..5196214f68 --- /dev/null +++ b/apps/web-roo-code/src/app/evals/workers-v2/page.tsx @@ -0,0 +1,91 @@ +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" + +const TITLE = "Build with Roo Code Cloud (V2 Preview) | Roo Code Evals" +const DESCRIPTION = + "Outcome-first, eval-backed recommendations for shipping production code. Start from what you need to ship and pick a setup." +const OG_DESCRIPTION = "Outcome-first recommendations for shipping production code" +const PATH = "/evals/workers-v2" + +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", + "shipping code", + "prototype", + "outcome-first", + ], +} + +export default function WorkersV2Page() { + const roles = getEngineerRoles() + const recommendations = getAllRecommendations() + + 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((recommendation) => recommendation.lastUpdated) + .sort() + .pop() + + return ( +
+ +
+ ) +} 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 842a7ebb6f..9d48a2b095 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 @@ -21,6 +21,7 @@ import { } from "lucide-react" import type { LucideIcon } from "lucide-react" import Link from "next/link" +import { useSearchParams } from "next/navigation" import type { ModelCandidate, LanguageScores, EngineerRole } from "@/lib/mock-recommendations" @@ -485,7 +486,7 @@ function CandidateCard({ target="_blank" rel="noopener noreferrer" className={`inline-flex w-full items-center justify-center gap-2 rounded-xl ${theme.buttonBg} ${theme.buttonHover} px-4 py-3 text-sm font-semibold text-white transition-all duration-200 hover:scale-[1.02] active:scale-[0.98] shadow-lg`}> - ☁️ Hire This Engineer + ☁️ Open in Roo Code Cloud @@ -620,7 +621,7 @@ function CompactCard({ target="_blank" rel="noopener noreferrer" className={`inline-flex w-full items-center justify-center gap-2 rounded-xl ${theme.buttonBg} ${theme.buttonHover} px-4 py-3 text-sm font-semibold text-white transition-all duration-200 hover:scale-[1.02] active:scale-[0.98] shadow-lg`}> - ☁️ Hire This Engineer + ☁️ Open in Roo Code Cloud @@ -643,6 +644,7 @@ export type CandidatesContentProps = { totalExercises: number lastUpdated: string cloudUrls: Record + workersRootPath?: string } // ── Main Content Component ────────────────────────────────────────────────── @@ -658,9 +660,22 @@ export function CandidatesContent({ totalExercises, lastUpdated, cloudUrls, + workersRootPath = "/evals/workers", }: CandidatesContentProps) { + const searchParams = useSearchParams() const theme = ROLE_THEMES[roleId] ?? DEFAULT_THEME const IconComponent = ICON_MAP[role.icon] ?? Code + const alternateWorkersRootPath = workersRootPath === "/evals/workers-v2" ? "/evals/workers" : "/evals/workers-v2" + const alternateVersionLabel = workersRootPath === "/evals/workers-v2" ? "View baseline" : "View V2 preview" + const setupQuery = (() => { + const outcome = searchParams.get("outcome") + if (!outcome) return "" + const params = new URLSearchParams() + params.set("outcome", outcome) + const mode = searchParams.get("mode") + if (mode) params.set("mode", mode) + return `?${params.toString()}` + })() return ( <> @@ -693,8 +708,10 @@ export function CandidatesContent({ Evals / - - Hire an AI Engineer + + Build with Roo Code Cloud / {role.name} @@ -753,9 +770,15 @@ export function CandidatesContent({ href="/evals/methodology" className="group inline-flex items-center gap-1.5 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"> - How we interview + Methodology +
+ + {alternateVersionLabel} + {/* Strengths + Trade-offs grid */} @@ -792,7 +815,7 @@ export function CandidatesContent({
- {/* ── Top Candidates: Best Overall ────────────────────────────── */} + {/* ── Top Models: Best Overall ────────────────────────────────── */}
- Top Candidates + Top Models @@ -835,7 +858,7 @@ export function CandidatesContent({ {budgetHire && ( )} - {/* ── All Candidates Table ────────────────────────────────────── */} + {/* ── All Models Table ────────────────────────────────────────── */}
{/* Subtle background */} - All Candidates + All Models 📊 Compare all candidates @@ -983,7 +1006,7 @@ export function CandidatesContent({ variants={containerVariants}> Back to all roles @@ -991,7 +1014,7 @@ export function CandidatesContent({ 📊 Compare candidates 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 998e1af261..d03f83fe67 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 @@ -2,6 +2,7 @@ import { useState, useMemo, useCallback } from "react" import Link from "next/link" +import { useSearchParams } from "next/navigation" import { motion } from "framer-motion" import { ArrowLeft, @@ -409,12 +410,12 @@ function ScatterTooltip({
- Daily Salary: + Daily Spend: ${data.dailyCost}/day
- Interview Score: + Eval Score: {data.score}
@@ -433,11 +434,29 @@ interface ComparisonChartProps { recommendation: RoleRecommendation role: EngineerRole roleId: string + workersRootPath?: string } -export function ComparisonChart({ recommendation, role, roleId }: ComparisonChartProps) { +export function ComparisonChart({ + recommendation, + role, + roleId, + workersRootPath = "/evals/workers", +}: ComparisonChartProps) { + const searchParams = useSearchParams() const { allCandidates } = recommendation const theme = ROLE_THEMES[roleId] ?? DEFAULT_THEME + const alternateWorkersRootPath = workersRootPath === "/evals/workers-v2" ? "/evals/workers" : "/evals/workers-v2" + const alternateVersionLabel = workersRootPath === "/evals/workers-v2" ? "View baseline" : "View V2 preview" + const setupQuery = (() => { + const outcome = searchParams.get("outcome") + if (!outcome) return "" + const params = new URLSearchParams() + params.set("outcome", outcome) + const mode = searchParams.get("mode") + if (mode) params.set("mode", mode) + return `?${params.toString()}` + })() // ── State ─────────────────────────────────────────────────────────────── const [selectedLanguage, setSelectedLanguage] = useState("all") @@ -561,15 +580,25 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar Evals / - - Hire an AI Engineer + + Build with Roo Code Cloud / - + {role.name} / - Compare Candidates + Compare Models + / + + {alternateVersionLabel} + {/* Title row */} @@ -579,7 +608,7 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar
-

Compare Candidates

+

Compare Models

{role.name}

@@ -599,7 +628,7 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar {filteredCandidates.length} - of {allCandidates.length} candidates shown + of {allCandidates.length} models shown
@@ -731,10 +760,10 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar className="rounded-2xl border border-border/50 bg-card/50 p-6 backdrop-blur-sm" variants={fadeUpVariants}>
-

Value Map: Salary vs Interview Score

+

Value Map: Spend vs Eval Score

- Upper-left = best value. Each dot is a candidate model. Size reflects success rate. + Upper-left = higher score at lower spend. Each dot is a model. Size reflects success rate.

{/* Tier legend */} @@ -753,7 +782,7 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar {scatterData.length === 0 ? (
-

No candidates match the current filters.

+

No models match the current filters.

Try adjusting the provider or success rate filters.

@@ -765,7 +794,7 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar `$${v}`} stroke="hsl(var(--muted-foreground))" @@ -773,7 +802,7 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar tick={{ fontSize: 11, fill: "hsl(var(--muted-foreground))" }} axisLine={false} label={{ - value: "Daily Salary ($)", + value: "Daily Spend ($)", position: "insideBottom", offset: -10, style: { fontSize: 11, fill: "hsl(var(--muted-foreground))" }, @@ -782,14 +811,14 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar - + {scatterData.map((entry, index) => ( -

No candidates match the current filters.

+

No models match the current filters.

Try adjusting the provider or success rate filters.

@@ -993,15 +1022,15 @@ export function ComparisonChart({ recommendation, role, roleId }: ComparisonChar
- Back to {role.name} candidates + Back to {role.name} models
All roles 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 d79b22eedd..9d03aa9cca 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 @@ -22,9 +22,9 @@ export async function generateMetadata({ params }: PageProps): Promise } } - const title = `Compare Candidates — ${role.name} | Roo Code Evals` - const description = `Interactive comparison of AI model candidates for the ${role.name} role. Compare composite score, success rate, cost efficiency, and speed.` - const ogDescription = `Compare Candidates — ${role.name}` + 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` return { @@ -57,19 +57,19 @@ export async function generateMetadata({ params }: PageProps): Promise }, keywords: [ ...SEO.keywords, - "AI engineer", + "AI coding", "model comparison", "coding evals", role.name.toLowerCase(), "bar chart", - "candidate comparison", + "model comparison", ], } } // ── Page Component ────────────────────────────────────────────────────────── -export default async function CompareCandidatesPage({ params }: PageProps) { +export default async function CompareModelsPage({ params }: PageProps) { const { roleId } = await params const recommendation = getRoleRecommendation(roleId) @@ -77,5 +77,12 @@ export default async function CompareCandidatesPage({ params }: PageProps) { notFound() } - return + return ( + + ) } 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 8daa554cd2..54ca16d0d2 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 @@ -23,9 +23,9 @@ export async function generateMetadata({ params }: PageProps): Promise } const { role } = recommendation - const title = `${role.name} — AI Engineer Candidates | Roo Code Evals` - const description = `Interview results for ${role.name} AI candidates. Compare models by success rate, cost, and speed across 5 languages.` - const ogDescription = `${role.name} — AI Engineer Candidates` + 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}` return { @@ -58,7 +58,8 @@ export async function generateMetadata({ params }: PageProps): Promise }, keywords: [ ...SEO.keywords, - "AI engineer", + "AI coding", + "coding agents", "model recommendations", "coding evals", role.name.toLowerCase(), @@ -99,6 +100,7 @@ export default async function RoleCandidatesPage({ params }: PageProps) { totalExercises={totalExercises} lastUpdated={lastUpdated} cloudUrls={cloudUrls} + workersRootPath="/evals/workers" /> ) } diff --git a/apps/web-roo-code/src/app/evals/workers/page.tsx b/apps/web-roo-code/src/app/evals/workers/page.tsx index d7a8f5cc17..a2b95a1bd8 100644 --- a/apps/web-roo-code/src/app/evals/workers/page.tsx +++ b/apps/web-roo-code/src/app/evals/workers/page.tsx @@ -8,10 +8,10 @@ import { WorkersContent } from "./workers-content" // ── SEO Metadata ──────────────────────────────────────────────────────────── -const TITLE = "Hire an AI Engineer | Roo Code Evals" +const TITLE = "Build with Roo Code Cloud | Roo Code Evals" const DESCRIPTION = - "Find the right AI coding model for your team. Compare interview results across Junior, Senior, and Staff Engineer roles." -const OG_DESCRIPTION = "Find the right AI coding model for your team" + "Eval-backed model recommendations for shipping production code. Pick a setup based on the work you're doing: single-file fixes, multi-file changes, review, and autonomous runs." +const OG_DESCRIPTION = "Eval-backed model recommendations for shipping production code" const PATH = "/evals/workers" export const metadata: Metadata = { @@ -44,18 +44,20 @@ export const metadata: Metadata = { }, keywords: [ ...SEO.keywords, - "AI engineer", + "AI coding", + "coding agents", + "roo code cloud", "model recommendations", "coding evals", "model comparison", - "hire AI", - "talent marketplace", + "shipping code", + "prototype", ], } // ── Page Component ────────────────────────────────────────────────────────── -export default function HireAnAIEngineerPage() { +export default function WorkersPage() { const roles = getEngineerRoles() const recommendations = getAllRecommendations() @@ -80,6 +82,10 @@ export default function HireAnAIEngineerPage() { totalExercises={totalExercises} totalModels={totalModels} lastUpdated={lastUpdated} + workersRootPath="/evals/workers" + enableOutcomeLayer={false} + alternateVersionHref="/evals/workers-v2" + alternateVersionLabel="View V2 preview" /> ) } 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 755cc4e3b7..e5c770ea6d 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 @@ -1,6 +1,6 @@ "use client" -import { useMemo } from "react" +import { useCallback, useMemo } from "react" import { motion } from "framer-motion" import { Code, @@ -20,10 +20,12 @@ import { } from "lucide-react" import type { LucideIcon } from "lucide-react" import Link from "next/link" +import { usePathname, useRouter, useSearchParams } from "next/navigation" import { ScatterChart, Scatter, XAxis, YAxis, ZAxis, Tooltip, ResponsiveContainer, Cell, ReferenceLine } from "recharts" import type { EngineerRole, RoleRecommendation } from "@/lib/mock-recommendations" import { TASKS_PER_DAY, MODEL_TIMELINE } from "@/lib/mock-recommendations" +import { EVAL_OUTCOMES, isEvalOutcomeId, type EvalOutcomeId } from "@/lib/eval-outcomes" // ── Icon Mapping ──────────────────────────────────────────────────────────── @@ -139,6 +141,37 @@ const ROLE_THEMES: Record = { const DEFAULT_THEME = ROLE_THEMES.senior! +// ── Outcome Layer: Optimization Modes ────────────────────────────────────── + +type EvalOptimizationMode = "best" | "fastest" | "cost" + +const OPTIMIZATION_MODES: Array<{ + id: EvalOptimizationMode + label: string + description: string +}> = [ + { id: "best", label: "Best", description: "Best overall quality across our eval suite." }, + { id: "fastest", label: "Fastest", description: "Lower latency per task when speed matters." }, + { id: "cost", label: "Most cost-effective", description: "Lower cost per task for high-volume work." }, +] + +function isEvalOptimizationMode(value: string): value is EvalOptimizationMode { + return value === "best" || value === "fastest" || value === "cost" +} + +function getModeCandidate(rec: RoleRecommendation | undefined, mode: EvalOptimizationMode) { + if (!rec) return null + if (mode === "fastest") return rec.speedHire ?? rec.best[0] ?? null + if (mode === "cost") return rec.budgetHire ?? rec.best[0] ?? null + return rec.best[0] ?? null +} + +function getModeLabel(mode: EvalOptimizationMode) { + if (mode === "fastest") return "Fastest" + if (mode === "cost") return "Most cost-effective" + return "Best" +} + // ── Framer Motion Variants ────────────────────────────────────────────────── const containerVariants = { @@ -284,6 +317,10 @@ type WorkersContentProps = { totalExercises: number totalModels: number lastUpdated: string | undefined + workersRootPath?: string + enableOutcomeLayer?: boolean + alternateVersionHref?: string + alternateVersionLabel?: string } export function WorkersContent({ @@ -293,8 +330,91 @@ export function WorkersContent({ totalExercises, totalModels, lastUpdated, + workersRootPath = "/evals/workers", + enableOutcomeLayer = false, + alternateVersionHref, + alternateVersionLabel, }: WorkersContentProps) { + const router = useRouter() + const pathname = usePathname() + const searchParams = useSearchParams() + + const selectedOutcomeId = useMemo(() => { + const outcome = searchParams.get("outcome") + if (!outcome) return null + return isEvalOutcomeId(outcome) ? outcome : null + }, [searchParams]) + + const selectedMode = useMemo((): EvalOptimizationMode => { + const mode = searchParams.get("mode") + if (!mode) return "best" + return isEvalOptimizationMode(mode) ? mode : "best" + }, [searchParams]) + + const setOutcome = useCallback( + (nextOutcomeId: EvalOutcomeId | null) => { + const params = new URLSearchParams(searchParams.toString()) + if (nextOutcomeId) params.set("outcome", nextOutcomeId) + else params.delete("outcome") + + const query = params.toString() + router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false }) + }, + [pathname, router, searchParams], + ) + + const setMode = useCallback( + (nextMode: EvalOptimizationMode) => { + const params = new URLSearchParams(searchParams.toString()) + params.set("mode", nextMode) + + const query = params.toString() + router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false }) + }, + [pathname, router, searchParams], + ) + const recByRole = new Map(recommendations.map((r) => [r.roleId, r])) + 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]) + + const setupQuery = useMemo(() => { + if (!enableOutcomeLayer || !selectedOutcomeId) return "" + const params = new URLSearchParams() + params.set("outcome", selectedOutcomeId) + params.set("mode", selectedMode) + const query = params.toString() + return query ? `?${query}` : "" + }, [enableOutcomeLayer, selectedOutcomeId, selectedMode]) + + const profileTitle = selectedOutcome?.builderProfile?.title ?? "Your Builder Profile" + const profileDescription = + selectedOutcome?.builderProfile?.description ?? + "A default setup built from our eval signals. It’s a baseline, not a guarantee." + const profileHowItWorks = selectedOutcome?.builderProfile?.howItWorks ?? selectedOutcome?.whyItWorks ?? [] + + const profileCapabilities = useMemo(() => { + if (!selectedOutcome) return [] + const fromProfile = selectedOutcome.builderProfile?.capabilities + if (fromProfile && fromProfile.length > 0) return fromProfile + return selectedOutcome.recommendedRoleIds.map((roleId) => { + const role = roleById.get(roleId) + return { + id: roleId, + name: role?.name ?? roleId, + description: role?.salaryRange ?? "", + roleId, + } + }) + }, [selectedOutcome, roleById]) + + const agentCapabilities = useMemo(() => profileCapabilities.filter((c) => Boolean(c.roleId)), [profileCapabilities]) + + const builtInCapabilities = useMemo(() => profileCapabilities.filter((c) => !c.roleId), [profileCapabilities]) // ── Timeline scatter data ────────────────────────────────────────────── const timelineData = useMemo(() => { @@ -340,6 +460,21 @@ export function WorkersContent({
+ {/* Blueprint grid overlay (V2) */} + {enableOutcomeLayer ? ( +
+ ) : null} + {/* Gradient fade from hero atmosphere to cards */}
@@ -351,33 +486,91 @@ export function WorkersContent({ variants={containerVariants}> {/* Badge */} - - - How we interview AI models - - +
+ + + How we run evals + + + {alternateVersionHref && alternateVersionLabel ? ( + + {alternateVersionLabel} + + ) : null} +
+ {enableOutcomeLayer ? ( + + Outcomes over artifacts + + ) : null} + {/* Heading */} - Hire an{" "} - - AI Engineer - + {enableOutcomeLayer ? ( + <> + Build from outcomes. +
Ship{" "} + + real code + + . + + ) : ( + <> + Build with{" "} + + Roo Code Cloud + + + )}
{/* Subheading */} - Every model runs the same coding tasks, same tools, same time limit. Pick the right - candidate for your team and budget. + {enableOutcomeLayer ? ( + <> + Pick what you're trying to ship. We assemble a Builder Profile: the + capabilities you need, plus a default model recommendation backed by eval data. + + ) : ( + <> + Outcomes over artifacts: start from the production codebase and ship as a reviewable + PR. Every model runs the same tasks, same tools, and the same time limit. Your repo + will differ—treat this as a baseline. + + )} + {enableOutcomeLayer ? ( + + + Start with Prototype → PR + + + + Browse outcomes + + + ) : null} + {/* Stats bar */}
- {/* ── Role Cards Grid ────────────────────────────────────────── */} -
- {/* Subtle section background */} - -
-
-
- + {/* ── Outcomes Overlay ───────────────────────────────────────── */} + {enableOutcomeLayer ? ( +
+
+ + +

+ Start with an outcome +

+

+ Pick what you're trying to ship. We assemble a Builder Profile: capabilities + plus a default model recommendation. It's a baseline, not a guarantee. +

+
-
- {/* Section connector */} - -

- Choose your agentic team members -

- -
+ + {EVAL_OUTCOMES.map((outcome) => { + const Icon = outcome.icon + const isSelected = outcome.id === selectedOutcomeId + const isFeatured = outcome.id === "prototype_to_pr" - - {roles.map((role) => { - const rec = recByRole.get(role.id) - const IconComponent = ICON_MAP[role.icon] ?? Code - const candidateCount = rec?.allCandidates.length ?? 0 - const exerciseCount = rec?.totalExercises ?? 0 - const theme = ROLE_THEMES[role.id] ?? DEFAULT_THEME - const topModel = rec?.best[0] - - return ( - -
- {/* Subtle glow on hover */} -
- -
- {/* Header: Icon + role badge */} -
-
- + return ( + setOutcome(isSelected ? null : outcome.id)} + className={[ + "group rounded-2xl border bg-card/40 p-5 text-left backdrop-blur-sm transition-all duration-200 hover:bg-card/60", + isSelected + ? "border-foreground/20 ring-1 ring-foreground/15" + : "border-border/50 hover:border-border", + isFeatured ? "lg:col-span-2" : "", + ].join(" ")}> +
+
+
- {topModel && ( - - Top: {topModel.displayName} - - )} -
- - {/* Role name + salary */} -

{role.name}

-

- {role.salaryRange} -

- - {/* Description */} -

- {role.description} -

- - {/* Best for */} -
-

- Best for -

-
- {role.bestFor.map((item) => ( - - {item} +
+ {isFeatured ? ( + + Recommended starting point - ))} + ) : null} +

+ {outcome.name} +

+

+ {outcome.description} +

+ + ) + })} + - {/* Strengths & Weaknesses side by side */} -
- {/* Strengths */} -
-

- Strengths -

-
    - {role.strengths.map((item) => ( -
  • - - {item} + {selectedOutcome ? ( + +
    +
    +

    + {profileTitle} +

    +

    + {selectedOutcome.name} +

    +

    + {profileDescription} +

    + + {profileHowItWorks.length > 0 ? ( +
    +

    + {selectedOutcome.builderProfile + ? "How it works" + : "Why it works"} +

    +
      + {profileHowItWorks.map((line) => ( +
    • + + {line}
    • ))}
    + ) : null} - {/* Weaknesses */} -
    -

    - Trade-offs -

    -
      - {role.weaknesses.map((item) => ( -
    • - - {item} + {selectedOutcome.builderProfile?.howItWorks ? ( +
      +

      + Why it works +

      +
        + {selectedOutcome.whyItWorks.map((line) => ( +
      • + + {line}
      • ))}
      -
    + ) : null} +
    - {/* Bottom stats + CTA */} -
    -
    - - - {candidateCount} candidates - - - - {exerciseCount.toLocaleString()} exercises +
    +
    +
    + + Optimize for + {OPTIMIZATION_MODES.map((mode) => { + const isSelected = mode.id === selectedMode + return ( + + ) + })}
    - - - View Candidates - - + + Capability set +
    + +
    +
    +

    + Agents +

    +

    + Click for candidates & settings +

    +
    +
    + {agentCapabilities.map((capability) => { + const roleId = capability.roleId! + const rec = recByRole.get(roleId) + const candidate = getModeCandidate(rec, selectedMode) + + return ( + +
    +
    +

    + {capability.name} +

    + {capability.description ? ( +

    + {capability.description} +

    + ) : null} +

    + {candidate ? ( + <> + + {getModeLabel(selectedMode)}: + {" "} + {candidate.displayName} + + ) : ( + + View models + + )} +

    +
    + +
    + + ) + })} +
    +
    + + {builtInCapabilities.length > 0 ? ( +
    +

    + Built-ins +

    +
    + {builtInCapabilities.map((capability) => ( +
    +
    + +
    +
    +

    + {capability.name} +

    + + Built-in + +
    +

    + {capability.description} +

    +
    +
    +
    + ))} +
    +
    + ) : null}
    - ) - })} + ) : null} + +
    +
+ ) : null} + + {/* ── Role Cards Grid (baseline only) ────────────────────────── */} + {!enableOutcomeLayer ? ( +
+ {/* Subtle section background */} + +
+
+
-
-
+ +
+ {/* Section connector */} + +

+ Choose a setup for the work +

+ +
+ + + {roles.map((role) => { + const rec = recByRole.get(role.id) + const IconComponent = ICON_MAP[role.icon] ?? Code + const candidateCount = rec?.allCandidates.length ?? 0 + const exerciseCount = rec?.totalExercises ?? 0 + const theme = ROLE_THEMES[role.id] ?? DEFAULT_THEME + const topModel = rec?.best[0] ?? null + + return ( + +
+ {/* Subtle glow on hover */} +
+ +
+ {/* Header: Icon + role badge */} +
+
+ +
+ {topModel && ( + + Top: {topModel.displayName} + + )} +
+ + {/* Profile name + descriptor */} +

{role.name}

+

+ {role.salaryRange} +

+ + {/* Description */} +

+ {role.description} +

+ + {/* Best for */} +
+

+ Best for +

+
+ {role.bestFor.map((item) => ( + + {item} + + ))} +
+
+ + {/* Strengths & Weaknesses side by side */} +
+ {/* Strengths */} +
+

+ Strengths +

+
    + {role.strengths.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ + {/* Weaknesses */} +
+

+ Trade-offs +

+
    + {role.weaknesses.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+
+ + {/* Bottom stats + CTA */} +
+
+ + + {candidateCount} models + + + + {exerciseCount.toLocaleString()} exercises + +
+ + + View models + + +
+
+
+ + ) + })} + +
+
+ ) : null} {/* ── AI Coding Capability Over Time ─────────────────────────── */}
@@ -582,7 +1021,7 @@ export function WorkersContent({ variants={containerVariants}> {/* Section header */} -

+

AI Coding Capability{" "} Over Time diff --git a/apps/web-roo-code/src/lib/eval-outcomes.ts b/apps/web-roo-code/src/lib/eval-outcomes.ts new file mode 100644 index 0000000000..85ced98347 --- /dev/null +++ b/apps/web-roo-code/src/lib/eval-outcomes.ts @@ -0,0 +1,171 @@ +import type { LucideIcon } from "lucide-react" +import { Bug, CheckCircle2, GitPullRequest, Sparkles, Workflow } from "lucide-react" + +export type EvalOutcomeId = + | "prototype_to_pr" + | "paper_cuts" + | "sentry_triage" + | "repro_to_fix" + | "review_guardrails" + | "issue_to_pr" + +export type EvalOutcomeCapability = { + id: string + name: string + description: string + /** + * Optional roleId for capabilities that map directly to a role page. + * Non-role capabilities represent Roo Code Cloud behaviors (validation, PR packaging, etc.). + */ + roleId?: string +} + +export type EvalOutcomeProfile = { + title: string + description: string + capabilities: EvalOutcomeCapability[] + howItWorks: string[] +} + +export type EvalOutcome = { + id: EvalOutcomeId + name: string + description: string + icon: LucideIcon + /** + * Ordered list of roleIds to suggest as a "setup". + * Keep roleIds stable even if display names evolve. + */ + recommendedRoleIds: string[] + whyItWorks: string[] + /** + * Optional profile details used to render a more comprehensive “exoskeleton” + * for an outcome. Start with the most important outcomes and expand over time. + */ + builderProfile?: EvalOutcomeProfile +} + +export const EVAL_OUTCOMES: EvalOutcome[] = [ + { + id: "prototype_to_pr", + name: "Prototype → PR", + description: "Build a working prototype on the production codebase, then turn it into a reviewable diff.", + icon: Sparkles, + recommendedRoleIds: ["senior", "reviewer"], + whyItWorks: [ + "Multi-file changes with a reviewer pass for coherence and edge cases.", + "Optimizes for shipping, not slides.", + ], + builderProfile: { + title: "Your Builder Profile", + description: + "A default set of capabilities for turning a working prototype into a reviewable PR—on the production codebase.", + capabilities: [ + { + id: "multi_file_builder", + name: "Multi-file Builder", + description: "Builds the prototype directly in your repo across the files it touches.", + roleId: "senior", + }, + { + id: "reviewer_guardrails", + name: "Reviewer & Guardrails", + description: "Reviews the diff for correctness, edge cases, and coherence before you merge.", + roleId: "reviewer", + }, + { + id: "environment_setup", + name: "Environment setup", + description: + "Bootstraps a working dev environment and runs the workflow without you fighting Git, installs, or tests.", + }, + { + id: "validation_loop", + name: "Validation loop", + description: "Runs tests/lint/typechecks and iterates until it’s clean (or flags what’s blocked).", + }, + { + id: "pr_ready_output", + name: "PR-ready output", + description: "Produces a focused diff plus a plain-English summary and review notes.", + }, + { + id: "straight_line_merge", + name: "Straight-line to merge", + description: + "No export/import step: the work is already on the production codebase, so merge is a straight line.", + }, + { + id: "scope_control", + name: "Scope control", + description: "Keeps diffs tight: smaller review surface, fewer surprises, and easier merges.", + }, + ], + howItWorks: [ + "Build a working prototype directly in the production codebase.", + "Convert the prototype into a tight diff (tests, cleanup, and safeguards).", + "Run a reviewer pass to catch edge cases and improve merge confidence.", + "Deliver a PR-ready result with context and next steps.", + ], + }, + }, + { + id: "paper_cuts", + name: "Paper cuts & small fixes", + description: "Fix the small stuff without dragging engineers off big projects.", + icon: CheckCircle2, + recommendedRoleIds: ["junior", "reviewer"], + whyItWorks: [ + "Small diffs are high-leverage when the work is well-scoped.", + "Reviewer keeps the quality bar and reduces surprise.", + ], + }, + { + id: "sentry_triage", + name: "Sentry triage", + description: "Turn recurring errors into concrete fixes with proof before review.", + icon: Bug, + recommendedRoleIds: ["autonomous", "reviewer"], + whyItWorks: [ + "Autonomous runs handle multi-step investigation and iteration.", + "Reviewer focuses on safety, correctness, and “does this hold up?”.", + ], + }, + { + id: "repro_to_fix", + name: "Bug repro → fix", + description: "Make the handoff less lossy: reproduce, patch, and validate in one loop.", + icon: Workflow, + recommendedRoleIds: ["senior", "reviewer"], + whyItWorks: [ + "Good default for ambiguous bugs that touch a few files.", + "Reviewer helps catch cross-team assumptions early.", + ], + }, + { + id: "review_guardrails", + name: "Guardrails & review", + description: "Raise the quality bar without becoming the blocker.", + icon: GitPullRequest, + recommendedRoleIds: ["reviewer"], + whyItWorks: [ + "Works alongside CI, linters, and team review.", + "Scales judgement through fast, consistent feedback.", + ], + }, + { + id: "issue_to_pr", + name: "Issue → PR", + description: "Run end-to-end work in the background and come back to a reviewable result.", + icon: GitPullRequest, + recommendedRoleIds: ["autonomous", "reviewer"], + whyItWorks: [ + "Handles out-of-band work while humans stay on the roadmap.", + "Pairs autonomy with guardrails for merge safety.", + ], + }, +] + +export function isEvalOutcomeId(value: string): value is EvalOutcomeId { + return EVAL_OUTCOMES.some((o) => o.id === value) +} diff --git a/apps/web-roo-code/src/lib/mock-recommendations.ts b/apps/web-roo-code/src/lib/mock-recommendations.ts index 99cc7f9b80..9a08487ca7 100644 --- a/apps/web-roo-code/src/lib/mock-recommendations.ts +++ b/apps/web-roo-code/src/lib/mock-recommendations.ts @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------- // Eval Recommendations: Types + Mock Data (S1.1a) // --------------------------------------------------------------------------- -// This file defines the API contract for the AI Engineer Talent Marketplace. +// This file defines the API contract for the /evals/workers recommendation pages. // The backend (Sprint 3-4) will produce data matching these exact types. // --------------------------------------------------------------------------- @@ -16,11 +16,11 @@ export const TASKS_PER_DAY = 80 // ── Types ────────────────────────────────────────────────────────────────── -/** Engineer role definition: maps task complexity to a hiring tier. */ +/** Engineer role definition: maps task complexity to a recommendation tier. */ export type EngineerRole = { id: string name: string - /** Daily salary range string, e.g. "$3–38/day" */ + /** Short descriptor shown under the profile name (scope, mode, etc.). */ salaryRange: string description: string bestFor: string[] @@ -80,51 +80,51 @@ export type RoleRecommendation = { const ENGINEER_ROLES: EngineerRole[] = [ { id: "junior", - name: "Junior Engineer", - salaryRange: "$2–10/day", + name: "Single-file Builder", + salaryRange: "Scope: single-file", description: - "Handles well-scoped, single-file tasks: boilerplate, simple bug fixes, and test generation at the lowest cost per task.", - bestFor: ["Single-file fixes", "Boilerplate generation", "Test generation", "Simple implementations"], - strengths: ["Cheap", "High throughput", "Best cost-to-quality ratio on simple tasks"], - weaknesses: ["Struggles with multi-file changes", "Limited reasoning depth", "May miss edge cases"], + "Best for tight diffs: boilerplate, small fixes, and test updates. Great when the work is clear and bounded.", + bestFor: ["Small fixes", "Boilerplate", "Test updates", "Simple implementations"], + strengths: ["Fast iteration", "Stays close to the requested change", "Great for well-scoped diffs"], + weaknesses: ["Not ideal for cross-cutting work", "Can miss edge cases in complex systems"], icon: "Code", }, { id: "senior", - name: "Senior Engineer", - salaryRange: "$10–26/day", + name: "Multi-file Builder", + salaryRange: "Scope: multi-file", description: - "The sweet spot for most engineering work. Senior-tier models balance cost and quality across multi-file refactors, feature development, and debugging.", - bestFor: ["Multi-file refactors", "Feature development", "Debugging", "Code review"], + "For most day-to-day shipping: feature work across a few files, refactors, and debugging with solid consistency.", + bestFor: ["Feature work", "Multi-file refactors", "Debugging", "Integrations"], strengths: [ - "Balanced cost/quality", - "Handles multi-file changes and cross-cutting refactors", - "Consistent pass rates across all five languages", + "Reliable for common product work", + "Handles multi-file changes and dependencies", + "Consistent across all five languages", ], - weaknesses: ["More expensive than junior", "Overkill for trivial tasks"], + weaknesses: ["Overkill for trivial diffs", "May need help on cross-cutting architecture"], icon: "GitBranch", }, { id: "staff", - name: "Staff Engineer", - salaryRange: "$8–34/day", + name: "Architecture & Refactor", + salaryRange: "Scope: cross-cutting", description: - "For architecture decisions, system design, and complex refactors. Staff-tier models handle ambiguous requirements and cross-cutting changes where other tiers fail.", - bestFor: ["Architecture decisions", "Complex features", "System design", "Ambiguous requirements"], + "For ambiguity and cross-cutting changes: architecture decisions, complex refactors, and work where correctness matters more than speed.", + bestFor: ["Complex refactors", "Architecture changes", "Ambiguous requirements", "System design"], strengths: [ - "Handles multi-step reasoning and ambiguous specs", - "Passes existing test suites consistently", - "Resolves underspecified requirements", + "Strong multi-step reasoning", + "Good at navigating bigger codebases", + "Better at making safe, coherent changes", ], - weaknesses: ["Most expensive", "Overkill for simple tasks", "Diminishing returns on easy work"], + weaknesses: ["Overkill for simple diffs", "Still needs human review before merge"], icon: "Building2", }, { id: "reviewer", - name: "Architecture Reviewer", - salaryRange: "$15–40/day", + name: "Reviewer & Guardrails", + salaryRange: "Mode: review", description: - "For code review, PR feedback, security analysis, and design critique. Reviewer-tier models catch issues other models miss and provide actionable, context-aware suggestions.", + "For PR feedback, security review, and design critique. Use this to improve quality and reduce surprises before merge.", bestFor: ["Code review", "PR feedback", "Security analysis", "Design critique", "Refactor guidance"], strengths: [ "Catches subtle bugs and logic errors", @@ -132,18 +132,18 @@ const ENGINEER_ROLES: EngineerRole[] = [ "Understands cross-file impact of changes", ], weaknesses: [ - "Not for writing code from scratch", - "More expensive than running linters", + "Not for writing features end-to-end", + "Not a replacement for CI and linters", "Review quality varies by codebase size", ], icon: "Search", }, { id: "autonomous", - name: "Autonomous Agent", - salaryRange: "$5–30/day", + name: "Autonomous Delivery", + salaryRange: "Mode: end-to-end", description: - "For issue-to-PR workflows, long-running tasks, and multi-step debugging with minimal supervision. Autonomous-tier models complete tasks end-to-end and recover from errors without human intervention.", + "For issue-to-PR workflows and long-running tasks. Best when you want an agent to run, iterate, and bring back a reviewable result.", bestFor: [ "Issue-to-PR workflows", "Multi-step debugging",