About
Run several coding agents side by side on one task, each with live status, streamed output and a pick the winner action.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Agent Lanes to your project and fits it to your theme.
Code
How Agent Lanes is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
"use client"
import * as React from "react"
import { AgentLane, AgentLanes, Button, RotateCcwIcon } from "@/components/ui/agent-lanes"
// The demo's times count back from the moment it renders. Read through `performance`, which
// Next.js allows while it prerenders a page; `Date.now()` during render fails the build there.
const demoNow = () => performance.timeOrigin + performance.now()
/* Three agents try three fixes for the same flaky test. One fails. */
interface Script {
id: string
name: string
monogram: string
description: string
steps: string[]
output: string
/** Characters revealed per 60 ms tick. */
speed: number
fails?: string
metrics: AgentLane["metrics"]
}
const SCRIPTS: Script[] = [
{
id: "a",
name: "Attempt A",
monogram: "A",
description: "Wait on the confirmation heading",
speed: 9,
steps: ["Reading checkout.spec.ts", "Editing the assertion", "Running the test 20 times"],
output:
'The test waits a fixed 2 s for the order to save, and the preview database sometimes takes longer.\n\nI replaced the sleep with an assertion on the heading, which retries until it appears:\n\n```ts\nawait expect(\n page.getByRole("heading", { name: "Order confirmed" }),\n).toBeVisible({ timeout: 10_000 })\n```\n\nPassed 20 of 20 runs.',
metrics: [
{ label: "Duration", value: "1 m 52 s", score: 112 },
{ label: "Diff", value: "+3 −1", score: 4 },
{ label: "Test runs", value: "20 of 20", score: 20, better: "higher" },
{ label: "Cost", value: "$0.21", score: 0.21 },
],
},
{
id: "b",
name: "Attempt B",
monogram: "B",
description: "Mock the payment API",
speed: 6,
steps: ["Reading the payment client", "Adding a route mock", "Running the test 20 times"],
output:
'The slow part is the call to the payment sandbox. I mocked it with page.route so the test stops depending on the network:\n\n```ts\nawait page.route("**/api/payments", (route) =>\n route.fulfill({ json: { status: "succeeded" } }),\n)\n```\n\nPassed 20 of 20 runs, but it no longer covers the real payment call.',
metrics: [
{ label: "Duration", value: "2 m 40 s", score: 160 },
{ label: "Diff", value: "+14 −0", score: 14 },
{ label: "Test runs", value: "20 of 20", score: 20, better: "higher" },
{ label: "Cost", value: "$0.34", score: 0.34 },
],
},
{
id: "c",
name: "Attempt C",
monogram: "C",
description: "Retry the whole test",
speed: 7,
steps: ["Reading playwright.config.ts", "Setting retries to 3", "Running the test 20 times"],
output:
'Setting retries: 3 in playwright.config.ts for the checkout project hides the flake in most runs.\n\n```ts\n{ name: "checkout", retries: 3 }\n```\n',
fails:
"Failed 2 of 20 runs even with retries. The race is still there, so retrying is not a fix.",
metrics: [
{ label: "Duration", value: "3 m 5 s", score: 185 },
{ label: "Diff", value: "+1 −0", score: 1 },
{ label: "Test runs", value: "18 of 20", score: 18, better: "higher" },
{ label: "Cost", value: "$0.18", score: 0.18 },
],
},
]
export default function AgentLanesDemo() {
const [run, setRun] = React.useState(0)
const [ticks, setTicks] = React.useState(0)
const [winner, setWinner] = React.useState<string>()
const [startedAt, setStartedAt] = React.useState(() => demoNow())
// Lanes retried in place: the tick and time they restarted from. A retried lane passes.
const [retried, setRetried] = React.useState<Record<string, { tick: number; at: number }>>({})
const longest =
Math.max(
...SCRIPTS.map((s) => (retried[s.id]?.tick ?? 0) + Math.ceil(s.output.length / s.speed)),
) + 12
React.useEffect(() => {
if (ticks > longest) return
const id = window.setTimeout(() => setTicks((t) => t + 1), 60)
return () => window.clearTimeout(id)
}, [ticks, longest, run])
const lanes: AgentLane[] = SCRIPTS.map((s) => {
const spawn = 10
const retry = retried[s.id]
const laneStart = retry?.at ?? startedAt
const fails = retry ? undefined : s.fails
const chars = Math.max(0, (ticks - (retry?.tick ?? 0) - spawn) * s.speed)
const finished = chars >= s.output.length
const progress = Math.min(1, chars / s.output.length)
const step = s.steps[Math.min(s.steps.length - 1, Math.floor(progress * s.steps.length))]
// Metrics describe a finished attempt, so they only appear once the lane is done.
if (ticks - (retry?.tick ?? 0) < spawn)
return {
...s,
metrics: undefined,
status: "spawning",
step: "Starting a worktree",
startedAt: laneStart,
}
if (!finished)
return {
...s,
metrics: undefined,
status: "running",
step,
output: s.output.slice(0, chars),
startedAt: laneStart,
}
return {
...s,
status: fails ? "failed" : "done",
step: fails ? "18 of 20 runs passed" : "20 of 20 runs passed",
output: s.output,
error: fails,
// The retry passes every run, so its row in the comparison says so.
metrics: retry
? s.metrics?.map((m) =>
m.label === "Test runs" ? { ...m, value: "20 of 20", score: 20 } : m,
)
: s.metrics,
durationMs: (s.metrics?.[0]?.score ?? 0) * 1000,
}
})
const allDone = lanes.every((l) => l.status === "done" || l.status === "failed")
return (
<div className="flex w-full max-w-5xl flex-col gap-3">
<AgentLanes
lanes={lanes}
title="Fix the flaky checkout test"
winnerId={winner}
onPick={setWinner}
onUndoPick={() => setWinner(undefined)}
onRetry={(id) => setRetried((prev) => ({ ...prev, [id]: { tick: ticks, at: Date.now() } }))}
defaultCompare={false}
/>
<div className="flex min-h-8 items-center justify-between gap-3 px-1">
<p className="text-[0.8125rem]/[1.25rem] text-(color:--chatcn-fg-subtle)" aria-live="polite">
{winner
? `Applied attempt ${winner.toUpperCase()} to fix/checkout-flake. The other worktrees were removed.`
: allDone
? "Compare the attempts, then use one."
: "Three agents work in separate worktrees."}
</p>
{allDone ? (
<Button
size="sm"
onClick={() => {
setWinner(undefined)
setRetried({})
setTicks(0)
setStartedAt(Date.now())
setRun((r) => r + 1)
}}
>
<RotateCcwIcon />
Run again
</Button>
) : null}
</div>
</div>
)
}













