@@ -0,0 +1,62 @@
import { useQuery } from '@tanstack/react-query' ;
import { useMemo , useState } from 'react' ;
import { api , type EvidencePackDetail , type EvidencePackSummary } from '../lib/api' ;
import { Card , StatusBadge } from '../components/ui/Card' ;
const HARNESSES = [
[ 'H1' , 'Context' , 'h1-context-report.json' ] , [ 'H2' , 'Tool' , 'h2-tool-audit.json' ] ,
[ 'H3' , 'Evaluation' , 'h3-eval-scorecard.json' ] , [ 'H4' , 'Security' , 'h4-security-report.json' ] ,
[ 'H5' , 'Governance' , 'h5-audit-chain-proof.json' ] , [ 'H6' , 'AgentOps' , 'h6-cost-telemetry.json' ] ,
[ 'H7' , 'Orchestration' , 'h7-orchestration-report.json' ] ,
] as const ;
function shortHash ( value : string | null ) { return value ? ` ${ value . slice ( 0 , 12 ) } … ${ value . slice ( - 8 ) } ` : 'not recorded' ; }
function label ( reason : string ) { return reason . replaceAll ( '_' , ' ' ) ; }
function statusFor ( report : Record < string , unknown > | undefined , detail : EvidencePackDetail ) {
if ( ! report ) return 'not recorded' ;
if ( report . chain_ok === false || report . audit_chain_ok === false || report . telemetry_ok === false ) return 'failed' ;
if ( Number ( report . blocked ? ? 0 ) > 0 || report . cost_spike_status === 'spike_detected' ) return 'attention' ;
return detail . integrity . ok ? 'recorded' : 'unverified' ;
}
function PackRow ( { pack , selected , onSelect } : { pack : EvidencePackSummary ; selected : boolean ; onSelect : ( ) = > void } ) {
return < button type = "button" onClick = { onSelect } className = { ` w-full rounded-xl border p-3.5 text-left transition ${ selected ? 'border-indigo-300 bg-indigo-50/70 shadow-sm' : 'border-transparent hover:border-slate-200 hover:bg-slate-50' } ` } >
< div className = "flex items-start justify-between gap-2" > < span className = "font-mono text-xs font-semibold text-slate-700" > { pack . run_id } < / span > < StatusBadge value = { pack . certified ? 'certified' : 'uncertified' } / > < / div >
< div className = "mt-2 flex items-center justify-between text-[11px] text-slate-500" > < span > { new Date ( pack . created_at ) . toLocaleString ( ) } < / span > < span > { pack . file_count } files < / span > < / div >
< / button > ;
}
function Detail ( { pack } : { pack : EvidencePackDetail } ) {
const reportEntries = Object . entries ( pack . reports ) ;
return < div className = "space-y-5" >
< section className = { ` overflow-hidden rounded-2xl border ${ pack . certified ? 'border-emerald-200 bg-gradient-to-br from-emerald-50 to-white' : 'border-amber-200 bg-gradient-to-br from-amber-50 to-white' } p-5 sm:p-6 ` } >
< div className = "flex flex-col justify-between gap-5 sm:flex-row sm:items-start" >
< div > < div className = "text-[11px] font-bold uppercase tracking-[0.16em] text-slate-500" > Evidence verdict < / div > < h1 className = "mt-2 text-2xl font-semibold tracking-tight text-slate-900" > { pack . certified ? 'Certified evidence pack' : 'Evidence captured — certification withheld' } < / h1 > < p className = "mt-2 max-w-2xl text-sm leading-6 text-slate-600" > Run < span className = "font-mono text-xs" > { pack . run_id } < / span > is bound to a file manifest and verified independently before it is shown here . < / p > < / div >
< div className = "flex flex-wrap gap-2" > < StatusBadge value = { pack . integrity . ok ? 'manifest verified' : 'integrity failed' } / > < StatusBadge value = { pack . signature === 'present' ? 'signature present' : 'unsigned' } / > < / div >
< / div >
{ pack . certification_reasons . length > 0 && < div className = "mt-5 rounded-xl border border-amber-200 bg-white/80 p-3.5" > < div className = "text-[11px] font-bold uppercase tracking-[0.13em] text-amber-700" > Why certification is withheld < / div > < div className = "mt-2 flex flex-wrap gap-2" > { pack . certification_reasons . map ( ( reason ) = > < span key = { reason } className = "rounded-full bg-amber-100 px-2.5 py-1 text-xs font-medium text-amber-800" > { label ( reason ) } < / span > ) } < / div > < / div > }
< / section >
< Card title = "H1– H7 chain of custody" right = { < span className = "font-mono text-[11px] text-slate-400" > manifest { shortHash ( pack . manifest_head ) } < / span > } >
< div className = "grid gap-2 sm:grid-cols-2 xl:grid-cols-7" > { HARNESSES . map ( ( [ id , name , file ] ) = > { const report = pack . reports [ file ] ; const state = statusFor ( report , pack ) ; return < div key = { id } className = "relative rounded-xl border border-slate-200 bg-slate-50/70 p-3" > < div className = "flex items-center justify-between" > < span className = "font-mono text-xs font-bold text-indigo-700" > { id } < / span > < span className = { ` h-2 w-2 rounded-full ${ state === 'recorded' ? 'bg-emerald-500' : state === 'failed' ? 'bg-rose-500' : state === 'attention' ? 'bg-amber-500' : 'bg-slate-300' } ` } / > < / div > < div className = "mt-2 text-sm font-semibold text-slate-800" > { name } < / div > < div className = "mt-1 text-[11px] text-slate-500" > { state } < / div > < / div > ; } ) } < / div >
< / Card >
< div className = "grid gap-5 xl:grid-cols-[1.1fr_0.9fr]" >
< Card title = "Gate reports" > < div className = "space-y-3" > { reportEntries . map ( ( [ name , report ] ) = > < details key = { name } className = "rounded-xl border border-slate-200 bg-slate-50/60 p-3.5" > < summary className = "cursor-pointer list-none text-sm font-semibold text-slate-700" > < span className = "font-mono text-xs text-indigo-700" > { name . replace ( '-report.json' , '' ) . replace ( '.json' , '' ) } < / span > < / summary > < dl className = "mt-3 grid grid-cols-2 gap-x-4 gap-y-2 border-t border-slate-200 pt-3 text-xs" > { Object . entries ( report ) . filter ( ( [ , value ] ) = > typeof value !== 'object' ) . map ( ( [ key , value ] ) = > < div key = { key } > < dt className = "text-slate-400" > { key . replaceAll ( '_' , ' ' ) } < / dt > < dd className = "mt-0.5 break-words font-medium text-slate-700" > { String ( value ) } < / dd > < / div > ) } < / dl > < / details > ) } < / div > < / Card >
< Card title = "Evidence manifest" right = { < span className = "text-xs text-slate-400" > { pack . files . length } bound files < / span > } > < div className = "max-h-[430px] space-y-2 overflow-y-auto pr-1" > { pack . files . map ( ( file ) = > < div key = { file . path } className = "rounded-xl border border-slate-100 bg-slate-50/70 p-3" > < div className = "break-all font-mono text-xs font-medium text-slate-700" > { file . path } < / div > < div className = "mt-1.5 flex justify-between gap-3 font-mono text-[10px] text-slate-400" > < span > { shortHash ( file . sha256 ) } < / span > < span > { file . bytes . toLocaleString ( ) } B < / span > < / div > < / div > ) } < / div > < / Card >
< / div >
< Card title = "Decision log" > < pre className = "max-h-80 overflow-auto whitespace-pre-wrap rounded-xl bg-slate-950 p-4 font-mono text-xs leading-5 text-slate-200" > { pack . decision_log ? ? 'No decision log was recorded.' } < / pre > < / Card >
< / div > ;
}
export function EvidencePacks() {
const { data , isLoading , isError } = useQuery ( { queryKey : [ 'evidence-packs' ] , queryFn : api.evidencePacks } ) ;
const [ selected , setSelected ] = useState ( '' ) ;
const activeId = selected || data ? . packs [ 0 ] ? . run_id || '' ;
const detail = useQuery ( { queryKey : [ 'evidence-pack' , activeId ] , queryFn : ( ) = > api . evidencePack ( activeId ) , enabled : Boolean ( activeId ) } ) ;
const heading = useMemo ( ( ) = > data ? . packs . length ? ? 0 , [ data ] ) ;
if ( isLoading ) return < div className = "text-slate-500" > Loading evidence inventory … < / div > ;
if ( isError || ! data ) return < div className = "rounded-2xl border border-rose-200 bg-rose-50 p-5 text-rose-700" > Evidence inventory could not be loaded . < / div > ;
if ( ! data . packs . length ) return < Card title = "Evidence packs" > < div className = "py-12 text-center" > < div className = "text-lg font-semibold text-slate-800" > No evidence pack has been created yet < / div > < p className = "mx-auto mt-2 max-w-lg text-sm leading-6 text-slate-500" > Create one with < code className = "rounded bg-slate-100 px-1.5 py-0.5 font-mono text-xs" > casan pack & lt ; run - id & gt ; < / code > ; it will appear here only after the on - disk manifest is available . < / p > < / div > < / Card > ;
return < div className = "space-y-5" > < div > < div className = "text-[11px] font-bold uppercase tracking-[0.16em] text-indigo-600" > Assurance workspace < / div > < h1 className = "mt-1 text-2xl font-semibold tracking-tight text-slate-900" > Evidence packs < span className = "text-slate-400" > ( { heading } ) < / span > < / h1 > < p className = "mt-1 text-sm text-slate-500" > Inspect certification , provenance , gates and immutable file hashes without opening raw logs . < / p > < / div > < div className = "grid gap-5 xl:grid-cols-[288px_minmax(0,1fr)]" > < aside className = "rounded-2xl border border-slate-200 bg-white p-2 shadow-sm" > < div className = "px-3 py-2 text-[11px] font-bold uppercase tracking-[0.13em] text-slate-400" > Available packs < / div > < div className = "space-y-1.5" > { data . packs . map ( ( pack ) = > < PackRow key = { pack . run_id } pack = { pack } selected = { pack . run_id === activeId } onSelect = { ( ) = > setSelected ( pack . run_id ) } / > ) } < / div > < / aside > < main > { detail . isLoading && < div className = "text-slate-500" > Verifying manifest … < / div > } { detail . isError && < div className = "rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-700" > The selected evidence pack could not be read safely . < / div > } { detail . data && < Detail pack = { detail . data } / > } < / main > < / div > < / div > ;
}