Add AI-powered secondary review for scrutiny warnings

- Create scrutiny-ai-review tool that uses AI to analyze warnings
- Integrate AI review into publish flow (app.py)
- Integrate AI review into Fabric sync script
- If AI review returns APPROVE with >=80% confidence, auto-approve
- Display AI review results in admin pending tools modal
- Shows verdict (APPROVE/REJECT/NEEDS_HUMAN_REVIEW) with confidence
- Shows per-finding analysis (FALSE_POSITIVE/LEGITIMATE_CONCERN)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
rob
2026-01-16 16:03:44 -04:00
co-authored by Claude Opus 4.5
parent b0bd692be5
commit 8471480aa7
3 changed files with 201 additions and 1 deletions
@@ -153,6 +153,12 @@
<div class="p-6 overflow-y-auto flex-grow">
<div id="detail-loading" class="py-8 text-center text-gray-500">Loading...</div>
<div id="detail-content" class="hidden">
<!-- AI Review (if available) -->
<div id="detail-ai-review-section" class="mb-6 hidden">
<h4 class="text-sm font-medium text-gray-700 mb-2">AI Review</h4>
<div id="detail-ai-review" class="rounded-md p-4"></div>
</div>
<!-- Scrutiny Warnings -->
<div id="detail-warnings-section" class="mb-6 hidden">
<h4 class="text-sm font-medium text-gray-700 mb-2">Scrutiny Warnings</h4>
@@ -239,6 +245,55 @@ async function viewTool(toolId) {
// Update title
document.getElementById('detail-title').textContent = `${tool.owner}/${tool.name} v${tool.version}`;
// AI Review (if available)
const aiReviewSection = document.getElementById('detail-ai-review-section');
const aiReviewDiv = document.getElementById('detail-ai-review');
const aiReview = tool.scrutiny_report?.ai_review;
if (aiReview) {
aiReviewSection.classList.remove('hidden');
const verdict = aiReview.overall_verdict || 'UNKNOWN';
const confidence = (aiReview.confidence * 100).toFixed(0) || '?';
let verdictColor, verdictBg;
if (verdict === 'APPROVE') {
verdictColor = 'text-green-800';
verdictBg = 'bg-green-50 border-green-200';
} else if (verdict === 'REJECT') {
verdictColor = 'text-red-800';
verdictBg = 'bg-red-50 border-red-200';
} else {
verdictColor = 'text-yellow-800';
verdictBg = 'bg-yellow-50 border-yellow-200';
}
let findingsHtml = '';
if (aiReview.findings && aiReview.findings.length > 0) {
findingsHtml = '<div class="mt-3 space-y-2">' + aiReview.findings.map(f => {
let fColor = f.verdict === 'FALSE_POSITIVE' ? 'text-green-700' :
f.verdict === 'LEGITIMATE_CONCERN' ? 'text-red-700' : 'text-yellow-700';
return `<div class="text-sm border-l-2 pl-3 ${f.verdict === 'FALSE_POSITIVE' ? 'border-green-400' : f.verdict === 'LEGITIMATE_CONCERN' ? 'border-red-400' : 'border-yellow-400'}">
<div class="font-medium ${fColor}">${escapeHtml(f.verdict || 'UNKNOWN')}</div>
<div class="text-gray-600">${escapeHtml(f.explanation || '')}</div>
</div>`;
}).join('') + '</div>';
}
aiReviewDiv.className = `rounded-md p-4 border ${verdictBg}`;
aiReviewDiv.innerHTML = `
<div class="flex items-center justify-between mb-2">
<span class="font-medium ${verdictColor}">${verdict}</span>
<span class="text-sm text-gray-500">${confidence}% confidence</span>
</div>
<div class="text-sm text-gray-700">${escapeHtml(aiReview.summary || '')}</div>
${findingsHtml}
`;
} else if (tool.scrutiny_report?.ai_review_error) {
aiReviewSection.classList.remove('hidden');
aiReviewDiv.className = 'rounded-md p-4 border bg-gray-50 border-gray-200';
aiReviewDiv.innerHTML = `<div class="text-sm text-gray-500">AI review failed: ${escapeHtml(tool.scrutiny_report.ai_review_error)}</div>`;
} else {
aiReviewSection.classList.add('hidden');
}
// Scrutiny Warnings
const warningsSection = document.getElementById('detail-warnings-section');
const warningsDiv = document.getElementById('detail-warnings');