Add verifications catalog page and per-run verifications tab

Verifications catalog (/verifications) shows all verification criteria
organized by category with search, mode filter, and grouped/list toggle.
Each criterion has a unique icon, type badge, mode badge, and evaluation
sparkline. List view adds F1 accuracy, pass@1, and category columns.

Per-run verifications tab (/runs/:id/verifications) shows the same
criteria with pass/fail status for a specific run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-01 16:31:47 -05:00
parent c3d46c3885
commit 706413ec65
5 changed files with 757 additions and 0 deletions

View file

@ -0,0 +1,216 @@
export type VerificationStatus = "pass" | "fail" | "na";
export type VerificationType = "ai" | "automated" | "analysis" | "ai-analysis";
export interface Criterion {
name: string;
description: string;
type: VerificationType | null;
status: VerificationStatus;
}
export interface VerificationCategory {
name: string;
question: string;
status: VerificationStatus;
criteria: Criterion[];
}
export const statusConfig = {
pass: {
label: "Pass",
color: "text-mint",
bg: "bg-mint/15",
dot: "bg-mint",
border: "border-l-mint/50",
},
fail: {
label: "Fail",
color: "text-coral",
bg: "bg-coral/15",
dot: "bg-coral",
border: "border-l-coral/50",
},
na: {
label: "N/A",
color: "text-navy-600",
bg: "bg-white/[0.04]",
dot: "bg-navy-600",
border: "border-l-navy-600/50",
},
} as const satisfies Record<
VerificationStatus,
{ label: string; color: string; bg: string; dot: string; border: string }
>;
export const typeConfig = {
ai: { label: "AI", color: "text-teal-300", bg: "bg-teal-500/10" },
automated: { label: "Automated", color: "text-mint", bg: "bg-mint/10" },
analysis: { label: "Analysis", color: "text-amber", bg: "bg-amber/10" },
"ai-analysis": { label: "AI + Analysis", color: "text-teal-300", bg: "bg-teal-500/10" },
} as const satisfies Record<
VerificationType,
{ label: string; color: string; bg: string }
>;
export const verificationCategories: VerificationCategory[] = [
{
name: "Traceability",
question: "Do we understand what this change is and why we're making it?",
status: "pass",
criteria: [
{ name: "Motivation", description: "Origin of proposal identified", type: "ai", status: "pass" },
{ name: "Specifications", description: "Requirements written down", type: "ai", status: "pass" },
{ name: "Documentation", description: "Developer and user docs added", type: "ai", status: "pass" },
{ name: "Minimization", description: "No extraneous changes", type: "ai", status: "pass" },
],
},
{
name: "Readability",
question: "Can a human or agent quickly read this and understand what it does?",
status: "pass",
criteria: [
{ name: "Formatting", description: "Code layout matches standard", type: "automated", status: "pass" },
{ name: "Linting", description: "Linter issues resolved", type: "automated", status: "pass" },
{ name: "Style", description: "House style applied", type: "ai", status: "pass" },
],
},
{
name: "Reliability",
question: "Will this behave correctly and safely under real-world conditions and failures?",
status: "pass",
criteria: [
{ name: "Completeness", description: "Implementation covers requirements", type: "ai", status: "pass" },
{ name: "Defects", description: "Potential or likely bugs remediated", type: "ai-analysis", status: "pass" },
{ name: "Performance", description: "Hot path impact identified", type: "ai", status: "pass" },
],
},
{
name: "Code Coverage",
question: "Do we have trustworthy, automated evidence that it works and won't regress?",
status: "fail",
criteria: [
{ name: "Test Coverage", description: "Production code exercised by unit tests", type: "analysis", status: "pass" },
{ name: "Test Quality", description: "Tests are robust and clear", type: "ai", status: "fail" },
{ name: "E2E Coverage", description: "Browser automation exercises UX", type: "analysis", status: "na" },
],
},
{
name: "Maintainability",
question: "Will this be easy to modify or extend later without creating new risk?",
status: "pass",
criteria: [
{ name: "Architecture", description: "Layering and dependency graph meets design", type: "analysis", status: "pass" },
{ name: "Interfaces", description: "", type: null, status: "pass" },
{ name: "Duplication", description: "Similar and identical code blocks identified", type: "analysis", status: "pass" },
{ name: "Simplicity", description: "Extra review for reducing complexity", type: "ai", status: "pass" },
{ name: "Dead Code", description: "Unexecuted code and dependencies removed", type: "analysis", status: "pass" },
],
},
{
name: "Security",
question: "Does this preserve or improve our security posture and avoid vulnerabilities?",
status: "pass",
criteria: [
{ name: "Vulnerabilities", description: "Security issues are remediated", type: "ai-analysis", status: "pass" },
{ name: "IaC Scanning", description: "", type: null, status: "pass" },
{ name: "Dependency Alerts", description: "Known CVEs are patched", type: "analysis", status: "pass" },
{ name: "Security Controls", description: "Organization standards applied", type: "ai", status: "pass" },
],
},
{
name: "Deployability",
question: "Is this changeset safe to ship to production immediately?",
status: "fail",
criteria: [
{ name: "Compatibility", description: "Breaking changes are avoided", type: "analysis", status: "pass" },
{ name: "Rollout / Rollback", description: "Known rollback plan if deploy fails", type: "ai", status: "fail" },
{ name: "Observability", description: "Logging, metrics, tracing instrumented", type: "ai", status: "fail" },
{ name: "Cost", description: "Tech ops costs estimated", type: "analysis", status: "pass" },
],
},
{
name: "Compliance",
question: "Does this meet our regulatory, contractual, and policy obligations?",
status: "pass",
criteria: [
{ name: "Change Control", description: "Separation of Duties policy met", type: "analysis", status: "pass" },
{ name: "AI Governance", description: "AI involvement was acceptable", type: "analysis", status: "pass" },
{ name: "Privacy", description: "PII is identified and handled to standards", type: "ai", status: "pass" },
{ name: "Accessibility", description: "Software meets accessibility requirements", type: "analysis", status: "pass" },
{ name: "Licensing", description: "Supply chain meets IP policy", type: "analysis", status: "pass" },
],
},
];
export type EvaluationResult = "pass" | "fail" | "skip";
export type VerificationMode = "active" | "evaluate" | "disabled";
export interface CriterionPerformance {
f1: number | null;
passAt1: number | null;
mode: VerificationMode;
evaluations: EvaluationResult[];
}
export const modeConfig = {
active: { label: "Active", color: "text-mint", bg: "bg-mint/10" },
evaluate: { label: "Evaluate", color: "text-amber", bg: "bg-amber/10" },
disabled: { label: "Disabled", color: "text-navy-600", bg: "bg-white/[0.04]" },
} as const satisfies Record<
VerificationMode,
{ label: string; color: string; bg: string }
>;
export const criterionPerformance: Record<string, CriterionPerformance> = {
"Motivation": { f1: 0.87, passAt1: 0.82, mode: "active", evaluations: ["pass","pass","fail","pass","pass","pass","pass","fail","pass","pass"] },
"Specifications": { f1: 0.83, passAt1: 0.78, mode: "active", evaluations: ["pass","fail","pass","pass","pass","fail","pass","pass","pass","pass"] },
"Documentation": { f1: 0.79, passAt1: 0.74, mode: "active", evaluations: ["pass","pass","pass","fail","pass","pass","fail","pass","pass","fail"] },
"Minimization": { f1: 0.72, passAt1: 0.68, mode: "evaluate", evaluations: ["pass","fail","pass","fail","pass","pass","fail","pass","pass","pass"] },
"Formatting": { f1: 0.99, passAt1: 0.98, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","pass","pass","pass","pass"] },
"Linting": { f1: 0.98, passAt1: 0.97, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","pass","pass","fail","pass"] },
"Style": { f1: 0.81, passAt1: 0.76, mode: "active", evaluations: ["pass","fail","pass","pass","pass","pass","fail","pass","pass","pass"] },
"Completeness": { f1: 0.76, passAt1: 0.71, mode: "active", evaluations: ["pass","pass","fail","pass","fail","pass","pass","pass","fail","pass"] },
"Defects": { f1: 0.84, passAt1: 0.79, mode: "active", evaluations: ["pass","pass","pass","fail","pass","pass","pass","pass","pass","fail"] },
"Performance": { f1: 0.69, passAt1: 0.63, mode: "evaluate", evaluations: ["fail","pass","pass","fail","pass","fail","pass","pass","fail","pass"] },
"Test Coverage": { f1: 0.95, passAt1: 0.93, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","fail","pass","pass","pass"] },
"Test Quality": { f1: 0.71, passAt1: 0.65, mode: "evaluate", evaluations: ["pass","fail","fail","pass","pass","fail","pass","fail","pass","pass"] },
"E2E Coverage": { f1: 0.91, passAt1: 0.88, mode: "active", evaluations: ["pass","pass","pass","fail","pass","pass","pass","pass","pass","pass"] },
"Architecture": { f1: 0.88, passAt1: 0.84, mode: "active", evaluations: ["pass","pass","pass","pass","fail","pass","pass","pass","pass","pass"] },
"Interfaces": { f1: null, passAt1: null, mode: "disabled", evaluations: [] },
"Duplication": { f1: 0.96, passAt1: 0.94, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","pass","fail","pass","pass"] },
"Simplicity": { f1: 0.74, passAt1: 0.69, mode: "active", evaluations: ["pass","fail","pass","pass","fail","pass","pass","fail","pass","pass"] },
"Dead Code": { f1: 0.93, passAt1: 0.90, mode: "active", evaluations: ["pass","pass","pass","pass","pass","fail","pass","pass","pass","pass"] },
"Vulnerabilities": { f1: 0.86, passAt1: 0.81, mode: "active", evaluations: ["pass","pass","fail","pass","pass","pass","pass","pass","fail","pass"] },
"IaC Scanning": { f1: null, passAt1: null, mode: "disabled", evaluations: [] },
"Dependency Alerts": { f1: 0.97, passAt1: 0.95, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","pass","pass","pass","fail"] },
"Security Controls": { f1: 0.80, passAt1: 0.75, mode: "active", evaluations: ["pass","pass","fail","pass","pass","fail","pass","pass","pass","pass"] },
"Compatibility": { f1: 0.89, passAt1: 0.85, mode: "active", evaluations: ["pass","pass","pass","pass","fail","pass","pass","pass","pass","pass"] },
"Rollout / Rollback": { f1: 0.66, passAt1: 0.60, mode: "evaluate", evaluations: ["fail","pass","fail","pass","fail","pass","pass","fail","pass","fail"] },
"Observability": { f1: 0.73, passAt1: 0.67, mode: "evaluate", evaluations: ["pass","fail","pass","fail","pass","pass","fail","pass","fail","pass"] },
"Cost": { f1: 0.78, passAt1: 0.72, mode: "evaluate", evaluations: ["pass","pass","fail","pass","fail","pass","pass","fail","pass","pass"] },
"Change Control": { f1: 0.94, passAt1: 0.91, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","pass","pass","fail","pass"] },
"AI Governance": { f1: 0.85, passAt1: 0.80, mode: "active", evaluations: ["pass","pass","pass","fail","pass","pass","pass","pass","pass","pass"] },
"Privacy": { f1: 0.77, passAt1: 0.72, mode: "active", evaluations: ["pass","fail","pass","pass","pass","fail","pass","pass","pass","fail"] },
"Accessibility": { f1: 0.90, passAt1: 0.87, mode: "active", evaluations: ["pass","pass","pass","pass","pass","fail","pass","pass","pass","pass"] },
"Licensing": { f1: 0.96, passAt1: 0.93, mode: "active", evaluations: ["pass","pass","pass","pass","pass","pass","pass","pass","pass","pass"] },
};
export function getCategorySummary(categories: readonly VerificationCategory[]) {
const passing = categories.filter((c) => c.status === "pass").length;
return { passing, total: categories.length };
}
export function getCriteriaSummary(criteria: readonly Criterion[]) {
return {
passing: criteria.filter((c) => c.status === "pass").length,
failing: criteria.filter((c) => c.status === "fail").length,
na: criteria.filter((c) => c.status === "na").length,
total: criteria.length,
};
}
export function getAllCriteria(categories: readonly VerificationCategory[]) {
return categories.flatMap((c) => c.criteria);
}

View file

@ -23,6 +23,7 @@ export default [
route("configuration", "routes/run-configuration.tsx"),
route("graph", "routes/run-graph.tsx"),
route("files", "routes/run-files-changed.tsx"),
route("verifications", "routes/run-verifications.tsx"),
route("usage", "routes/run-usage.tsx"),
route("retro", "routes/run-retro.tsx"),
]),

View file

@ -8,6 +8,7 @@ const tabs = [
{ name: "Overview", path: "", count: null },
{ name: "Stages", path: "/stages/detect-drift", count: 4 },
{ name: "Files Changed", path: "/files", count: 3 },
{ name: "Verifications", path: "/verifications", count: null },
{ name: "Retro", path: "/retro", count: null },
{ name: "Usage", path: "/usage", count: null },
];

View file

@ -0,0 +1,128 @@
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@headlessui/react";
import {
CheckCircleIcon,
XCircleIcon,
MinusCircleIcon,
ChevronRightIcon,
} from "@heroicons/react/20/solid";
import {
verificationCategories,
statusConfig,
typeConfig,
getCriteriaSummary,
} from "../data/verifications";
import type {
VerificationStatus,
VerificationType,
VerificationCategory,
} from "../data/verifications";
function StatusIcon({
status,
className = "size-5",
}: {
status: VerificationStatus;
className?: string;
}) {
switch (status) {
case "pass":
return <CheckCircleIcon className={`${className} text-mint`} />;
case "fail":
return <XCircleIcon className={`${className} text-coral`} />;
case "na":
return <MinusCircleIcon className={`${className} text-navy-600`} />;
}
}
function TypeBadge({ type }: { type: VerificationType | null }) {
if (type === null) return null;
const config = typeConfig[type];
return (
<span
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
>
{config.label}
</span>
);
}
function CategoryCard({ category }: { category: VerificationCategory }) {
const criteriaStats = getCriteriaSummary(category.criteria);
const applicable = criteriaStats.total - criteriaStats.na;
const config = statusConfig[category.status];
return (
<Disclosure
as="div"
defaultOpen={category.status === "fail"}
className={`rounded-md border border-white/[0.06] overflow-hidden border-l-2 ${config.border}`}
>
<DisclosureButton className="group flex w-full items-center gap-3 px-4 py-3.5 text-left transition-colors hover:bg-white/[0.02]">
<StatusIcon status={category.status} />
<div className="min-w-0 flex-1">
<div className="flex items-baseline gap-3">
<span className="shrink-0 text-sm font-semibold text-white">
{category.name}
</span>
<span className="truncate text-xs text-navy-600">
{category.question}
</span>
</div>
</div>
<span className="shrink-0 font-mono text-xs tabular-nums text-navy-600">
{criteriaStats.passing}/{applicable}
</span>
<ChevronRightIcon className="size-4 shrink-0 text-navy-600 transition-transform duration-200 group-data-open:rotate-90" />
</DisclosureButton>
<DisclosurePanel
transition
className="origin-top transition duration-200 ease-out data-closed:-translate-y-1 data-closed:opacity-0"
>
<div className="border-t border-white/[0.06]">
<table className="w-full text-sm">
<tbody>
{category.criteria.map((criterion) => (
<tr
key={criterion.name}
className="border-b border-white/[0.04] last:border-b-0 transition-colors hover:bg-white/[0.015]"
>
<td className="w-8 py-2.5 pl-5 pr-0">
<span
className={`inline-block size-2 rounded-full ${statusConfig[criterion.status].dot}`}
/>
</td>
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-ice-100">
{criterion.name}
</td>
<td className="py-2.5 px-3 text-navy-600">
{criterion.description || (
<span className="italic">Not configured</span>
)}
</td>
<td className="whitespace-nowrap py-2.5 pl-3 pr-4 text-right">
<TypeBadge type={criterion.type} />
</td>
</tr>
))}
</tbody>
</table>
</div>
</DisclosurePanel>
</Disclosure>
);
}
export default function RunVerifications() {
return (
<div className="space-y-3">
{verificationCategories.map((category) => (
<CategoryCard key={category.name} category={category} />
))}
</div>
);
}

View file

@ -0,0 +1,411 @@
import { useState } from "react";
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@headlessui/react";
import {
ChevronRightIcon,
LightBulbIcon,
ClipboardDocumentListIcon,
BookOpenIcon,
FunnelIcon,
Bars3BottomLeftIcon,
WrenchIcon,
PaintBrushIcon,
CheckBadgeIcon,
BugAntIcon,
BoltIcon,
BeakerIcon,
StarIcon,
ComputerDesktopIcon,
CubeTransparentIcon,
ArrowsRightLeftIcon,
DocumentDuplicateIcon,
SparklesIcon,
ArchiveBoxXMarkIcon,
ShieldExclamationIcon,
ServerStackIcon,
ExclamationTriangleIcon,
LockClosedIcon,
PuzzlePieceIcon,
ArrowUturnLeftIcon,
EyeIcon,
CurrencyDollarIcon,
ClipboardDocumentCheckIcon,
CpuChipIcon,
FingerPrintIcon,
HandRaisedIcon,
ScaleIcon,
MapPinIcon,
DocumentTextIcon,
ShieldCheckIcon,
WrenchScrewdriverIcon,
KeyIcon,
RocketLaunchIcon,
BuildingLibraryIcon,
} from "@heroicons/react/20/solid";
import {
MagnifyingGlassIcon,
ChevronDownIcon,
} from "@heroicons/react/24/outline";
import {
verificationCategories,
typeConfig,
modeConfig,
criterionPerformance,
} from "../data/verifications";
import type {
VerificationType,
VerificationMode,
EvaluationResult,
VerificationCategory,
} from "../data/verifications";
import type { Route } from "./+types/verifications";
export const handle = { wide: true };
export function meta({}: Route.MetaArgs) {
return [{ title: "Verifications — Arc" }];
}
type IconComponent = React.ComponentType<{ className?: string }>;
function TrafficLightIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 20 20" fill="currentColor" className={className}>
<path
fillRule="evenodd"
d="M7 3a3 3 0 0 1 6 0v14a3 3 0 0 1-6 0V3Zm3 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
clipRule="evenodd"
/>
</svg>
);
}
const criterionIcons: Record<string, IconComponent> = {
"Motivation": LightBulbIcon,
"Specifications": ClipboardDocumentListIcon,
"Documentation": BookOpenIcon,
"Minimization": FunnelIcon,
"Formatting": Bars3BottomLeftIcon,
"Linting": WrenchIcon,
"Style": PaintBrushIcon,
"Completeness": CheckBadgeIcon,
"Defects": BugAntIcon,
"Performance": BoltIcon,
"Test Coverage": BeakerIcon,
"Test Quality": StarIcon,
"E2E Coverage": ComputerDesktopIcon,
"Architecture": CubeTransparentIcon,
"Interfaces": ArrowsRightLeftIcon,
"Duplication": DocumentDuplicateIcon,
"Simplicity": SparklesIcon,
"Dead Code": ArchiveBoxXMarkIcon,
"Vulnerabilities": ShieldExclamationIcon,
"IaC Scanning": ServerStackIcon,
"Dependency Alerts": ExclamationTriangleIcon,
"Security Controls": LockClosedIcon,
"Compatibility": PuzzlePieceIcon,
"Rollout / Rollback": ArrowUturnLeftIcon,
"Observability": EyeIcon,
"Cost": CurrencyDollarIcon,
"Change Control": ClipboardDocumentCheckIcon,
"AI Governance": CpuChipIcon,
"Privacy": FingerPrintIcon,
"Accessibility": HandRaisedIcon,
"Licensing": ScaleIcon,
};
const categoryIcons: Record<string, IconComponent> = {
"Traceability": MapPinIcon,
"Readability": DocumentTextIcon,
"Reliability": ShieldCheckIcon,
"Code Coverage": TrafficLightIcon,
"Maintainability": WrenchScrewdriverIcon,
"Security": KeyIcon,
"Deployability": RocketLaunchIcon,
"Compliance": BuildingLibraryIcon,
};
function TypeBadge({ type }: { type: VerificationType | null }) {
if (type === null) return null;
const config = typeConfig[type];
return (
<span
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
>
{config.label}
</span>
);
}
type ViewMode = "grouped" | "ungrouped";
function CategoryCard({ category }: { category: VerificationCategory }) {
return (
<Disclosure
as="div"
className="rounded-md border border-white/[0.06] overflow-hidden"
>
<DisclosureButton className="group flex w-full items-center gap-3 px-4 py-3.5 text-left transition-colors hover:bg-white/[0.02]">
{(() => {
const CatIcon = categoryIcons[category.name];
return CatIcon ? <CatIcon className="size-5 shrink-0 text-ice-300" /> : null;
})()}
<div className="min-w-0 flex-1">
<div className="flex items-baseline gap-3">
<span className="shrink-0 text-sm font-semibold text-white">
{category.name}
</span>
<span className="truncate text-xs text-navy-600">
{category.question}
</span>
</div>
</div>
<span className="shrink-0 text-xs text-navy-600">
<span className="font-mono tabular-nums">{category.criteria.length}</span> controls
</span>
<ChevronRightIcon className="size-4 shrink-0 text-navy-600 transition-transform duration-200 group-data-open:rotate-90" />
</DisclosureButton>
<DisclosurePanel
transition
className="origin-top transition duration-200 ease-out data-closed:-translate-y-1 data-closed:opacity-0"
>
<div className="border-t border-white/[0.06]">
<table className="w-full text-sm">
<tbody>
{category.criteria.map((criterion) => {
const Icon = criterionIcons[criterion.name];
const perf = criterionPerformance[criterion.name];
return (
<tr
key={criterion.name}
className="border-b border-white/[0.04] last:border-b-0 transition-colors hover:bg-white/[0.015]"
>
<td className="w-8 py-2.5 pl-5 pr-0">
{Icon && <Icon className="size-4 text-ice-300" />}
</td>
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-ice-100">
{criterion.name}
</td>
<td className="py-2.5 px-3 text-navy-600">
{criterion.description || (
<span className="italic">Not configured</span>
)}
</td>
<td className="whitespace-nowrap py-2.5 pl-3 pr-1 text-right">
<TypeBadge type={criterion.type} />
</td>
<td className="whitespace-nowrap py-2.5 px-1">
{perf && <ModeBadge mode={perf.mode} />}
</td>
<td className="whitespace-nowrap py-2.5 pl-1 pr-4">
{perf && <EvaluationDots evaluations={perf.evaluations} />}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</DisclosurePanel>
</Disclosure>
);
}
function GroupedView({ categories }: { categories: readonly VerificationCategory[] }) {
return (
<div className="space-y-3">
{categories.map((category) => (
<CategoryCard key={category.name} category={category} />
))}
</div>
);
}
function ModeBadge({ mode }: { mode: VerificationMode }) {
const config = modeConfig[mode];
return (
<span
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${config.color} ${config.bg}`}
>
{config.label}
</span>
);
}
function EvaluationDots({ evaluations }: { evaluations: readonly EvaluationResult[] }) {
if (evaluations.length === 0) {
return <span className="text-xs italic text-navy-600"></span>;
}
return (
<div className="flex items-center gap-0.5">
{evaluations.map((result, i) => (
<span
key={i}
className={`inline-block size-2.5 rounded-sm ${
result === "pass"
? "bg-mint/70"
: result === "fail"
? "bg-coral/70"
: "bg-navy-600/50"
}`}
/>
))}
</div>
);
}
function UngroupedView({ categories }: { categories: readonly VerificationCategory[] }) {
return (
<div className="rounded-md border border-white/[0.06] overflow-hidden">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-white/[0.06] bg-navy-800/60 text-left text-xs text-navy-600">
<th className="w-8 py-2.5 pl-4 pr-0 font-medium" />
<th className="py-2.5 pl-2 pr-3 font-medium">Verification</th>
<th className="py-2.5 px-3 font-medium">Description</th>
<th className="py-2.5 px-3 font-medium">Category</th>
<th className="py-2.5 px-3 font-medium text-right">Type</th>
<th className="py-2.5 px-3 font-medium text-right">Accuracy (F1)</th>
<th className="py-2.5 px-3 font-medium text-right">pass@1</th>
<th className="py-2.5 px-3 font-medium">Mode</th>
<th className="py-2.5 pl-3 pr-4 font-medium">Evaluations</th>
</tr>
</thead>
<tbody>
{categories.flatMap((category) =>
category.criteria.map((criterion) => {
const Icon = criterionIcons[criterion.name];
const perf = criterionPerformance[criterion.name];
return (
<tr
key={`${category.name}-${criterion.name}`}
className="border-b border-white/[0.04] last:border-b-0 transition-colors hover:bg-white/[0.015]"
>
<td className="w-8 py-2.5 pl-4 pr-0">
{Icon && <Icon className="size-4 text-ice-300" />}
</td>
<td className="whitespace-nowrap py-2.5 pl-2 pr-3 font-medium text-ice-100">
{criterion.name}
</td>
<td className="py-2.5 px-3 text-navy-600">
{criterion.description || (
<span className="italic">Not configured</span>
)}
</td>
<td className="whitespace-nowrap py-2.5 px-3 text-xs text-navy-600">
{category.name}
</td>
<td className="whitespace-nowrap py-2.5 px-3 text-right">
<TypeBadge type={criterion.type} />
</td>
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-ice-100">
{perf?.f1 != null ? perf.f1.toFixed(2) : <span className="text-navy-600"></span>}
</td>
<td className="whitespace-nowrap py-2.5 px-3 text-right font-mono text-xs tabular-nums text-ice-100">
{perf?.passAt1 != null ? perf.passAt1.toFixed(2) : <span className="text-navy-600"></span>}
</td>
<td className="whitespace-nowrap py-2.5 px-3">
{perf && <ModeBadge mode={perf.mode} />}
</td>
<td className="whitespace-nowrap py-2.5 pl-3 pr-4">
{perf && <EvaluationDots evaluations={perf.evaluations} />}
</td>
</tr>
);
}),
)}
</tbody>
</table>
</div>
);
}
function filterCategories(
categories: readonly VerificationCategory[],
query: string,
modeFilter: VerificationMode | "all",
): VerificationCategory[] {
const lowerQuery = query.toLowerCase();
return categories
.map((category) => {
const filtered = category.criteria.filter((c) => {
const perf = criterionPerformance[c.name];
const matchesMode = modeFilter === "all" || perf?.mode === modeFilter;
const matchesQuery =
lowerQuery === "" ||
c.name.toLowerCase().includes(lowerQuery) ||
c.description.toLowerCase().includes(lowerQuery) ||
category.name.toLowerCase().includes(lowerQuery);
return matchesMode && matchesQuery;
});
return { ...category, criteria: filtered };
})
.filter((category) => category.criteria.length > 0);
}
export default function Verifications() {
const [view, setView] = useState<ViewMode>("grouped");
const [query, setQuery] = useState("");
const [modeFilter, setModeFilter] = useState<VerificationMode | "all">("all");
const filtered = filterCategories(verificationCategories, query, modeFilter);
return (
<div className="space-y-4">
{/* Toolbar */}
<div className="flex gap-3">
<div className="relative flex-1">
<MagnifyingGlassIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-navy-600" />
<input
type="text"
placeholder="Search verifications…"
value={query}
onChange={(e) => setQuery(e.target.value)}
className="w-full rounded-md border border-white/[0.06] bg-navy-800/80 py-2 pl-9 pr-3 text-sm text-ice-100 placeholder-navy-600 outline-none transition-colors focus:border-teal-500/40 focus:ring-0"
/>
</div>
<div className="relative">
<select
value={modeFilter}
onChange={(e) => setModeFilter(e.target.value as VerificationMode | "all")}
className="appearance-none rounded-md border border-white/[0.06] bg-navy-800/80 py-2 pl-3 pr-8 text-sm text-ice-100 outline-none transition-colors focus:border-teal-500/40 focus:ring-0"
>
<option value="all">All modes</option>
<option value="active">Active</option>
<option value="evaluate">Evaluate</option>
<option value="disabled">Disabled</option>
</select>
<ChevronDownIcon className="pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-navy-600" />
</div>
<div className="flex items-center gap-1 rounded-md border border-white/[0.06] bg-navy-800/80 p-0.5">
<button
type="button"
onClick={() => setView("grouped")}
className={`inline-flex items-center gap-1.5 rounded px-2.5 py-1 text-xs font-medium transition-colors ${view === "grouped" ? "bg-white/[0.06] text-teal-500" : "text-navy-600 hover:text-ice-300"}`}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="size-3.5" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 0 1-1.125-1.125v-3.75ZM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-8.25ZM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-1.5Z" />
</svg>
Grouped
</button>
<button
type="button"
onClick={() => setView("ungrouped")}
className={`inline-flex items-center gap-1.5 rounded px-2.5 py-1 text-xs font-medium transition-colors ${view === "ungrouped" ? "bg-white/[0.06] text-teal-500" : "text-navy-600 hover:text-ice-300"}`}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="size-3.5" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" />
</svg>
List
</button>
</div>
</div>
{view === "grouped" ? <GroupedView categories={filtered} /> : <UngroupedView categories={filtered} />}
</div>
);
}