Replace "ensures", "guarantee", "foolproof", "world-class" and similar absolute terms with evidence-based language throughout public pages, JS components, and FAQ content. Changes apply inst_017 (no absolute assurance terms) consistently. Replacements: - "ensures X" → "validates X", "so that X", "supports X", "maintains X" - "guarantee" → removed or rephrased with qualified language - "foolproof" → "infallible" - "architecturally impossible" → "architecture prevents without explicit override flags" Preserved: published research papers (architectural-alignment*.html), EU AI Act quotes, Te Tiriti treaty language, and FAQ meta-commentary that deliberately critiques this language (lines 2842-2896). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
221 lines
7.9 KiB
JavaScript
221 lines
7.9 KiB
JavaScript
// Stakeholder definitions
|
|
const stakeholders = [
|
|
{
|
|
id: 'developer',
|
|
name: 'Developer (You)',
|
|
icon: '👨💻',
|
|
color: 'blue',
|
|
perspective: {
|
|
concern: 'Professional Reputation & Timeline',
|
|
view: 'Public disclosure could damage my reputation and delay the project launch. I worked hard on this code and a vulnerability report might make me look incompetent.',
|
|
priority: 'Protect career progress while maintaining ethical standards'
|
|
}
|
|
},
|
|
{
|
|
id: 'users',
|
|
name: 'End Users',
|
|
icon: '👥',
|
|
color: 'green',
|
|
perspective: {
|
|
concern: 'Data Safety & Trust',
|
|
view: 'If my data is at risk, I have a right to know immediately—regardless of the developer\'s reputation concerns. Silence prioritizes the developer over my safety.',
|
|
priority: 'Transparency and immediate protection from potential harm'
|
|
}
|
|
},
|
|
{
|
|
id: 'organization',
|
|
name: 'Your Organization',
|
|
icon: '🏢',
|
|
color: 'purple',
|
|
perspective: {
|
|
concern: 'Liability & Brand Protection',
|
|
view: 'Uncontrolled disclosure could expose us to legal liability. We need time to assess the vulnerability, prepare a fix, and coordinate with legal counsel before any public statement.',
|
|
priority: 'Managed disclosure that minimizes organizational risk'
|
|
}
|
|
},
|
|
{
|
|
id: 'security-community',
|
|
name: 'Security Community',
|
|
icon: '🔒',
|
|
color: 'orange',
|
|
perspective: {
|
|
concern: 'Responsible Disclosure Norms',
|
|
view: 'Follow established responsible disclosure practices: private notification, reasonable fix timeline (typically 90 days), then coordinated public disclosure. This balances safety with fairness.',
|
|
priority: 'Adherence to community norms that have proven effective'
|
|
}
|
|
},
|
|
{
|
|
id: 'competitors',
|
|
name: 'Competitors',
|
|
icon: '🏪',
|
|
color: 'red',
|
|
perspective: {
|
|
concern: 'Market Dynamics',
|
|
view: 'Your vulnerability might reveal weaknesses in similar products we build. We\'d prefer you disclose quietly so we can check our own code without public pressure.',
|
|
priority: 'Minimize market disruption from security revelations'
|
|
}
|
|
},
|
|
{
|
|
id: 'regulators',
|
|
name: 'Data Protection Regulators',
|
|
icon: '⚖️',
|
|
color: 'indigo',
|
|
perspective: {
|
|
concern: 'Compliance & User Rights',
|
|
view: 'GDPR and similar frameworks require prompt notification of data breaches. If user data is at risk, you may have legal obligations to disclose within specific timeframes (typically 72 hours).',
|
|
priority: 'Comply with data protection law'
|
|
}
|
|
}
|
|
];
|
|
|
|
let selectedStakeholders = [];
|
|
let currentDecision = null;
|
|
|
|
// Initialize stakeholder cards
|
|
function initStakeholders() {
|
|
const grid = document.getElementById('stakeholder-grid');
|
|
grid.innerHTML = stakeholders.map(s => `
|
|
<div class="stakeholder-card" data-stakeholder="${s.id}">
|
|
<div class="text-4xl mb-2 text-center">${s.icon}</div>
|
|
<h4 class="font-semibold text-gray-900 text-center text-sm">${s.name}</h4>
|
|
</div>
|
|
`).join('');
|
|
|
|
// Add click handlers
|
|
document.querySelectorAll('.stakeholder-card').forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
const id = card.getAttribute('data-stakeholder');
|
|
toggleStakeholder(id, card);
|
|
});
|
|
});
|
|
}
|
|
|
|
function toggleStakeholder(id, cardElement) {
|
|
const index = selectedStakeholders.indexOf(id);
|
|
|
|
if (index > -1) {
|
|
// Deselect
|
|
selectedStakeholders.splice(index, 1);
|
|
cardElement.classList.remove('stakeholder-selected');
|
|
} else {
|
|
// Select
|
|
selectedStakeholders.push(id);
|
|
cardElement.classList.add('stakeholder-selected');
|
|
}
|
|
|
|
// Update continue button
|
|
const continueBtn = document.getElementById('continue-to-perspectives');
|
|
continueBtn.disabled = selectedStakeholders.length < 2;
|
|
}
|
|
|
|
function showPerspectives() {
|
|
// Hide stakeholder selection
|
|
document.getElementById('stakeholder-selection').classList.add('hidden');
|
|
|
|
// Show perspectives section
|
|
const section = document.getElementById('perspectives-section');
|
|
section.classList.remove('hidden');
|
|
section.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
|
|
// Populate perspectives
|
|
const container = document.getElementById('perspectives-container');
|
|
container.innerHTML = selectedStakeholders.map(id => {
|
|
const stakeholder = stakeholders.find(s => s.id === id);
|
|
return `
|
|
<div class="perspective-card border-${stakeholder.color}-500 fade-in">
|
|
<div class="flex items-start gap-4">
|
|
<div class="text-4xl flex-shrink-0">${stakeholder.icon}</div>
|
|
<div class="flex-1">
|
|
<h4 class="font-bold text-gray-900 mb-2">${stakeholder.name}: ${stakeholder.perspective.concern}</h4>
|
|
<p class="text-gray-700 mb-2">${stakeholder.perspective.view}</p>
|
|
<p class="text-sm text-${stakeholder.color}-600 font-semibold">Priority: ${stakeholder.perspective.priority}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
function showDecisionSection() {
|
|
// Hide perspectives
|
|
document.getElementById('perspectives-section').classList.add('hidden');
|
|
|
|
// Show decision section
|
|
const section = document.getElementById('decision-section');
|
|
section.classList.remove('hidden');
|
|
section.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
|
|
function makeDecision(decision) {
|
|
currentDecision = decision;
|
|
|
|
// Hide decision section
|
|
document.getElementById('decision-section').classList.add('hidden');
|
|
|
|
// Show explanation
|
|
const section = document.getElementById('explanation-section');
|
|
section.classList.remove('hidden');
|
|
section.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
|
|
function showAutonomousPath() {
|
|
document.getElementById('decision-question').classList.add('hidden');
|
|
document.getElementById('autonomous-path').classList.remove('hidden');
|
|
document.getElementById('autonomous-path').scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
|
|
function showDeliberationPath() {
|
|
document.getElementById('decision-question').classList.add('hidden');
|
|
document.getElementById('deliberation-path').classList.remove('hidden');
|
|
document.getElementById('stakeholder-selection').scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
|
|
function resetDemo() {
|
|
// Reset state
|
|
selectedStakeholders = [];
|
|
currentDecision = null;
|
|
|
|
// Show decision question
|
|
document.getElementById('decision-question').classList.remove('hidden');
|
|
|
|
// Hide all paths
|
|
document.getElementById('autonomous-path').classList.add('hidden');
|
|
document.getElementById('deliberation-path').classList.add('hidden');
|
|
|
|
// Reset deliberation path sections
|
|
document.getElementById('stakeholder-selection').classList.remove('hidden');
|
|
document.getElementById('perspectives-section').classList.add('hidden');
|
|
document.getElementById('decision-section').classList.add('hidden');
|
|
document.getElementById('explanation-section').classList.add('hidden');
|
|
|
|
// Reinitialize stakeholders
|
|
initStakeholders();
|
|
|
|
// Scroll to top
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
}
|
|
|
|
// Event listeners
|
|
document.getElementById('autonomous-btn').addEventListener('click', showAutonomousPath);
|
|
document.getElementById('deliberation-btn').addEventListener('click', showDeliberationPath);
|
|
document.getElementById('reset-from-autonomous').addEventListener('click', () => {
|
|
resetDemo();
|
|
// Automatically show deliberation path
|
|
setTimeout(() => {
|
|
showDeliberationPath();
|
|
}, 100);
|
|
});
|
|
document.getElementById('continue-to-perspectives').addEventListener('click', showPerspectives);
|
|
document.getElementById('continue-to-decision').addEventListener('click', showDecisionSection);
|
|
document.getElementById('reset-demo').addEventListener('click', resetDemo);
|
|
|
|
// Decision option handlers
|
|
document.querySelectorAll('.decision-option').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const decision = btn.getAttribute('data-decision');
|
|
makeDecision(decision);
|
|
});
|
|
});
|
|
|
|
// Initialize
|
|
initStakeholders();
|