feat(cultural-sensitivity): implement Phase 2 - admin UI with cultural flags (inst_081)

Phase 2: Cultural Sensitivity Admin UI
- Display cultural sensitivity analysis results in admin interfaces
- Visual indicators for risk levels (LOW/MEDIUM/HIGH)
- Show concerns and suggested adaptations to human reviewers
- Human-in-the-loop workflow: AI flags, human decides

Implementation:

1. Media Inquiry Admin (public/js/admin/media-triage.js:435-503)
   - Cultural Sensitivity Analysis section in inquiry details modal
   - Shows risk level with color-coded badges (green/yellow/red)
   - Lists cultural concerns with context
   - Displays suggested adaptations
   - Framework compliance note: "AI flags concerns but never blocks"
   - Appears after response is created (response.cultural_sensitivity)

2. Blog Curation Admin (public/js/admin/blog-curation.js:371-398)
   - Cultural risk badge in blog post queue list
   - Color-coded by risk level (LOW=green, MEDIUM=yellow, HIGH=red)
   - HIGH risk shows "⚠️ Human review recommended"
   - Lists cultural concerns inline
   - Shows count of suggested adaptations
   - Appears after publish (moderation.cultural_sensitivity)

UI Features:
- 🌍 Cultural Sensitivity icon for visibility
- Risk-based color coding (traffic light pattern)
- Expandable concern details
- Suggested adaptations inline
- Timestamps for audit trail
- Non-blocking workflow (flags for review, doesn't prevent action)

Human Approval Workflow:
- Existing respond() API already stores cultural_sensitivity data
- Existing publish() API already stores cultural_sensitivity data
- UI displays flags and suggestions
- Human reviewer makes final decision (inst_081 pluralism)
- No new endpoints needed - workflow integrated into existing approval flow

Next: Deploy Phase 2, monitor Phase 3 daily reminders for learning/refinement

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
TheFlow 2025-10-25 11:22:42 +13:00
parent 7645c49ab3
commit f6963de4c6
2 changed files with 99 additions and 0 deletions

View file

@ -367,6 +367,35 @@ async function loadDraftQueue() {
</div>
</div>
` : ''}
<!-- Cultural Sensitivity Check (inst_081) -->
${item.data.moderation?.cultural_sensitivity ? `
<div class="mt-2">
<span class="px-2 py-1 ${
item.data.moderation.cultural_sensitivity.risk_level === 'HIGH' ? 'bg-red-100 text-red-800' :
item.data.moderation.cultural_sensitivity.risk_level === 'MEDIUM' ? 'bg-yellow-100 text-yellow-800' :
'bg-green-100 text-green-800'
} text-xs rounded">
🌍 Cultural Risk: ${item.data.moderation.cultural_sensitivity.risk_level}
${item.data.moderation.cultural_sensitivity.risk_level === 'HIGH' ? ' ⚠️ Human review recommended' : ''}
</span>
${item.data.moderation.cultural_sensitivity.concerns?.length > 0 ? `
<div class="mt-2 space-y-1">
${item.data.moderation.cultural_sensitivity.concerns.map(c => `
<div class="text-xs bg-orange-50 border border-orange-200 rounded px-2 py-1">
<strong class="text-orange-900">${c.type.replace(/_/g, ' ')}:</strong>
<span class="text-orange-700">${c.detail}</span>
</div>
`).join('')}
</div>
` : ''}
${item.data.moderation.cultural_sensitivity.suggestions?.length > 0 ? `
<div class="mt-1 text-xs text-blue-700">
💡 ${item.data.moderation.cultural_sensitivity.suggestions.length} adaptation(s) suggested
</div>
` : ''}
</div>
` : ''}
</div>
<div class="ml-4 flex flex-col items-end gap-2">
<span class="px-3 py-1 text-xs rounded ${item.priority === 'high' ? 'bg-red-100 text-red-800' : 'bg-yellow-100 text-yellow-800'}">

View file

@ -432,6 +432,76 @@ async function showInquiryDetails(inquiryId) {
`;
}
// Cultural Sensitivity Check (Phase 1: inst_081 pluralism)
if (currentInquiry.response?.cultural_sensitivity) {
const cultural = currentInquiry.response.cultural_sensitivity;
const riskColors = {
'LOW': { bg: 'green', text: 'green' },
'MEDIUM': { bg: 'yellow', text: 'yellow' },
'HIGH': { bg: 'red', text: 'red' }
};
const colors = riskColors[cultural.risk_level] || riskColors['LOW'];
html += `
<!-- Cultural Sensitivity Analysis (inst_081) -->
<div class="mb-6">
<h4 class="text-sm font-semibold text-gray-900 mb-3">🌍 Cultural Sensitivity Analysis</h4>
<!-- Risk Level -->
<div class="bg-${colors.bg}-50 rounded-lg p-4 mb-3 border border-${colors.bg}-200">
<p class="text-sm font-medium text-${colors.text}-900 mb-2">
${cultural.risk_level === 'HIGH' ? '⚠️ ' : cultural.risk_level === 'MEDIUM' ? '⚡ ' : '✓ '}Risk Level: ${cultural.risk_level}
</p>
<p class="text-sm text-${colors.text}-800 mb-2">
<strong>Recommended Action:</strong> ${cultural.recommended_action}
</p>
${cultural.risk_level === 'HIGH' ? `
<p class="text-xs text-${colors.text}-700 font-semibold">
🚨 Human review recommended before sending (inst_081 pluralism)
</p>
` : ''}
</div>
${cultural.concerns?.length > 0 ? `
<!-- Concerns -->
<div class="bg-orange-50 rounded-lg p-4 mb-3">
<p class="text-sm font-medium text-orange-900 mb-2">Cultural Concerns (${cultural.concerns.length}):</p>
<ul class="space-y-2 text-sm text-orange-800">
${cultural.concerns.map(concern => `
<li class="border-l-2 border-orange-400 pl-3">
<strong>${concern.type.replace(/_/g, ' ')}:</strong> ${escapeHtml(concern.detail)}
${concern.audience_context ? `<br><span class="text-xs text-orange-700">Context: ${escapeHtml(concern.audience_context)}</span>` : ''}
</li>
`).join('')}
</ul>
</div>
` : ''}
${cultural.suggestions?.length > 0 ? `
<!-- Suggestions -->
<div class="bg-blue-50 rounded-lg p-4">
<p class="text-sm font-medium text-blue-900 mb-2">Suggested Adaptations (${cultural.suggestions.length}):</p>
<ul class="list-disc list-inside space-y-1 text-sm text-blue-800">
${cultural.suggestions.map(suggestion => `
<li>${escapeHtml(suggestion.suggestion || suggestion)}</li>
`).join('')}
</ul>
</div>
` : ''}
<div class="mt-3 pt-3 border-t border-gray-200">
<p class="text-xs text-gray-600">
<strong>Framework:</strong> PluralisticDeliberationOrchestrator (inst_081) |
<strong>Checked:</strong> ${new Date(cultural.checked_at).toLocaleString()}
</p>
<p class="text-xs text-gray-500 mt-1 italic">
Note: AI flags cultural concerns but never blocks. Human decides final approach.
</p>
</div>
</div>
`;
}
content.innerHTML = html;
// Update modal buttons visibility