\$\{html\}<\/div>`;/g,
+ 'chartEl.innerHTML = `
${html}
`; setProgressBarWidths(chartEl);'
+ );
+
+ fs.writeFileSync(auditFile, auditContent);
+ console.log('β Fixed audit-analytics.js');
+}
+
+// rule-manager.js
+const ruleManagerFile = path.join(__dirname, '../public/js/admin/rule-manager.js');
+let ruleManagerContent = fs.readFileSync(ruleManagerFile, 'utf8');
+
+if (!ruleManagerContent.includes('setProgressBarWidths')) {
+ // Add helper before last })
+ const lastBrace = ruleManagerContent.lastIndexOf('})();');
+ ruleManagerContent = ruleManagerContent.slice(0, lastBrace) + helper + '\n' + ruleManagerContent.slice(lastBrace);
+
+ // Add calls after container.innerHTML assignments
+ ruleManagerContent = ruleManagerContent.replace(
+ /(container\.innerHTML = `[\s\S]*?`;)/g,
+ '$1 setProgressBarWidths(container);'
+ );
+
+ fs.writeFileSync(ruleManagerFile, ruleManagerContent);
+ console.log('β Fixed rule-manager.js');
+}
+
+// rule-editor.js
+const ruleEditorFile = path.join(__dirname, '../public/js/admin/rule-editor.js');
+let ruleEditorContent = fs.readFileSync(ruleEditorFile, 'utf8');
+
+if (!ruleEditorContent.includes('setProgressBarWidths')) {
+ // Add helper before last })
+ const lastBrace = ruleEditorContent.lastIndexOf('})();');
+ ruleEditorContent = ruleEditorContent.slice(0, lastBrace) + helper + '\n' + ruleEditorContent.slice(lastBrace);
+
+ // Add calls after modal content is set
+ ruleEditorContent = ruleEditorContent.replace(
+ /(modalContent\.innerHTML = `[\s\S]*?`;)(\s+\/\/ Show modal)/g,
+ '$1 setProgressBarWidths(modalContent);$2'
+ );
+
+ fs.writeFileSync(ruleEditorFile, ruleEditorContent);
+ console.log('β Fixed rule-editor.js');
+}
+
+console.log('\nβ
Progress bar helpers added\n');
diff --git a/scripts/add-sections-from-db-markdown.js b/scripts/add-sections-from-db-markdown.js
new file mode 100644
index 00000000..95ca38ac
--- /dev/null
+++ b/scripts/add-sections-from-db-markdown.js
@@ -0,0 +1,325 @@
+#!/usr/bin/env node
+/**
+ * Add Card View Sections to Documents (Using DB Markdown)
+ *
+ * Generates sections from the content_markdown field stored in the database
+ * for documents that don't have corresponding MD files on disk.
+ */
+
+require('dotenv').config();
+
+const { connect, close } = require('../src/utils/db.util');
+const Document = require('../src/models/Document.model');
+const { marked } = require('marked');
+
+// List of document slugs that need sections
+const SLUGS_NEEDING_SECTIONS = [
+ // 5 newly imported archives
+ 'case-studies-real-world-llm-failure-modes-appendix',
+ 'implementation-guide-python-examples',
+ 'tractatus-framework-enforcement-claude-code',
+ 'research-topic-concurrent-session-architecture',
+ 'research-topic-rule-proliferation-transactional-overhead',
+
+ // 5 technical reference docs
+ 'implementation-roadmap-24-month-deployment-plan',
+ 'api-reference-complete',
+ 'api-javascript-examples',
+ 'api-python-examples',
+ 'openapi-specification',
+
+ // 5 case studies
+ 'the-27027-incident-a-case-study-in-pattern-recognition-bias',
+ 'when-frameworks-fail-and-why-thats-ok',
+ 'our-framework-in-action-detecting-and-correcting-ai-fabrications',
+ 'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery',
+ 'case-studies-real-world-llm-failure-modes',
+
+ // 2 Phase 5 PoC summaries
+ 'phase-5-poc-session-1-summary',
+ 'phase-5-poc-session-2-summary'
+];
+
+function extractSectionsFromMarkdown(markdown) {
+ const lines = markdown.split('\n');
+ const sections = [];
+ let currentSection = null;
+ let contentBuffer = [];
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+
+ // Match H2 headers (## Title)
+ const h2Match = line.match(/^## (.+)$/);
+ if (h2Match) {
+ // Save previous section if exists
+ if (currentSection) {
+ currentSection.content_md = contentBuffer.join('\n').trim();
+ sections.push(currentSection);
+ }
+
+ // Start new section
+ currentSection = {
+ title: h2Match[1].trim(),
+ content_md: ''
+ };
+ contentBuffer = [];
+ continue;
+ }
+
+ // Collect content for current section
+ if (currentSection) {
+ contentBuffer.push(line);
+ }
+ }
+
+ // Save final section
+ if (currentSection) {
+ currentSection.content_md = contentBuffer.join('\n').trim();
+ sections.push(currentSection);
+ }
+
+ return sections;
+}
+
+function generateExcerpt(markdown, maxLength = 150) {
+ let text = markdown
+ .replace(/^#+\s+/gm, '')
+ .replace(/\*\*(.+?)\*\*/g, '$1')
+ .replace(/\*(.+?)\*/g, '$1')
+ .replace(/\[(.+?)\]\(.+?\)/g, '$1')
+ .replace(/`(.+?)`/g, '$1')
+ .replace(/^[-*+]\s+/gm, '')
+ .replace(/^\d+\.\s+/gm, '')
+ .replace(/\n{2,}/g, ' ')
+ .trim();
+
+ if (text.length > maxLength) {
+ text = text.substring(0, maxLength).trim();
+ const lastPeriod = text.lastIndexOf('.');
+ if (lastPeriod > maxLength * 0.7) {
+ text = text.substring(0, lastPeriod + 1);
+ } else {
+ text += '...';
+ }
+ }
+
+ return text;
+}
+
+function estimateReadingTime(text) {
+ const wordCount = text.split(/\s+/).length;
+ const minutes = Math.ceil(wordCount / 200);
+ return Math.max(1, minutes);
+}
+
+function classifySection(title, content) {
+ const titleLower = title.toLowerCase();
+ const contentLower = content.toLowerCase();
+
+ if (
+ titleLower.includes('limitation') ||
+ titleLower.includes('failure') ||
+ titleLower.includes('warning') ||
+ titleLower.includes('security') ||
+ titleLower.includes('risk') ||
+ content.match(/β οΈ|critical|warning|caution|danger/gi)
+ ) {
+ return 'critical';
+ }
+
+ if (
+ titleLower.includes('glossary') ||
+ titleLower.includes('reference') ||
+ titleLower.includes('contact') ||
+ titleLower.includes('license') ||
+ titleLower.includes('getting started')
+ ) {
+ return 'reference';
+ }
+
+ if (
+ titleLower.includes('technical') ||
+ titleLower.includes('architecture') ||
+ titleLower.includes('implementation') ||
+ titleLower.includes('integration') ||
+ titleLower.includes('api') ||
+ content.match(/```|`[a-z]+`|function|class|const|import/gi)
+ ) {
+ return 'technical';
+ }
+
+ if (
+ titleLower.includes('how') ||
+ titleLower.includes('guide') ||
+ titleLower.includes('tutorial') ||
+ titleLower.includes('example') ||
+ titleLower.includes('use case') ||
+ titleLower.includes('should use') ||
+ titleLower.includes('contributing')
+ ) {
+ return 'practical';
+ }
+
+ return 'conceptual';
+}
+
+function determineTechnicalLevel(content) {
+ const contentLower = content.toLowerCase();
+
+ if (
+ content.match(/```[\s\S]+```/g) ||
+ contentLower.includes('api') ||
+ contentLower.includes('implementation') ||
+ contentLower.includes('integration') ||
+ contentLower.includes('architecture')
+ ) {
+ return 'advanced';
+ }
+
+ if (
+ contentLower.includes('service') ||
+ contentLower.includes('component') ||
+ contentLower.includes('system') ||
+ contentLower.includes('framework')
+ ) {
+ return 'intermediate';
+ }
+
+ return 'beginner';
+}
+
+function generateSlug(title) {
+ return title
+ .toLowerCase()
+ .replace(/[^a-z0-9\s-]/g, '')
+ .replace(/\s+/g, '-')
+ .replace(/-+/g, '-')
+ .replace(/^-|-$/g, '');
+}
+
+async function addSectionsToDocument(slug) {
+ console.log(`\nπ Processing: ${slug}`);
+
+ try {
+ // Find document
+ const doc = await Document.findBySlug(slug);
+ if (!doc) {
+ console.log(` β Document not found`);
+ return { success: false, reason: 'not_found' };
+ }
+
+ // Check if already has sections
+ if (doc.sections && doc.sections.length > 0) {
+ console.log(` βοΈ Already has ${doc.sections.length} sections`);
+ return { success: false, reason: 'has_sections' };
+ }
+
+ // Check if has content_markdown
+ if (!doc.content_markdown) {
+ console.log(` β No content_markdown field`);
+ return { success: false, reason: 'no_markdown' };
+ }
+
+ // Extract sections from markdown
+ const rawSections = extractSectionsFromMarkdown(doc.content_markdown);
+
+ if (rawSections.length === 0) {
+ console.log(` β οΈ No H2 sections found in markdown`);
+ return { success: false, reason: 'no_h2' };
+ }
+
+ console.log(` π Found ${rawSections.length} sections`);
+
+ // Process each section
+ const sections = [];
+ for (let i = 0; i < rawSections.length; i++) {
+ const raw = rawSections[i];
+
+ if (!raw.content_md.trim()) {
+ continue;
+ }
+
+ const content_html = marked(raw.content_md);
+ const excerpt = generateExcerpt(raw.content_md);
+ const readingTime = estimateReadingTime(raw.content_md);
+ const category = classifySection(raw.title, raw.content_md);
+ const technicalLevel = determineTechnicalLevel(raw.content_md);
+ const sectionSlug = generateSlug(raw.title);
+
+ sections.push({
+ number: i + 1,
+ title: raw.title,
+ slug: sectionSlug,
+ content_html,
+ excerpt,
+ readingTime,
+ technicalLevel,
+ category
+ });
+ }
+
+ // Update document
+ const updated = await Document.update(doc._id.toString(), { sections });
+
+ if (!updated) {
+ console.log(` β Failed to update`);
+ return { success: false, reason: 'update_failed' };
+ }
+
+ console.log(` β
Added ${sections.length} sections`);
+ sections.forEach(s => {
+ console.log(` ${s.number}. ${s.title} (${s.category}, ${s.readingTime}min)`);
+ });
+
+ return { success: true, sections: sections.length };
+
+ } catch (error) {
+ console.error(` β Error: ${error.message}`);
+ return { success: false, error: error.message };
+ }
+}
+
+async function main() {
+ try {
+ console.log('π Adding Card View Sections to 17 Documents\n');
+ console.log('βββββββββββββββββββββββββββββββββββββββββββββββββββ\n');
+
+ await connect();
+
+ let added = 0;
+ let skipped = 0;
+ let noH2 = 0;
+ let failed = 0;
+
+ for (const slug of SLUGS_NEEDING_SECTIONS) {
+ const result = await addSectionsToDocument(slug);
+
+ if (result.success) {
+ added++;
+ } else if (result.reason === 'has_sections') {
+ skipped++;
+ } else if (result.reason === 'no_h2') {
+ noH2++;
+ } else {
+ failed++;
+ }
+ }
+
+ console.log('\nβββββββββββββββββββββββββββββββββββββββββββββββββββ');
+ console.log('\nπ Summary:');
+ console.log(` β
Added sections: ${added}`);
+ console.log(` βοΈ Skipped (already have sections): ${skipped}`);
+ console.log(` β οΈ No H2 sections found: ${noH2}`);
+ console.log(` β Failed: ${failed}`);
+ console.log(` π¦ Total: ${SLUGS_NEEDING_SECTIONS.length}`);
+
+ await close();
+
+ } catch (error) {
+ console.error('\nβ Fatal error:', error);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/scripts/add-sections-to-17-docs.js b/scripts/add-sections-to-17-docs.js
new file mode 100644
index 00000000..a1e26d54
--- /dev/null
+++ b/scripts/add-sections-to-17-docs.js
@@ -0,0 +1,139 @@
+#!/usr/bin/env node
+/**
+ * Add Card View Sections to 17 Documents
+ *
+ * Adds card view sections to:
+ * - 5 newly imported archives
+ * - 12 existing documents without sections
+ */
+
+require('dotenv').config();
+
+const fs = require('fs');
+const path = require('path');
+const { connect, close } = require('../src/utils/db.util');
+const Document = require('../src/models/Document.model');
+const { processMarkdownFile } = require('./generate-card-sections.js');
+
+const DOCS_TO_PROCESS = [
+ // 5 newly imported archives
+ { slug: 'case-studies-real-world-llm-failure-modes-appendix', mdPath: 'docs/markdown/case-studies.md' },
+ { slug: 'implementation-guide-python-examples', mdPath: 'docs/api/examples-python.md' },
+ { slug: 'tractatus-framework-enforcement-claude-code', mdPath: 'docs/claude-code-framework-enforcement.md' },
+ { slug: 'research-topic-concurrent-session-architecture', mdPath: 'docs/research/concurrent-session-architecture-limitations.md' },
+ { slug: 'research-topic-rule-proliferation-transactional-overhead', mdPath: 'docs/research/rule-proliferation-and-transactional-overhead.md' },
+
+ // 5 technical reference docs without sections
+ { slug: 'implementation-roadmap-24-month-deployment-plan', mdPath: 'docs/markdown/implementation-roadmap-24-month-deployment-plan.md' },
+ { slug: 'api-reference-complete', mdPath: 'docs/markdown/api-reference-complete.md' },
+ { slug: 'api-javascript-examples', mdPath: 'docs/api/examples-javascript.md' },
+ { slug: 'api-python-examples', mdPath: 'docs/api/examples-python.md' },
+ { slug: 'openapi-specification', mdPath: 'docs/markdown/openapi-specification.md' },
+
+ // 5 case studies without sections
+ { slug: 'the-27027-incident-a-case-study-in-pattern-recognition-bias', mdPath: 'docs/case-studies/27027-incident-detailed-analysis.md' },
+ { slug: 'when-frameworks-fail-and-why-thats-ok', mdPath: 'docs/case-studies/when-frameworks-fail-oct-2025.md' },
+ { slug: 'our-framework-in-action-detecting-and-correcting-ai-fabrications', mdPath: 'docs/case-studies/framework-in-action-oct-2025.md' },
+ { slug: 'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery', mdPath: 'docs/case-studies/real-world-governance-case-study-oct-2025.md' },
+ { slug: 'case-studies-real-world-llm-failure-modes', mdPath: 'docs/markdown/case-studies.md' },
+
+ // 2 Phase 5 PoC summaries
+ { slug: 'phase-5-poc-session-1-summary', mdPath: 'docs/markdown/phase-5-session1-summary.md' },
+ { slug: 'phase-5-poc-session-2-summary', mdPath: 'docs/markdown/phase-5-session2-summary.md' }
+];
+
+async function addSectionsToDocument(docInfo) {
+ console.log(`\nπ Processing: ${docInfo.slug}`);
+
+ try {
+ // Check if document exists
+ const doc = await Document.findBySlug(docInfo.slug);
+ if (!doc) {
+ console.log(` β Document not found in database`);
+ return { success: false, reason: 'not_found' };
+ }
+
+ // Check if already has sections
+ if (doc.sections && doc.sections.length > 0) {
+ console.log(` βοΈ Already has ${doc.sections.length} sections, skipping`);
+ return { success: false, reason: 'has_sections' };
+ }
+
+ // Build full path to markdown file
+ const fullPath = path.join('/home/theflow/projects/tractatus', docInfo.mdPath);
+
+ // Check if markdown file exists
+ if (!fs.existsSync(fullPath)) {
+ console.log(` β Markdown file not found: ${fullPath}`);
+ return { success: false, reason: 'md_not_found' };
+ }
+
+ // Generate sections
+ console.log(` π Generating sections from: ${docInfo.mdPath}`);
+ const sections = await processMarkdownFile(fullPath);
+
+ if (!sections || sections.length === 0) {
+ console.log(` β οΈ No sections generated (possibly no H2 headers)`);
+ return { success: false, reason: 'no_sections' };
+ }
+
+ // Update document with sections
+ const updated = await Document.update(doc._id.toString(), { sections });
+
+ if (!updated) {
+ console.log(` β Failed to update document`);
+ return { success: false, reason: 'update_failed' };
+ }
+
+ console.log(` β
Added ${sections.length} sections`);
+ return { success: true, sections: sections.length };
+
+ } catch (error) {
+ console.error(` β Error: ${error.message}`);
+ return { success: false, error: error.message };
+ }
+}
+
+async function main() {
+ try {
+ console.log('π Adding Card View Sections to 17 Documents\n');
+ console.log('βββββββββββββββββββββββββββββββββββββββββββββββββββ\n');
+
+ await connect();
+
+ let added = 0;
+ let skipped = 0;
+ let notFound = 0;
+ let failed = 0;
+
+ for (const docInfo of DOCS_TO_PROCESS) {
+ const result = await addSectionsToDocument(docInfo);
+
+ if (result.success) {
+ added++;
+ } else if (result.reason === 'has_sections') {
+ skipped++;
+ } else if (result.reason === 'not_found' || result.reason === 'md_not_found') {
+ notFound++;
+ } else {
+ failed++;
+ }
+ }
+
+ console.log('\nβββββββββββββββββββββββββββββββββββββββββββββββββββ');
+ console.log('\nπ Summary:');
+ console.log(` β
Added sections: ${added}`);
+ console.log(` βοΈ Skipped (already have sections): ${skipped}`);
+ console.log(` β Not found: ${notFound}`);
+ console.log(` β Failed: ${failed}`);
+ console.log(` π¦ Total processed: ${DOCS_TO_PROCESS.length}`);
+
+ await close();
+
+ } catch (error) {
+ console.error('\nβ Fatal error:', error);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/scripts/add-sections-to-documents.js b/scripts/add-sections-to-documents.js
new file mode 100644
index 00000000..bff9755a
--- /dev/null
+++ b/scripts/add-sections-to-documents.js
@@ -0,0 +1,105 @@
+/**
+ * Add section-based cards to all existing documents
+ * Parses documents and adds sections array for card-based UI
+ */
+
+const fs = require('fs');
+const path = require('path');
+const { MongoClient } = require('mongodb');
+const { parseDocumentSections } = require('../src/utils/document-section-parser');
+const { markdownToHtml } = require('../src/utils/markdown.util');
+
+const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/tractatus_prod';
+const DOCS_DIR = process.env.DOCS_DIR || '/var/www/tractatus/docs/markdown';
+
+async function main() {
+ console.log('=== Adding Sections to Documents ===\n');
+
+ const client = new MongoClient(MONGODB_URI);
+
+ try {
+ await client.connect();
+ const db = client.db();
+ const collection = db.collection('documents');
+
+ // Get all documents
+ const documents = await collection.find({}).toArray();
+ console.log(`Found ${documents.length} documents in database\n`);
+
+ const markdownFiles = {
+ 'tractatus-agentic-governance-system-glossary-of-terms': 'GLOSSARY.md',
+ 'introduction-to-the-tractatus-framework': 'introduction.md',
+ 'core-concepts-of-the-tractatus-framework': 'core-concepts.md',
+ 'implementation-guide': 'implementation-guide.md',
+ 'case-studies-real-world-llm-failure-modes': 'case-studies.md'
+ };
+
+ for (const doc of documents) {
+ console.log(`Processing: ${doc.title}`);
+
+ const markdownFile = markdownFiles[doc.slug];
+ if (!markdownFile) {
+ console.log(` β No markdown file found for ${doc.slug}`);
+ continue;
+ }
+
+ const filePath = path.join(DOCS_DIR, markdownFile);
+ if (!fs.existsSync(filePath)) {
+ console.log(` β File not found: ${filePath}`);
+ continue;
+ }
+
+ // Read markdown content
+ const markdown = fs.readFileSync(filePath, 'utf-8');
+
+ // Parse sections
+ const sections = parseDocumentSections(markdown, doc.content_html);
+
+ // Add HTML content to each section
+ const sectionsWithHtml = sections.map(section => ({
+ ...section,
+ content_html: markdownToHtml(section.content)
+ }));
+
+ console.log(` β Parsed ${sections.length} sections`);
+
+ // Category breakdown
+ const categoryCount = {};
+ sectionsWithHtml.forEach(s => {
+ categoryCount[s.category] = (categoryCount[s.category] || 0) + 1;
+ });
+ console.log(` Categories:`, categoryCount);
+
+ // Technical level breakdown
+ const levelCount = {};
+ sectionsWithHtml.forEach(s => {
+ levelCount[s.technicalLevel] = (levelCount[s.technicalLevel] || 0) + 1;
+ });
+ console.log(` Levels:`, levelCount);
+
+ // Update document with sections
+ await collection.updateOne(
+ { _id: doc._id },
+ {
+ $set: {
+ sections: sectionsWithHtml,
+ 'metadata.sections_count': sections.length,
+ 'metadata.last_section_update': new Date()
+ }
+ }
+ );
+
+ console.log(` β Updated document\n`);
+ }
+
+ console.log('=== Section Addition Complete ===');
+
+ } catch (error) {
+ console.error('Error:', error);
+ process.exit(1);
+ } finally {
+ await client.close();
+ }
+}
+
+main();
diff --git a/scripts/analyze-instruction-violations.js b/scripts/analyze-instruction-violations.js
new file mode 100755
index 00000000..8028c14d
--- /dev/null
+++ b/scripts/analyze-instruction-violations.js
@@ -0,0 +1,366 @@
+#!/usr/bin/env node
+
+/**
+ * Instruction History Analytics
+ *
+ * Analyzes instruction-history.json to provide insights into:
+ * - Instruction usage patterns
+ * - Most violated instructions
+ * - Instructions never referenced
+ * - Quadrant distribution
+ * - Enforcement effectiveness
+ *
+ * Usage: node scripts/analyze-instruction-violations.js
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+const INSTRUCTION_HISTORY_PATH = path.join(__dirname, '../.claude/instruction-history.json');
+const INCIDENTS_PATH = path.join(__dirname, '../.claude/framework-incidents.json');
+
+/**
+ * Color output helpers
+ */
+const colors = {
+ reset: '\x1b[0m',
+ bright: '\x1b[1m',
+ green: '\x1b[32m',
+ yellow: '\x1b[33m',
+ blue: '\x1b[34m',
+ red: '\x1b[31m',
+ cyan: '\x1b[36m',
+ magenta: '\x1b[35m'
+};
+
+function log(message, color = 'reset') {
+ console.log(`${colors[color]}${message}${colors.reset}`);
+}
+
+function header(message) {
+ console.log('');
+ log('β'.repeat(80), 'cyan');
+ log(` ${message}`, 'bright');
+ log('β'.repeat(80), 'cyan');
+ console.log('');
+}
+
+function section(message) {
+ console.log('');
+ log(`βΆ ${message}`, 'blue');
+}
+
+/**
+ * Load instruction history
+ */
+function loadInstructions() {
+ try {
+ const data = fs.readFileSync(INSTRUCTION_HISTORY_PATH, 'utf8');
+ return JSON.parse(data);
+ } catch (err) {
+ log(`Error loading instruction history: ${err.message}`, 'red');
+ process.exit(1);
+ }
+}
+
+/**
+ * Load framework incidents (if exists)
+ */
+function loadIncidents() {
+ try {
+ if (!fs.existsSync(INCIDENTS_PATH)) {
+ return { incidents: [] };
+ }
+ const data = fs.readFileSync(INCIDENTS_PATH, 'utf8');
+ return JSON.parse(data);
+ } catch (err) {
+ log(`Warning: Could not load incidents database: ${err.message}`, 'yellow');
+ return { incidents: [] };
+ }
+}
+
+/**
+ * Analyze instruction usage
+ */
+function analyzeInstructions(history) {
+ const instructions = history.instructions || [];
+ const active = instructions.filter(i => i.active);
+ const inactive = instructions.filter(i => !i.active);
+
+ // Quadrant distribution
+ const byQuadrant = {
+ STRATEGIC: active.filter(i => i.quadrant === 'STRATEGIC').length,
+ OPERATIONAL: active.filter(i => i.quadrant === 'OPERATIONAL').length,
+ TACTICAL: active.filter(i => i.quadrant === 'TACTICAL').length,
+ SYSTEM: active.filter(i => i.quadrant === 'SYSTEM').length,
+ STOCHASTIC: active.filter(i => i.quadrant === 'STOCHASTIC').length
+ };
+
+ // Persistence distribution
+ const byPersistence = {
+ HIGH: active.filter(i => i.persistence === 'HIGH').length,
+ MEDIUM: active.filter(i => i.persistence === 'MEDIUM').length,
+ LOW: active.filter(i => i.persistence === 'LOW').length,
+ VARIABLE: active.filter(i => i.persistence === 'VARIABLE').length
+ };
+
+ // Temporal scope distribution
+ const byScope = {
+ PERMANENT: active.filter(i => i.temporal_scope === 'PERMANENT').length,
+ PROJECT: active.filter(i => i.temporal_scope === 'PROJECT').length,
+ PHASE: active.filter(i => i.temporal_scope === 'PHASE').length,
+ SESSION: active.filter(i => i.temporal_scope === 'SESSION').length
+ };
+
+ return {
+ total: instructions.length,
+ active: active.length,
+ inactive: inactive.length,
+ byQuadrant,
+ byPersistence,
+ byScope,
+ instructions: active
+ };
+}
+
+/**
+ * Analyze violations from incidents database
+ */
+function analyzeViolations(incidents) {
+ const violations = {};
+
+ incidents.forEach(incident => {
+ const instId = incident.instruction_violated || incident.id;
+ if (!violations[instId]) {
+ violations[instId] = {
+ count: 0,
+ tokens_wasted: 0,
+ incidents: []
+ };
+ }
+ violations[instId].count++;
+ violations[instId].tokens_wasted += incident.tokens_wasted || 0;
+ violations[instId].incidents.push(incident);
+ });
+
+ return violations;
+}
+
+/**
+ * Find never-referenced instructions
+ */
+function findUnusedInstructions(instructions, violations) {
+ const neverViolated = instructions.filter(i => !violations[i.id]);
+
+ return {
+ neverViolated: neverViolated.map(i => i.id),
+ strategicNeverViolated: neverViolated.filter(i => i.quadrant === 'STRATEGIC').map(i => i.id)
+ };
+}
+
+/**
+ * Calculate enforcement effectiveness
+ */
+function calculateEnforcement(instructions, violations) {
+ // Hook-enforced instructions (those with architectural enforcement)
+ const hookEnforced = instructions.filter(i =>
+ i.enforcement === 'architectural' ||
+ i.enforcement === 'hook' ||
+ i.notes?.includes('hook') ||
+ i.notes?.includes('architectural')
+ );
+
+ const voluntary = instructions.filter(i =>
+ !hookEnforced.includes(i)
+ );
+
+ // Calculate violation rates
+ const hookEnforcedViolations = hookEnforced.filter(i => violations[i.id]);
+ const voluntaryViolations = voluntary.filter(i => violations[i.id]);
+
+ const hookEnforcementRate = hookEnforced.length > 0
+ ? ((hookEnforced.length - hookEnforcedViolations.length) / hookEnforced.length * 100).toFixed(1)
+ : 0;
+
+ const voluntaryComplianceRate = voluntary.length > 0
+ ? ((voluntary.length - voluntaryViolations.length) / voluntary.length * 100).toFixed(1)
+ : 0;
+
+ return {
+ hookEnforced: hookEnforced.length,
+ voluntary: voluntary.length,
+ hookEnforcementRate,
+ voluntaryComplianceRate,
+ hookEnforcedViolations: hookEnforcedViolations.length,
+ voluntaryViolations: voluntaryViolations.length
+ };
+}
+
+/**
+ * Main analytics
+ */
+function main() {
+ header('Instruction History Analytics');
+
+ // Load data
+ const history = loadInstructions();
+ const incidentsData = loadIncidents();
+ const incidents = incidentsData.incidents || [];
+
+ log(`π Analyzing ${history.instructions?.length || 0} instructions and ${incidents.length} incidents`, 'cyan');
+
+ // Analyze
+ const analysis = analyzeInstructions(history);
+ const violations = analyzeViolations(incidents);
+ const unused = findUnusedInstructions(analysis.instructions, violations);
+ const enforcement = calculateEnforcement(analysis.instructions, violations);
+
+ // Display results
+
+ // 1. Overview
+ section('1. Instruction Overview');
+ log(` Total instructions: ${analysis.total}`, 'cyan');
+ log(` Active: ${analysis.active}`, 'green');
+ log(` Inactive: ${analysis.inactive}`, 'yellow');
+
+ // 2. Distribution by Quadrant
+ section('2. Distribution by Quadrant');
+ Object.entries(analysis.byQuadrant).forEach(([quadrant, count]) => {
+ const bar = 'β'.repeat(Math.ceil(count / 2));
+ log(` ${quadrant.padEnd(12)}: ${count.toString().padStart(2)} ${bar}`, 'cyan');
+ });
+
+ // 3. Distribution by Persistence
+ section('3. Distribution by Persistence');
+ Object.entries(analysis.byPersistence).forEach(([level, count]) => {
+ const bar = 'β'.repeat(Math.ceil(count / 2));
+ log(` ${level.padEnd(12)}: ${count.toString().padStart(2)} ${bar}`, 'cyan');
+ });
+
+ // 4. Distribution by Temporal Scope
+ section('4. Distribution by Temporal Scope');
+ Object.entries(analysis.byScope).forEach(([scope, count]) => {
+ const bar = 'β'.repeat(Math.ceil(count / 2));
+ log(` ${scope.padEnd(12)}: ${count.toString().padStart(2)} ${bar}`, 'cyan');
+ });
+
+ // 5. Most Violated Instructions
+ section('5. Most Violated Instructions');
+ const violationList = Object.entries(violations)
+ .sort((a, b) => b[1].count - a[1].count);
+
+ if (violationList.length === 0) {
+ log(' β
No violations recorded', 'green');
+ } else {
+ violationList.slice(0, 10).forEach(([instId, data]) => {
+ const instruction = analysis.instructions.find(i => i.id === instId);
+ const text = instruction ? instruction.text.substring(0, 60) + '...' : 'Unknown instruction';
+ log(` ${instId}: ${data.count} violation(s), ${data.tokens_wasted.toLocaleString()} tokens wasted`, 'red');
+ log(` "${text}"`, 'yellow');
+ });
+ }
+
+ // 6. Never Violated Instructions
+ section('6. Never Violated Instructions');
+ if (unused.neverViolated.length === 0) {
+ log(' β οΈ All instructions have been violated at least once!', 'yellow');
+ } else {
+ log(` ${unused.neverViolated.length} instructions with 100% compliance:`, 'green');
+ unused.neverViolated.slice(0, 10).forEach(instId => {
+ const instruction = analysis.instructions.find(i => i.id === instId);
+ if (instruction) {
+ log(` ${instId}: ${instruction.text.substring(0, 70)}`, 'cyan');
+ }
+ });
+ if (unused.neverViolated.length > 10) {
+ log(` ... and ${unused.neverViolated.length - 10} more`, 'cyan');
+ }
+ }
+
+ // 7. Enforcement Effectiveness
+ section('7. Enforcement Effectiveness');
+ log(` Hook-enforced instructions: ${enforcement.hookEnforced}`, 'cyan');
+ log(` Violations: ${enforcement.hookEnforcedViolations}`, enforcement.hookEnforcedViolations > 0 ? 'red' : 'green');
+ log(` Compliance rate: ${enforcement.hookEnforcementRate}%`, enforcement.hookEnforcementRate >= 95 ? 'green' : 'yellow');
+
+ console.log('');
+ log(` Voluntary compliance instructions: ${enforcement.voluntary}`, 'cyan');
+ log(` Violations: ${enforcement.voluntaryViolations}`, enforcement.voluntaryViolations > 0 ? 'red' : 'green');
+ log(` Compliance rate: ${enforcement.voluntaryComplianceRate}%`, enforcement.voluntaryComplianceRate >= 95 ? 'green' : 'yellow');
+
+ console.log('');
+ if (enforcement.hookEnforcementRate > enforcement.voluntaryComplianceRate) {
+ log(` β
Hook enforcement is ${(enforcement.hookEnforcementRate - enforcement.voluntaryComplianceRate).toFixed(1)}% more effective`, 'green');
+ log(` π‘ Recommendation: Convert more voluntary compliance to architectural enforcement`, 'cyan');
+ } else if (enforcement.voluntaryComplianceRate >= 95) {
+ log(` β
Voluntary compliance is working well (${enforcement.voluntaryComplianceRate}%)`, 'green');
+ } else {
+ log(` β οΈ Consider improving enforcement mechanisms`, 'yellow');
+ }
+
+ // 8. Recommendations
+ section('8. Recommendations');
+
+ const recommendations = [];
+
+ // High-violation instructions needing enforcement
+ const highViolation = violationList.filter(([_, data]) => data.count >= 2);
+ if (highViolation.length > 0) {
+ recommendations.push({
+ priority: 'HIGH',
+ text: `${highViolation.length} instruction(s) violated 2+ times - add architectural enforcement`,
+ details: highViolation.map(([id]) => id)
+ });
+ }
+
+ // Strategic instructions that are never violated (good!)
+ if (unused.strategicNeverViolated.length > 0) {
+ recommendations.push({
+ priority: 'INFO',
+ text: `${unused.strategicNeverViolated.length} STRATEGIC instructions have 100% compliance - document success`,
+ details: unused.strategicNeverViolated.slice(0, 5)
+ });
+ }
+
+ // Voluntary compliance gaps
+ if (enforcement.voluntaryComplianceRate < 80) {
+ recommendations.push({
+ priority: 'MEDIUM',
+ text: `Voluntary compliance at ${enforcement.voluntaryComplianceRate}% - convert to hooks or improve documentation`,
+ details: []
+ });
+ }
+
+ if (recommendations.length === 0) {
+ log(' β
No recommendations - framework is performing well!', 'green');
+ } else {
+ recommendations.forEach((rec, i) => {
+ const color = rec.priority === 'HIGH' ? 'red' : rec.priority === 'MEDIUM' ? 'yellow' : 'cyan';
+ log(` ${i + 1}. [${rec.priority}] ${rec.text}`, color);
+ if (rec.details.length > 0) {
+ rec.details.forEach(detail => {
+ log(` - ${detail}`, 'cyan');
+ });
+ }
+ });
+ }
+
+ // Summary footer
+ header('Analytics Complete');
+ console.log('');
+ log(` π Key Metrics:`, 'bright');
+ log(` Active Instructions: ${analysis.active}`, 'cyan');
+ log(` Recorded Violations: ${incidents.length}`, incidents.length > 0 ? 'yellow' : 'green');
+ log(` Tokens Wasted: ${Object.values(violations).reduce((sum, v) => sum + v.tokens_wasted, 0).toLocaleString()}`, 'red');
+ log(` Hook Enforcement Rate: ${enforcement.hookEnforcementRate}%`, 'green');
+ log(` Voluntary Compliance Rate: ${enforcement.voluntaryComplianceRate}%`, 'yellow');
+ console.log('');
+ log(` π‘ Next Steps:`, 'bright');
+ log(` - Review high-violation instructions for enforcement gaps`, 'cyan');
+ log(` - Document successful compliance patterns`, 'cyan');
+ log(` - Convert voluntary β architectural where violations occur`, 'cyan');
+ console.log('');
+}
+
+// Run
+main();
diff --git a/scripts/archive-all-internal-documents.js b/scripts/archive-all-internal-documents.js
new file mode 100644
index 00000000..a82b3ec1
--- /dev/null
+++ b/scripts/archive-all-internal-documents.js
@@ -0,0 +1,171 @@
+/**
+ * Archive All Internal Documents
+ * Mass archive of project tracking, internal, and confidential documents
+ */
+
+const { MongoClient } = require('mongodb');
+
+// MongoDB connection
+const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/tractatus_dev';
+const DB_NAME = process.env.MONGODB_DB || 'tractatus_dev';
+
+// Documents to mark as confidential (not archived, just hidden)
+const CONFIDENTIAL_DOCS = [
+ 'security-audit-report',
+ 'koha-stripe-payment-setup-guide',
+ 'koha-production-deployment-guide',
+ 'appendix-e-contact-information',
+ 'cover-letter-for-anthropic-submission'
+];
+
+// Documents to archive (project tracking, internal planning)
+const ARCHIVE_PATTERNS = [
+ 'phase-2-', // All Phase 2 planning docs
+ 'session-handoff', // All session handoffs
+ 'appendix-b-', // Duplicate case studies
+ 'implementation-roadmap', // Outdated planning
+ 'implementation-guide-python', // Outdated code examples
+ 'research-foundations-scholarly', // Academic, not practical
+ 'tractatus-blog-post-outlines', // Internal planning
+ 'tractatus-project-implementation-progress-report', // Project tracking
+ 'tractatus-production-comprehensive-testing-checklist', // Internal testing
+ 'tractatus-production-testing-results', // Test results
+ 'tractatus-governance-framework-test-suite', // Internal testing
+ 'ai-features-implementation-session' // Session tracking
+];
+
+async function main() {
+ console.log('=== Archiving All Internal Documents ===\n');
+
+ let client;
+
+ try {
+ // Connect to MongoDB
+ console.log('Connecting to MongoDB...');
+ client = await MongoClient.connect(MONGODB_URI);
+ const db = client.db(DB_NAME);
+ const collection = db.collection('documents');
+ console.log('β Connected\n');
+
+ // 1. Mark confidential documents
+ console.log('=== Marking Confidential Documents ===\n');
+ let confidentialCount = 0;
+
+ for (const slug of CONFIDENTIAL_DOCS) {
+ const result = await collection.updateOne(
+ { slug },
+ {
+ $set: {
+ visibility: 'confidential',
+ category: 'internal',
+ order: 999
+ }
+ }
+ );
+
+ if (result.matchedCount > 0) {
+ console.log(`β Marked confidential: ${slug}`);
+ confidentialCount++;
+ }
+ }
+ console.log(`\nβ Total confidential: ${confidentialCount}\n`);
+
+ // 2. Archive documents matching patterns
+ console.log('=== Archiving Project Tracking Documents ===\n');
+ let archivedCount = 0;
+
+ const allDocs = await collection.find({ visibility: { $ne: 'confidential' } }).toArray();
+
+ for (const doc of allDocs) {
+ const shouldArchive = ARCHIVE_PATTERNS.some(pattern =>
+ doc.slug.includes(pattern)
+ );
+
+ if (shouldArchive && doc.visibility !== 'archived') {
+ const result = await collection.updateOne(
+ { _id: doc._id },
+ {
+ $set: {
+ visibility: 'archived',
+ category: 'project-tracking',
+ order: 999,
+ archiveNote: 'Internal project tracking document. Not relevant for public documentation.'
+ }
+ }
+ );
+
+ if (result.matchedCount > 0) {
+ console.log(`β Archived: ${doc.slug}`);
+ archivedCount++;
+ }
+ }
+ }
+ console.log(`\nβ Total archived: ${archivedCount}\n`);
+
+ // 3. Handle recent case studies (keep but categorize)
+ console.log('=== Categorizing Case Studies ===\n');
+ const caseStudySlugs = [
+ 'our-framework-in-action-detecting-and-correcting-ai-fabrications',
+ 'framework-governance-in-action-pre-publication-security-audit',
+ 'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery',
+ 'when-frameworks-fail-and-why-thats-ok'
+ ];
+
+ let caseStudyCount = 0;
+ for (const [index, slug] of caseStudySlugs.entries()) {
+ const result = await collection.updateOne(
+ { slug },
+ {
+ $set: {
+ visibility: 'public',
+ category: 'practical',
+ audience: 'general',
+ order: 8 + index // Orders 8-11
+ }
+ }
+ );
+
+ if (result.matchedCount > 0) {
+ console.log(`β Categorized: ${slug} (order: ${8 + index})`);
+ caseStudyCount++;
+ }
+ }
+ console.log(`\nβ Total case studies: ${caseStudyCount}\n`);
+
+ // Summary
+ console.log('=== Final Summary ===\n');
+ const publicCount = await collection.countDocuments({ visibility: 'public' });
+ const archivedTotal = await collection.countDocuments({ visibility: 'archived' });
+ const confidentialTotal = await collection.countDocuments({ visibility: 'confidential' });
+
+ console.log(`π Public documents: ${publicCount}`);
+ console.log(`π¦ Archived documents: ${archivedTotal}`);
+ console.log(`π Confidential documents: ${confidentialTotal}`);
+ console.log(`\nTotal: ${publicCount + archivedTotal + confidentialTotal} documents\n`);
+
+ // Show public documents
+ console.log('=== Public Documents (Final List) ===\n');
+ const publicDocs = await collection.find({ visibility: 'public' })
+ .sort({ order: 1 })
+ .project({ title: 1, order: 1, category: 1 })
+ .toArray();
+
+ publicDocs.forEach(doc => {
+ console.log(` ${doc.order}. ${doc.title} [${doc.category}]`);
+ });
+
+ } catch (error) {
+ console.error('\nβ Error:', error.message);
+ console.error(error.stack);
+ process.exit(1);
+ } finally {
+ if (client) await client.close();
+ }
+}
+
+// Run if called directly
+if (require.main === module) {
+ main();
+}
+
+module.exports = { main };
diff --git a/scripts/archive-outdated-documents.js b/scripts/archive-outdated-documents.js
new file mode 100644
index 00000000..65701f32
--- /dev/null
+++ b/scripts/archive-outdated-documents.js
@@ -0,0 +1,147 @@
+/**
+ * Archive Outdated Documents
+ * Sets visibility: 'archived' for 10 documents identified in audit
+ */
+
+const { MongoClient } = require('mongodb');
+
+// MongoDB connection
+const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/tractatus_dev';
+const DB_NAME = process.env.MONGODB_DB || 'tractatus_dev';
+
+// Documents to archive with reasons
+const DOCUMENTS_TO_ARCHIVE = [
+ {
+ slug: 'introduction-to-the-tractatus-framework',
+ category: 'archived',
+ archiveNote: 'Superseded by Architectural Overview & Research Status. References outdated filesystem-only architecture.',
+ reason: 'Outdated architecture (pre-MongoDB)'
+ },
+ {
+ slug: 'tractatus-based-llm-architecture-for-ai-safety',
+ category: 'archived',
+ archiveNote: 'Historical architecture proposal. See Architectural Overview for implemented architecture.',
+ reason: 'Pre-Phase 5 architecture proposal'
+ },
+ {
+ slug: 'executive-brief-tractatus-based-llm-architecture-for-ai-safety',
+ category: 'archived',
+ archiveNote: 'Historical brief based on pre-Phase 5 architecture. See Architectural Overview for current status.',
+ reason: 'Pre-Phase 5 executive brief'
+ },
+ {
+ slug: 'tractatus-framework-enforcement-for-claude-code',
+ category: 'archived',
+ archiveNote: 'Development tool documentation. See Implementation Guide for production deployment.',
+ reason: 'Internal development tool'
+ },
+ {
+ slug: 'organizational-theory-foundations-of-the-tractatus-framework',
+ category: 'archived',
+ archiveNote: 'Academic foundations. See Core Concepts for practical overview.',
+ reason: 'Academic content, not practical'
+ },
+ {
+ slug: 'phase-5-poc-session-1-summary',
+ category: 'project-tracking',
+ archiveNote: 'Project tracking - Phase 5 Session 1. See Architectural Overview for complete project history.',
+ reason: 'Project tracking'
+ },
+ {
+ slug: 'phase-5-poc-session-2-summary',
+ category: 'project-tracking',
+ archiveNote: 'Project tracking - Phase 5 Session 2. See Architectural Overview for complete project history.',
+ reason: 'Project tracking'
+ },
+ {
+ slug: 'research-scope-feasibility-of-llm-integrated-tractatus-framework',
+ category: 'research-proposal',
+ archiveNote: 'Research proposal (not completed work). See Architectural Overview for actual implementation status.',
+ reason: 'Research proposal'
+ },
+ {
+ slug: 'research-topic-concurrent-session-architecture-limitations-in-claude-code-governance',
+ category: 'research-topic',
+ archiveNote: 'Open research question. See Architectural Overview for current architecture limitations.',
+ reason: 'Open research question'
+ },
+ {
+ slug: 'research-topic-rule-proliferation-and-transactional-overhead-in-ai-governance',
+ category: 'research-topic',
+ archiveNote: 'Open research question. See Architectural Overview for current governance approach.',
+ reason: 'Open research question'
+ }
+];
+
+async function main() {
+ console.log('=== Archiving Outdated Documents ===\n');
+
+ let client;
+
+ try {
+ // Connect to MongoDB
+ console.log('Connecting to MongoDB...');
+ client = await MongoClient.connect(MONGODB_URI);
+ const db = client.db(DB_NAME);
+ const collection = db.collection('documents');
+ console.log('β Connected\n');
+
+ let archived = 0;
+ let notFound = 0;
+
+ // Archive each document
+ for (const doc of DOCUMENTS_TO_ARCHIVE) {
+ console.log(`Archiving: ${doc.slug}`);
+ console.log(` Reason: ${doc.reason}`);
+
+ const result = await collection.updateOne(
+ { slug: doc.slug },
+ {
+ $set: {
+ visibility: 'archived',
+ category: doc.category,
+ archiveNote: doc.archiveNote,
+ order: 999
+ }
+ }
+ );
+
+ if (result.matchedCount > 0) {
+ console.log(` β Archived\n`);
+ archived++;
+ } else {
+ console.log(` β Not found in database\n`);
+ notFound++;
+ }
+ }
+
+ // Summary
+ console.log('=== Summary ===\n');
+ console.log(`β Archived: ${archived} documents`);
+ if (notFound > 0) {
+ console.log(`β Not found: ${notFound} documents`);
+ }
+ console.log(`\nTotal processed: ${DOCUMENTS_TO_ARCHIVE.length}`);
+
+ // Verify archives
+ console.log('\n=== Verification ===\n');
+ const archivedCount = await collection.countDocuments({ visibility: 'archived' });
+ const publicCount = await collection.countDocuments({ visibility: 'public' });
+ console.log(`Archived documents: ${archivedCount}`);
+ console.log(`Public documents: ${publicCount}`);
+
+ } catch (error) {
+ console.error('\nβ Error:', error.message);
+ console.error(error.stack);
+ process.exit(1);
+ } finally {
+ if (client) await client.close();
+ }
+}
+
+// Run if called directly
+if (require.main === module) {
+ main();
+}
+
+module.exports = { main };
diff --git a/scripts/audit-accessibility.js b/scripts/audit-accessibility.js
new file mode 100755
index 00000000..840a1a13
--- /dev/null
+++ b/scripts/audit-accessibility.js
@@ -0,0 +1,223 @@
+#!/usr/bin/env node
+
+/**
+ * Accessibility Audit Script
+ *
+ * Runs automated accessibility checks on all main pages
+ * using pa11y (WCAG 2.1 AA standard)
+ *
+ * Copyright 2025 Tractatus Project
+ * Licensed under Apache License 2.0
+ */
+
+const pa11y = require('pa11y');
+const fs = require('fs');
+const path = require('path');
+
+const colors = {
+ reset: '\x1b[0m',
+ bright: '\x1b[1m',
+ green: '\x1b[32m',
+ yellow: '\x1b[33m',
+ red: '\x1b[31m',
+ cyan: '\x1b[36m'
+};
+
+function log(message, color = 'reset') {
+ console.log(`${colors[color]}${message}${colors.reset}`);
+}
+
+function section(message) {
+ console.log('');
+ log(`βΆ ${message}`, 'cyan');
+}
+
+function success(message) {
+ log(` β ${message}`, 'green');
+}
+
+function warning(message) {
+ log(` β ${message}`, 'yellow');
+}
+
+function error(message) {
+ log(` β ${message}`, 'red');
+}
+
+// Pages to audit
+const pages = [
+ { name: 'Homepage', url: 'http://localhost:9000/' },
+ { name: 'Researcher', url: 'http://localhost:9000/researcher.html' },
+ { name: 'Implementer', url: 'http://localhost:9000/implementer.html' },
+ { name: 'Leader', url: 'http://localhost:9000/leader.html' },
+ { name: 'About', url: 'http://localhost:9000/about.html' },
+ { name: 'Values', url: 'http://localhost:9000/about/values.html' },
+ { name: 'Media Inquiry', url: 'http://localhost:9000/media-inquiry.html' },
+ { name: 'Case Submission', url: 'http://localhost:9000/case-submission.html' },
+ { name: 'Docs', url: 'http://localhost:9000/docs.html' }
+];
+
+// pa11y configuration
+const pa11yConfig = {
+ standard: 'WCAG2AA',
+ timeout: 30000,
+ wait: 1000,
+ chromeLaunchConfig: {
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
+ },
+ // Common issues to ignore (if needed)
+ ignore: []
+};
+
+async function auditPage(page) {
+ try {
+ const results = await pa11y(page.url, pa11yConfig);
+
+ return {
+ name: page.name,
+ url: page.url,
+ issues: results.issues,
+ error: false
+ };
+ } catch (err) {
+ return {
+ name: page.name,
+ url: page.url,
+ error: true,
+ errorMessage: err.message
+ };
+ }
+}
+
+function categorizeIssues(issues) {
+ const categorized = {
+ error: [],
+ warning: [],
+ notice: []
+ };
+
+ issues.forEach(issue => {
+ categorized[issue.type].push(issue);
+ });
+
+ return categorized;
+}
+
+function printIssue(issue, index) {
+ const typeColor = {
+ error: 'red',
+ warning: 'yellow',
+ notice: 'cyan'
+ };
+
+ console.log('');
+ log(` ${index + 1}. [${issue.type.toUpperCase()}] ${issue.message}`, typeColor[issue.type]);
+ log(` Code: ${issue.code}`, 'reset');
+ log(` Element: ${issue.context.substring(0, 100)}${issue.context.length > 100 ? '...' : ''}`, 'reset');
+ log(` Selector: ${issue.selector}`, 'reset');
+}
+
+async function main() {
+ log('β'.repeat(70), 'cyan');
+ log(' Tractatus Accessibility Audit (WCAG 2.1 AA)', 'bright');
+ log('β'.repeat(70), 'cyan');
+ console.log('');
+
+ const allResults = [];
+ let totalErrors = 0;
+ let totalWarnings = 0;
+ let totalNotices = 0;
+
+ for (const page of pages) {
+ section(`Auditing: ${page.name}`);
+ const result = await auditPage(page);
+ allResults.push(result);
+
+ if (result.error) {
+ error(`Failed to audit: ${result.errorMessage}`);
+ continue;
+ }
+
+ const categorized = categorizeIssues(result.issues);
+
+ const errorCount = categorized.error.length;
+ const warningCount = categorized.warning.length;
+ const noticeCount = categorized.notice.length;
+
+ totalErrors += errorCount;
+ totalWarnings += warningCount;
+ totalNotices += noticeCount;
+
+ if (errorCount === 0 && warningCount === 0 && noticeCount === 0) {
+ success(`No accessibility issues found!`);
+ } else {
+ if (errorCount > 0) error(`${errorCount} errors`);
+ if (warningCount > 0) warning(`${warningCount} warnings`);
+ if (noticeCount > 0) log(` βΉ ${noticeCount} notices`, 'cyan');
+
+ // Print first 3 errors/warnings
+ const criticalIssues = [...categorized.error, ...categorized.warning].slice(0, 3);
+ if (criticalIssues.length > 0) {
+ log(' Top issues:', 'bright');
+ criticalIssues.forEach((issue, idx) => {
+ printIssue(issue, idx);
+ });
+ }
+ }
+ }
+
+ // Summary
+ console.log('');
+ log('β'.repeat(70), 'cyan');
+ log(' Summary', 'bright');
+ log('β'.repeat(70), 'cyan');
+ console.log('');
+
+ log(` Pages Audited: ${pages.length}`, 'bright');
+ log(` Total Errors: ${totalErrors}`, totalErrors > 0 ? 'red' : 'green');
+ log(` Total Warnings: ${totalWarnings}`, totalWarnings > 0 ? 'yellow' : 'green');
+ log(` Total Notices: ${totalNotices}`, 'cyan');
+ console.log('');
+
+ // Save detailed report
+ const reportPath = path.join(__dirname, '../audit-reports/accessibility-report.json');
+ const reportDir = path.dirname(reportPath);
+
+ if (!fs.existsSync(reportDir)) {
+ fs.mkdirSync(reportDir, { recursive: true });
+ }
+
+ fs.writeFileSync(reportPath, JSON.stringify({
+ timestamp: new Date().toISOString(),
+ standard: 'WCAG 2.1 AA',
+ summary: {
+ pagesAudited: pages.length,
+ totalErrors,
+ totalWarnings,
+ totalNotices
+ },
+ results: allResults
+ }, null, 2));
+
+ success(`Detailed report saved: ${reportPath}`);
+ console.log('');
+
+ // Exit code based on errors
+ if (totalErrors > 0) {
+ error('Accessibility audit FAILED - errors found');
+ process.exit(1);
+ } else if (totalWarnings > 0) {
+ warning('Accessibility audit PASSED with warnings');
+ process.exit(0);
+ } else {
+ success('Accessibility audit PASSED');
+ process.exit(0);
+ }
+}
+
+main().catch(err => {
+ console.error('');
+ error(`Audit failed: ${err.message}`);
+ console.error(err.stack);
+ process.exit(1);
+});
diff --git a/scripts/check-card-view-status.js b/scripts/check-card-view-status.js
new file mode 100644
index 00000000..694c5410
--- /dev/null
+++ b/scripts/check-card-view-status.js
@@ -0,0 +1,118 @@
+const { MongoClient } = require('mongodb');
+
+// The 34 public documents by slug
+const PUBLIC_SLUGS = [
+ // Getting Started
+ 'introduction',
+ 'architectural-safeguards-against-llm-hierarchical-dominance-prose',
+ 'core-concepts',
+ 'tractatus-ai-safety-framework-core-values-and-principles',
+
+ // Technical Reference
+ 'technical-architecture',
+ 'implementation-guide',
+ 'implementation-roadmap-24-month-deployment-plan',
+ 'GLOSSARY',
+ 'comparison-matrix',
+ 'implementation-guide-v1.1',
+ 'api-reference-complete',
+ 'api-javascript-examples',
+ 'api-python-examples',
+ 'openapi-specification',
+
+ // Theory & Research
+ 'executive-summary-tractatus-inflection-point',
+ 'architectural-overview-and-research-status',
+ 'organizational-theory-foundations',
+ 'pluralistic-values-research-foundations',
+
+ // Advanced Topics
+ 'value-pluralism-faq',
+ 'pluralistic-values-deliberation-plan-v2',
+
+ // Case Studies
+ 'the-27027-incident-a-case-study-in-pattern-recognition-bias',
+ 'when-frameworks-fail-and-why-thats-ok',
+ 'our-framework-in-action-detecting-and-correcting-ai-fabrications',
+ 'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery',
+ 'case-studies-real-world-llm-failure-modes',
+
+ // Business & Leadership
+ 'business-case-tractatus-framework',
+
+ // Archives
+ 'llm-integration-feasibility-research-scope',
+ 'case-studies-real-world-llm-failure-modes-appendix',
+ 'implementation-guide-python-examples',
+ 'tractatus-framework-enforcement-claude-code',
+ 'research-topic-concurrent-session-architecture',
+ 'research-topic-rule-proliferation-transactional-overhead',
+ 'phase-5-poc-session-1-summary',
+ 'phase-5-poc-session-2-summary'
+];
+
+async function checkCardViewStatus() {
+ const client = new MongoClient('mongodb://localhost:27017');
+
+ try {
+ await client.connect();
+ const db = client.db('tractatus_dev');
+ const collection = db.collection('documents');
+
+ console.log(`\n=== CHECKING CARD VIEW STATUS FOR 34 PUBLIC DOCUMENTS ===\n`);
+
+ const documents = await collection.find({
+ slug: { $in: PUBLIC_SLUGS }
+ }).toArray();
+
+ console.log(`Found ${documents.length} / ${PUBLIC_SLUGS.length} documents in database\n`);
+
+ const withCards = [];
+ const withoutCards = [];
+ const notFound = [];
+
+ PUBLIC_SLUGS.forEach(slug => {
+ const doc = documents.find(d => d.slug === slug);
+ if (!doc) {
+ notFound.push(slug);
+ } else if (doc.sections && doc.sections.length > 0) {
+ withCards.push({
+ slug: doc.slug,
+ title: doc.title,
+ sections: doc.sections.length,
+ category: doc.category || 'none',
+ order: doc.order || 999
+ });
+ } else {
+ withoutCards.push({
+ slug: doc.slug,
+ title: doc.title,
+ category: doc.category || 'none',
+ order: doc.order || 999
+ });
+ }
+ });
+
+ console.log(`β
WITH CARD VIEW (${withCards.length} docs):`);
+ withCards.forEach(doc => {
+ console.log(` [order:${doc.order}] ${doc.title}`);
+ console.log(` slug: ${doc.slug} | sections: ${doc.sections} | category: ${doc.category}`);
+ });
+
+ console.log(`\nβ WITHOUT CARD VIEW (${withoutCards.length} docs):`);
+ withoutCards.forEach(doc => {
+ console.log(` [order:${doc.order}] ${doc.title}`);
+ console.log(` slug: ${doc.slug} | category: ${doc.category}`);
+ });
+
+ if (notFound.length > 0) {
+ console.log(`\nβ οΈ NOT FOUND IN DATABASE (${notFound.length} slugs):`);
+ notFound.forEach(slug => console.log(` ${slug}`));
+ }
+
+ } finally {
+ await client.close();
+ }
+}
+
+checkCardViewStatus().catch(console.error);
diff --git a/scripts/check-color-contrast.js b/scripts/check-color-contrast.js
new file mode 100755
index 00000000..7ec6f72d
--- /dev/null
+++ b/scripts/check-color-contrast.js
@@ -0,0 +1,226 @@
+#!/usr/bin/env node
+
+/**
+ * Color Contrast Checker
+ *
+ * Verifies color contrast ratios meet WCAG 2.1 AA standards (4.5:1 normal text, 3:1 large text)
+ *
+ * Copyright 2025 Tractatus Project
+ * Licensed under Apache License 2.0
+ */
+
+const colors = {
+ reset: '\x1b[0m',
+ bright: '\x1b[1m',
+ green: '\x1b[32m',
+ yellow: '\x1b[33m',
+ red: '\x1b[31m',
+ cyan: '\x1b[36m'
+};
+
+function log(message, color = 'reset') {
+ console.log(`${colors[color]}${message}${colors.reset}`);
+}
+
+function success(message) {
+ log(` β ${message}`, 'green');
+}
+
+function warning(message) {
+ log(` β ${message}`, 'yellow');
+}
+
+function error(message) {
+ log(` β ${message}`, 'red');
+}
+
+/**
+ * Convert hex color to RGB
+ */
+function hexToRgb(hex) {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : null;
+}
+
+/**
+ * Calculate relative luminance (WCAG formula)
+ */
+function getLuminance(rgb) {
+ const rsRGB = rgb.r / 255;
+ const gsRGB = rgb.g / 255;
+ const bsRGB = rgb.b / 255;
+
+ const r = rsRGB <= 0.03928 ? rsRGB / 12.92 : Math.pow((rsRGB + 0.055) / 1.055, 2.4);
+ const g = gsRGB <= 0.03928 ? gsRGB / 12.92 : Math.pow((gsRGB + 0.055) / 1.055, 2.4);
+ const b = bsRGB <= 0.03928 ? bsRGB / 12.92 : Math.pow((bsRGB + 0.055) / 1.055, 2.4);
+
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+}
+
+/**
+ * Calculate contrast ratio between two colors
+ */
+function getContrastRatio(color1, color2) {
+ const rgb1 = hexToRgb(color1);
+ const rgb2 = hexToRgb(color2);
+
+ const lum1 = getLuminance(rgb1);
+ const lum2 = getLuminance(rgb2);
+
+ const lighter = Math.max(lum1, lum2);
+ const darker = Math.min(lum1, lum2);
+
+ return (lighter + 0.05) / (darker + 0.05);
+}
+
+/**
+ * Check if contrast ratio meets WCAG AA standards
+ */
+function meetsWCAG_AA(ratio, largeText = false) {
+ const threshold = largeText ? 3.0 : 4.5;
+ return ratio >= threshold;
+}
+
+/**
+ * Tailwind color palette (common colors used in Tractatus site)
+ */
+const tailwindColors = {
+ 'white': '#ffffff',
+ 'gray-50': '#f9fafb',
+ 'gray-100': '#f3f4f6',
+ 'gray-200': '#e5e7eb',
+ 'gray-300': '#d1d5db',
+ 'gray-400': '#9ca3af',
+ 'gray-500': '#6b7280',
+ 'gray-600': '#4b5563',
+ 'gray-700': '#374151',
+ 'gray-800': '#1f2937',
+ 'gray-900': '#111827',
+ 'blue-50': '#eff6ff',
+ 'blue-100': '#dbeafe',
+ 'blue-400': '#60a5fa',
+ 'blue-500': '#3b82f6',
+ 'blue-600': '#2563eb',
+ 'blue-700': '#1d4ed8',
+ 'blue-800': '#1e40af',
+ 'blue-900': '#1e3a8a',
+ 'purple-500': '#a855f7',
+ 'purple-600': '#9333ea',
+ 'purple-700': '#7e22ce',
+ 'green-500': '#22c55e',
+ 'green-600': '#16a34a',
+ 'green-700': '#15803d',
+ 'yellow-600': '#ca8a04',
+ 'amber-500': '#f59e0b',
+ 'amber-800': '#92400e',
+ 'amber-900': '#78350f',
+ 'red-600': '#dc2626'
+};
+
+/**
+ * Color combinations used on site
+ */
+const colorCombinations = [
+ // Body text on backgrounds
+ { name: 'Body text (gray-900 on white)', fg: 'gray-900', bg: 'white', largeText: false },
+ { name: 'Body text (gray-700 on white)', fg: 'gray-700', bg: 'white', largeText: false },
+ { name: 'Body text (gray-600 on white)', fg: 'gray-600', bg: 'white', largeText: false },
+ { name: 'Muted text (gray-500 on white)', fg: 'gray-500', bg: 'white', largeText: false },
+
+ // Links
+ { name: 'Link (blue-600 on white)', fg: 'blue-600', bg: 'white', largeText: false },
+ { name: 'Link hover (blue-700 on white)', fg: 'blue-700', bg: 'white', largeText: false },
+
+ // Buttons
+ { name: 'Button text (white on blue-600)', fg: 'white', bg: 'blue-600', largeText: false },
+ { name: 'Button hover (white on blue-700)', fg: 'white', bg: 'blue-700', largeText: false },
+ { name: 'Purple button (white on purple-600)', fg: 'white', bg: 'purple-600', largeText: false },
+ { name: 'Green button (white on green-700)', fg: 'white', bg: 'green-700', largeText: false },
+
+ // Hero section
+ { name: 'Hero subtitle (blue-100 on blue-700)', fg: 'blue-100', bg: 'blue-700', largeText: true },
+
+ // Footer
+ { name: 'Footer text (gray-400 on gray-900)', fg: 'gray-400', bg: 'gray-900', largeText: false },
+ { name: 'Footer links (blue-400 on gray-900)', fg: 'blue-400', bg: 'gray-900', largeText: false },
+
+ // Alerts/Messages
+ { name: 'Success message (green-900 on green-50)', fg: '#065f46', bg: '#d1fae5', largeText: false },
+ { name: 'Error message (red-900 on red-50)', fg: '#991b1b', bg: '#fee2e2', largeText: false },
+ { name: 'Warning message (amber-900 on amber-50)', fg: 'amber-900', bg: '#fef3c7', largeText: false },
+
+ // Cards/Sections
+ { name: 'Card text (gray-700 on white)', fg: 'gray-700', bg: 'white', largeText: false },
+ { name: 'Card header (gray-900 on white)', fg: 'gray-900', bg: 'white', largeText: true },
+];
+
+/**
+ * Main check
+ */
+function main() {
+ log('β'.repeat(70), 'cyan');
+ log(' Color Contrast Checker (WCAG 2.1 AA)', 'bright');
+ log('β'.repeat(70), 'cyan');
+ console.log('');
+
+ let passCount = 0;
+ let failCount = 0;
+ let warnings = 0;
+
+ colorCombinations.forEach(combo => {
+ const fgColor = tailwindColors[combo.fg] || combo.fg;
+ const bgColor = tailwindColors[combo.bg] || combo.bg;
+
+ const ratio = getContrastRatio(fgColor, bgColor);
+ const passes = meetsWCAG_AA(ratio, combo.largeText);
+ const threshold = combo.largeText ? '3:1' : '4.5:1';
+
+ const ratioStr = ratio.toFixed(2) + ':1';
+
+ if (passes) {
+ success(`${combo.name.padEnd(45)} ${ratioStr.padStart(8)} (>= ${threshold}) β`);
+ passCount++;
+ } else {
+ // Check if it's close (within 0.3 of threshold)
+ const minRatio = combo.largeText ? 3.0 : 4.5;
+ if (ratio >= minRatio - 0.3) {
+ warning(`${combo.name.padEnd(45)} ${ratioStr.padStart(8)} (< ${threshold}) β `);
+ warnings++;
+ } else {
+ error(`${combo.name.padEnd(45)} ${ratioStr.padStart(8)} (< ${threshold}) β`);
+ failCount++;
+ }
+ }
+ });
+
+ console.log('');
+ log('β'.repeat(70), 'cyan');
+ log(' Summary', 'bright');
+ log('β'.repeat(70), 'cyan');
+ console.log('');
+ log(` Combinations Checked: ${colorCombinations.length}`, 'bright');
+ log(` Passed: ${passCount}`, 'green');
+ if (warnings > 0) log(` Warnings: ${warnings}`, 'yellow');
+ if (failCount > 0) log(` Failed: ${failCount}`, 'red');
+ console.log('');
+
+ if (failCount > 0) {
+ error('Some color combinations fail WCAG AA standards');
+ console.log('');
+ process.exit(1);
+ } else if (warnings > 0) {
+ warning('All combinations pass, but some are borderline');
+ console.log('');
+ process.exit(0);
+ } else {
+ success('All color combinations meet WCAG AA standards');
+ console.log('');
+ process.exit(0);
+ }
+}
+
+main();
diff --git a/scripts/check-csp-violations.js b/scripts/check-csp-violations.js
new file mode 100644
index 00000000..9bb59360
--- /dev/null
+++ b/scripts/check-csp-violations.js
@@ -0,0 +1,212 @@
+#!/usr/bin/env node
+
+/**
+ * CSP Violation Scanner
+ * Scans HTML and JS files for Content Security Policy violations
+ *
+ * Violations checked (inst_008):
+ * - Inline event handlers (onclick, onload, etc.)
+ * - Inline styles (style="...")
+ * - Inline scripts ()
+ * - javascript: URLs
+ *
+ * Usage:
+ * node scripts/check-csp-violations.js [pattern]
+ *
+ * Examples:
+ * node scripts/check-csp-violations.js
+ * node scripts/check-csp-violations.js public
+ */
+
+const fs = require('fs');
+const path = require('path');
+const { execSync } = require('child_process');
+
+// Default patterns to scan
+const DEFAULT_PATTERNS = [
+ 'public/**/*.html',
+ 'public/**/*.js'
+];
+
+// CSP violation patterns
+const VIOLATION_PATTERNS = {
+ inline_event_handlers: {
+ regex: /\s(on[a-z]+)=["'][^"']*["']/gi,
+ description: 'Inline event handler',
+ severity: 'HIGH'
+ },
+ inline_styles: {
+ regex: /\sstyle=["'][^"']*["']/gi,
+ description: 'Inline style attribute',
+ severity: 'HIGH'
+ },
+ inline_scripts: {
+ regex: /`,
+ ``,
+ `4. Ensure script loads at appropriate time (defer/async if needed)`
+ ],
+ example: 'See public/js/*.js for existing patterns'
+ },
+ javascript_urls: {
+ priority: 'CRITICAL',
+ approach: 'Replace with proper event handlers',
+ steps: [
+ `1. Remove href="javascript:..." attribute`,
+ ``,
+ `2. Add event listener in external JS:`,
+ ` document.getElementById('link-id').addEventListener('click', function(e) {`,
+ ` e.preventDefault();`,
+ ` // Action code here`,
+ ` });`,
+ ``,
+ `3. For links that don't navigate, consider using