4203 lines
No EOL
265 KiB
TypeScript
Executable file
4203 lines
No EOL
265 KiB
TypeScript
Executable file
return (
|
|
<PlatformAuthProvider>
|
|
<SubscriptionGate requiredPlan="student" featureName="Operator Runboard" fallbackMode="overlay">
|
|
<div className="h-full overflow-auto bg-gray-950 text-gray-100" data-testid="runboard-root">
|
|
<div className="max-w-7xl mx-auto px-6 py-6 space-y-6" data-testid="runboard-surface">
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold text-gray-100">Runboard</h1>
|
|
<p className="text-sm text-gray-400">
|
|
Queue-to-artifact visibility and failure drill-down linked to corpus run endpoints.
|
|
</p>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
Shortcuts: <span className="font-mono">R</span> refresh, <span className="font-mono">J/K</span> next/previous run, <span className="font-mono">Shift+R</span> retry failed, <span className="font-mono">Shift+C</span> cancel active run, <span className="font-mono">D</span> refresh route details, <span className="font-mono">Z</span> refresh gate verify suite, <span className="font-mono">Shift+Z</span> refresh advanced verify suite, <span className="font-mono">V</span> execute gate verify suite, <span className="font-mono">W</span> execute gate smoke subset, <span className="font-mono">Shift+V</span> execute gate verify-suite stability, <span className="font-mono">N</span> load last gate verify suite, <span className="font-mono">Shift+N</span> load last gate stability packet, <span className="font-mono">X</span> execute advanced verify suite, <span className="font-mono">Shift+W</span> execute advanced smoke subset, <span className="font-mono">Shift+X</span> execute advanced stability packet, <span className="font-mono">Q</span> load last advanced verify suite, <span className="font-mono">Shift+Q</span> load last advanced stability packet, <span className="font-mono">1/Shift+1</span> gate JSON/live last, <span className="font-mono">2/Shift+2</span> gate stability JSON/live last, <span className="font-mono">3/Shift+3</span> advanced JSON/live last, <span className="font-mono">4/Shift+4</span> advanced stability JSON/live last, <span className="font-mono">T</span> copy graph diff triage, <span className="font-mono">Shift+T</span> copy route doc context, <span className="font-mono">M</span> copy mapped issue payload, <span className="font-mono">P</span> open primary doc, <span className="font-mono">S</span> open secondary doc, <span className="font-mono">U</span> open mapped doc, <span className="font-mono">Shift+U</span> open route doc, <span className="font-mono">L</span> copy route replay link, <span className="font-mono">Shift+L</span> open route replay link, <span className="font-mono">Y</span> copy run ID, <span className="font-mono">Shift+Y</span> copy remediation pack, <span className="font-mono">E</span> open compare route in editor, <span className="font-mono">A</span> apply compare route filters, <span className="font-mono">F</span> apply compare route focus, <span className="font-mono">B</span> run action, <span className="font-mono">Shift+B</span> artifact action, <span className="font-mono">Shift+F</span> reset compare route filters, <span className="font-mono">Shift+P</span> clear project filter, <span className="font-mono">Shift+S</span> clear status filter, <span className="font-mono">Shift+K</span> clear kind filter, <span className="font-mono">G</span> select compare route run, <span className="font-mono">H</span> select compare route artifact, <span className="font-mono">I</span> open integrity report, <span className="font-mono">Shift+I</span> refresh integrity summary, <span className="font-mono">O</span> open audit export.
|
|
</p>
|
|
<p className="text-[11px] text-gray-500 mt-1">
|
|
Recovery shortcuts: <span className="font-mono">5/Shift+5/6</span> gate stale refresh/execute/load-last, <span className="font-mono">7/Shift+7/8</span> advanced stale refresh/execute/load-last.
|
|
</p>
|
|
{(gateVerifyCueAgeLabel || advancedVerifyCueAgeLabel) && (
|
|
<p
|
|
className={`text-[11px] mt-1 ${hasStaleVerifyCue ? 'text-red-300' : 'text-gray-500'}`}
|
|
data-testid="runboard-verify-cue-freshness-hint"
|
|
>
|
|
Verify cue freshness:
|
|
{gateVerifyCueAgeLabel && (
|
|
<span>
|
|
{' '}gate <span className="font-mono text-gray-300">{gateVerifyCueAgeLabel}</span>
|
|
{' '}(<span className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueStalenessChipToneClass(gateVerifyCueStaleness || 'fresh')}`}>
|
|
{gateVerifyCueStatusLabel || 'Fresh'}
|
|
</span>)
|
|
</span>
|
|
)}
|
|
{advancedVerifyCueAgeLabel && (
|
|
<span>
|
|
{gateVerifyCueAgeLabel ? ' ·' : ''} advanced <span className="font-mono text-gray-300">{advancedVerifyCueAgeLabel}</span>
|
|
{' '}(<span className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueStalenessChipToneClass(advancedVerifyCueStaleness || 'fresh')}`}>
|
|
{advancedVerifyCueStatusLabel || 'Fresh'}
|
|
</span>)
|
|
</span>
|
|
)}
|
|
</p>
|
|
)}
|
|
{videoStudioHandoff.note && (
|
|
<div
|
|
className="mt-3 rounded border border-[#A6926E]/35 bg-[#C4A96B]/10 px-3 py-2 text-xs text-[#F2E4BF] flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-video-studio-handoff-banner"
|
|
>
|
|
<span>{videoStudioHandoff.note}</span>
|
|
<a
|
|
href={videoStudioHandoff.videoStudioHref}
|
|
className="rounded border border-[#A6926E]/40 bg-black/10 px-2 py-1 text-[11px] text-[#F6E7C0] hover:bg-black/20"
|
|
>
|
|
Back to Video Studio
|
|
</a>
|
|
{videoStudioHandoff.editorHref && (
|
|
<a
|
|
href={videoStudioHandoff.editorHref}
|
|
className="rounded border border-[#A6926E]/40 bg-black/10 px-2 py-1 text-[11px] text-[#F6E7C0] hover:bg-black/20"
|
|
>
|
|
Open Editor Workspace
|
|
</a>
|
|
)}
|
|
</div>
|
|
)}
|
|
{diagramStudioHandoff.note && (
|
|
<div
|
|
className="mt-3 rounded border border-cyan-500/25 bg-cyan-500/10 px-3 py-2 text-xs text-cyan-100 flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-diagram-studio-handoff-banner"
|
|
>
|
|
<span>{diagramStudioHandoff.note}</span>
|
|
<a
|
|
href={diagramStudioHandoff.diagramStudioHref}
|
|
className="rounded border border-cyan-400/30 bg-black/10 px-2 py-1 text-[11px] text-cyan-100 hover:bg-black/20"
|
|
>
|
|
Back to AI Diagram Studio
|
|
</a>
|
|
{diagramStudioHandoff.sceneReviewHref && (
|
|
<a
|
|
href={diagramStudioHandoff.sceneReviewHref}
|
|
className="rounded border border-cyan-400/30 bg-black/10 px-2 py-1 text-[11px] text-cyan-100 hover:bg-black/20"
|
|
>
|
|
Open Scene Review
|
|
</a>
|
|
)}
|
|
</div>
|
|
)}
|
|
{mindMapHandoff.note && (
|
|
<div
|
|
className="mt-3 rounded border border-emerald-500/25 bg-emerald-500/10 px-3 py-2 text-xs text-emerald-100 flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-mindmap-handoff-banner"
|
|
>
|
|
<span>{mindMapHandoff.note}</span>
|
|
<a
|
|
href={mindMapHandoff.mindMapHref}
|
|
className="rounded border border-emerald-400/30 bg-black/10 px-2 py-1 text-[11px] text-emerald-100 hover:bg-black/20"
|
|
>
|
|
Back to MindMap
|
|
</a>
|
|
</div>
|
|
)}
|
|
{hasVerifyCueRecoveryTelemetry && (
|
|
<div className="mt-1 flex flex-wrap items-center gap-2" data-testid="runboard-verify-cue-recovery-telemetry-container">
|
|
<p className="text-[11px] text-gray-500" data-testid="runboard-verify-cue-recovery-telemetry">
|
|
Recovery telemetry (session):
|
|
{' '}gate total <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-total">{gateVerifyCueRecoveryTelemetry.total}</span>
|
|
{' '}· guide <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-guidance">{gateVerifyCueRecoveryTelemetry.bySource['cue-guidance']}</span>
|
|
{' '}· palette <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-palette">{gateVerifyCueRecoveryTelemetry.bySource['command-palette']}</span>
|
|
{' '}· shortcut <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-shortcut">{gateVerifyCueRecoveryTelemetry.bySource['runboard-shortcut']}</span>
|
|
{' '}· mode r/e/l <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-mode-refresh">{gateVerifyCueRecoveryTelemetry.byMode.refresh}</span>/<span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-mode-execute">{gateVerifyCueRecoveryTelemetry.byMode.execute}</span>/<span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-mode-load-last">{gateVerifyCueRecoveryTelemetry.byMode['load-last']}</span>
|
|
{' '}· gate src/mode{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueTelemetryChipToneClass(gateVerifyCueRecoveryTelemetryStaleness)}`}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-gate-last-source-chip"
|
|
>
|
|
<span data-testid="runboard-verify-cue-recovery-telemetry-gate-last-source">{gateVerifyCueRecoveryTelemetry.lastSource || 'n/a'}</span>
|
|
</span>
|
|
/<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueTelemetryChipToneClass(gateVerifyCueRecoveryTelemetryStaleness)}`}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-gate-last-mode-chip"
|
|
>
|
|
<span data-testid="runboard-verify-cue-recovery-telemetry-gate-last-mode">{gateVerifyCueRecoveryTelemetry.lastMode || 'n/a'}</span>
|
|
</span>
|
|
{' '}· tone{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueTelemetryChipToneClass(gateVerifyCueRecoveryTelemetryStaleness)}`}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-gate-status-chip"
|
|
>
|
|
<span data-testid="runboard-verify-cue-recovery-telemetry-gate-status">{gateVerifyCueRecoveryTelemetryStatusLabel}</span>
|
|
</span>
|
|
{' '}· at <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-gate-last-at">{gateVerifyCueRecoveryLastAtLabel}</span>
|
|
{' '}· advanced total <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-total">{advancedVerifyCueRecoveryTelemetry.total}</span>
|
|
{' '}· guide <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-guidance">{advancedVerifyCueRecoveryTelemetry.bySource['cue-guidance']}</span>
|
|
{' '}· palette <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-palette">{advancedVerifyCueRecoveryTelemetry.bySource['command-palette']}</span>
|
|
{' '}· shortcut <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-shortcut">{advancedVerifyCueRecoveryTelemetry.bySource['runboard-shortcut']}</span>
|
|
{' '}· mode r/e/l <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-mode-refresh">{advancedVerifyCueRecoveryTelemetry.byMode.refresh}</span>/<span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-mode-execute">{advancedVerifyCueRecoveryTelemetry.byMode.execute}</span>/<span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-mode-load-last">{advancedVerifyCueRecoveryTelemetry.byMode['load-last']}</span>
|
|
{' '}· advanced src/mode{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueTelemetryChipToneClass(advancedVerifyCueRecoveryTelemetryStaleness)}`}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-advanced-last-source-chip"
|
|
>
|
|
<span data-testid="runboard-verify-cue-recovery-telemetry-advanced-last-source">{advancedVerifyCueRecoveryTelemetry.lastSource || 'n/a'}</span>
|
|
</span>
|
|
/<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueTelemetryChipToneClass(advancedVerifyCueRecoveryTelemetryStaleness)}`}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-advanced-last-mode-chip"
|
|
>
|
|
<span data-testid="runboard-verify-cue-recovery-telemetry-advanced-last-mode">{advancedVerifyCueRecoveryTelemetry.lastMode || 'n/a'}</span>
|
|
</span>
|
|
{' '}· tone{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueTelemetryChipToneClass(advancedVerifyCueRecoveryTelemetryStaleness)}`}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-advanced-status-chip"
|
|
>
|
|
<span data-testid="runboard-verify-cue-recovery-telemetry-advanced-status">{advancedVerifyCueRecoveryTelemetryStatusLabel}</span>
|
|
</span>
|
|
{' '}· at <span className="font-mono text-gray-300" data-testid="runboard-verify-cue-recovery-telemetry-advanced-last-at">{advancedVerifyCueRecoveryLastAtLabel}</span>
|
|
</p>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={resetVerifyCueRecoveryTelemetry}
|
|
data-testid="runboard-verify-cue-recovery-telemetry-reset"
|
|
title="Clear stale-recovery telemetry counters for this session"
|
|
>
|
|
Reset telemetry
|
|
</button>
|
|
<span
|
|
className="text-[10px] text-gray-500"
|
|
data-testid="runboard-verify-cue-recovery-telemetry-reset-note"
|
|
>
|
|
Session-only reset; verify-cue history stays persisted.
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<button
|
|
onClick={() => void loadRuns(undefined, { notifyIfInFlight: true })}
|
|
data-testid="runboard-runs-refresh"
|
|
className="px-3 py-2 rounded-lg bg-gray-800 hover:bg-gray-700 text-sm transition-colors flex items-center gap-2"
|
|
>
|
|
<ItshoverRefreshIcon size={14} />
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
|
|
{compareRemediationRoutePlan.active && (
|
|
<div className="rounded border border-[#A6926E]/40 bg-[#C4A96B]/8 p-3" data-testid="runboard-compare-remediation-route-banner">
|
|
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
|
<div className="min-w-0">
|
|
<p className="text-xs uppercase tracking-wide text-[#D8CBAF]">{compareRemediationRoutePlan.title}</p>
|
|
<p className="mt-1 text-sm text-gray-100">{compareRemediationRoutePlan.routeLabel}</p>
|
|
<p className="mt-1 text-xs text-gray-400">{compareRemediationRoutePlan.summary}</p>
|
|
<p className="mt-2 text-[11px] text-gray-500">
|
|
Compare issue: <span className="text-gray-300">{compareRemediationRoutePlan.issueLabel}</span>
|
|
{queryIntegrityIssueKey ? (
|
|
<span>
|
|
{' '}· mapped integrity key <span className="text-gray-300">{queryIntegrityIssueKey}</span>
|
|
</span>
|
|
) : null}
|
|
</p>
|
|
{compareRouteGraphDiffSummary && (
|
|
<p
|
|
className="mt-1 text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-graph-diff-context"
|
|
>
|
|
Graph diff:
|
|
{' '}<span className="text-gray-300">
|
|
{compareRouteGraphDiffSummary.baselineName || compareRouteGraphDiffSummary.baselineId || 'baseline'}
|
|
</span>
|
|
{' '}→ <span className="text-gray-300">
|
|
{compareRouteGraphDiffSummary.targetName || compareRouteGraphDiffSummary.targetId || 'target'}
|
|
</span>
|
|
{' '}· node Δ <span className="text-gray-300">{formatSignedMetric(compareRouteGraphDiffSummary.nodeDelta)}</span>
|
|
{' '}· edge Δ <span className="text-gray-300">{formatSignedMetric(compareRouteGraphDiffSummary.edgeDelta)}</span>
|
|
{' '}· +nodes <span className="text-gray-300">{compareRouteGraphDiffSummary.addedNodes ?? '-'}</span>
|
|
{' '}· -nodes <span className="text-gray-300">{compareRouteGraphDiffSummary.removedNodes ?? '-'}</span>
|
|
{' '}· +edges <span className="text-gray-300">{compareRouteGraphDiffSummary.addedEdges ?? '-'}</span>
|
|
{' '}· -edges <span className="text-gray-300">{compareRouteGraphDiffSummary.removedEdges ?? '-'}</span>
|
|
</p>
|
|
)}
|
|
{(queryPrimaryDocId || querySecondaryDocId) && (
|
|
<div
|
|
className="mt-2 flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-compare-remediation-route-doc-context"
|
|
>
|
|
<span className="text-[11px] text-gray-500">Compare documents:</span>
|
|
{queryPrimaryDocId && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
openCompareRoutePrimaryDoc({
|
|
notifyBlocked: false,
|
|
})
|
|
}}
|
|
disabled={compareRemediationRoutePrimaryDocOpenState.disabled}
|
|
title={compareRemediationRoutePrimaryDocOpenState.title}
|
|
data-testid="runboard-compare-remediation-route-open-primary-doc"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
Primary {queryPrimaryDocId}
|
|
</button>
|
|
)}
|
|
{querySecondaryDocId && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
openCompareRouteSecondaryDoc({
|
|
notifyBlocked: false,
|
|
})
|
|
}}
|
|
disabled={compareRemediationRouteSecondaryDocOpenState.disabled}
|
|
title={compareRemediationRouteSecondaryDocOpenState.title}
|
|
data-testid="runboard-compare-remediation-route-open-secondary-doc"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
Compare {querySecondaryDocName || querySecondaryDocId}
|
|
</button>
|
|
)}
|
|
{compareRemediationEditorContextHref && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
openCompareRemediationContextInEditor({
|
|
notifyBlocked: false,
|
|
notifyOpened: false,
|
|
})
|
|
}}
|
|
disabled={compareRemediationOpenEditorContextState.disabled}
|
|
title={compareRemediationOpenEditorContextState.title}
|
|
data-testid="runboard-compare-remediation-route-open-editor-compare-context"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
Open compare in editor
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleRefreshIntegrityReport()}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 inline-flex items-center gap-1.5"
|
|
disabled={integrityRefreshControlState.disabled}
|
|
title={integrityRefreshControlState.title}
|
|
data-testid="runboard-compare-remediation-route-refresh-integrity"
|
|
>
|
|
<ItshoverRefreshIcon size={12} />
|
|
{isLoadingIntegrity ? 'Refreshing integrity…' : 'Refresh integrity'}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={openIntegrityReportJson}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
Open integrity report
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleCopyText(compareRemediationRoutePlan.commandPackText, 'Compare remediation route pack copied.')}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
Copy route command pack
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{compareRemediationRoutePlan.suggestedFilters && (
|
|
<div
|
|
className="mt-3 rounded border border-gray-800 bg-gray-950/60 p-2"
|
|
data-testid="runboard-compare-remediation-route-filters"
|
|
>
|
|
<p className="text-[11px] uppercase tracking-wide text-gray-500">Suggested run filters</p>
|
|
<p
|
|
className="mt-1 text-xs text-gray-300"
|
|
data-testid="runboard-compare-remediation-route-filters-summary"
|
|
>
|
|
Status {formatRunboardFilterValueLabel(compareRemediationRoutePlan.suggestedFilters.statusFilter)}
|
|
{' '}· Kind {formatRunboardFilterValueLabel(compareRemediationRoutePlan.suggestedFilters.kindFilter)}
|
|
</p>
|
|
<p
|
|
className="mt-1 text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-filters-rationale"
|
|
>
|
|
{compareRemediationRoutePlan.suggestedFilters.rationale}
|
|
</p>
|
|
<div className="mt-2 flex flex-wrap items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={handleApplyCompareRemediationRouteFilters}
|
|
disabled={compareRemediationApplyFiltersState.disabled}
|
|
title={compareRemediationApplyFiltersState.title}
|
|
data-testid="runboard-compare-remediation-route-apply-filters"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{compareRemediationSuggestedFiltersApplied ? 'Route filters applied' : 'Apply route filters'}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleResetCompareRemediationRouteFilters}
|
|
disabled={compareRemediationResetFiltersState.disabled}
|
|
title={compareRemediationResetFiltersState.title}
|
|
data-testid="runboard-compare-remediation-route-reset-filters"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Reset filters
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleClearCompareRemediationProjectFilter}
|
|
disabled={compareRemediationClearProjectFilterState.disabled}
|
|
title={compareRemediationClearProjectFilterState.title}
|
|
data-testid="runboard-compare-remediation-route-clear-project-filter"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Clear project filter
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{compareRemediationRoutePlan.selectionHints && (
|
|
<div
|
|
className="mt-3 rounded border border-gray-800 bg-gray-950/60 p-2"
|
|
data-testid="runboard-compare-remediation-route-focus"
|
|
>
|
|
<p className="text-[11px] uppercase tracking-wide text-gray-500">Suggested run/artifact focus</p>
|
|
<p className="mt-1 text-xs text-gray-300" data-testid="runboard-compare-remediation-route-focus-summary">
|
|
Run {compareRemediationRoutePlan.selectionHints.runSelection === 'first_failed_filtered' ? 'first failed route match' : 'first route match'}
|
|
{' '}· Artifact {compareRemediationRoutePlan.selectionHints.artifactStatusPreference}
|
|
{compareRemediationRoutePlan.selectionHints.artifactTypePreference ? ` ${compareRemediationRoutePlan.selectionHints.artifactTypePreference}` : ''}
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-compare-remediation-route-focus-rationale">
|
|
{compareRemediationRoutePlan.selectionHints.rationale}
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-compare-remediation-route-focus-candidates">
|
|
Candidate run: <span className="text-gray-300">{compareRemediationSuggestedRunId || '-'}</span>
|
|
{' '}· Candidate artifact: <span className="text-gray-300">{compareRemediationSuggestedArtifactId || '-'}</span>
|
|
</p>
|
|
<div className="mt-2 flex flex-wrap items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={handleSelectCompareRemediationRouteRun}
|
|
disabled={compareRemediationSelectRunState.disabled}
|
|
title={compareRemediationSelectRunState.title}
|
|
data-testid="runboard-compare-remediation-route-select-run"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Select route run
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleSelectCompareRemediationRouteArtifact}
|
|
disabled={compareRemediationSelectArtifactState.disabled}
|
|
title={compareRemediationSelectArtifactState.title}
|
|
data-testid="runboard-compare-remediation-route-select-artifact"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Select route artifact
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleApplyCompareRemediationRouteFocus}
|
|
disabled={compareRemediationApplyFocusState.disabled}
|
|
title={compareRemediationApplyFocusState.title}
|
|
data-testid="runboard-compare-remediation-route-apply-focus"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Apply route focus
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{compareRemediationRoutePlan.actionHints && compareRemediationRouteActionReadiness && (
|
|
<div
|
|
className="mt-3 rounded border border-gray-800 bg-gray-950/60 p-2"
|
|
data-testid="runboard-compare-remediation-route-actions"
|
|
>
|
|
<p className="text-[11px] uppercase tracking-wide text-gray-500">Suggested remediation actions</p>
|
|
<p
|
|
className="mt-1 text-xs text-gray-300"
|
|
data-testid="runboard-compare-remediation-route-actions-summary"
|
|
>
|
|
Run: {compareRemediationRoutePlan.actionHints.runActionLabel}
|
|
{' '}· Artifact: {compareRemediationRoutePlan.actionHints.artifactActionLabel}
|
|
</p>
|
|
<p
|
|
className="mt-1 text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-actions-rationale"
|
|
>
|
|
{compareRemediationRoutePlan.actionHints.rationale}
|
|
</p>
|
|
<p
|
|
className="mt-1 text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-actions-integrity"
|
|
>
|
|
Integrity focus: <span className="text-gray-300">{compareRemediationRouteActionReadiness.integrityFocusLabel}</span>
|
|
{' '}· Severity <span className={compareRemediationRouteActionReadiness.integritySeverityToneClass}>{compareRemediationRouteActionReadiness.integritySeverityLabel}</span>
|
|
{' '}· Compare attached <span className="text-gray-300">{compareRemediationRouteActionReadiness.compareAttachedCount ?? '-'}</span>
|
|
{' '}· Live mapped count <span className="text-gray-300">{compareRemediationRouteActionReadiness.liveIntegrityCount ?? '-'}</span>
|
|
{' '}({compareRemediationRouteActionReadiness.liveIntegrityStateLabel})
|
|
</p>
|
|
<p
|
|
className="mt-1 text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-actions-integrity-mapping"
|
|
>
|
|
Mapping state: <span className={compareRemediationRouteActionReadiness.integrityMappingStateToneClass}>{compareRemediationRouteActionReadiness.integrityMappingStateLabel}</span>
|
|
{' '}· Basis <span className="text-gray-300">{compareRemediationRouteActionReadiness.integrityMappingBasisLabel}</span>
|
|
{' '}· Preview <span className="text-gray-300">{compareRemediationRouteActionReadiness.mappedIssuePreviewLabel}</span>
|
|
{' '}· Entries <span className="text-gray-300">{compareRemediationRouteActionReadiness.mappedIssuePayloadEntryCount ?? '-'}</span>
|
|
{' '}· Mapped docs <span className="text-gray-300">{compareRemediationRouteActionReadiness.mappedIssueDocumentIds.length}/{compareRemediationRouteActionReadiness.mappedIssueDocumentTotalCount}</span>
|
|
{compareRemediationRouteActionReadiness.mappedIssueDocumentIdsTruncated ? (
|
|
<span>{' '}· <span className="text-amber-300">truncated</span> · <span className="text-amber-300">omitted {compareRemediationRouteActionReadiness.mappedIssueDocumentOmittedCount}</span></span>
|
|
) : null}
|
|
</p>
|
|
<div
|
|
className="mt-1 flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-compare-remediation-route-actions-graph-diff-priority"
|
|
>
|
|
<span className="text-[11px] text-gray-500">Graph diff triage:</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${compareRemediationRouteActionReadiness.graphDiffSeverityToneClass}`}
|
|
data-testid="runboard-compare-remediation-route-actions-graph-diff-severity-chip"
|
|
>
|
|
Severity {compareRemediationRouteActionReadiness.graphDiffSeverityLabel}
|
|
{compareRemediationRouteActionReadiness.graphDiffSeverityScore !== null
|
|
? ` (${compareRemediationRouteActionReadiness.graphDiffSeverityScore})`
|
|
: ''}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${compareRemediationRouteActionReadiness.graphDiffTriagePriorityToneClass}`}
|
|
data-testid="runboard-compare-remediation-route-actions-graph-diff-priority-chip"
|
|
>
|
|
Priority {compareRemediationRouteActionReadiness.graphDiffTriagePriorityLabel}
|
|
</span>
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleCopyCompareRouteGraphDiffTriage()}
|
|
disabled={compareRemediationGraphDiffTriageCopyState.disabled}
|
|
title={compareRemediationGraphDiffTriageCopyState.title}
|
|
data-testid="runboard-compare-remediation-route-actions-copy-graph-diff-triage"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
Copy graph diff triage
|
|
</button>
|
|
</div>
|
|
{compareRemediationRouteActionReadiness.mappedIssueDocumentIds.length > 0 && (
|
|
<div
|
|
className="mt-1 flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-compare-remediation-route-actions-mapped-doc-links"
|
|
>
|
|
<span className="text-[11px] text-gray-500">
|
|
Mapped documents ({compareRemediationRouteActionReadiness.mappedIssueDocumentIds.length}/{compareRemediationRouteActionReadiness.mappedIssueDocumentTotalCount}
|
|
{compareRemediationRouteActionReadiness.mappedIssueDocumentIdsTruncated
|
|
? `, truncated, +${compareRemediationRouteActionReadiness.mappedIssueDocumentOmittedCount} omitted`
|
|
: ''}
|
|
):
|
|
</span>
|
|
{compareRemediationRouteActionReadiness.mappedIssueDocumentIds.map((documentId, index) => (
|
|
<button
|
|
key={`mapped-doc-${documentId}`}
|
|
type="button"
|
|
onClick={() => openEntityJson(documentId)}
|
|
title={`Open mapped document entity ${documentId}`}
|
|
data-testid={`runboard-compare-remediation-route-actions-open-mapped-doc-${index}`}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
{documentId}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
{compareRemediationRouteActionReadiness.routeContextDocumentIds.length > 0 && (
|
|
<div
|
|
className="mt-1 flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-compare-remediation-route-actions-route-doc-links"
|
|
>
|
|
<span className="text-[11px] text-gray-500">
|
|
Route documents ({compareRemediationRouteActionReadiness.routeContextDocumentIds.length}/{compareRemediationRouteActionReadiness.routeContextDocumentTotalCount}
|
|
{compareRemediationRouteActionReadiness.routeContextDocumentIdsTruncated
|
|
? `, truncated, +${compareRemediationRouteActionReadiness.routeContextDocumentOmittedCount} omitted`
|
|
: ''}
|
|
):
|
|
</span>
|
|
{compareRemediationRouteActionReadiness.routeContextDocumentIds.map((documentId, index) => (
|
|
<button
|
|
key={`route-doc-${documentId}`}
|
|
type="button"
|
|
onClick={() => openEntityJson(documentId)}
|
|
title={`Open route context document entity ${documentId}`}
|
|
data-testid={`runboard-compare-remediation-route-actions-open-route-doc-${index}`}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
{documentId}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
{compareRemediationRouteActionReadiness.routeContextDocumentCopy && (
|
|
<div className="mt-1 flex flex-wrap items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleCopyCompareRouteDocContext()}
|
|
disabled={compareRemediationRouteDocContextCopyState.disabled}
|
|
title={compareRemediationRouteDocContextCopyState.title}
|
|
data-testid="runboard-compare-remediation-route-actions-copy-route-doc-context"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
Copy route doc context
|
|
</button>
|
|
</div>
|
|
)}
|
|
{compareRemediationRouteReplayLinkPayload && (
|
|
<div className="mt-1 flex flex-wrap items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleCopyCompareRouteReplayLink()}
|
|
disabled={compareRemediationRouteReplayLinkCopyState.disabled}
|
|
title={compareRemediationRouteReplayLinkCopyState.title}
|
|
data-testid="runboard-compare-remediation-route-actions-copy-route-context-href"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
Copy route replay link
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
openCompareRouteReplayLink({
|
|
notifyBlocked: false,
|
|
notifyOpened: false,
|
|
})
|
|
}}
|
|
disabled={compareRemediationRouteReplayLinkOpenState.disabled}
|
|
title={compareRemediationRouteReplayLinkOpenState.title}
|
|
data-testid="runboard-compare-remediation-route-actions-open-route-context-href"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
Open route replay link
|
|
</button>
|
|
</div>
|
|
)}
|
|
{compareRemediationRouteActionReadiness.mappedIssuePayloadCopy && (
|
|
<div className="mt-1">
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleCopyCompareRouteMappedIssuePayload()}
|
|
disabled={compareRemediationRouteMappedIssuePayloadCopyState.disabled}
|
|
title={compareRemediationRouteMappedIssuePayloadCopyState.title}
|
|
data-testid="runboard-compare-remediation-route-actions-copy-mapped-issue"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
Copy mapped issue payload
|
|
</button>
|
|
<details
|
|
className="mt-2 rounded border border-gray-800 bg-gray-950 p-2"
|
|
data-testid="runboard-compare-remediation-route-actions-mapped-issue-preview"
|
|
>
|
|
<summary className="cursor-pointer text-[11px] text-gray-400">
|
|
Inspect mapped issue payload
|
|
</summary>
|
|
<pre
|
|
className="mt-2 max-h-40 overflow-auto whitespace-pre-wrap break-all text-[10px] text-gray-300"
|
|
data-testid="runboard-compare-remediation-route-actions-mapped-issue-json"
|
|
>
|
|
{compareRemediationRouteActionReadiness.mappedIssuePayloadCopy}
|
|
</pre>
|
|
</details>
|
|
</div>
|
|
)}
|
|
<div className="mt-2 flex flex-wrap items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={handleExecuteCompareRemediationRouteRunAction}
|
|
disabled={!compareRemediationRouteActionReadiness.runReady}
|
|
title={compareRemediationRouteActionReadiness.runActionTooltip}
|
|
data-testid="runboard-compare-remediation-route-run-action"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverArrowBackUpIcon size={12} className="text-gray-300" />
|
|
{compareRemediationRoutePlan.actionHints.runActionLabel}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleExecuteCompareRemediationRouteArtifactAction}
|
|
disabled={!compareRemediationRouteActionReadiness.artifactReady}
|
|
title={compareRemediationRouteActionReadiness.artifactActionTooltip}
|
|
data-testid="runboard-compare-remediation-route-artifact-action"
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} className="text-gray-300" />
|
|
{compareRemediationRoutePlan.actionHints.artifactActionLabel}
|
|
</button>
|
|
</div>
|
|
<div className="mt-2 space-y-1">
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-run-action-readiness"
|
|
>
|
|
Run action readiness:{' '}
|
|
<span className={compareRemediationRouteActionReadiness.runReady ? 'text-emerald-300' : 'text-amber-300'}>
|
|
{compareRemediationRouteActionReadiness.runReady ? 'ready' : 'blocked'}
|
|
</span>
|
|
{compareRemediationRouteActionReadiness.runReason ? (
|
|
<span>
|
|
{' '}· <span className="text-gray-400">{compareRemediationRouteActionReadiness.runReason}</span>
|
|
</span>
|
|
) : null}
|
|
</p>
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-artifact-action-readiness"
|
|
>
|
|
Artifact action readiness:{' '}
|
|
<span className={compareRemediationRouteActionReadiness.artifactReady ? 'text-emerald-300' : 'text-amber-300'}>
|
|
{compareRemediationRouteActionReadiness.artifactReady ? 'ready' : 'blocked'}
|
|
</span>
|
|
{compareRemediationRouteActionReadiness.artifactReason ? (
|
|
<span>
|
|
{' '}· <span className="text-gray-400">{compareRemediationRouteActionReadiness.artifactReason}</span>
|
|
</span>
|
|
) : null}
|
|
</p>
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-run-action-blockers"
|
|
>
|
|
Run blockers:{' '}
|
|
<span className={compareRemediationRouteActionReadiness.runBlockerCodes.length > 0 ? 'text-amber-300' : 'text-emerald-300'}>
|
|
{compareRemediationRouteActionReadiness.runBlockerCodes.length > 0
|
|
? compareRemediationRouteActionReadiness.runBlockerCodes.join(', ')
|
|
: 'none'}
|
|
</span>
|
|
</p>
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-run-action-next-step"
|
|
>
|
|
Run next step:{' '}
|
|
<span className={compareRemediationRouteActionReadiness.runReady ? 'text-emerald-300' : 'text-gray-300'}>
|
|
{compareRemediationRouteActionReadiness.runNextStep}
|
|
</span>
|
|
</p>
|
|
{compareRemediationRouteRunQuickActions.length > 0 && (
|
|
<div
|
|
className="flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-compare-remediation-route-run-quick-actions"
|
|
>
|
|
<span className="text-[11px] text-gray-500">Run quick actions:</span>
|
|
{compareRemediationRouteRunQuickActions.map((state) => (
|
|
<button
|
|
key={`run-${state.actionId}`}
|
|
type="button"
|
|
onClick={() => handleExecuteCompareRemediationRouteQuickAction(state.actionId)}
|
|
disabled={state.disabled}
|
|
title={state.title}
|
|
data-testid={`runboard-compare-remediation-route-run-quick-action-${state.actionId}`}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
{compareRouteQuickActionIcon(state.actionId)}
|
|
{compareRouteQuickActionLabel(state.actionId)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
{compareRemediationRouteRunQuickActionsNote && (
|
|
<p className="text-[11px] text-gray-500" data-testid="runboard-compare-remediation-route-run-quick-actions-note">
|
|
{compareRemediationRouteRunQuickActionsNote}
|
|
</p>
|
|
)}
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-run-action-preconditions"
|
|
>
|
|
Run preconditions:{' '}
|
|
<span className="text-gray-300">
|
|
{compareRemediationRouteActionReadiness.runPreconditionSummary}
|
|
</span>
|
|
</p>
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-artifact-action-blockers"
|
|
>
|
|
Artifact blockers:{' '}
|
|
<span className={compareRemediationRouteActionReadiness.artifactBlockerCodes.length > 0 ? 'text-amber-300' : 'text-emerald-300'}>
|
|
{compareRemediationRouteActionReadiness.artifactBlockerCodes.length > 0
|
|
? compareRemediationRouteActionReadiness.artifactBlockerCodes.join(', ')
|
|
: 'none'}
|
|
</span>
|
|
</p>
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-artifact-action-next-step"
|
|
>
|
|
Artifact next step:{' '}
|
|
<span className={compareRemediationRouteActionReadiness.artifactReady ? 'text-emerald-300' : 'text-gray-300'}>
|
|
{compareRemediationRouteActionReadiness.artifactNextStep}
|
|
</span>
|
|
</p>
|
|
{compareRemediationRouteArtifactQuickActions.length > 0 && (
|
|
<div
|
|
className="flex flex-wrap items-center gap-2"
|
|
data-testid="runboard-compare-remediation-route-artifact-quick-actions"
|
|
>
|
|
<span className="text-[11px] text-gray-500">Artifact quick actions:</span>
|
|
{compareRemediationRouteArtifactQuickActions.map((state) => (
|
|
<button
|
|
key={`artifact-${state.actionId}`}
|
|
type="button"
|
|
onClick={() => handleExecuteCompareRemediationRouteQuickAction(state.actionId)}
|
|
disabled={state.disabled}
|
|
title={state.title}
|
|
data-testid={`runboard-compare-remediation-route-artifact-quick-action-${state.actionId}`}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5"
|
|
>
|
|
{compareRouteQuickActionIcon(state.actionId)}
|
|
{compareRouteQuickActionLabel(state.actionId)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
{compareRemediationRouteArtifactQuickActionsNote && (
|
|
<p className="text-[11px] text-gray-500" data-testid="runboard-compare-remediation-route-artifact-quick-actions-note">
|
|
{compareRemediationRouteArtifactQuickActionsNote}
|
|
</p>
|
|
)}
|
|
<p
|
|
className="text-[11px] text-gray-500"
|
|
data-testid="runboard-compare-remediation-route-artifact-action-preconditions"
|
|
>
|
|
Artifact preconditions:{' '}
|
|
<span className="text-gray-300">
|
|
{compareRemediationRouteActionReadiness.artifactPreconditionSummary}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{compareRemediationRoutePlan.commands.length > 0 && (
|
|
<div className="mt-3 rounded border border-gray-800 bg-gray-950/60 p-2">
|
|
<p className="text-[11px] uppercase tracking-wide text-gray-500">Route command hints</p>
|
|
<div className="mt-2 space-y-2">
|
|
{compareRemediationRoutePlan.commands.map((item) => (
|
|
<div key={item.id} className="rounded border border-gray-800 bg-gray-950 p-2">
|
|
<div className="flex items-start justify-between gap-2">
|
|
<div className="min-w-0">
|
|
<p className="text-xs text-gray-200">{item.title}</p>
|
|
<p className="mt-1 text-[11px] text-gray-500">{item.rationale}</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleCopyText(item.command, `${item.title} command copied.`)}
|
|
className="px-2 py-1 rounded text-xs border border-gray-700 text-gray-300 hover:text-gray-100 hover:border-[#C4A96B]/50 inline-flex items-center gap-1.5"
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
Copy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-9 gap-3">
|
|
<div className="card p-3">
|
|
<p className="text-xs text-gray-400">Total</p>
|
|
<p className="text-xl font-semibold">{runCounts.total}</p>
|
|
</div>
|
|
<div className="card p-3">
|
|
<p className="text-xs text-gray-400">Queued</p>
|
|
<p className="text-xl font-semibold">{runCounts.queued}</p>
|
|
</div>
|
|
<div className="card p-3">
|
|
<p className="text-xs text-gray-400">Running</p>
|
|
<p className="text-xl font-semibold text-indigo-300">{runCounts.running}</p>
|
|
</div>
|
|
<div className="card p-3">
|
|
<p className="text-xs text-gray-400">Succeeded</p>
|
|
<p className="text-xl font-semibold text-emerald-300">{runCounts.succeeded}</p>
|
|
</div>
|
|
<div className="card p-3">
|
|
<p className="text-xs text-gray-400">Failed</p>
|
|
<p className="text-xl font-semibold text-red-300">{runCounts.failed}</p>
|
|
</div>
|
|
<div className="card p-3">
|
|
<p className="text-xs text-gray-400">Canceled</p>
|
|
<p className="text-xl font-semibold text-amber-300">{runCounts.canceled}</p>
|
|
</div>
|
|
<div className="card overflow-hidden p-3 md:col-span-2 xl:col-span-3" data-testid="runboard-queue-health">
|
|
<div className="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<p className="text-xs text-gray-400">Queue Health</p>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide ${queueHealthAgingSignalToneClass}`}
|
|
data-testid="runboard-queue-health-header-aging-badge"
|
|
>
|
|
Aging {queueHealthAgingSignal.status}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${queueHealthAgingSignal.hotspotKind === 'none' ? 'text-gray-300 border-gray-700 bg-gray-900/60' : queueHealthAgingSignalToneClass}`}
|
|
data-testid="runboard-queue-health-header-hotspot-chip"
|
|
>
|
|
Hotspot {queueHealthAgingSignal.hotspotKind} {fmtMs(queueHealthAgingSignal.hotspotMaxMs)}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${queueHealthHeaderPressureChip.toneClass}`}
|
|
data-testid="runboard-queue-health-header-pressure-chip"
|
|
>
|
|
{queueHealthHeaderPressureChip.label}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${queueHealthHeaderTrendChip.toneClass}`}
|
|
data-testid="runboard-queue-health-header-trend-chip"
|
|
>
|
|
{queueHealthHeaderTrendChip.label}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${queueHealthHeaderTrendDeltaChip.toneClass}`}
|
|
data-testid="runboard-queue-health-header-trend-delta-chip"
|
|
>
|
|
{queueHealthHeaderTrendDeltaChip.label}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${queueHealthHeaderPressureDeltaChip.toneClass}`}
|
|
data-testid="runboard-queue-health-header-pressure-delta-chip"
|
|
>
|
|
{queueHealthHeaderPressureDeltaChip.label}
|
|
</span>
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-medium ${queueHealthHeaderAgingDeltaChip.toneClass}`}
|
|
data-testid="runboard-queue-health-header-aging-delta-chip"
|
|
>
|
|
{queueHealthHeaderAgingDeltaChip.label}
|
|
</span>
|
|
</div>
|
|
<p className={`text-xl font-semibold ${queueHealth.statusToneClass}`}>{queueHealth.statusLabel}</p>
|
|
</div>
|
|
<div className="shrink-0 xl:text-right">
|
|
<p className="text-[11px] uppercase tracking-wide text-gray-500">Active Queue</p>
|
|
<p className="text-lg font-semibold text-gray-100">{queueHealth.activeQueueCount}</p>
|
|
</div>
|
|
</div>
|
|
<div className="mt-3 grid grid-cols-3 gap-2 text-xs">
|
|
<div className="rounded-md bg-gray-900/60 px-2 py-2">
|
|
<p className="text-gray-500">Success</p>
|
|
<p className="font-semibold text-emerald-300">{queueHealth.successRatePct}%</p>
|
|
</div>
|
|
<div className="rounded-md bg-gray-900/60 px-2 py-2">
|
|
<p className="text-gray-500">Failure</p>
|
|
<p className="font-semibold text-red-300">{queueHealth.failureRatePct}%</p>
|
|
</div>
|
|
<div className="rounded-md bg-gray-900/60 px-2 py-2">
|
|
<p className="text-gray-500">Queue Share</p>
|
|
<p className="font-semibold text-indigo-300">{queueHealth.queueSharePct}%</p>
|
|
</div>
|
|
</div>
|
|
<p className="mt-2 text-[11px] text-gray-500">
|
|
Terminal sample: <span className="text-gray-300">{queueHealth.terminalRunCount}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-kind-mix">
|
|
Kind mix: <span className="text-gray-300">C{runKindCounts.compile}</span> / <span className="text-gray-300">T{runKindCounts.task}</span> / <span className="text-gray-300">O{runKindCounts.other}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-kind-mix">
|
|
Active mix: <span className="text-gray-300">C{activeQueueKindCounts.compile}</span> / <span className="text-gray-300">T{activeQueueKindCounts.task}</span> / <span className="text-gray-300">O{activeQueueKindCounts.other}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-terminal-failure-by-kind">
|
|
Failure by kind (terminal): <span className="text-gray-300">C{pct(terminalFailureKindCounts.compile, terminalRunKindCounts.compile)}%</span> / <span className="text-gray-300">T{pct(terminalFailureKindCounts.task, terminalRunKindCounts.task)}%</span> / <span className="text-gray-300">O{pct(terminalFailureKindCounts.other, terminalRunKindCounts.other)}%</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-focus">
|
|
Focus: active <span className="text-gray-300">{queueHealthFocus.activeFocus.kind}</span> ({queueHealthFocus.activeFocus.count}), failure hotspot <span className="text-gray-300">{queueHealthFocus.terminalFailureFocus.kind}</span> ({queueHealthFocus.terminalFailureFocus.count})
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-drivers">
|
|
Drivers: <span className="text-gray-300">{queueHealthDrivers.join(' / ')}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-action">
|
|
Action: <span className="text-gray-300">{queueHealthActionHint}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-wait">
|
|
Active wait (queue): <span className="text-gray-300">avg {fmtMs(activeQueueWaitStats.avgMs)}</span> / <span className="text-gray-300">max {fmtMs(activeQueueWaitStats.maxMs)}</span>
|
|
<span className="text-gray-500"> ({activeQueueWaitStats.sampleCount} samples)</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-wait-by-kind">
|
|
Active wait by kind (avg): <span className="text-gray-300">C{fmtMs(activeQueueWaitByKindStats.compile.avgMs)}</span> / <span className="text-gray-300">T{fmtMs(activeQueueWaitByKindStats.task.avgMs)}</span> / <span className="text-gray-300">O{fmtMs(activeQueueWaitByKindStats.other.avgMs)}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-wait-delta-by-kind">
|
|
Active wait by kind (Δ avg):{' '}
|
|
<span className={queueHealthPerKindAvgWaitDeltaLineTones.compile}>C{queueHealthPerKindAvgWaitDeltaLine.compileLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindAvgWaitDeltaLineTones.task}>T{queueHealthPerKindAvgWaitDeltaLine.taskLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindAvgWaitDeltaLineTones.other}>O{queueHealthPerKindAvgWaitDeltaLine.otherLabel}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-max-wait-by-kind">
|
|
Active wait by kind (max): <span className="text-gray-300">C{fmtMs(activeQueueWaitByKindStats.compile.maxMs)}</span> / <span className="text-gray-300">T{fmtMs(activeQueueWaitByKindStats.task.maxMs)}</span> / <span className="text-gray-300">O{fmtMs(activeQueueWaitByKindStats.other.maxMs)}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-max-wait-delta-by-kind">
|
|
Active wait by kind (Δ max):{' '}
|
|
<span className={queueHealthPerKindMaxWaitDeltaLineTones.compile}>C{queueHealthPerKindMaxWaitDeltaLine.compileLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindMaxWaitDeltaLineTones.task}>T{queueHealthPerKindMaxWaitDeltaLine.taskLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindMaxWaitDeltaLineTones.other}>O{queueHealthPerKindMaxWaitDeltaLine.otherLabel}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-active-wait-delta-summary">
|
|
Per-kind trend:{' '}
|
|
<span className="text-gray-400">Δ avg</span>{' '}
|
|
<span className={queueHealthPerKindAvgWaitDeltaLineTones.compile}>C{queueHealthPerKindAvgWaitDeltaLine.compileLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindAvgWaitDeltaLineTones.task}>T{queueHealthPerKindAvgWaitDeltaLine.taskLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindAvgWaitDeltaLineTones.other}>O{queueHealthPerKindAvgWaitDeltaLine.otherLabel}</span>
|
|
<span className="text-gray-500"> · </span>
|
|
<span className="text-gray-400">Δ max</span>{' '}
|
|
<span className={queueHealthPerKindMaxWaitDeltaLineTones.compile}>C{queueHealthPerKindMaxWaitDeltaLine.compileLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindMaxWaitDeltaLineTones.task}>T{queueHealthPerKindMaxWaitDeltaLine.taskLabel}</span> /{' '}
|
|
<span className={queueHealthPerKindMaxWaitDeltaLineTones.other}>O{queueHealthPerKindMaxWaitDeltaLine.otherLabel}</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-aging-signal">
|
|
Queue aging:{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1.5 py-0.5 font-medium ${queueHealthAgingSignalToneClass}`}
|
|
data-testid="runboard-queue-health-aging-signal-badge"
|
|
>
|
|
{queueHealthAgingSignal.status}
|
|
</span>
|
|
<span className="text-gray-500"> (hotspot {queueHealthAgingSignal.hotspotKind}, {fmtMs(queueHealthAgingSignal.hotspotMaxMs)})</span>
|
|
</p>
|
|
<p className="mt-1 text-[11px] text-gray-500" data-testid="runboard-queue-health-aging-threshold-gap">
|
|
Queue aging gap:{' '}
|
|
<span
|
|
className={queueAgingDeltaToneClass(queueHealthAgingThresholdGap.watchDeltaMs)}
|
|
data-testid="runboard-queue-health-aging-gap-watch"
|
|
>
|
|
watch {fmtSignedMsDelta(queueHealthAgingThresholdGap.watchDeltaMs)}
|
|
</span>{' '}
|
|
/{' '}
|
|
<span
|
|
className={queueAgingDeltaToneClass(queueHealthAgingThresholdGap.elevatedDeltaMs)}
|
|
data-testid="runboard-queue-health-aging-gap-elevated"
|
|
>
|
|
elevated {fmtSignedMsDelta(queueHealthAgingThresholdGap.elevatedDeltaMs)}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card space-y-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
|
<div>
|
|
<label htmlFor={statusFilterInputId} className="text-xs text-gray-400 mb-1 block">Status</label>
|
|
<select
|
|
id={statusFilterInputId}
|
|
value={statusFilter}
|
|
onChange={(event) => setStatusFilter(event.target.value as typeof RUN_STATUSES[number])}
|
|
className="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm"
|
|
>
|
|
{RUN_STATUSES.map((status) => (
|
|
<option key={status} value={status}>{status}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label htmlFor={kindFilterInputId} className="text-xs text-gray-400 mb-1 block">Kind</label>
|
|
<select
|
|
id={kindFilterInputId}
|
|
value={kindFilter}
|
|
onChange={(event) => setKindFilter(event.target.value)}
|
|
className="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm"
|
|
>
|
|
<option value="all">all</option>
|
|
<option value="compile">compile</option>
|
|
<option value="task">task</option>
|
|
</select>
|
|
</div>
|
|
<div className="md:col-span-2">
|
|
<label htmlFor={projectFilterInputId} className="text-xs text-gray-400 mb-1 block">Project ID</label>
|
|
<input
|
|
id={projectFilterInputId}
|
|
value={projectFilter}
|
|
onChange={(event) => setProjectFilter(event.target.value)}
|
|
placeholder="Filter project_id"
|
|
className="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm"
|
|
/>
|
|
</div>
|
|
<div className="flex items-end">
|
|
<button
|
|
onClick={() => void loadRuns(undefined, { notifyIfInFlight: true })}
|
|
className="w-full px-3 py-2 rounded-lg bg-[#C4A96B] hover:bg-[#A6926E] text-sm transition-colors"
|
|
>
|
|
Apply Filters
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{(runsError || detailsError || integrityError || actionError) && (
|
|
<div className="space-y-2">
|
|
{runsError && (
|
|
<SurfaceNotice
|
|
tone="error"
|
|
message="Runs Load Failed"
|
|
detail={runsError}
|
|
/>
|
|
)}
|
|
{detailsError && (
|
|
<SurfaceNotice
|
|
tone="error"
|
|
message="Run Drill-Down Load Failed"
|
|
detail={detailsError}
|
|
/>
|
|
)}
|
|
{integrityError && (
|
|
<SurfaceNotice
|
|
tone="error"
|
|
message="Integrity Report Load Failed"
|
|
detail={integrityError}
|
|
/>
|
|
)}
|
|
{actionError && (
|
|
<SurfaceNotice
|
|
tone="error"
|
|
message="Run Action Failed"
|
|
detail={actionError}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
|
|
<div className="card p-0 overflow-hidden">
|
|
<div className="px-4 py-3 border-b border-gray-800 text-sm text-gray-300 flex items-center justify-between">
|
|
<span>Runs</span>
|
|
{isLoadingRuns && <span className="text-xs text-gray-500">Loading...</span>}
|
|
</div>
|
|
<div
|
|
className="max-h-[480px] overflow-auto divide-y divide-gray-800"
|
|
aria-live="polite"
|
|
aria-busy={isLoadingRuns}
|
|
>
|
|
{isLoadingRuns && (
|
|
<SurfacePanelState
|
|
kind="loading"
|
|
title="Loading Runs"
|
|
detail="Refreshing run queue and status snapshots."
|
|
/>
|
|
)}
|
|
{!isLoadingRuns && !runs.length && (
|
|
<SurfacePanelState
|
|
kind="empty"
|
|
title="No Runs Found"
|
|
detail="No run records match the current filter criteria."
|
|
/>
|
|
)}
|
|
{runs.map((run, index) => (
|
|
<button
|
|
key={`run-row-${run.run_id}-${run.project_id || 'no-project'}-${run.kind || 'no-kind'}-${index}`}
|
|
onClick={() => handleSelectRun(run.run_id)}
|
|
aria-pressed={selectedRunId === run.run_id}
|
|
aria-label={`Select run ${run.run_id}`}
|
|
className={`w-full text-left px-4 py-3 transition-colors ${selectedRunId === run.run_id ? 'bg-gray-800/80' : 'hover:bg-gray-800/40'
|
|
}`}
|
|
>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<p className="text-sm font-medium text-gray-200 truncate">{run.run_id}</p>
|
|
<CorpusStatusPill size="sm" tone={statusTone(run.status)}>
|
|
{run.status}
|
|
</CorpusStatusPill>
|
|
</div>
|
|
<p className="text-xs text-gray-500 truncate mt-1">
|
|
project: {run.project_id} • kind: {run.kind}
|
|
</p>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
updated: {fmtTime(run.updated_at)}
|
|
</p>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card p-0 overflow-hidden">
|
|
<div className="px-4 py-3 border-b border-gray-800 text-sm text-gray-300 flex items-center justify-between">
|
|
<span>Failure Drill-Down</span>
|
|
{isLoadingDetails && <span className="text-xs text-gray-500">Loading...</span>}
|
|
</div>
|
|
{!selectedRun && (
|
|
<SurfacePanelState
|
|
kind={isLoadingDetails ? 'loading' : 'empty'}
|
|
title={isLoadingDetails ? 'Loading Run Details' : 'Select a Run'}
|
|
detail={isLoadingDetails ? 'Fetching summary, artifacts, and audit events.' : 'Choose a run from the left panel to inspect drill-down details.'}
|
|
/>
|
|
)}
|
|
{selectedRun && (
|
|
<div className="p-4 space-y-4">
|
|
<div className="flex items-center gap-2">
|
|
{['failed', 'canceled'].includes(selectedRun.status) && (
|
|
<button
|
|
onClick={() => void handleRetryRun()}
|
|
disabled={isApplyingAction}
|
|
className="px-2.5 py-1.5 rounded bg-indigo-600 hover:bg-indigo-700 disabled:bg-gray-700 text-white text-xs flex items-center gap-1.5"
|
|
>
|
|
<ItshoverArrowBackUpIcon size={12} />
|
|
{isApplyingAction ? 'Applying...' : 'Queue Retry'}
|
|
</button>
|
|
)}
|
|
{['queued', 'running'].includes(selectedRun.status) && (
|
|
<button
|
|
onClick={() => void handleCancelRun()}
|
|
disabled={isApplyingAction}
|
|
className="px-2.5 py-1.5 rounded bg-amber-700 hover:bg-amber-800 disabled:bg-gray-700 text-white text-xs"
|
|
>
|
|
{isApplyingAction ? 'Applying...' : 'Cancel Run'}
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={() => void loadRunDetails(selectedRun.run_id, { notifyIfInFlight: true })}
|
|
disabled={isLoadingDetails}
|
|
className="px-2.5 py-1.5 rounded bg-gray-800 hover:bg-gray-700 disabled:bg-gray-700 text-gray-200 text-xs"
|
|
>
|
|
Refresh Details
|
|
</button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
<div className="bg-gray-900 rounded border border-gray-800 p-3">
|
|
<p className="text-xs text-gray-500">Run</p>
|
|
<p className="text-gray-200">{selectedRun.run_id}</p>
|
|
<p className="text-xs text-gray-500 mt-1">project: {selectedRun.project_id}</p>
|
|
</div>
|
|
<div className="bg-gray-900 rounded border border-gray-800 p-3">
|
|
<p className="text-xs text-gray-500">Status</p>
|
|
<p className="text-gray-200">{selectedRun.status}</p>
|
|
<p className="text-xs text-gray-500 mt-1">kind: {selectedRun.kind}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3 text-xs">
|
|
<div className="bg-gray-900 rounded border border-gray-800 p-3">
|
|
<div className="flex items-center gap-2 text-gray-400">
|
|
<ItshoverClockIcon size={12} />
|
|
<span>Pipeline Total</span>
|
|
</div>
|
|
<p className="text-sm text-gray-200 mt-1">{fmtMs(selectedSummary?.summary?.timing?.pipeline_total_ms ?? selectedRun.summary.timing.pipeline_total_ms)}</p>
|
|
</div>
|
|
<div className="bg-gray-900 rounded border border-gray-800 p-3">
|
|
<div className="flex items-center gap-2 text-gray-400">
|
|
<ItshoverTriangleAlertIcon size={12} />
|
|
<span>Integrity Flags</span>
|
|
</div>
|
|
<p className="text-sm text-gray-200 mt-1">
|
|
{(selectedSummary?.summary?.integrity?.artifacts_failed_count ?? selectedRun.summary.integrity.artifacts_failed_count) || 0}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{(() => {
|
|
const checkpointPosture = describeRunCheckpointPosture(selectedRun, selectedSummary)
|
|
const actionCue = describeSelectedRunActionCue({
|
|
run: selectedRun,
|
|
summary: selectedSummary,
|
|
isLoadingDetails,
|
|
})
|
|
const stageSlices = deriveSelectedRunStages({
|
|
run: selectedRun,
|
|
summary: selectedSummary,
|
|
artifactCount: selectedArtifacts.length,
|
|
isLoadingDetails,
|
|
})
|
|
const retrospective = deriveSelectedRunRetrospective({
|
|
run: selectedRun,
|
|
summary: selectedSummary,
|
|
artifactCount: selectedArtifacts.length,
|
|
isLoadingDetails,
|
|
})
|
|
return (
|
|
<div className="space-y-4" data-testid="runboard-selected-run-operator-summary">
|
|
<div
|
|
className="rounded border border-amber-800/40 bg-amber-950/20 p-3 space-y-1.5"
|
|
data-testid="runboard-selected-run-action-cue"
|
|
>
|
|
<div className="flex items-center gap-2 text-amber-200">
|
|
<ItshoverTriangleAlertIcon size={12} />
|
|
<span className="text-xs font-medium tracking-wide uppercase">Recommended next operator action</span>
|
|
</div>
|
|
<p className="text-sm text-amber-100" data-testid="runboard-selected-run-action-cue-label">
|
|
{actionCue.label}
|
|
</p>
|
|
<p className="text-xs text-amber-100/90" data-testid="runboard-selected-run-action-cue-guidance">
|
|
{actionCue.guidance}
|
|
</p>
|
|
<p className="text-[11px] text-amber-200/80" data-testid="runboard-selected-run-action-cue-primary-action">
|
|
Recommended button path: {actionCue.primaryAction}
|
|
</p>
|
|
</div>
|
|
|
|
<div
|
|
className="rounded border border-sky-800/40 bg-sky-950/20 p-3 space-y-2"
|
|
data-testid="runboard-selected-run-stage-strip"
|
|
>
|
|
<div className="flex items-center gap-2 text-sky-200">
|
|
<ItshoverClockIcon size={12} />
|
|
<span className="text-xs font-medium tracking-wide uppercase">Stage-Oriented Run Visibility</span>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
{stageSlices.map((stage) => {
|
|
const tone = stage.status === 'blocked'
|
|
? 'border-red-800/50 bg-red-950/20 text-red-200'
|
|
: stage.status === 'active'
|
|
? 'border-amber-700/50 bg-amber-950/20 text-amber-200'
|
|
: stage.status === 'complete'
|
|
? 'border-emerald-800/50 bg-emerald-950/20 text-emerald-200'
|
|
: 'border-gray-800 bg-gray-950/40 text-gray-300'
|
|
return (
|
|
<div
|
|
key={stage.key}
|
|
className={`rounded border px-2.5 py-2 ${tone}`}
|
|
data-testid={`runboard-selected-run-stage-${stage.key}`}
|
|
>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<span className="text-xs font-medium">{stage.label}</span>
|
|
<span className="text-[11px] uppercase tracking-wide opacity-80">{stage.status}</span>
|
|
</div>
|
|
<p className="text-[11px] mt-1 opacity-90">{stage.detail}</p>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="rounded border border-indigo-800/50 bg-indigo-950/20 p-3 space-y-1.5"
|
|
data-testid="runboard-selected-run-checkpoint-posture"
|
|
>
|
|
<div className="flex items-center gap-2 text-indigo-200">
|
|
<ItshoverShieldCheckIcon size={12} />
|
|
<span className="text-xs font-medium tracking-wide uppercase">Checkpoint / Resume Posture</span>
|
|
</div>
|
|
<p className="text-sm text-indigo-100" data-testid="runboard-selected-run-checkpoint-posture-label">
|
|
{checkpointPosture.label}
|
|
</p>
|
|
<p className="text-xs text-indigo-100/90" data-testid="runboard-selected-run-checkpoint-posture-detail">
|
|
{checkpointPosture.detail}
|
|
</p>
|
|
<p className="text-[11px] text-indigo-200/80" data-testid="runboard-selected-run-checkpoint-posture-note">
|
|
{checkpointPosture.note}
|
|
</p>
|
|
</div>
|
|
|
|
<div
|
|
className="rounded border border-emerald-800/40 bg-emerald-950/20 p-3 space-y-2"
|
|
data-testid="runboard-selected-run-retrospective"
|
|
>
|
|
<div className="flex items-center gap-2 text-emerald-200">
|
|
<ItshoverCheckedIcon size={12} />
|
|
<span className="text-xs font-medium tracking-wide uppercase">Post-Run Retrospective</span>
|
|
</div>
|
|
<p className="text-sm text-emerald-100" data-testid="runboard-selected-run-retrospective-headline">
|
|
{retrospective.headline}
|
|
</p>
|
|
{retrospective.loadingNote && (
|
|
<p className="text-[11px] text-emerald-200/80" data-testid="runboard-selected-run-retrospective-loading-note">
|
|
{retrospective.loadingNote}
|
|
</p>
|
|
)}
|
|
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2 text-[11px] text-emerald-50/90">
|
|
{retrospective.facts.map((fact, index) => (
|
|
<li
|
|
key={`retrospective-fact-${index}-${fact}`}
|
|
className="rounded border border-emerald-800/30 bg-emerald-950/20 px-2 py-1.5"
|
|
>
|
|
{fact}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="rounded border border-emerald-800/30 bg-emerald-950/20 px-2.5 py-2">
|
|
<p className="text-[11px] text-emerald-200/80 uppercase tracking-wide">Suggested next step</p>
|
|
<p className="text-xs text-emerald-100 mt-1" data-testid="runboard-selected-run-retrospective-next-step">
|
|
{retrospective.nextStep}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
})()}
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-integrity-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400">Artifacts ({selectedArtifacts.length})</div>
|
|
<div
|
|
className="max-h-56 overflow-auto divide-y divide-gray-800"
|
|
role="listbox"
|
|
aria-label="Artifact List"
|
|
aria-busy={isLoadingDetails}
|
|
>
|
|
{isLoadingDetails && !selectedArtifacts.length && (
|
|
<SurfacePanelState
|
|
kind="loading"
|
|
title="Loading Artifacts"
|
|
detail="Fetching artifact inventory for selected run."
|
|
className="py-3 text-xs"
|
|
/>
|
|
)}
|
|
{!isLoadingDetails && !selectedArtifacts.length && (
|
|
<SurfacePanelState
|
|
kind="empty"
|
|
title="No Artifacts Registered"
|
|
detail="This run has no artifact records yet."
|
|
className="py-3 text-xs"
|
|
/>
|
|
)}
|
|
{selectedArtifacts.map((artifact, index) => (
|
|
<div
|
|
key={`selected-artifact-${index}-${artifact.id}-${artifact.type}-${artifact.uri || 'no-uri'}`}
|
|
role="option"
|
|
tabIndex={0}
|
|
aria-selected={selectedArtifactId === artifact.id}
|
|
aria-label={`Artifact ${artifact.id}`}
|
|
className={`px-3 py-2 text-xs cursor-pointer ${selectedArtifactId === artifact.id ? 'bg-gray-800/70' : ''
|
|
}`}
|
|
onClick={() => handleSelectArtifact(artifact.id)}
|
|
onKeyDown={(event) => {
|
|
if (event.target !== event.currentTarget) return
|
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
event.preventDefault()
|
|
handleSelectArtifact(artifact.id)
|
|
}
|
|
}}
|
|
>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<p className="text-gray-300 truncate">{artifact.id}</p>
|
|
<CorpusStatusPill size="xs" tone={statusTone(artifact.status)}>
|
|
{artifact.status}
|
|
</CorpusStatusPill>
|
|
</div>
|
|
<p className="text-gray-500 mt-1 truncate">type: {artifact.type}</p>
|
|
<div className="flex items-center gap-2">
|
|
<p className="text-gray-500 truncate flex-1">uri: {artifact.uri || '-'}</p>
|
|
{artifact.uri && (
|
|
<span className="flex items-center gap-2">
|
|
<a
|
|
href={artifact.uri}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="text-indigo-300 hover:text-indigo-200"
|
|
title="Open artifact URI"
|
|
onClick={(event) => event.stopPropagation()}
|
|
>
|
|
<ItshoverExternalLinkIcon size={12} />
|
|
</a>
|
|
<button
|
|
className="text-gray-400 hover:text-gray-200"
|
|
title="Copy artifact URI"
|
|
onClick={(event) => {
|
|
event.stopPropagation()
|
|
void handleCopyArtifactUri(artifact.uri)
|
|
}}
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
</button>
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Integrity Resolution Loop</span>
|
|
<div className="flex flex-wrap items-center justify-end gap-1 max-w-[78%]">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshIntegrityReport()}
|
|
disabled={integrityRefreshControlState.disabled}
|
|
title={integrityRefreshControlState.title}
|
|
data-testid="runboard-integrity-loop-refresh-integrity"
|
|
>
|
|
{isLoadingIntegrity ? 'Refreshing...' : 'Refresh Integrity'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openIntegrityReportJson()}
|
|
>
|
|
Open Integrity JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/integrity/report`)}`, 'Integrity report command copied.')}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!integritySummary && !isLoadingIntegrity && !integrityError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh integrity report to view current issue counts and prioritize remediation actions.
|
|
</p>
|
|
)}
|
|
{integritySummary && (
|
|
<div className="space-y-2">
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs">
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Entities</p>
|
|
<p className="text-gray-200 font-medium">{integritySummary.entities}</p>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Links</p>
|
|
<p className="text-gray-200 font-medium">{integritySummary.links}</p>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Runs</p>
|
|
<p className="text-gray-200 font-medium">{integritySummary.runs}</p>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Artifacts</p>
|
|
<p className="text-gray-200 font-medium">{integritySummary.artifacts}</p>
|
|
</div>
|
|
</div>
|
|
{integrityIssueRows.length > 0 ? (
|
|
<ul className="space-y-1">
|
|
{integrityIssueRows.map((issue) => (
|
|
<li key={String(issue.key)} className="text-xs text-gray-300 flex items-center justify-between gap-2 rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<span className="flex items-center gap-2">
|
|
<ItshoverTriangleAlertIcon size={12} className="text-amber-300" />
|
|
{issue.label}
|
|
</span>
|
|
<span className="text-amber-300 font-medium">{issue.count}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<div className="text-xs text-emerald-300 rounded border border-emerald-700/40 bg-emerald-900/20 px-2 py-1.5 flex items-center gap-2">
|
|
<ItshoverShieldCheckIcon size={12} />
|
|
No priority integrity issues currently detected in monitored classes.
|
|
</div>
|
|
)}
|
|
<div className="rounded border border-gray-800 bg-gray-900/70">
|
|
<div className="px-2 py-1.5 border-b border-gray-800 text-[11px] text-gray-400">
|
|
Cross-Document Reference Fix Queue ({unresolvedReferenceIssues.length})
|
|
</div>
|
|
<div className="px-2 py-2 space-y-2">
|
|
{!unresolvedReferenceIssues.length && (
|
|
<p className="text-xs text-emerald-300">
|
|
No unresolved cross-document references currently detected.
|
|
</p>
|
|
)}
|
|
{unresolvedReferenceIssues.map((issue) => (
|
|
<div key={issue.document_id} className="rounded border border-gray-800 bg-gray-900 px-2 py-2 text-xs">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<p className="text-gray-200">
|
|
document: <span className="font-mono">{issue.document_id}</span>
|
|
</p>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
aria-label={`Auto-clean unresolved refs for ${issue.document_id}`}
|
|
onClick={() => void handleApplyReferenceCleanup(issue.document_id, issue.refs)}
|
|
disabled={activeReferenceFixDocumentId === issue.document_id}
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{activeReferenceFixDocumentId === issue.document_id ? 'Applying...' : 'Auto-clean'}
|
|
</button>
|
|
<button
|
|
aria-label={`Open entity ${issue.document_id}`}
|
|
onClick={() => openEntityJson(issue.document_id)}
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
>
|
|
Open Entity
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<p className="text-gray-400 mt-1">
|
|
refs: <span className="font-mono">{issue.refs.join(', ') || '-'}</span>
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<p className="text-[11px] text-gray-500">
|
|
report generated: {fmtTime(integrityGeneratedAt)}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-schema-pack-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Schema Pack Portability</span>
|
|
<div className="flex flex-wrap items-center justify-end gap-1 max-w-[78%]">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshSchemaPacks()}
|
|
disabled={isLoadingSchemaPacks}
|
|
data-testid="runboard-schema-pack-refresh"
|
|
>
|
|
{isLoadingSchemaPacks ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openSchemaPacksJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openSchemaPackManagerJson()}
|
|
>
|
|
Manager JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/schema/packs`)}`, 'Schema-pack command copied.')}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!schemaPacksReport && !isLoadingSchemaPacks && !schemaPacksError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh schema packs to verify concept-agnostic base-only vs extension-pack runtime behavior.
|
|
</p>
|
|
)}
|
|
{schemaPacksError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{schemaPacksError}
|
|
</div>
|
|
)}
|
|
{schemaPacksReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Active packs: <span className="text-gray-200" data-testid="runboard-schema-pack-active">{schemaPacksReport.active.join(', ') || 'none'}</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Concept-agnostic core: <span className="text-gray-200">{schemaPacksReport.compatibility.concept_agnostic_core_pack}</span>
|
|
{' '}· Extension {schemaPacksReport.compatibility.extension_pack_id}:{' '}
|
|
<span className={schemaPacksReport.compatibility.extension_pack_enabled ? 'text-amber-300' : 'text-emerald-300'} data-testid="runboard-schema-pack-extension-state">
|
|
{schemaPacksReport.compatibility.extension_pack_enabled ? 'enabled' : 'disabled'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Config raw: <span className="font-mono text-gray-300">{schemaPacksReport.configured_raw || '-'}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Available packs: {schemaPacksReport.available.map((pack) => `${pack.id}(${pack.entity_type_count}/${pack.relation_rule_count})`).join(', ') || 'none'}
|
|
</p>
|
|
<p className="text-gray-500">{schemaPacksReport.compatibility.note}</p>
|
|
</div>
|
|
)}
|
|
{schemaPackManagerError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-schema-pack-manager-error">
|
|
{schemaPackManagerError}
|
|
</div>
|
|
)}
|
|
{schemaPackManagerReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-2" data-testid="runboard-schema-pack-manager-panel">
|
|
<p className="text-gray-400">
|
|
Manager generation:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-schema-pack-manager-generation">
|
|
{schemaPackManagerReport.generation}
|
|
</span>
|
|
{' '}· active{' '}
|
|
<span className="text-gray-200" data-testid="runboard-schema-pack-manager-active">
|
|
{schemaPackManagerReport.active.join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Installed: <span className="font-mono text-gray-300" data-testid="runboard-schema-pack-manager-installed">
|
|
{schemaPackManagerReport.installed.map((pack) => `${pack.id}@${pack.version}`).join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
<div className="flex flex-wrap gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshSchemaPackManager()}
|
|
disabled={isLoadingSchemaPackManager}
|
|
data-testid="runboard-schema-pack-manager-refresh"
|
|
>
|
|
{isLoadingSchemaPackManager ? 'Refreshing...' : 'Refresh Manager'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightSchemaPacks(['base'])}
|
|
disabled={isLoadingSchemaPackManager}
|
|
data-testid="runboard-schema-pack-manager-preflight-base"
|
|
>
|
|
Preflight base
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightSchemaPacks(['base', 'tsl'])}
|
|
disabled={isLoadingSchemaPackManager}
|
|
data-testid="runboard-schema-pack-manager-preflight-base-tsl"
|
|
>
|
|
Preflight base+tsl
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-sky-700 text-sky-200 hover:bg-sky-900/30 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleApplySchemaPackPreflight()}
|
|
disabled={isLoadingSchemaPackManager || !schemaPackPreflightReport || !schemaPackPreflightReport.can_apply}
|
|
data-testid="runboard-schema-pack-manager-apply-preflight"
|
|
>
|
|
Apply preflight
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-emerald-700 text-emerald-200 hover:bg-emerald-900/30 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleEnableSchemaPack('tsl')}
|
|
disabled={isLoadingSchemaPackManager || schemaPackManagerReport.active.includes('tsl')}
|
|
data-testid="runboard-schema-pack-manager-enable-tsl"
|
|
>
|
|
Enable tsl
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-amber-700 text-amber-200 hover:bg-amber-900/30 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleDisableSchemaPack('tsl')}
|
|
disabled={isLoadingSchemaPackManager || !schemaPackManagerReport.active.includes('tsl')}
|
|
data-testid="runboard-schema-pack-manager-disable-tsl"
|
|
>
|
|
Disable tsl
|
|
</button>
|
|
</div>
|
|
{schemaPackPreflightReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-950/70 px-2 py-1.5 space-y-1">
|
|
<p className="text-gray-400">
|
|
Last preflight:{' '}
|
|
<span className="text-gray-200">{schemaPackPreflightReport.preflight_id}</span>
|
|
{' '}· can apply{' '}
|
|
<span className={schemaPackPreflightReport.can_apply ? 'text-emerald-300' : 'text-amber-300'}>
|
|
{schemaPackPreflightReport.can_apply ? 'yes' : 'no'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Target: <span className="font-mono text-gray-300">{schemaPackPreflightReport.target_active.join(', ') || 'none'}</span>
|
|
{' '}· blockers{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{schemaPackPreflightReport.blockers.map((blocker) => blocker.code).join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-structural-query-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Structural Query DSL</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightStructuralQuery(STRUCTURAL_QUERY_DOCUMENTS_DSL)}
|
|
disabled={isLoadingStructuralQuery}
|
|
data-testid="runboard-structural-query-preflight-documents"
|
|
>
|
|
{isLoadingStructuralQuery ? 'Running...' : 'Preflight docs'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleExecuteStructuralQuery(STRUCTURAL_QUERY_DOCUMENTS_DSL)}
|
|
disabled={isLoadingStructuralQuery}
|
|
data-testid="runboard-structural-query-execute-documents"
|
|
>
|
|
Execute docs
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightStructuralQuery(STRUCTURAL_QUERY_DOCUMENT_CONCEPT_LINKS_DSL)}
|
|
disabled={isLoadingStructuralQuery}
|
|
data-testid="runboard-structural-query-preflight-links"
|
|
>
|
|
Preflight links
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleExecuteStructuralQuery(STRUCTURAL_QUERY_DOCUMENT_CONCEPT_LINKS_DSL)}
|
|
disabled={isLoadingStructuralQuery}
|
|
data-testid="runboard-structural-query-execute-links"
|
|
>
|
|
Execute links
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s -X POST ${quoteShell(`${apiBaseUrl}/api/corpus/query/structural/execute`)} -H "Content-Type: application/json" -d '${JSON.stringify({ query: STRUCTURAL_QUERY_DOCUMENTS_DSL, persist: true })}'`,
|
|
'Structural query command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!structuralQueryPreflightReport && !structuralQueryExecuteReport && !isLoadingStructuralQuery && !structuralQueryError && (
|
|
<p className="text-xs text-gray-400">
|
|
Run preflight and execute to validate typed structural query planning against live corpus entities/links.
|
|
</p>
|
|
)}
|
|
{structuralQueryError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{structuralQueryError}
|
|
</div>
|
|
)}
|
|
{structuralQueryPreflightReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Preflight id:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-structural-query-last-id">
|
|
{structuralQueryPreflightReport.query_id}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Target:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-structural-query-last-target">
|
|
{structuralQueryPreflightReport.target}
|
|
</span>
|
|
{' '}· can execute{' '}
|
|
<span
|
|
className={structuralQueryPreflightReport.can_execute ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-structural-query-can-execute"
|
|
>
|
|
{structuralQueryPreflightReport.can_execute ? 'yes' : 'no'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Estimated matches:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-query-total-matches">
|
|
{structuralQueryPreflightReport.estimated_result?.total_matches ?? '-'}
|
|
</span>
|
|
{' '}· blockers{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{structuralQueryPreflightReport.blockers.map((blocker) => blocker.code).join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{structuralQueryExecuteReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-950/70 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Execute id:{' '}
|
|
<span className="text-gray-200">{structuralQueryExecuteReport.query_id}</span>
|
|
{' '}· hash <span className="font-mono text-gray-300">{structuralQueryExecuteReport.query_hash}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Matches{' '}
|
|
<span className="font-mono text-gray-300">{structuralQueryExecuteReport.result_summary.total_matches}</span>
|
|
{' '}· returned{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-query-returned">
|
|
{structuralQueryExecuteReport.result_summary.count}
|
|
</span>
|
|
{' '}· has_more {structuralQueryExecuteReport.result_summary.has_more ? 'yes' : 'no'}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Persisted: {structuralQueryOutput?.persisted ? 'yes' : 'no'}
|
|
{structuralQueryOutput?.path ? ` · ${structuralQueryOutput.path}` : ''}
|
|
</p>
|
|
{structuralQueryResult && (
|
|
<p className="text-gray-500">
|
|
Result keys: <span className="font-mono text-gray-300">{Object.keys(structuralQueryResult).join(', ') || 'none'}</span>
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-structural-transaction-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Structural Transactions (OCC)</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightStructuralTransaction(STRUCTURAL_TRANSACTION_NOOP_CHANGESET)}
|
|
disabled={isLoadingStructuralTransaction}
|
|
data-testid="runboard-structural-transaction-preflight-noop"
|
|
>
|
|
{isLoadingStructuralTransaction ? 'Running...' : 'Preflight no-op'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleExecuteStructuralTransaction(STRUCTURAL_TRANSACTION_NOOP_CHANGESET)}
|
|
disabled={isLoadingStructuralTransaction}
|
|
data-testid="runboard-structural-transaction-execute-noop"
|
|
>
|
|
Execute no-op
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshStructuralTransactionLast()}
|
|
disabled={isLoadingStructuralTransaction}
|
|
data-testid="runboard-structural-transaction-refresh-last"
|
|
>
|
|
Refresh last
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => window.open(`${apiBaseUrl}/api/corpus/transactions/structural/last`, '_blank', 'noopener,noreferrer')}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s -X POST ${quoteShell(`${apiBaseUrl}/api/corpus/transactions/structural/execute`)} -H \"Content-Type: application/json\" -d '${JSON.stringify({ changeset: STRUCTURAL_TRANSACTION_NOOP_CHANGESET, strict_occ: true, persist: true })}'`,
|
|
'Structural transaction command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!structuralTransactionPreflightReport && !structuralTransactionExecuteReport && !structuralTransactionLastReport && !isLoadingStructuralTransaction && !structuralTransactionError && (
|
|
<p className="text-xs text-gray-400">
|
|
Validate structural transaction change-set governance with optimistic concurrency controls and deterministic transaction hashes.
|
|
</p>
|
|
)}
|
|
{structuralTransactionError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{structuralTransactionError}
|
|
</div>
|
|
)}
|
|
{structuralTransactionPreflightReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Preflight transaction:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-structural-transaction-last-id">
|
|
{structuralTransactionPreflightReport.transaction_id}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Can apply:{' '}
|
|
<span
|
|
className={structuralTransactionPreflightReport.can_apply ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-structural-transaction-can-apply"
|
|
>
|
|
{structuralTransactionPreflightReport.can_apply ? 'yes' : 'no'}
|
|
</span>
|
|
{' '}· strict_occ {structuralTransactionPreflightReport.strict_occ ? 'yes' : 'no'}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Ops{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-transaction-operation-count">
|
|
{structuralTransactionPreflightReport.summary.operation_count}
|
|
</span>
|
|
{' '}· blockers{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{structuralTransactionPreflightReport.blockers.map((blocker) => blocker.code).join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{structuralTransactionExecuteReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-950/70 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Execute transaction:{' '}
|
|
<span className="text-gray-200">{structuralTransactionExecuteReport.transaction_id}</span>
|
|
{' '}· hash <span className="font-mono text-gray-300">{structuralTransactionExecuteReport.transaction_hash}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Applied{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-transaction-applied-count">
|
|
{structuralTransactionExecuteReport.summary.applied_count}
|
|
</span>
|
|
{' '}· duplicate {structuralTransactionExecuteReport.summary.duplicate_count}
|
|
{' '}· blocked {structuralTransactionExecuteReport.summary.blocked_count}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Persisted: {structuralTransactionOutput?.persisted ? 'yes' : 'no'}
|
|
{structuralTransactionOutput?.path ? ` · ${structuralTransactionOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
{structuralTransactionLastReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900/70 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Last artifact mode:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-structural-transaction-last-mode">
|
|
{structuralTransactionLastReport.mode}
|
|
</span>
|
|
{' '}· tx{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-transaction-last-artifact-id">
|
|
{structuralTransactionLastReport.transaction_id}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Snapshot before{' '}
|
|
<span className="font-mono text-gray-300">{structuralTransactionLastReport.structural_snapshot_hash_before}</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-structural-merge-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Structural Patch/Merge (3-way)</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightStructuralMerge(STRUCTURAL_MERGE_NOOP_PATCH)}
|
|
disabled={isLoadingStructuralMerge}
|
|
data-testid="runboard-structural-merge-preflight-noop"
|
|
>
|
|
{isLoadingStructuralMerge ? 'Running...' : 'Preflight no-op'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleExecuteStructuralMerge(STRUCTURAL_MERGE_NOOP_PATCH)}
|
|
disabled={isLoadingStructuralMerge}
|
|
data-testid="runboard-structural-merge-execute-noop"
|
|
>
|
|
Execute no-op
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshStructuralMergeLast()}
|
|
disabled={isLoadingStructuralMerge}
|
|
data-testid="runboard-structural-merge-refresh-last"
|
|
>
|
|
Refresh last
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => window.open(`${apiBaseUrl}/api/corpus/merge/structural/last`, '_blank', 'noopener,noreferrer')}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s -X POST ${quoteShell(`${apiBaseUrl}/api/corpus/merge/structural/preflight`)} -H \"Content-Type: application/json\" -d '${JSON.stringify({ ...STRUCTURAL_MERGE_NOOP_PATCH, persist: true })}'`,
|
|
'Structural merge command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!structuralMergePreflightReport && !structuralMergeExecuteReport && !structuralMergeLastReport && !isLoadingStructuralMerge && !structuralMergeError && (
|
|
<p className="text-xs text-gray-400">
|
|
Run three-way structural patch merge simulation and export conflict bundles with policy/arbitration telemetry.
|
|
</p>
|
|
)}
|
|
{structuralMergeError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{structuralMergeError}
|
|
</div>
|
|
)}
|
|
{structuralMergePreflightReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Preflight merge:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-structural-merge-last-id">
|
|
{structuralMergePreflightReport.merge_id}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Can apply:{' '}
|
|
<span
|
|
className={structuralMergePreflightReport.can_apply ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-structural-merge-can-apply"
|
|
>
|
|
{structuralMergePreflightReport.can_apply ? 'yes' : 'no'}
|
|
</span>
|
|
{' '}· policy {structuralMergePreflightReport.policy_pack}/{structuralMergePreflightReport.arbitration_strategy}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Conflicts{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-merge-conflicts-total">
|
|
{structuralMergePreflightReport.summary.conflicts_total}
|
|
</span>
|
|
{' '}· unresolved{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{structuralMergePreflightReport.summary.unresolved_conflicts}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{structuralMergeExecuteReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-950/70 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Execute merge:{' '}
|
|
<span className="text-gray-200">{structuralMergeExecuteReport.merge_id}</span>
|
|
{' '}· hash <span className="font-mono text-gray-300">{structuralMergeExecuteReport.merge_hash}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Merged ops{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-merge-merged-op-count">
|
|
{structuralMergeExecuteReport.summary.merged_operation_count}
|
|
</span>
|
|
{' '}· conflicts {structuralMergeExecuteReport.summary.conflicts_total}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Policy verdict: <span className={structuralMergeExecuteReport.policy.verdict === 'allow' ? 'text-emerald-300' : 'text-amber-300'}>
|
|
{structuralMergeExecuteReport.policy.verdict}
|
|
</span>
|
|
{' '}· risk <span className="font-mono text-gray-300">{structuralMergeExecuteReport.policy.risk_score}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Persisted: {structuralMergeOutput?.persisted ? 'yes' : 'no'}
|
|
{structuralMergeOutput?.merge_path ? ` · ${structuralMergeOutput.merge_path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
{structuralMergeConflictBundle && (
|
|
<div className="rounded border border-gray-800 bg-gray-900/70 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Conflict bundle: <span className="text-gray-200">{structuralMergeConflictBundle.merge_id}</span>
|
|
{' '}· total {structuralMergeConflictBundle.summary.conflicts_total}
|
|
{' '}· unresolved {structuralMergeConflictBundle.summary.unresolved_conflicts}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Blockers: <span className="font-mono text-gray-300">{structuralMergeConflictBundle.blockers.map((blocker) => blocker.code).join(', ') || 'none'}</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{structuralMergeLastReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900/70 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Last artifact mode:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-structural-merge-last-mode">
|
|
{structuralMergeLastReport.mode}
|
|
</span>
|
|
{' '}· merge{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-structural-merge-last-artifact-id">
|
|
{structuralMergeLastReport.merge_id}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-novelty-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Novelty Gradient & Saturation</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineNovelty({ persist: true })}
|
|
disabled={isLoadingEngineNovelty}
|
|
data-testid="runboard-engine-novelty-refresh"
|
|
>
|
|
{isLoadingEngineNovelty ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineNoveltyJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/novelty?profile_id=runboard_operator&scope=all&persist=true`)}`,
|
|
'Novelty command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineNoveltyReport && !isLoadingEngineNovelty && !engineNoveltyError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh novelty report to inspect structural novelty gradient and saturation drift against baseline.
|
|
</p>
|
|
)}
|
|
{engineNoveltyError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{engineNoveltyError}
|
|
</div>
|
|
)}
|
|
{engineNoveltyReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Novelty score:{' '}
|
|
<span className="text-indigo-200 font-medium" data-testid="runboard-engine-novelty-score">
|
|
{engineNoveltyReport.novelty.novelty_gradient_score}
|
|
</span>
|
|
{' '}· Trend:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-novelty-trend">
|
|
{engineNoveltyReport.novelty.novelty_trend_state}
|
|
</span>
|
|
{' '}· Δ{' '}
|
|
<span className="text-gray-300">
|
|
{engineNoveltyReport.novelty.novelty_gradient_delta ?? '-'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Saturation score:{' '}
|
|
<span className="text-indigo-200 font-medium" data-testid="runboard-engine-novelty-saturation-score">
|
|
{engineNoveltyReport.saturation.saturation_score}
|
|
</span>
|
|
{' '}· State:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-novelty-saturation-state">
|
|
{engineNoveltyReport.saturation.saturation_state}
|
|
</span>
|
|
{' '}· Density{' '}
|
|
<span className="text-gray-300">{engineNoveltyReport.saturation.structural_density}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Growth E/L/R/A:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{[
|
|
engineNoveltyReport.novelty.growth.entities,
|
|
engineNoveltyReport.novelty.growth.links,
|
|
engineNoveltyReport.novelty.growth.runs,
|
|
engineNoveltyReport.novelty.growth.artifacts,
|
|
].join('/')}
|
|
</span>
|
|
{' '}· Growth rate{' '}
|
|
<span className="font-mono text-gray-300">{engineNoveltyReport.novelty.growth_rate_pct}%</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
New types: entity[{engineNoveltyReport.novelty.new_entity_types.join(', ') || 'none'}], relation[{engineNoveltyReport.novelty.new_relation_types.join(', ') || 'none'}]
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Baseline loaded: {engineNoveltyReport.baseline.loaded ? 'yes' : 'no'} · snapshot {engineNoveltyReport.baseline.snapshot_id || '-'}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(engineNoveltyReport.generated_at)}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-capabilities-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Adapter Capability Negotiation</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineCapabilities({ persist: true })}
|
|
disabled={isLoadingEngineCapabilities}
|
|
data-testid="runboard-engine-capabilities-refresh"
|
|
>
|
|
{isLoadingEngineCapabilities ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineCapabilitiesJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/capabilities?profile_id=runboard_operator&scope=all&sandbox_profile=strict&required_capabilities=json_mode,deterministic_seed&persist=true`)}`,
|
|
'Capability negotiation command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineCapabilitiesReport && !isLoadingEngineCapabilities && !engineCapabilitiesError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh adapter capability negotiation to verify strict sandbox compatibility before route execution.
|
|
</p>
|
|
)}
|
|
{engineCapabilitiesError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{engineCapabilitiesError}
|
|
</div>
|
|
)}
|
|
{engineCapabilitiesReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Verdict:{' '}
|
|
<span
|
|
className={engineCapabilitiesReport.negotiation.policy_verdict === 'allow' ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-capabilities-verdict"
|
|
>
|
|
{engineCapabilitiesReport.negotiation.policy_verdict}
|
|
</span>
|
|
{' '}· Compatible:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-capabilities-compatible">
|
|
{engineCapabilitiesReport.negotiation.compatible ? 'yes' : 'no'}
|
|
</span>
|
|
{' '}· Sandbox{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-capabilities-sandbox">
|
|
{engineCapabilitiesReport.negotiation.sandbox_profile.id}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Selected adapter:{' '}
|
|
<span className="font-mono text-indigo-200" data-testid="runboard-engine-capabilities-selected-adapter">
|
|
{engineCapabilitiesReport.negotiation.selected_adapter_id || '-'}
|
|
</span>
|
|
{' '}· transport{' '}
|
|
<span className="text-gray-300">{engineCapabilitiesReport.negotiation.selected_adapter_transport || '-'}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Required capabilities: <span className="font-mono text-gray-300" data-testid="runboard-engine-capabilities-required">
|
|
{engineCapabilitiesReport.negotiation.required_capabilities.join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Missing required: <span className="font-mono text-gray-300" data-testid="runboard-engine-capabilities-missing">
|
|
{engineCapabilitiesReport.negotiation.missing_required_capabilities.join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Deterministic required: {engineCapabilitiesReport.negotiation.sandbox_profile.deterministic_required ? 'yes' : 'no'}
|
|
{' '}· hash <span className="font-mono text-gray-300">{engineCapabilitiesReport.negotiation.negotiation_hash}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(engineCapabilitiesReport.generated_at)}
|
|
{engineCapabilitiesOutput?.persisted ? ` · persisted ${engineCapabilitiesOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-export-manifest-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Ingestion/Export Adapter Manifest</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineExportManifest({ persist: true, manifestScope: 'all', artifactKind: 'evidence_bundle' })}
|
|
disabled={isLoadingEngineExportManifest}
|
|
data-testid="runboard-engine-export-manifest-refresh"
|
|
>
|
|
{isLoadingEngineExportManifest ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineExportManifestJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/export-manifest?profile_id=runboard_operator&scope=all&manifest_scope=all&artifact_kind=evidence_bundle&persist=true`)}`,
|
|
'Ingestion/export manifest command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineExportManifestReport && !isLoadingEngineExportManifest && !engineExportManifestError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh adapter manifest to validate deterministic export contracts and provenance signing coverage.
|
|
</p>
|
|
)}
|
|
{engineExportManifestError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{engineExportManifestError}
|
|
</div>
|
|
)}
|
|
{engineExportManifestReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Scope:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-export-manifest-scope">
|
|
{engineExportManifestReport.manifest.manifest_scope}
|
|
</span>
|
|
{' '}· artifact kind{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineExportManifestReport.manifest.artifact_kind || '-'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Selected ingest/export:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineExportManifestReport.manifest.selected_ingest_adapter_id || '-'}
|
|
</span>
|
|
{' '} /{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-export-manifest-selected-export">
|
|
{engineExportManifestReport.manifest.selected_export_adapter_id || '-'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Interfaces: ingest{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineExportManifestReport.manifest.ingestion_interfaces.length}
|
|
</span>
|
|
{' '}· export{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineExportManifestReport.manifest.export_interfaces.length}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Manifest hash:{' '}
|
|
<span className="font-mono text-gray-300 break-all" data-testid="runboard-engine-export-manifest-hash">
|
|
{engineExportManifestReport.manifest.manifest_hash}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Provenance signature:{' '}
|
|
<span className="font-mono text-indigo-200 break-all">
|
|
{engineExportManifestReport.provenance.signature}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(engineExportManifestReport.generated_at)}
|
|
{engineExportManifestOutput?.persisted ? ` · persisted ${engineExportManifestOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-observability-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Observability Benchmark & Provenance</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineObservability({ persist: true, sampleLimit: 200, benchmarkIterations: 5 })}
|
|
disabled={isLoadingEngineObservability}
|
|
data-testid="runboard-engine-observability-refresh"
|
|
>
|
|
{isLoadingEngineObservability ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineObservabilityJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/observability?profile_id=runboard_operator&scope=all&sample_limit=200&benchmark_iterations=5&persist=true`)}`,
|
|
'Observability command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineObservabilityReport && !isLoadingEngineObservability && !engineObservabilityError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh observability report to evaluate benchmark timings and signed evidence provenance.
|
|
</p>
|
|
)}
|
|
{engineObservabilityError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{engineObservabilityError}
|
|
</div>
|
|
)}
|
|
{engineObservabilityReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Benchmark:{' '}
|
|
<span className="text-gray-200">
|
|
{engineObservabilityReport.observability.benchmark.benchmark_iterations}
|
|
</span>
|
|
{' '}iter · sampled{' '}
|
|
<span className="text-gray-200">
|
|
{engineObservabilityReport.observability.benchmark.sampled_runs_count}
|
|
</span>
|
|
{' '}/{' '}
|
|
<span className="text-gray-200">
|
|
{engineObservabilityReport.observability.benchmark.filtered_runs_total}
|
|
</span>
|
|
{' '}· baseline{' '}
|
|
<span className="text-gray-200">
|
|
{engineObservabilityReport.observability.benchmark.baseline_state}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Queue p95{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineObservabilityReport.observability.benchmark.timing_ms.queue_wait.p95 ?? '-'}
|
|
</span>
|
|
{' '}ms · pipeline p95{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineObservabilityReport.observability.benchmark.timing_ms.pipeline_total.p95 ?? '-'}
|
|
</span>
|
|
{' '}ms
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Throughput: runs/h{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineObservabilityReport.observability.benchmark.throughput.terminal_runs_per_hour ?? '-'}
|
|
</span>
|
|
{' '}· artifacts/h{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineObservabilityReport.observability.benchmark.throughput.artifacts_ready_per_hour ?? '-'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Deterministic:{' '}
|
|
<span
|
|
className={engineObservabilityReport.observability.benchmark.deterministic.consistent ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-observability-consistent"
|
|
>
|
|
{engineObservabilityReport.observability.benchmark.deterministic.consistent ? 'yes' : 'no'}
|
|
</span>
|
|
{' '}· hash{' '}
|
|
<span className="font-mono text-gray-300 break-all" data-testid="runboard-engine-observability-benchmark-hash">
|
|
{engineObservabilityReport.observability.benchmark.deterministic.benchmark_signature_hash}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Provenance signature:{' '}
|
|
<span className="font-mono text-indigo-200 break-all" data-testid="runboard-engine-observability-signature">
|
|
{engineObservabilityReport.provenance.signature}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Signer: {engineObservabilityReport.provenance.signature_algorithm} · source {engineObservabilityReport.provenance.key_source}
|
|
{' '}· fingerprint <span className="font-mono text-gray-300">{engineObservabilityReport.provenance.key_fingerprint}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(engineObservabilityReport.generated_at)}
|
|
{engineObservabilityOutput?.persisted ? ` · persisted ${engineObservabilityOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-policy-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Policy Engine & Arbitration</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEnginePolicy({ persist: true, policyPack: 'balanced', strategy: 'weighted', maxIssues: 200 })}
|
|
disabled={isLoadingEnginePolicy}
|
|
data-testid="runboard-engine-policy-refresh"
|
|
>
|
|
{isLoadingEnginePolicy ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEnginePolicy({ persist: true, policyPack: 'conservative', strategy: 'fail_fast', maxIssues: 200 })}
|
|
disabled={isLoadingEnginePolicy}
|
|
data-testid="runboard-engine-policy-refresh-conservative"
|
|
>
|
|
Conservative
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEnginePolicyJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/policy?profile_id=runboard_operator&scope=all&policy_pack=balanced&arbitration_strategy=weighted&persist=true`)}`,
|
|
'Policy/arbitration command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!enginePolicyReport && !isLoadingEnginePolicy && !enginePolicyError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh policy/arbitration to inspect contradiction risk scoring and hold/allow verdict logic.
|
|
</p>
|
|
)}
|
|
{enginePolicyError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-engine-policy-error">
|
|
{enginePolicyError}
|
|
</div>
|
|
)}
|
|
{enginePolicyReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Verdict:{' '}
|
|
<span
|
|
className={(enginePolicyReport.policy?.verdict ?? '') === 'allow' ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-policy-verdict"
|
|
>
|
|
{enginePolicyReport.policy?.verdict ?? 'unknown'}
|
|
</span>
|
|
{' '}· State{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-policy-state">
|
|
{enginePolicyReport.policy?.state ?? 'unknown'}
|
|
</span>
|
|
{' '}· Pack{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-policy-pack">
|
|
{enginePolicyReport.policy?.policy_pack?.id ?? 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Strategy: <span className="font-mono text-gray-300" data-testid="runboard-engine-policy-strategy">{enginePolicyReport.policy?.arbitration_strategy ?? 'n/a'}</span>
|
|
{' '}· risk <span className="font-mono text-gray-300" data-testid="runboard-engine-policy-risk-score">{enginePolicyReport.policy?.risk_score ?? 0}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Severity counts C/H/M/L:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-policy-severity-counts">
|
|
{[
|
|
enginePolicyReport.policy?.severity_counts?.critical ?? 0,
|
|
enginePolicyReport.policy?.severity_counts?.high ?? 0,
|
|
enginePolicyReport.policy?.severity_counts?.medium ?? 0,
|
|
enginePolicyReport.policy?.severity_counts?.low ?? 0,
|
|
].join('/')}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Issues: <span className="font-mono text-gray-300">{enginePolicyReport.issues?.count ?? 0}</span> of{' '}
|
|
<span className="font-mono text-gray-300">{enginePolicyReport.issues?.total ?? 0}</span>
|
|
{' '}· blockers{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-policy-blockers">
|
|
{(enginePolicyReport.policy?.blocker_codes ?? []).join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(enginePolicyReport.generated_at)}
|
|
{enginePolicyOutput?.persisted ? ` · persisted ${enginePolicyOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-excalidraw-policy-ops-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex flex-wrap items-start justify-between gap-2">
|
|
<span>Whiteboard Policy Ops Control Plane</span>
|
|
<div className="flex max-w-full flex-wrap items-center justify-end gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshExcalidrawPolicyDrift()}
|
|
disabled={isLoadingExcalidrawPolicyDrift}
|
|
data-testid="runboard-excalidraw-policy-drift-refresh"
|
|
>
|
|
{isLoadingExcalidrawPolicyDrift ? 'Refreshing...' : 'Drift'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshExcalidrawPolicyDriftRollup()}
|
|
disabled={isLoadingExcalidrawPolicyDriftRollup}
|
|
data-testid="runboard-excalidraw-policy-drift-rollup-refresh"
|
|
>
|
|
{isLoadingExcalidrawPolicyDriftRollup ? 'Refreshing...' : 'Drift Rollup'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightExcalidrawPolicy()}
|
|
disabled={isLoadingExcalidrawPolicyPreflight}
|
|
data-testid="runboard-excalidraw-policy-preflight-refresh"
|
|
>
|
|
{isLoadingExcalidrawPolicyPreflight ? 'Preflighting...' : 'Preflight'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePreflightExcalidrawPolicyBatch()}
|
|
disabled={isLoadingExcalidrawPolicyBatchPreflight}
|
|
data-testid="runboard-excalidraw-policy-batch-preflight-refresh"
|
|
>
|
|
{isLoadingExcalidrawPolicyBatchPreflight ? 'Preflighting...' : 'Batch Preflight'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshExcalidrawPolicyAudit()}
|
|
disabled={isLoadingExcalidrawPolicyAudit}
|
|
data-testid="runboard-excalidraw-policy-audit-refresh"
|
|
>
|
|
{isLoadingExcalidrawPolicyAudit ? 'Refreshing...' : 'Audit'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openExcalidrawPolicyDriftJson()}
|
|
>
|
|
Drift JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openExcalidrawPolicyDriftRollupJson()}
|
|
>
|
|
Rollup JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openExcalidrawPolicyAuditJson()}
|
|
>
|
|
Audit JSON
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
<input
|
|
value={excalidrawPolicySceneId}
|
|
onChange={(event) => setExcalidrawPolicySceneId(event.target.value)}
|
|
placeholder="scene id (required)"
|
|
className="rounded border border-gray-700 bg-gray-900 text-xs text-gray-200 px-2 py-1.5"
|
|
data-testid="runboard-excalidraw-policy-scene-id"
|
|
/>
|
|
<select
|
|
value={excalidrawPolicyPreset}
|
|
onChange={(event) => setExcalidrawPolicyPreset(event.target.value)}
|
|
className="rounded border border-gray-700 bg-gray-900 text-xs text-gray-200 px-2 py-1.5"
|
|
data-testid="runboard-excalidraw-policy-preset"
|
|
>
|
|
<option value="strict_pilot">strict_pilot</option>
|
|
<option value="review_advisory">review_advisory</option>
|
|
<option value="open_discovery">open_discovery</option>
|
|
</select>
|
|
</div>
|
|
<textarea
|
|
value={excalidrawPolicyBatchSceneIds}
|
|
onChange={(event) => setExcalidrawPolicyBatchSceneIds(event.target.value)}
|
|
placeholder="batch scene ids (comma/newline separated)"
|
|
className="rounded border border-gray-700 bg-gray-900 text-xs text-gray-200 px-2 py-1.5 min-h-[68px]"
|
|
data-testid="runboard-excalidraw-policy-batch-scene-ids"
|
|
/>
|
|
<div className="text-[11px] text-gray-500">
|
|
Controls drift telemetry, preflight command-pack routing, override audit timeline, and batch escalation linkage into Gate E / Gate F evidence packets.
|
|
</div>
|
|
{excalidrawPolicyDriftError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-excalidraw-policy-drift-error">
|
|
{excalidrawPolicyDriftError}
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyDriftRollupError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-excalidraw-policy-drift-rollup-error">
|
|
{excalidrawPolicyDriftRollupError}
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyPreflightError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-excalidraw-policy-preflight-error">
|
|
{excalidrawPolicyPreflightError}
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyBatchPreflightError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-excalidraw-policy-batch-preflight-error">
|
|
{excalidrawPolicyBatchPreflightError}
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyAuditError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-excalidraw-policy-audit-error">
|
|
{excalidrawPolicyAuditError}
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyDriftReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Drift state:{' '}
|
|
<span className={excalidrawPolicyDriftReport.drift.has_drift ? 'text-amber-300' : 'text-emerald-300'} data-testid="runboard-excalidraw-policy-drift-state">
|
|
{excalidrawPolicyDriftReport.drift.state}
|
|
</span>
|
|
{' '}· preset{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-drift-preset">
|
|
{excalidrawPolicyDriftReport.current_policy.preset_id || 'custom'}
|
|
</span>
|
|
{' '}· drift signals{' '}
|
|
<span className="font-mono text-gray-300">{excalidrawPolicyDriftReport.drift.drift_count}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Recommendation: {excalidrawPolicyDriftReport.recommendation}
|
|
</p>
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyDriftRollupReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Drift rollup:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-rollup-escalation">
|
|
{excalidrawPolicyDriftRollupReport.rollup.escalation_state}
|
|
</span>
|
|
{' '}· recommended gate{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-rollup-recommended-gate">
|
|
{excalidrawPolicyDriftRollupReport.rollup.recommended_gate}
|
|
</span>
|
|
{' '}· requested{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-rollup-total-scenes">
|
|
{excalidrawPolicyDriftRollupReport.summary.total_scenes_requested}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Route states R/W/B/N:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-rollup-route-counts">
|
|
{[
|
|
excalidrawPolicyDriftRollupReport.summary.route_state_counts.ready,
|
|
excalidrawPolicyDriftRollupReport.summary.route_state_counts.warning,
|
|
excalidrawPolicyDriftRollupReport.summary.route_state_counts.blocked,
|
|
excalidrawPolicyDriftRollupReport.summary.route_state_counts.not_found,
|
|
].join('/')}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Drift states A/D/B/M:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-rollup-drift-counts">
|
|
{[
|
|
excalidrawPolicyDriftRollupReport.summary.drift_state_counts.aligned,
|
|
excalidrawPolicyDriftRollupReport.summary.drift_state_counts.drift_detected,
|
|
excalidrawPolicyDriftRollupReport.summary.drift_state_counts.baseline_missing,
|
|
excalidrawPolicyDriftRollupReport.summary.drift_state_counts.scene_missing,
|
|
].join('/')}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Gate evidence: <span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-rollup-evidence-pack-id">{excalidrawPolicyDriftRollupReport.gate_evidence_pack.evidence_pack_id}</span>
|
|
{' '}· status <span className="font-mono text-gray-300">{excalidrawPolicyDriftRollupReport.gate_evidence_pack.status}</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyPreflightReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Preflight:{' '}
|
|
<span className={excalidrawPolicyPreflightReport.route.state === 'blocked' ? 'text-amber-300' : 'text-emerald-300'} data-testid="runboard-excalidraw-policy-preflight-state">
|
|
{excalidrawPolicyPreflightReport.route.state}
|
|
</span>
|
|
{' '}· blocked{' '}
|
|
<span className="font-mono text-gray-300">{excalidrawPolicyPreflightReport.summary.blocked}</span>
|
|
{' '}· warnings{' '}
|
|
<span className="font-mono text-gray-300">{excalidrawPolicyPreflightReport.summary.warnings}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Command pack:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-preflight-command-pack-size">
|
|
{excalidrawPolicyPreflightReport.route.command_pack.length}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyBatchPreflightReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Batch preflight:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-batch-preflight-escalation">
|
|
{excalidrawPolicyBatchPreflightReport.rollup.escalation_state}
|
|
</span>
|
|
{' '}· scenes{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-batch-preflight-scenes">
|
|
{excalidrawPolicyBatchPreflightReport.summary.total_scenes_requested}
|
|
</span>
|
|
{' '}· missing{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-batch-preflight-missing">
|
|
{excalidrawPolicyBatchPreflightReport.summary.scenes_not_found}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Route states R/W/B/N:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-batch-preflight-route-counts">
|
|
{[
|
|
excalidrawPolicyBatchPreflightReport.summary.route_state_counts.ready,
|
|
excalidrawPolicyBatchPreflightReport.summary.route_state_counts.warning,
|
|
excalidrawPolicyBatchPreflightReport.summary.route_state_counts.blocked,
|
|
excalidrawPolicyBatchPreflightReport.summary.route_state_counts.not_found,
|
|
].join('/')}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Gate evidence links:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-batch-preflight-gate-e">
|
|
{excalidrawPolicyBatchPreflightReport.gate_evidence_pack.gates.gate_e.gate_id}
|
|
</span>
|
|
{' '}·{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-batch-preflight-gate-f">
|
|
{excalidrawPolicyBatchPreflightReport.gate_evidence_pack.gates.gate_f.gate_id}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{excalidrawPolicyAuditReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Audit entries:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-audit-total">
|
|
{excalidrawPolicyAuditReport.summary.total_entries}
|
|
</span>
|
|
{' '}· overrides{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-audit-overrides">
|
|
{excalidrawPolicyAuditReport.summary.override_entries}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Latest mode:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{excalidrawPolicyAuditReport.timeline[0]?.policy?.mode || 'n/a'}
|
|
</span>
|
|
{' '}· latest drift{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-excalidraw-policy-audit-latest-drift">
|
|
{excalidrawPolicyAuditReport.timeline[0]?.drift_vs_current?.state || 'n/a'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* PDFdelta Compare — C1 (Andromeda task list) */}
|
|
<div id="pdfdelta-panel" className="border border-gray-800 rounded" data-testid="runboard-pdfdelta-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>PDFdelta Corpus Ingestion</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePdfdeltaProbe()}
|
|
disabled={isLoadingPdfdeltaProbe}
|
|
data-testid="runboard-pdfdelta-probe"
|
|
>
|
|
{isLoadingPdfdeltaProbe ? 'Probing...' : 'Probe Adapter'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePdfdeltaCompare()}
|
|
disabled={isLoadingPdfdeltaCompare}
|
|
data-testid="runboard-pdfdelta-compare"
|
|
>
|
|
{isLoadingPdfdeltaCompare ? 'Comparing...' : 'Compare'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handlePdfdeltaCompareLast()}
|
|
disabled={isLoadingPdfdeltaCompareLast}
|
|
data-testid="runboard-pdfdelta-compare-last"
|
|
>
|
|
{isLoadingPdfdeltaCompareLast ? 'Loading...' : 'Last Report'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{pdfdeltaHandoff.note && (
|
|
<div
|
|
className="rounded border border-violet-500/30 bg-violet-950/20 px-2 py-1.5 text-xs text-violet-100"
|
|
data-testid="runboard-pdfdelta-handoff-note"
|
|
>
|
|
{pdfdeltaHandoff.note}
|
|
</div>
|
|
)}
|
|
{/* Path inputs */}
|
|
<div className="grid grid-cols-1 gap-1.5">
|
|
<label className="text-[11px] text-gray-500">Old PDF path</label>
|
|
<input
|
|
type="text"
|
|
value={pdfdeltaOldPdfPath}
|
|
onChange={(e) => setPdfdeltaOldPdfPath(e.target.value)}
|
|
className="w-full bg-gray-900 border border-gray-700 rounded px-2 py-1 text-xs text-gray-100 font-mono focus:outline-none focus:border-gray-500"
|
|
data-testid="runboard-pdfdelta-old-path"
|
|
placeholder="mirrors/pdfdelta/examples/old.pdf"
|
|
/>
|
|
<label className="text-[11px] text-gray-500">New PDF path</label>
|
|
<input
|
|
type="text"
|
|
value={pdfdeltaNewPdfPath}
|
|
onChange={(e) => setPdfdeltaNewPdfPath(e.target.value)}
|
|
className="w-full bg-gray-900 border border-gray-700 rounded px-2 py-1 text-xs text-gray-100 font-mono focus:outline-none focus:border-gray-500"
|
|
data-testid="runboard-pdfdelta-new-path"
|
|
placeholder="mirrors/pdfdelta/examples/new.pdf"
|
|
/>
|
|
</div>
|
|
{!pdfdeltaProbeReport && !pdfdeltaCompareReport && !isLoadingPdfdeltaProbe && !isLoadingPdfdeltaCompare && !pdfdeltaError && (
|
|
<p className="text-xs text-gray-400">
|
|
Probe the pdfdelta adapter, then compare two PDF files. Results include compare_id, content hash, source provenance, and signed evidence.
|
|
</p>
|
|
)}
|
|
{pdfdeltaError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-pdfdelta-error">
|
|
{pdfdeltaError}
|
|
</div>
|
|
)}
|
|
{pdfdeltaProbeReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1" data-testid="runboard-pdfdelta-probe-report">
|
|
<p className="text-gray-400">
|
|
Adapter:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-pdfdelta-probe-interface-id">
|
|
{pdfdeltaProbeReport.report?.adapter_interface?.interface_id ?? 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Provenance fields:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{(pdfdeltaProbeReport.report?.adapter_interface?.provenance_fields ?? []).join(', ') || 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Probe attempts:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{pdfdeltaProbeReport.report?.probe?.attempts?.length ?? 0}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{pdfdeltaCompareReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1" data-testid="runboard-pdfdelta-compare-report">
|
|
<p className="text-gray-400">
|
|
Compare ID:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-pdfdelta-compare-id">
|
|
{pdfdeltaCompareReport.report?.compare?.compare_id ?? 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Hash:{' '}
|
|
<span className="font-mono text-gray-300 text-[10px]">
|
|
{pdfdeltaCompareReport.report?.compare?.compare_hash?.slice(0, 16) ?? 'n/a'}…
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Old exists:{' '}
|
|
<span className={`font-mono ${pdfdeltaCompareReport.report?.compare?.source?.old_exists ? 'text-emerald-400' : 'text-red-400'}`}>
|
|
{pdfdeltaCompareReport.report?.compare?.source?.old_exists ? 'yes' : 'no'}
|
|
</span>
|
|
{' '}· New exists:{' '}
|
|
<span className={`font-mono ${pdfdeltaCompareReport.report?.compare?.source?.new_exists ? 'text-emerald-400' : 'text-red-400'}`}>
|
|
{pdfdeltaCompareReport.report?.compare?.source?.new_exists ? 'yes' : 'no'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Adapter binding:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{pdfdeltaCompareReport.report?.compare?.ingestion_provenance?.adapter_binding ?? 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Pipeline version:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{pdfdeltaCompareReport.report?.compare?.ingestion_provenance?.pipeline_version ?? 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Evidence signature:{' '}
|
|
<span className="font-mono text-gray-300 text-[10px]">
|
|
{pdfdeltaCompareReport.report?.evidence?.provenance?.signature?.slice(0, 16) ?? 'n/a'}…
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Persisted:{' '}
|
|
<span className={`font-mono ${pdfdeltaCompareReport.output?.persisted ? 'text-emerald-400' : 'text-gray-400'}`}>
|
|
{pdfdeltaCompareReport.output?.persisted ? `yes · ${pdfdeltaCompareReport.output.path}` : 'no'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-gate-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Generic Gate Evaluation Packet</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineGateEvaluation({
|
|
persist: true,
|
|
gateId: 'gate_f_engine',
|
|
policyPack: 'balanced',
|
|
strategy: 'weighted',
|
|
sandboxProfile: 'strict',
|
|
requiredCapabilities: 'json_mode,deterministic_seed',
|
|
manifestScope: 'all',
|
|
artifactKind: 'evidence_bundle',
|
|
replayCount: 3,
|
|
sampleLimit: 200,
|
|
benchmarkIterations: 5,
|
|
maxIssues: 200,
|
|
})}
|
|
disabled={isLoadingEngineGateEvaluation}
|
|
data-testid="runboard-engine-gate-refresh"
|
|
>
|
|
{isLoadingEngineGateEvaluation ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineGateEvaluation({
|
|
persist: true,
|
|
gateId: 'gate_f_engine',
|
|
policyPack: 'conservative',
|
|
strategy: 'fail_fast',
|
|
sandboxProfile: 'strict',
|
|
requiredCapabilities: 'json_mode,deterministic_seed',
|
|
manifestScope: 'all',
|
|
artifactKind: 'evidence_bundle',
|
|
replayCount: 3,
|
|
sampleLimit: 200,
|
|
benchmarkIterations: 5,
|
|
maxIssues: 200,
|
|
})}
|
|
disabled={isLoadingEngineGateEvaluation}
|
|
data-testid="runboard-engine-gate-refresh-conservative"
|
|
>
|
|
Conservative
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineGateEvaluationJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/gates/evaluate?profile_id=runboard_operator&scope=all&gate_id=gate_f_engine&gate_policy_pack=balanced&arbitration_strategy=weighted&sandbox_profile=strict&required_capabilities=json_mode,deterministic_seed&manifest_scope=all&artifact_kind=evidence_bundle&replay_count=3&sample_limit=200&benchmark_iterations=5&max_issues=200&persist=true`)}`,
|
|
'Generic gate decision command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineGateEvaluationReport && !isLoadingEngineGateEvaluation && !engineGateEvaluationError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh generic gate evaluation to inspect deterministic decision packet verdict, blockers, and remediation action queue.
|
|
</p>
|
|
)}
|
|
{engineGateEvaluationError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-engine-gate-error">
|
|
{engineGateEvaluationError}
|
|
</div>
|
|
)}
|
|
{engineGateEvaluationReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Verdict:{' '}
|
|
<span
|
|
className={(engineGateEvaluationReport.gate?.verdict ?? '') === 'allow' ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-gate-verdict"
|
|
>
|
|
{engineGateEvaluationReport.gate?.verdict ?? 'unknown'}
|
|
</span>
|
|
{' '}· State{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-gate-state">
|
|
{engineGateEvaluationReport.gate?.state ?? 'unknown'}
|
|
</span>
|
|
{' '}· Gate{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-gate-id">
|
|
{engineGateEvaluationReport.gate?.gate_id ?? 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Decision ID: <span className="font-mono text-gray-300" data-testid="runboard-engine-gate-decision-id">{engineGateEvaluationReport.gate?.decision_id ?? 'n/a'}</span>
|
|
{' '}· hash <span className="font-mono text-gray-300 break-all" data-testid="runboard-engine-gate-decision-hash">{engineGateEvaluationReport.gate?.decision_hash ?? 'n/a'}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Pack{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-policy-pack">{engineGateEvaluationReport.gate?.policy_pack?.id ?? 'n/a'}</span>
|
|
{' '}· strategy{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-strategy">{engineGateEvaluationReport.gate?.arbitration_strategy ?? 'n/a'}</span>
|
|
{' '}· risk <span className="font-mono text-gray-300" data-testid="runboard-engine-gate-risk-score">{engineGateEvaluationReport.gate?.risk_score ?? 0}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Failed checks: <span className="font-mono text-gray-300" data-testid="runboard-engine-gate-failed">{engineGateEvaluationReport.checks?.failed ?? 0}</span>
|
|
{' '}/ {engineGateEvaluationReport.checks?.total ?? 0}
|
|
{' '}· blockers{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-blockers">
|
|
{(engineGateEvaluationReport.gate?.blocker_codes ?? []).join(', ') || 'none'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Action queue:{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-action-queue-size">
|
|
{Array.isArray(engineGateEvaluationReport.action_queue) ? engineGateEvaluationReport.action_queue.length : 0}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(engineGateEvaluationReport.generated_at)}
|
|
{engineGateEvaluationOutput?.persisted ? ` · persisted ${engineGateEvaluationOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-gate-verify-suite-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex flex-wrap items-start justify-between gap-2">
|
|
<span>Gate Verify Suite Toolkit</span>
|
|
<div className="flex max-w-full flex-wrap items-center justify-end gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Refresh Gate Verify Suite', 'panel')
|
|
void handleRefreshEngineGateVerifySuite({
|
|
persist: true,
|
|
execute: false,
|
|
fuzzSeed: '1337',
|
|
fuzzCount: 8,
|
|
perfIterations: 1,
|
|
perfWarmup: 0,
|
|
perfP95MaxMs: 5000,
|
|
perfAvgMaxMs: 3000,
|
|
perfErrorRateMaxPct: 0,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-refresh"
|
|
>
|
|
{isLoadingEngineGateVerifySuite ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Execute Gate Verify Suite', 'panel')
|
|
void handleRefreshEngineGateVerifySuite({
|
|
persist: true,
|
|
execute: true,
|
|
fuzzSeed: '1337',
|
|
fuzzCount: 8,
|
|
perfIterations: 1,
|
|
perfWarmup: 0,
|
|
perfP95MaxMs: 5000,
|
|
perfAvgMaxMs: 3000,
|
|
perfErrorRateMaxPct: 0,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-execute"
|
|
>
|
|
Execute
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Execute gate verify-suite smoke subset"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Execute Gate Verify Smoke', 'panel')
|
|
void handleRefreshEngineGateVerifySuite({
|
|
persist: false,
|
|
execute: true,
|
|
stepIds: ['golden', 'matrix'],
|
|
fuzzSeed: '1337',
|
|
fuzzCount: 8,
|
|
perfIterations: 1,
|
|
perfWarmup: 0,
|
|
perfP95MaxMs: 5000,
|
|
perfAvgMaxMs: 3000,
|
|
perfErrorRateMaxPct: 0,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-execute-smoke"
|
|
>
|
|
Smoke
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Execute gate verify-suite stability x3"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Execute Gate Stability x3', 'panel')
|
|
void handleRefreshEngineGateVerifySuite({
|
|
persist: false,
|
|
execute: true,
|
|
repeat: 3,
|
|
stepIds: ['golden', 'matrix'],
|
|
fuzzSeed: '1337',
|
|
fuzzCount: 8,
|
|
perfIterations: 1,
|
|
perfWarmup: 0,
|
|
perfP95MaxMs: 5000,
|
|
perfAvgMaxMs: 3000,
|
|
perfErrorRateMaxPct: 0,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-execute-stability"
|
|
>
|
|
x3
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Load last persisted gate verify-suite report"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Load Last Gate Verify', 'panel')
|
|
void handleLoadEngineGateVerifySuiteLast()
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-load-last"
|
|
>
|
|
Last Verify
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Execute gate verify-suite stability packet"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Execute Gate Stability Packet', 'panel')
|
|
void handleRefreshEngineGateVerifySuiteStability({
|
|
persist: true,
|
|
execute: true,
|
|
repeat: 3,
|
|
stepIds: ['golden', 'matrix'],
|
|
fuzzSeed: '1337',
|
|
fuzzCount: 8,
|
|
perfIterations: 1,
|
|
perfWarmup: 0,
|
|
perfP95MaxMs: 5000,
|
|
perfAvgMaxMs: 3000,
|
|
perfErrorRateMaxPct: 0,
|
|
stepTimeoutMs: 120000,
|
|
minPassRuns: 3,
|
|
requireAllRunsPassed: true,
|
|
requireConsistentSignature: true,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuiteStability}
|
|
data-testid="runboard-engine-gate-verify-suite-stability-packet"
|
|
>
|
|
Packet
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Load last persisted gate verify-suite stability packet"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Load Last Gate Stability', 'panel')
|
|
void handleLoadEngineGateVerifySuiteStabilityLast()
|
|
}}
|
|
disabled={isLoadingEngineGateVerifySuiteStability}
|
|
data-testid="runboard-engine-gate-verify-suite-stability-load-last"
|
|
>
|
|
Last Stability
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open gate verify-suite report JSON endpoint"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Open Gate Verify JSON', 'panel')
|
|
openEngineGateVerifySuiteJson()
|
|
}}
|
|
>
|
|
JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open last persisted gate verify-suite report JSON endpoint"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Open Last Gate Verify JSON', 'panel')
|
|
openEngineGateVerifySuiteLastJson()
|
|
}}
|
|
data-testid="runboard-engine-gate-verify-suite-open-last-json"
|
|
>
|
|
Last JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open gate verify-suite stability packet JSON endpoint"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Open Gate Stability JSON', 'panel')
|
|
openEngineGateVerifySuiteStabilityJson()
|
|
}}
|
|
>
|
|
Stab JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open last persisted gate verify-suite stability packet JSON endpoint"
|
|
onClick={() => {
|
|
recordGateVerifySuiteAction('Open Last Gate Stability JSON', 'panel')
|
|
openEngineGateVerifySuiteStabilityLastJson()
|
|
}}
|
|
data-testid="runboard-engine-gate-verify-suite-open-last-stability-json"
|
|
>
|
|
Last Stab
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Copy gate verify-suite cURL command"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/gates/verify-suite?base_url=${encodeURIComponent(apiBaseUrl)}&persist=true`)}`,
|
|
'Gate verify-suite command copied.'
|
|
)}
|
|
>
|
|
cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{engineGateVerifySuiteLastAction && (
|
|
<p className="text-[11px] text-gray-500" data-testid="runboard-engine-gate-verify-suite-action-cue">
|
|
{(isLoadingEngineGateVerifySuite || isLoadingEngineGateVerifySuiteStability) ? 'In progress' : 'Last action'}:{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteLastAction.label}</span>
|
|
{' '}· source{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteLastAction.source}</span>
|
|
{' '}· at{' '}
|
|
<span className="font-mono text-gray-300">{fmtTime(engineGateVerifySuiteLastAction.at)}</span>
|
|
{' '}· age{' '}
|
|
<span className="font-mono text-gray-300">{formatRunboardVerifyCueAge(engineGateVerifySuiteLastAction.at)}</span>
|
|
{' '}· status{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueStalenessChipToneClass(gateVerifyCueStaleness || 'fresh')}`}
|
|
data-testid="runboard-engine-gate-verify-suite-action-cue-status-chip"
|
|
>
|
|
{gateVerifyCueStatusLabel || 'Fresh'}
|
|
</span>
|
|
</p>
|
|
)}
|
|
{engineGateVerifySuiteLastAction && gateVerifyCueGuidance && (
|
|
<div className="flex flex-wrap items-center gap-2" data-testid="runboard-engine-gate-verify-suite-action-cue-guidance-row">
|
|
<p
|
|
className={`text-[11px] ${verifyCueStalenessToneClass(gateVerifyCueStaleness || 'fresh')}`}
|
|
data-testid="runboard-engine-gate-verify-suite-action-cue-guidance"
|
|
>
|
|
{gateVerifyCueGuidance}
|
|
</p>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => triggerGateVerifyCueRecovery('refresh', 'cue-guidance')}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-action-cue-recovery-refresh"
|
|
title="Refresh gate verify-suite now"
|
|
>
|
|
Refresh
|
|
</button>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => triggerGateVerifyCueRecovery('execute', 'cue-guidance')}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-action-cue-recovery-execute"
|
|
title="Execute gate verify-suite now"
|
|
>
|
|
Execute
|
|
</button>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => triggerGateVerifyCueRecovery('load-last', 'cue-guidance')}
|
|
disabled={isLoadingEngineGateVerifySuite}
|
|
data-testid="runboard-engine-gate-verify-suite-action-cue-recovery-load-last"
|
|
title="Load last persisted gate verify-suite report"
|
|
>
|
|
Last
|
|
</button>
|
|
</div>
|
|
)}
|
|
{!engineGateVerifySuiteReport && !isLoadingEngineGateVerifySuite && !engineGateVerifySuiteError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh gate verify suite to inspect deterministic golden/matrix/fuzz/perf toolkit coverage state.
|
|
</p>
|
|
)}
|
|
{engineGateVerifySuiteError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-engine-gate-verify-suite-error">
|
|
{engineGateVerifySuiteError}
|
|
</div>
|
|
)}
|
|
{engineGateVerifySuiteStabilityError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-engine-gate-verify-suite-stability-error">
|
|
{engineGateVerifySuiteStabilityError}
|
|
</div>
|
|
)}
|
|
{engineGateVerifySuiteReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
State:{' '}
|
|
<span
|
|
className={engineGateVerifySuiteReport.summary?.passed ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-gate-verify-suite-state"
|
|
>
|
|
{engineGateVerifySuiteReport.summary?.passed ? 'passed' : 'review'}
|
|
</span>
|
|
{' '}· steps{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-verify-suite-step-count">
|
|
{engineGateVerifySuiteReport.summary?.steps_total ?? 0}
|
|
</span>
|
|
{' '}· passed{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-verify-suite-passed">
|
|
{engineGateVerifySuiteReport.summary?.steps_passed ?? 0}
|
|
</span>
|
|
{' '}· failed{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-gate-verify-suite-failed">
|
|
{engineGateVerifySuiteReport.summary?.steps_failed ?? 0}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Report hash:{' '}
|
|
<span className="font-mono text-gray-300 break-all" data-testid="runboard-engine-gate-verify-suite-hash">
|
|
{engineGateVerifySuiteReport.report_hash || 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Source{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteOutput?.source || 'n/a'}</span>
|
|
{' '}· duration{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{fmtMs(engineGateVerifySuiteReport.summary?.total_duration_ms ?? null)}
|
|
</span>
|
|
{' '}· steps{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{(engineGateVerifySuiteOutput?.step_ids ?? []).join(', ') || 'all'}
|
|
</span>
|
|
{engineGateVerifySuiteOutput?.persisted ? ` · persisted ${engineGateVerifySuiteOutput.path}` : ''}
|
|
</p>
|
|
<p className="text-gray-500" data-testid="runboard-engine-gate-verify-suite-telemetry">
|
|
Generated{' '}
|
|
<span className="font-mono text-gray-300">{fmtTime(engineGateVerifySuiteReport.generated_at)}</span>
|
|
{' '}· mode{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteOutput?.execution?.executed ? 'executed' : 'projection'}
|
|
</span>
|
|
{' '}· artifact{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteOutput?.persisted ? 'persisted' : 'ephemeral'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Execution:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteOutput?.execution?.executed
|
|
? (
|
|
engineGateVerifySuiteOutput.execution.timed_out
|
|
? `timeout ${engineGateVerifySuiteOutput.execution.timeout_ms ?? 'n/a'}ms`
|
|
: `exit ${engineGateVerifySuiteOutput.execution.exit_status ?? 'n/a'}`
|
|
)
|
|
: 'artifact projection'}
|
|
</span>
|
|
{engineGateVerifySuiteOutput?.execution?.step_timeout_ms != null ? (
|
|
<span>
|
|
{' '}· step-timeout{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteOutput.execution.step_timeout_ms}ms</span>
|
|
</span>
|
|
) : null}
|
|
</p>
|
|
{engineGateVerifySuiteReport.stability && (
|
|
<p className="text-gray-500">
|
|
Stability:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteReport.stability.pass_count}/{engineGateVerifySuiteReport.stability.runs_total}
|
|
</span>
|
|
{' '}· consistent{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteReport.stability.consistent_summary_signature ? 'yes' : 'no'}
|
|
</span>
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
{engineGateVerifySuiteStabilityReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1" data-testid="runboard-engine-gate-verify-suite-stability-report">
|
|
<p className="text-gray-400">
|
|
Stability packet:{' '}
|
|
<span
|
|
className={engineGateVerifySuiteStabilityReport.approved ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-gate-verify-suite-stability-state"
|
|
>
|
|
{engineGateVerifySuiteStabilityReport.status}
|
|
</span>
|
|
{' '}· severity{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteStabilityReport.severity}</span>
|
|
{' '}· checks{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteStabilityReport.summary.passed_checks}/{engineGateVerifySuiteStabilityReport.summary.total_checks}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Recommendation{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteStabilityReport.recommendation}</span>
|
|
{' '}· source{' '}
|
|
<span className="font-mono text-gray-300">{engineGateVerifySuiteStabilityOutput?.source || 'n/a'}</span>
|
|
{engineGateVerifySuiteStabilityOutput?.persisted ? ` · persisted ${engineGateVerifySuiteStabilityOutput.path}` : ''}
|
|
</p>
|
|
<p className="text-gray-500" data-testid="runboard-engine-gate-verify-suite-stability-telemetry">
|
|
Generated{' '}
|
|
<span className="font-mono text-gray-300">{fmtTime(engineGateVerifySuiteStabilityReport.generated_at)}</span>
|
|
{' '}· mode{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteStabilityReport.execution?.executed ? 'executed' : 'projection'}
|
|
</span>
|
|
{' '}· artifact{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineGateVerifySuiteStabilityOutput?.persisted ? 'persisted' : 'ephemeral'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-advanced-verify-suite-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex flex-wrap items-start justify-between gap-2">
|
|
<span>Advanced Verify Suite Toolkit</span>
|
|
<div className="flex max-w-full flex-wrap items-center justify-end gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Refresh Advanced Verify Suite', 'panel')
|
|
void handleRefreshEngineAdvancedVerifySuite({
|
|
persist: true,
|
|
execute: false,
|
|
policyPack: 'balanced',
|
|
strategy: 'weighted',
|
|
maxIssues: 200,
|
|
sandboxProfile: 'strict',
|
|
requiredCapabilities: 'json_mode,deterministic_seed',
|
|
manifestScope: 'all',
|
|
artifactKind: 'evidence_bundle',
|
|
replayCount: 3,
|
|
sampleLimit: 200,
|
|
benchmarkIterations: 5,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-refresh"
|
|
>
|
|
{isLoadingEngineAdvancedVerifySuite ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Execute Advanced Verify Suite', 'panel')
|
|
void handleRefreshEngineAdvancedVerifySuite({
|
|
persist: true,
|
|
execute: true,
|
|
policyPack: 'balanced',
|
|
strategy: 'weighted',
|
|
maxIssues: 200,
|
|
sandboxProfile: 'strict',
|
|
requiredCapabilities: 'json_mode,deterministic_seed',
|
|
manifestScope: 'all',
|
|
artifactKind: 'evidence_bundle',
|
|
replayCount: 3,
|
|
sampleLimit: 200,
|
|
benchmarkIterations: 5,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-execute"
|
|
>
|
|
Execute
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Execute advanced verify-suite smoke subset"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Execute Advanced Verify Smoke', 'panel')
|
|
void handleRefreshEngineAdvancedVerifySuite({
|
|
persist: false,
|
|
execute: true,
|
|
stepIds: ['policy', 'capabilities'],
|
|
policyPack: 'balanced',
|
|
strategy: 'weighted',
|
|
maxIssues: 200,
|
|
sandboxProfile: 'strict',
|
|
requiredCapabilities: 'json_mode,deterministic_seed',
|
|
manifestScope: 'all',
|
|
artifactKind: 'evidence_bundle',
|
|
replayCount: 3,
|
|
sampleLimit: 200,
|
|
benchmarkIterations: 5,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-execute-smoke"
|
|
>
|
|
Smoke
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Load last persisted advanced verify-suite report"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Load Last Advanced Verify', 'panel')
|
|
void handleLoadEngineAdvancedVerifySuiteLast()
|
|
}}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-load-last"
|
|
>
|
|
Last
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Execute advanced verify-suite stability packet"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Execute Advanced Stability Packet', 'panel')
|
|
void handleRefreshEngineAdvancedVerifySuiteStability({
|
|
persist: true,
|
|
execute: true,
|
|
repeat: 3,
|
|
minPassRuns: 3,
|
|
requireAllRunsPassed: true,
|
|
requireConsistentSignature: true,
|
|
policyPack: 'balanced',
|
|
strategy: 'weighted',
|
|
maxIssues: 200,
|
|
sandboxProfile: 'strict',
|
|
requiredCapabilities: 'json_mode,deterministic_seed',
|
|
manifestScope: 'all',
|
|
artifactKind: 'evidence_bundle',
|
|
replayCount: 3,
|
|
sampleLimit: 200,
|
|
benchmarkIterations: 5,
|
|
})
|
|
}}
|
|
disabled={isLoadingEngineAdvancedVerifySuiteStability}
|
|
data-testid="runboard-engine-advanced-verify-suite-stability-packet"
|
|
>
|
|
Packet
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
title="Load last persisted advanced verify-suite stability packet"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Load Last Advanced Stability', 'panel')
|
|
void handleLoadEngineAdvancedVerifySuiteStabilityLast()
|
|
}}
|
|
disabled={isLoadingEngineAdvancedVerifySuiteStability}
|
|
data-testid="runboard-engine-advanced-verify-suite-stability-load-last"
|
|
>
|
|
Last Stability
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open advanced verify-suite report JSON endpoint"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Open Advanced Verify JSON', 'panel')
|
|
openEngineAdvancedVerifySuiteJson()
|
|
}}
|
|
>
|
|
JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open last persisted advanced verify-suite report JSON endpoint"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Open Last Advanced Verify JSON', 'panel')
|
|
openEngineAdvancedVerifySuiteLastJson()
|
|
}}
|
|
data-testid="runboard-engine-advanced-verify-suite-open-last-json"
|
|
>
|
|
Last JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open advanced verify-suite stability packet JSON endpoint"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Open Advanced Stability JSON', 'panel')
|
|
openEngineAdvancedVerifySuiteStabilityJson()
|
|
}}
|
|
>
|
|
Stab JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Open last persisted advanced verify-suite stability packet JSON endpoint"
|
|
onClick={() => {
|
|
recordAdvancedVerifySuiteAction('Open Last Advanced Stability JSON', 'panel')
|
|
openEngineAdvancedVerifySuiteStabilityLastJson()
|
|
}}
|
|
data-testid="runboard-engine-advanced-verify-suite-open-last-stability-json"
|
|
>
|
|
Last Stab
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
title="Copy advanced verify-suite cURL command"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/advanced/verify-suite?profile_id=runboard_operator&scope=all&policy_pack=balanced&arbitration_strategy=weighted&sandbox_profile=strict&required_capabilities=json_mode,deterministic_seed&manifest_scope=all&artifact_kind=evidence_bundle&replay_count=3&sample_limit=200&benchmark_iterations=5&max_issues=200&persist=true`)}`,
|
|
'Advanced verify-suite command copied.'
|
|
)}
|
|
>
|
|
cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{engineAdvancedVerifySuiteLastAction && (
|
|
<p className="text-[11px] text-gray-500" data-testid="runboard-engine-advanced-verify-suite-action-cue">
|
|
{(isLoadingEngineAdvancedVerifySuite || isLoadingEngineAdvancedVerifySuiteStability) ? 'In progress' : 'Last action'}:{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteLastAction.label}</span>
|
|
{' '}· source{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteLastAction.source}</span>
|
|
{' '}· at{' '}
|
|
<span className="font-mono text-gray-300">{fmtTime(engineAdvancedVerifySuiteLastAction.at)}</span>
|
|
{' '}· age{' '}
|
|
<span className="font-mono text-gray-300">{formatRunboardVerifyCueAge(engineAdvancedVerifySuiteLastAction.at)}</span>
|
|
{' '}· status{' '}
|
|
<span
|
|
className={`inline-flex items-center rounded border px-1 py-0.5 text-[10px] font-mono ${verifyCueStalenessChipToneClass(advancedVerifyCueStaleness || 'fresh')}`}
|
|
data-testid="runboard-engine-advanced-verify-suite-action-cue-status-chip"
|
|
>
|
|
{advancedVerifyCueStatusLabel || 'Fresh'}
|
|
</span>
|
|
</p>
|
|
)}
|
|
{engineAdvancedVerifySuiteLastAction && advancedVerifyCueGuidance && (
|
|
<div className="flex flex-wrap items-center gap-2" data-testid="runboard-engine-advanced-verify-suite-action-cue-guidance-row">
|
|
<p
|
|
className={`text-[11px] ${verifyCueStalenessToneClass(advancedVerifyCueStaleness || 'fresh')}`}
|
|
data-testid="runboard-engine-advanced-verify-suite-action-cue-guidance"
|
|
>
|
|
{advancedVerifyCueGuidance}
|
|
</p>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => triggerAdvancedVerifyCueRecovery('refresh', 'cue-guidance')}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-action-cue-recovery-refresh"
|
|
title="Refresh advanced verify-suite now"
|
|
>
|
|
Refresh
|
|
</button>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => triggerAdvancedVerifyCueRecovery('execute', 'cue-guidance')}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-action-cue-recovery-execute"
|
|
title="Execute advanced verify-suite now"
|
|
>
|
|
Execute
|
|
</button>
|
|
<button
|
|
className="text-[10px] px-1.5 py-0.5 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => triggerAdvancedVerifyCueRecovery('load-last', 'cue-guidance')}
|
|
disabled={isLoadingEngineAdvancedVerifySuite}
|
|
data-testid="runboard-engine-advanced-verify-suite-action-cue-recovery-load-last"
|
|
title="Load last persisted advanced verify-suite report"
|
|
>
|
|
Last
|
|
</button>
|
|
</div>
|
|
)}
|
|
{!engineAdvancedVerifySuiteReport && !isLoadingEngineAdvancedVerifySuite && !engineAdvancedVerifySuiteError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh advanced verify-suite to inspect deterministic policy/capabilities/replay/export-manifest/observability validation state.
|
|
</p>
|
|
)}
|
|
{engineAdvancedVerifySuiteError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-engine-advanced-verify-suite-error">
|
|
{engineAdvancedVerifySuiteError}
|
|
</div>
|
|
)}
|
|
{engineAdvancedVerifySuiteStabilityError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5" data-testid="runboard-engine-advanced-verify-suite-stability-error">
|
|
{engineAdvancedVerifySuiteStabilityError}
|
|
</div>
|
|
)}
|
|
{engineAdvancedVerifySuiteReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
State:{' '}
|
|
<span
|
|
className={engineAdvancedVerifySuiteReport.summary?.passed ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-advanced-verify-suite-state"
|
|
>
|
|
{engineAdvancedVerifySuiteReport.summary?.passed ? 'passed' : 'review'}
|
|
</span>
|
|
{' '}· steps{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-advanced-verify-suite-step-count">
|
|
{engineAdvancedVerifySuiteReport.summary?.steps_total ?? 0}
|
|
</span>
|
|
{' '}· passed{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-advanced-verify-suite-passed">
|
|
{engineAdvancedVerifySuiteReport.summary?.steps_passed ?? 0}
|
|
</span>
|
|
{' '}· failed{' '}
|
|
<span className="font-mono text-gray-300" data-testid="runboard-engine-advanced-verify-suite-failed">
|
|
{engineAdvancedVerifySuiteReport.summary?.steps_failed ?? 0}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Report hash:{' '}
|
|
<span className="font-mono text-gray-300 break-all" data-testid="runboard-engine-advanced-verify-suite-hash">
|
|
{engineAdvancedVerifySuiteReport.report_hash || 'n/a'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Source{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteOutput?.source || 'n/a'}</span>
|
|
{' '}· steps{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{(engineAdvancedVerifySuiteOutput?.step_ids ?? []).join(', ') || 'all'}
|
|
</span>
|
|
{' '}· critical/high failed{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineAdvancedVerifySuiteReport.checks?.critical_failed ?? 0}/{engineAdvancedVerifySuiteReport.checks?.high_failed ?? 0}
|
|
</span>
|
|
{engineAdvancedVerifySuiteOutput?.persisted ? ` · persisted ${engineAdvancedVerifySuiteOutput.path}` : ''}
|
|
</p>
|
|
<p className="text-gray-500" data-testid="runboard-engine-advanced-verify-suite-telemetry">
|
|
Generated{' '}
|
|
<span className="font-mono text-gray-300">{fmtTime(engineAdvancedVerifySuiteReport.generated_at)}</span>
|
|
{' '}· mode{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineAdvancedVerifySuiteOutput?.execution?.executed ? 'executed' : 'projection'}
|
|
</span>
|
|
{' '}· artifact{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineAdvancedVerifySuiteOutput?.persisted ? 'persisted' : 'ephemeral'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Execution:{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineAdvancedVerifySuiteOutput?.execution?.executed
|
|
? (
|
|
engineAdvancedVerifySuiteOutput.execution.timed_out
|
|
? `timeout ${engineAdvancedVerifySuiteOutput.execution.timeout_ms ?? 'n/a'}ms`
|
|
: `exit ${engineAdvancedVerifySuiteOutput.execution.exit_status ?? 'n/a'}`
|
|
)
|
|
: 'artifact projection'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
{engineAdvancedVerifySuiteStabilityReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1" data-testid="runboard-engine-advanced-verify-suite-stability-report">
|
|
<p className="text-gray-400">
|
|
Stability packet:{' '}
|
|
<span
|
|
className={engineAdvancedVerifySuiteStabilityReport.approved ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-advanced-verify-suite-stability-state"
|
|
>
|
|
{engineAdvancedVerifySuiteStabilityReport.status}
|
|
</span>
|
|
{' '}· severity{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteStabilityReport.severity}</span>
|
|
{' '}· checks{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteStabilityReport.summary.passed_checks}/{engineAdvancedVerifySuiteStabilityReport.summary.total_checks}</span>
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Recommendation{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteStabilityReport.recommendation}</span>
|
|
{' '}· source{' '}
|
|
<span className="font-mono text-gray-300">{engineAdvancedVerifySuiteStabilityOutput?.source || 'n/a'}</span>
|
|
{engineAdvancedVerifySuiteStabilityOutput?.persisted ? ` · persisted ${engineAdvancedVerifySuiteStabilityOutput.path}` : ''}
|
|
</p>
|
|
<p className="text-gray-500" data-testid="runboard-engine-advanced-verify-suite-stability-telemetry">
|
|
Generated{' '}
|
|
<span className="font-mono text-gray-300">{fmtTime(engineAdvancedVerifySuiteStabilityReport.generated_at)}</span>
|
|
{' '}· mode{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineAdvancedVerifySuiteStabilityReport.execution?.executed ? 'executed' : 'projection'}
|
|
</span>
|
|
{' '}· artifact{' '}
|
|
<span className="font-mono text-gray-300">
|
|
{engineAdvancedVerifySuiteStabilityOutput?.persisted ? 'persisted' : 'ephemeral'}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-reproducibility-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Minimal Engine Reproducibility</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineReproducibility({ persist: true })}
|
|
disabled={isLoadingEngineRepro}
|
|
data-testid="runboard-engine-reproducibility-refresh"
|
|
>
|
|
{isLoadingEngineRepro ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineReproducibilityJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/reproducibility?profile_id=runboard_operator&scope=all&persist=true`)}`,
|
|
'Minimal-engine reproducibility command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineReproReport && !isLoadingEngineRepro && !engineReproError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh reproducibility report to capture deterministic bundle hash, git-linked structural snapshot, and semantic diff state.
|
|
</p>
|
|
)}
|
|
{engineReproError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{engineReproError}
|
|
</div>
|
|
)}
|
|
{engineReproReport && (
|
|
<div className="space-y-2">
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs">
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Schema</p>
|
|
<p className="text-gray-200 font-medium" data-testid="runboard-engine-reproducibility-schema-version">{engineReproReport.schema_version}</p>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Profile</p>
|
|
<p className="text-gray-200 font-medium" data-testid="runboard-engine-reproducibility-profile-id">{engineReproReport.profile.profile_id}</p>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Snapshot</p>
|
|
<p className="text-gray-200 font-medium" data-testid="runboard-engine-reproducibility-snapshot-id">{engineReproReport.structural_snapshot.snapshot_id}</p>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5">
|
|
<p className="text-gray-500">Bundle</p>
|
|
<p className="text-gray-200 font-medium" data-testid="runboard-engine-reproducibility-bundle-id">{engineReproReport.deterministic_bundle.bundle_id}</p>
|
|
</div>
|
|
</div>
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Git:{' '}
|
|
<span className="text-gray-200 font-mono" data-testid="runboard-engine-reproducibility-git-ref">
|
|
{engineReproReport.git.branch_name}@{engineReproReport.git.commit_hash}
|
|
</span>
|
|
{' '}· dirty <span className={engineReproReport.git.dirty_state ? 'text-amber-300' : 'text-emerald-300'}>{engineReproReport.git.dirty_state ? 'yes' : 'no'}</span>
|
|
{' '}· source{' '}
|
|
<span
|
|
className={
|
|
engineReproGitIdentitySource === 'git' || engineReproGitIdentitySource === 'git+env'
|
|
? 'text-emerald-300'
|
|
: engineReproGitIdentitySource === 'env'
|
|
? 'text-amber-300'
|
|
: 'text-red-300'
|
|
}
|
|
data-testid="runboard-engine-reproducibility-git-source"
|
|
>
|
|
{engineReproGitIdentitySource}
|
|
</span>
|
|
{' '}· context{' '}
|
|
<span
|
|
className={engineReproGitContextLinked ? 'text-emerald-300' : 'text-red-300'}
|
|
data-testid="runboard-engine-reproducibility-git-context"
|
|
>
|
|
{engineReproGitContextLinked ? 'linked' : 'incomplete'}
|
|
</span>
|
|
</p>
|
|
{engineReproGitIdentityNeedsAttention ? (
|
|
<p
|
|
className="text-amber-200"
|
|
data-testid="runboard-engine-reproducibility-git-context-warning"
|
|
>
|
|
Git-linked snapshot context is not fully trusted for Gate F release checks. Set runtime envs
|
|
{' '}<span className="font-mono">CORPUS_ENGINE_GIT_COMMIT</span>{' '}
|
|
and{' '}
|
|
<span className="font-mono">CORPUS_ENGINE_GIT_BRANCH</span>.
|
|
</p>
|
|
) : null}
|
|
<p className="text-gray-400">
|
|
Snapshot hash: <span className="text-gray-200 font-mono break-all" data-testid="runboard-engine-reproducibility-snapshot-hash">{engineReproReport.structural_snapshot.snapshot_hash}</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Bundle hash: <span className="text-indigo-200 font-mono break-all" data-testid="runboard-engine-reproducibility-bundle-hash">{engineReproReport.deterministic_bundle.bundle_hash}</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Semantic diff: <span
|
|
className={
|
|
engineReproReport.semantic_diff.state === 'changed'
|
|
? 'text-amber-300'
|
|
: engineReproReport.semantic_diff.state === 'unchanged'
|
|
? 'text-emerald-300'
|
|
: 'text-gray-300'
|
|
}
|
|
data-testid="runboard-engine-reproducibility-diff-state"
|
|
>
|
|
{engineReproReport.semantic_diff.state}
|
|
</span>
|
|
{engineReproOutput?.baseline_loaded ? (
|
|
<span className="text-gray-500">
|
|
{' '}· baseline {engineReproOutput.baseline_snapshot_hash || 'unknown'}
|
|
</span>
|
|
) : (
|
|
<span className="text-gray-500"> · baseline unavailable</span>
|
|
)}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Snapshot counts: entities {engineReproReport.structural_snapshot.counts.entities}, links {engineReproReport.structural_snapshot.counts.links}, runs {engineReproReport.structural_snapshot.counts.runs}, artifacts {engineReproReport.structural_snapshot.counts.artifacts}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
report generated: {fmtTime(engineReproReport.generated_at)}
|
|
{engineReproOutput?.persisted ? ` · persisted ${engineReproOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded" data-testid="runboard-engine-replay-panel">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Deterministic Replay</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
onClick={() => void handleRefreshEngineReplay({ persist: true, replayCount: 3 })}
|
|
disabled={isLoadingEngineReplay}
|
|
data-testid="runboard-engine-replay-refresh"
|
|
>
|
|
{isLoadingEngineReplay ? 'Refreshing...' : 'Refresh'}
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openEngineReplayJson()}
|
|
>
|
|
Open JSON
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(
|
|
`curl -s ${quoteShell(`${apiBaseUrl}/api/corpus/engine/replay?profile_id=runboard_operator&scope=all&replay_count=3&persist=true`)}`,
|
|
'Deterministic replay command copied.'
|
|
)}
|
|
>
|
|
Copy cURL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{!engineReplayReport && !isLoadingEngineReplay && !engineReplayError && (
|
|
<p className="text-xs text-gray-400">
|
|
Refresh replay report to verify repeatability signatures and baseline state transitions.
|
|
</p>
|
|
)}
|
|
{engineReplayError && (
|
|
<div className="text-xs text-amber-200 rounded border border-amber-700/40 bg-amber-900/20 px-2 py-1.5">
|
|
{engineReplayError}
|
|
</div>
|
|
)}
|
|
{engineReplayReport && (
|
|
<div className="rounded border border-gray-800 bg-gray-900 px-2 py-1.5 text-xs space-y-1">
|
|
<p className="text-gray-400">
|
|
Replay count:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-replay-count">
|
|
{engineReplayReport.replay.replay_count}
|
|
</span>
|
|
{' '}· consistent:{' '}
|
|
<span
|
|
className={engineReplayReport.replay.replay_consistent ? 'text-emerald-300' : 'text-amber-300'}
|
|
data-testid="runboard-engine-replay-consistent"
|
|
>
|
|
{engineReplayReport.replay.replay_consistent ? 'yes' : 'no'}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Signature:{' '}
|
|
<span className="font-mono text-indigo-200 break-all" data-testid="runboard-engine-replay-signature">
|
|
{engineReplayReport.replay.replay_signature_hash}
|
|
</span>
|
|
</p>
|
|
<p className="text-gray-400">
|
|
Baseline state:{' '}
|
|
<span className="text-gray-200" data-testid="runboard-engine-replay-baseline-state">
|
|
{engineReplayReport.baseline.state}
|
|
</span>
|
|
{' '}· loaded {engineReplayReport.baseline.loaded ? 'yes' : 'no'}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Mismatch counts: bundle {engineReplayReport.replay.mismatches.bundle_hash_iterations.length},
|
|
{' '}snapshot {engineReplayReport.replay.mismatches.snapshot_hash_iterations.length},
|
|
{' '}input {engineReplayReport.replay.mismatches.canonical_input_hash_iterations.length},
|
|
{' '}key {engineReplayReport.replay.mismatches.reproducibility_key_iterations.length}
|
|
</p>
|
|
<p className="text-gray-500">
|
|
Generated: {fmtTime(engineReplayReport.generated_at)}
|
|
{engineReplayOutput?.persisted ? ` · persisted ${engineReplayOutput.path}` : ''}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400">Remediation Hints</div>
|
|
<ul className="px-3 py-2 space-y-1">
|
|
{remediationHints.map((hint, index) => (
|
|
<li key={`remediation-hint-${index}-${hint}`} className="text-xs text-gray-300 flex items-start gap-2">
|
|
<span className="mt-1 h-1.5 w-1.5 rounded-full bg-[#C4A96B]" />
|
|
<span>{hint}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400 flex items-center justify-between gap-2">
|
|
<span>Remediation Commands</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => openSelectedRunAuditExport()}
|
|
>
|
|
Open Audit
|
|
</button>
|
|
<button
|
|
className="text-[11px] px-2 py-1 rounded border border-gray-700 text-gray-300 hover:bg-gray-800"
|
|
onClick={() => void handleCopyText(remediationPackText, 'Remediation pack copied.')}
|
|
>
|
|
Copy All
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="px-3 py-2 space-y-2">
|
|
{remediationCommands.map((item) => (
|
|
<div key={item.id} className="rounded border border-gray-800 bg-gray-900 p-2">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<p className="text-xs text-gray-200">{item.title}</p>
|
|
<button
|
|
className="text-gray-400 hover:text-gray-200"
|
|
title="Copy command"
|
|
onClick={() => void handleCopyText(item.command)}
|
|
>
|
|
<ItshoverCopyIcon size={12} />
|
|
</button>
|
|
</div>
|
|
<p className="text-[11px] text-gray-500 mt-1">{item.rationale}</p>
|
|
<pre className="text-[11px] text-indigo-200 mt-2 whitespace-pre-wrap break-all">{item.command}</pre>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border border-gray-800 rounded">
|
|
<div className="px-3 py-2 border-b border-gray-800 text-xs text-gray-400">
|
|
Action History ({selectedAuditEvents.length})
|
|
</div>
|
|
<div className="max-h-56 overflow-auto divide-y divide-gray-800">
|
|
{isLoadingDetails && !selectedAuditEvents.length && (
|
|
<SurfacePanelState
|
|
kind="loading"
|
|
title="Loading Audit Events"
|
|
detail="Fetching run timeline events."
|
|
className="py-3 text-xs"
|
|
/>
|
|
)}
|
|
{!isLoadingDetails && !selectedAuditEvents.length && (
|
|
<SurfacePanelState
|
|
kind="empty"
|
|
title="No Audit Events"
|
|
detail="No timeline events were returned for this run."
|
|
className="py-3 text-xs"
|
|
/>
|
|
)}
|
|
{selectedAuditEvents.map((event) => (
|
|
<div key={event.event_id} className="px-3 py-2 text-xs">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<span className="text-[10px] px-2 py-0.5 rounded border text-gray-300 bg-gray-800 border-gray-700">
|
|
{event.event_type}
|
|
</span>
|
|
<span className="text-gray-500">{fmtTime(event.event_at)}</span>
|
|
</div>
|
|
<p className="text-gray-300 mt-1">{describeAuditEvent(event)}</p>
|
|
<p className="text-gray-500 mt-1 truncate">event_id: {event.event_id}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-xs">
|
|
<div className="flex items-center gap-2 text-gray-400">
|
|
<ItshoverCheckedIcon size={12} className="text-emerald-400" />
|
|
<span>Updated: {fmtTime(selectedRun.updated_at)}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-gray-400">
|
|
<ItshoverXCircleIcon size={12} className="text-red-400" />
|
|
<span>Failure reason: {selectedRun.failure_reason || '-'}</span>
|
|
</div>
|
|
</div>
|
|
{selectedArtifact && (
|
|
<div className="text-xs text-gray-500">
|
|
selected artifact: {selectedArtifact.id}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</SubscriptionGate>
|
|
</PlatformAuthProvider>
|
|
) |