CSP Compliance (complete): - Install Tailwind CSS v3 locally (24KB build) - Replace CDN with /css/tailwind.css in all HTML files - Extract all inline scripts to external JS files - Created 6 external JS files for demos & docs - All pages now comply with script-src 'self' Three Audience Paths (complete): - Created /researcher.html (academic/theoretical) - Created /implementer.html (practical integration) - Created /advocate.html (mission/values/community) - Updated homepage links to audience pages - Each path has dedicated nav, hero, resources, CTAs Files Modified (20): - 7 HTML files (CSP compliance) - 3 audience landing pages (new) - 6 external JS files (extracted) - package.json (Tailwind v3) - tailwind.config.js (new) - Built CSS (24KB minified) All resources CSP-compliant, all pages tested 200 OK 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
219 lines
7 KiB
JavaScript
219 lines
7 KiB
JavaScript
const scenarios = {
|
|
optimize_images: {
|
|
title: "Optimize Image Loading",
|
|
description: "Implement lazy loading and compression for better performance",
|
|
domain: "technical",
|
|
allowed: true,
|
|
reason: "Technical optimization within defined parameters. No values trade-offs required.",
|
|
alternatives: null,
|
|
code: `// BoundaryEnforcer Check
|
|
const boundary = enforcer.enforce({
|
|
type: 'performance_optimization',
|
|
action: 'implement_lazy_loading'
|
|
});
|
|
|
|
// Result: ALLOWED
|
|
{
|
|
allowed: true,
|
|
reason: "Technical decision, no values impact",
|
|
proceed: true
|
|
}`
|
|
},
|
|
privacy_vs_analytics: {
|
|
title: "Enable Analytics Tracking",
|
|
description: "Add Google Analytics to track user behavior",
|
|
domain: "values",
|
|
allowed: false,
|
|
reason: "Privacy vs. analytics is an irreducible values trade-off. Different users have different privacy expectations.",
|
|
alternatives: [
|
|
"Research privacy-friendly analytics options (e.g., Plausible, Fathom)",
|
|
"Analyze current user behavior from server logs",
|
|
"Document pros/cons of different analytics approaches",
|
|
"Present options with privacy impact assessment"
|
|
],
|
|
code: `// BoundaryEnforcer Check
|
|
const boundary = enforcer.enforce({
|
|
type: 'privacy_policy',
|
|
action: 'enable_tracking',
|
|
domain: 'values'
|
|
});
|
|
|
|
// Result: BLOCKED
|
|
{
|
|
allowed: false,
|
|
reason: "Privacy vs. convenience trade-off",
|
|
requires_human_decision: true,
|
|
boundary_section: "12.1"
|
|
}`
|
|
},
|
|
auto_subscribe: {
|
|
title: "Auto-Subscribe Users",
|
|
description: "Automatically subscribe new users to newsletter",
|
|
domain: "user_agency",
|
|
allowed: false,
|
|
reason: "This determines the level of user control and agency. Opt-in vs. opt-out affects user autonomy.",
|
|
alternatives: [
|
|
"Implement explicit opt-in during registration",
|
|
"Implement opt-out with clear unsubscribe",
|
|
"Research industry best practices for consent",
|
|
"Document GDPR compliance implications"
|
|
],
|
|
code: `// BoundaryEnforcer Check
|
|
const boundary = enforcer.enforce({
|
|
type: 'user_consent',
|
|
action: 'auto_subscribe',
|
|
domain: 'user_agency'
|
|
});
|
|
|
|
// Result: BLOCKED
|
|
{
|
|
allowed: false,
|
|
reason: "Affects user agency and control",
|
|
requires_human_decision: true,
|
|
boundary_section: "12.2"
|
|
}`
|
|
},
|
|
delete_old_data: {
|
|
title: "Delete Old User Data",
|
|
description: "Automatically delete user data older than 6 months",
|
|
domain: "irreversible",
|
|
allowed: false,
|
|
reason: "Data deletion is irreversible and may have legal/compliance implications.",
|
|
alternatives: [
|
|
"Check backup status and retention policies",
|
|
"Verify legal data retention requirements",
|
|
"Confirm user consent for deletion",
|
|
"Implement archive rather than delete"
|
|
],
|
|
code: `// BoundaryEnforcer Check
|
|
const boundary = enforcer.enforce({
|
|
type: 'data_deletion',
|
|
action: 'delete_user_data',
|
|
domain: 'irreversible'
|
|
});
|
|
|
|
// Result: BLOCKED
|
|
{
|
|
allowed: false,
|
|
reason: "Irreversible action with legal implications",
|
|
requires_human_approval: true,
|
|
boundary_section: "12.3"
|
|
}`
|
|
},
|
|
cache_strategy: {
|
|
title: "Implement Caching Strategy",
|
|
description: "Add Redis caching for frequently accessed data",
|
|
domain: "technical",
|
|
allowed: true,
|
|
reason: "Technical implementation decision within established patterns. No values impact.",
|
|
alternatives: null,
|
|
code: `// BoundaryEnforcer Check
|
|
const boundary = enforcer.enforce({
|
|
type: 'technical_implementation',
|
|
action: 'add_caching'
|
|
});
|
|
|
|
// Result: ALLOWED
|
|
{
|
|
allowed: true,
|
|
reason: "Technical decision with clear constraints",
|
|
proceed: true
|
|
}`
|
|
},
|
|
content_moderation: {
|
|
title: "Automatic Content Moderation",
|
|
description: "AI automatically removes inappropriate content",
|
|
domain: "values",
|
|
allowed: false,
|
|
reason: "Defining 'inappropriate' involves values judgments about free speech, community standards, and cultural context.",
|
|
alternatives: [
|
|
"Implement flagging system for human review",
|
|
"Create tiered moderation (AI flags, human decides)",
|
|
"Research community moderation models",
|
|
"Document content policy options for decision"
|
|
],
|
|
code: `// BoundaryEnforcer Check
|
|
const boundary = enforcer.enforce({
|
|
type: 'content_policy',
|
|
action: 'auto_moderate',
|
|
domain: 'values'
|
|
});
|
|
|
|
// Result: BLOCKED
|
|
{
|
|
allowed: false,
|
|
reason: "Content standards are values decisions",
|
|
requires_human_decision: true,
|
|
boundary_section: "12.1"
|
|
}`
|
|
}
|
|
};
|
|
|
|
// Event listeners
|
|
document.querySelectorAll('.scenario-card').forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
const decision = card.getAttribute('data-decision');
|
|
showResult(scenarios[decision]);
|
|
|
|
// Highlight selected
|
|
document.querySelectorAll('.scenario-card').forEach(c => {
|
|
c.classList.remove('ring-2', 'ring-blue-500');
|
|
});
|
|
card.classList.add('ring-2', 'ring-blue-500');
|
|
});
|
|
});
|
|
|
|
function showResult(scenario) {
|
|
document.getElementById('empty-state').classList.add('hidden');
|
|
document.getElementById('result-content').classList.remove('hidden');
|
|
|
|
// Decision info
|
|
document.getElementById('decision-title').textContent = scenario.title;
|
|
document.getElementById('decision-desc').textContent = scenario.description;
|
|
|
|
// Verdict
|
|
const verdict = document.getElementById('verdict');
|
|
if (scenario.allowed) {
|
|
verdict.innerHTML = `
|
|
<div class="flex items-start">
|
|
<svg class="w-8 h-8 text-green-600 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
</svg>
|
|
<div>
|
|
<div class="text-lg font-semibold text-green-900 mb-1">✅ ALLOWED</div>
|
|
<div class="text-green-800">AI can automate this decision</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
verdict.className = 'rounded-lg p-6 mb-6 bg-green-100 border border-green-300';
|
|
} else {
|
|
verdict.innerHTML = `
|
|
<div class="flex items-start">
|
|
<svg class="w-8 h-8 text-red-600 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
</svg>
|
|
<div>
|
|
<div class="text-lg font-semibold text-red-900 mb-1">🚫 BLOCKED</div>
|
|
<div class="text-red-800">Requires human judgment</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
verdict.className = 'rounded-lg p-6 mb-6 bg-red-100 border border-red-300';
|
|
}
|
|
|
|
// Reasoning
|
|
document.getElementById('reasoning').textContent = scenario.reason;
|
|
|
|
// Alternatives
|
|
if (scenario.alternatives) {
|
|
document.getElementById('ai-alternatives').classList.remove('hidden');
|
|
document.getElementById('alternatives-list').innerHTML = scenario.alternatives
|
|
.map(alt => `<li>${alt}</li>`)
|
|
.join('');
|
|
} else {
|
|
document.getElementById('ai-alternatives').classList.add('hidden');
|
|
}
|
|
|
|
// Code example
|
|
document.getElementById('code-example').textContent = scenario.code;
|
|
}
|