import * as React from 'react'; import { CheckCircle2, AlertTriangle, XCircle, ChevronDown, ChevronRight, ShieldCheck, ShieldAlert, } from 'lucide-react'; import { cn } from '../../lib/utils'; import { overallStatus, type CheckStatus, type ContentCheck } from './checks'; /** Redundant (color + icon + label) tone per check status — never color alone. */ const CHECK_TONE: Record = { pass: { label: 'Passing', dot: 'bg-emerald-500', text: 'text-emerald-700', softBg: 'bg-emerald-50', Icon: CheckCircle2 }, warn: { label: 'Warnings', dot: 'bg-amber-500', text: 'text-amber-700', softBg: 'bg-amber-50', Icon: AlertTriangle }, fail: { label: 'Failing', dot: 'bg-red-500', text: 'text-red-700', softBg: 'bg-red-50', Icon: XCircle }, }; /** One issue the AI judge raised against a guardrail. */ export interface JudgeIssue { guardrail?: string; severity?: string; problem?: string; fix?: string; } /** The AI judge's output stored alongside the content record. */ export interface JudgeVerdict { /** Overall verdict word (e.g. "approve", "revise", "reject"). */ verdict?: string; /** Per-dimension scores, keyed by dimension name. */ scores?: Record; /** Specific problems the judge flagged. */ issues?: JudgeIssue[]; /** One-line prose summary. */ summary?: string; } /** Reviewer's manual override of the automated checks. */ export interface ValidationOverride { overridden: boolean; reason?: string; } export interface ValidationChecklistProps { /** The (recomputed) deterministic checks to render. */ checks: ContentCheck[]; /** The AI judge output — renders a verdict badge, scores, and issues. */ judgeVerdict?: JudgeVerdict; /** Current override state. When set, the overall pill reflects it. */ override?: ValidationOverride; /** Provide to make the override affordance interactive. Omit → read-only. */ onOverride?: (next: ValidationOverride) => void; title?: string; className?: string; } /** A tone pill naming the roll-up state. */ function StatusPill({ status, label }: { status: CheckStatus; label?: string }) { const tone = CHECK_TONE[status]; const Icon = tone.Icon; return ( ); } /** Colour a judge verdict word without needing to know the exact vocabulary. */ function verdictStatus(verdict?: string): CheckStatus { const v = (verdict ?? '').toLowerCase(); if (/pass|approve|accept|ok|good/.test(v)) return 'pass'; if (/fail|reject|block|deny|bad/.test(v)) return 'fail'; return 'warn'; } /** * Presentational, bring-your-own-data validation panel (768w.16.10.1). * * Surfaces the buried machine review of an AI-generated content record as a * scannable pass/warn/fail checklist plus the AI judge's verdict — so a Foundry * tenant can accept/reject in one glance. Stateless except for local UI toggles * (expand issues, edit override reason); the app owns the data and persistence. */ export function ValidationChecklist({ checks, judgeVerdict, override, onOverride, title = 'Validation', className, }: ValidationChecklistProps) { const overall = overallStatus(checks); const [issuesOpen, setIssuesOpen] = React.useState(false); const [overrideOpen, setOverrideOpen] = React.useState(false); const [reason, setReason] = React.useState(override?.reason ?? ''); const counts = React.useMemo( () => ({ pass: checks.filter((c) => c.status === 'pass').length, warn: checks.filter((c) => c.status === 'warn').length, fail: checks.filter((c) => c.status === 'fail').length, }), [checks], ); const issues = judgeVerdict?.issues ?? []; const scoreEntries = judgeVerdict?.scores ? Object.entries(judgeVerdict.scores) : []; return (
{/* Header: title + overall pill */}
{override?.overridden && ( )}
{/* Checks list */} {checks.length === 0 ? (

No checks to show.

) : (
    {checks.map((check) => { const tone = CHECK_TONE[check.status]; const Icon = tone.Icon; return (
  • ); })}
)} {/* AI judge section */} {judgeVerdict && (
AI judge {judgeVerdict.verdict && }
{judgeVerdict.summary &&

{judgeVerdict.summary}

} {scoreEntries.length > 0 && (
{scoreEntries.map(([dim, val]) => (
{dim}
{String(val)}
))}
)} {issues.length > 0 && (
{issuesOpen && (
    {issues.map((issue, i) => (
  • {issue.guardrail && {issue.guardrail}} {issue.severity && ( {issue.severity} )}
    {issue.problem &&

    {issue.problem}

    } {issue.fix &&

    Fix: {issue.fix}

    }
  • ))}
)}
)}
)} {/* Override affordance — interactive only when onOverride is provided */} {onOverride && (
{override?.overridden ? (

Checks overridden

{override.reason &&

{override.reason}

}
) : overrideOpen ? (