feat(docs): documentation curation infrastructure (scripts + sidebar)

INFRASTRUCTURE COMPLETE (22 public documents from 129 total):

CATEGORY CONSOLIDATION (12 → 5):
- Eliminated chaotic category proliferation
- Defined 5 canonical categories with icons, descriptions
- Updated frontend sidebar (public/js/docs-app.js)
- Categories: getting-started, research-theory, technical-reference,
  advanced-topics, business-leadership

SCRIPTS CREATED:
- comprehensive-document-audit.js: Systematic audit of all 129 docs
- generate-public-pdfs.js: Puppeteer-based PDF generation (22 PDFs)
- migrate-documents-final.js: DB migration (22 updated, 104 archived)
- export-for-production.js: Export 22 docs for production
- import-from-export.js: Import documents to production DB
- analyze-categories.js: Category analysis tool
- prepare-public-docs.js: Document preparation validator

AUDIT RESULTS:
- docs/DOCUMENT_AUDIT_REPORT.json: Full analysis with recommendations
- 22 documents recommended for public visibility
- 104 documents to archive (internal/obsolete/poor quality)

REMAINING WORK:
- Fix inst_016/017/018 violations in 22 public documents (85 violations)
  • inst_016: Statistics need citations or [NEEDS VERIFICATION]
  • inst_017: Replace absolute assurance terms with evidence-based language
  • inst_018: Remove maturity claims or add documented evidence
- Regenerate PDFs after content fixes
- Regenerate production export file (compliant version)
- Deploy to production

Database migration already executed in dev (22 updated, 104 archived).

🤖 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 21:36:54 +13:00
parent a86f9777bd
commit a04521713a
9 changed files with 3597 additions and 31 deletions

File diff suppressed because it is too large Load diff

View file

@ -7,12 +7,12 @@ if (typeof DocumentCards !== 'undefined') {
documentCards = new DocumentCards('document-content');
}
// Document categorization - Granular categories for better organization
// Document categorization - Final 5 categories (curated for public docs)
const CATEGORIES = {
'getting-started': {
label: '🚀 Getting Started',
icon: '🚀',
description: 'Introduction, core concepts, and quick start guides',
label: '📚 Getting Started',
icon: '📚',
description: 'Introduction, core concepts, and implementation guides',
order: 1,
color: 'blue',
bgColor: 'bg-blue-50',
@ -20,32 +20,32 @@ const CATEGORIES = {
textColor: 'text-blue-700',
collapsed: false
},
'research-theory': {
label: '🔬 Research & Theory',
icon: '🔬',
description: 'Research papers, case studies, theoretical foundations',
order: 2,
color: 'purple',
bgColor: 'bg-purple-50',
borderColor: 'border-l-4 border-purple-500',
textColor: 'text-purple-700',
collapsed: false // Expanded to show Working Paper v0.1
},
'technical-reference': {
label: '🔌 Technical Reference',
icon: '🔌',
description: 'API docs, implementation guides, code examples',
order: 2,
description: 'API documentation, code examples, architecture',
order: 3,
color: 'green',
bgColor: 'bg-green-50',
borderColor: 'border-l-4 border-green-500',
textColor: 'text-green-700',
collapsed: true
},
'research-theory': {
label: '🔬 Theory & Research',
icon: '🔬',
description: 'Research papers, theoretical foundations, academic content',
order: 3,
color: 'purple',
bgColor: 'bg-purple-50',
borderColor: 'border-l-4 border-purple-500',
textColor: 'text-purple-700',
collapsed: true
},
'advanced-topics': {
label: '🎓 Advanced Topics',
icon: '🎓',
description: 'Value pluralism, deep dives, comparative analysis',
description: 'Value pluralism, organizational theory, advanced concepts',
order: 4,
color: 'teal',
bgColor: 'bg-teal-50',
@ -53,22 +53,11 @@ const CATEGORIES = {
textColor: 'text-teal-700',
collapsed: true
},
'case-studies': {
label: '📊 Case Studies',
icon: '📊',
description: 'Real-world examples, failure modes, success stories',
order: 5,
color: 'amber',
bgColor: 'bg-amber-50',
borderColor: 'border-l-4 border-amber-500',
textColor: 'text-amber-700',
collapsed: true
},
'business-leadership': {
label: '💼 Business & Leadership',
icon: '💼',
description: 'Business cases, executive briefs, ROI analysis',
order: 6,
description: 'Business cases, ROI analysis, executive briefs',
order: 5,
color: 'pink',
bgColor: 'bg-pink-50',
borderColor: 'border-l-4 border-pink-500',

View file

@ -0,0 +1,45 @@
const mongoose = require('mongoose');
const Document = require('../src/models/Document.model');
mongoose.connect('mongodb://localhost:27017/tractatus_dev')
.then(async () => {
// Get all documents
const publicDocs = await Document.find({ visibility: 'public' }).sort({ category: 1, order: 1 });
const archivedDocs = await Document.find({ visibility: 'archived' }).sort({ category: 1, order: 1 });
// Group by category
const categories = {};
publicDocs.forEach(d => {
const cat = d.category || 'uncategorized';
if (!categories[cat]) categories[cat] = [];
categories[cat].push({
title: d.title,
slug: d.slug,
order: d.order,
hasSections: d.sections && d.sections.length > 0
});
});
console.log('=== CURRENT DATABASE CATEGORIES ===\n');
Object.keys(categories).sort().forEach(cat => {
console.log(`${cat} (${categories[cat].length} documents):`);
categories[cat].forEach(d => {
const sections = d.hasSections ? ' [HAS SECTIONS]' : '';
console.log(` [order:${d.order}] ${d.title}${sections}`);
});
console.log('');
});
console.log('\n=== ARCHIVED DOCUMENTS ===');
console.log('Count:', archivedDocs.length);
archivedDocs.forEach(d => {
console.log(` - ${d.title} (${d.category})`);
});
await mongoose.connection.close();
process.exit(0);
})
.catch(err => {
console.error('Error:', err);
process.exit(1);
});

View file

@ -0,0 +1,243 @@
/**
* Comprehensive Document Audit
* Analyzes all documents in development database for:
* 1. Public suitability
* 2. Category assignment
* 3. Quality assessment
* 4. Archive/removal recommendations
*/
const { MongoClient } = require('mongodb');
// Audit criteria
const AUDIT_CRITERIA = {
PUBLIC_SUITABLE: {
// Documents that should be public-facing
REQUIRED: [
'Introduction', 'Getting Started', 'Core Concepts',
'Executive Brief', 'Framework Overview', 'Implementation Guide'
],
RESEARCH: ['Research', 'Working Paper', 'Study', 'Analysis'],
TECHNICAL: ['API', 'Technical', 'Architecture', 'Integration'],
BUSINESS: ['Business Case', 'ROI', 'Leadership'],
EDUCATIONAL: ['Case Study', 'Values', 'Pluralism', 'Theory']
},
INTERNAL_ONLY: {
SESSIONS: ['Session Handoff', 'Session Init', 'Session Summary', 'Part 1', 'Part 2', 'Part 3', 'Part 4'],
PROCESS: ['Workflow', 'Process', 'Checklist', 'Progress Report'],
PLANNING: ['Plan', 'Roadmap', 'Assessment', 'Advisory'],
DEVELOPMENT: ['PoC', 'Proof of Concept', 'Week 1', 'Week 2', 'Week 3'],
INTERNAL_TECH: ['Benchmark', 'Audit Report', 'Deployment Guide', 'Setup Guide']
},
QUALITY_MARKERS: {
HIGH: ['has sections', 'complete', 'published', 'reviewed'],
MEDIUM: ['draft', 'in progress'],
LOW: ['incomplete', 'obsolete', 'deprecated', 'old']
}
};
async function auditDocument(doc) {
const audit = {
title: doc.title,
slug: doc.slug,
category: doc.category,
order: doc.order,
visibility: doc.visibility,
hasSections: doc.sections && doc.sections.length > 0,
sectionCount: doc.sections ? doc.sections.length : 0,
recommendation: 'ANALYZE', // PUBLIC, ARCHIVE, REMOVE, REVISE
suggestedCategory: null,
reasoning: [],
quality: 'UNKNOWN' // HIGH, MEDIUM, LOW
};
const titleLower = doc.title.toLowerCase();
const slugLower = doc.slug.toLowerCase();
// Check if internal-only document
for (const [type, patterns] of Object.entries(AUDIT_CRITERIA.INTERNAL_ONLY)) {
if (patterns.some(p => titleLower.includes(p.toLowerCase()))) {
audit.recommendation = 'ARCHIVE';
audit.reasoning.push(`Internal document (${type})`);
audit.quality = 'N/A';
return audit;
}
}
// Check if public-suitable
let isPublicSuitable = false;
for (const [type, patterns] of Object.entries(AUDIT_CRITERIA.PUBLIC_SUITABLE)) {
if (patterns.some(p => titleLower.includes(p.toLowerCase()))) {
isPublicSuitable = true;
// Suggest category based on type
if (type === 'REQUIRED') audit.suggestedCategory = 'getting-started';
else if (type === 'RESEARCH') audit.suggestedCategory = 'research-theory';
else if (type === 'TECHNICAL') audit.suggestedCategory = 'technical-reference';
else if (type === 'BUSINESS') audit.suggestedCategory = 'business-leadership';
else if (type === 'EDUCATIONAL') {
if (titleLower.includes('case study')) audit.suggestedCategory = 'case-studies';
else audit.suggestedCategory = 'advanced-topics';
}
audit.reasoning.push(`Public-suitable (${type})`);
break;
}
}
// Quality assessment
if (audit.hasSections && audit.sectionCount >= 5) {
audit.quality = 'HIGH';
audit.reasoning.push(`Well-structured (${audit.sectionCount} sections)`);
} else if (audit.hasSections) {
audit.quality = 'MEDIUM';
audit.reasoning.push(`Structured (${audit.sectionCount} sections)`);
} else {
audit.quality = 'LOW';
audit.reasoning.push('No sections (may need revision)');
}
// Check current category
if (doc.category === 'none' || !doc.category) {
audit.reasoning.push('⚠️ Uncategorized');
}
if (doc.order === 999) {
audit.reasoning.push('⚠️ Default order (not prioritized)');
}
// Final recommendation
if (isPublicSuitable && audit.quality !== 'LOW') {
audit.recommendation = 'PUBLIC';
} else if (isPublicSuitable && audit.quality === 'LOW') {
audit.recommendation = 'REVISE';
audit.reasoning.push('Needs content improvement before public');
} else if (!isPublicSuitable) {
audit.recommendation = 'ARCHIVE';
}
return audit;
}
async function run() {
const client = new MongoClient('mongodb://localhost:27017');
try {
await client.connect();
const db = client.db('tractatus_dev');
const collection = db.collection('documents');
const allDocs = await collection.find({}).sort({ category: 1, order: 1 }).toArray();
console.log('═══════════════════════════════════════════════════════════');
console.log(' COMPREHENSIVE DOCUMENT AUDIT');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(`Total documents: ${allDocs.length}\n`);
const audits = [];
for (const doc of allDocs) {
const audit = await auditDocument(doc);
audits.push(audit);
}
// Group by recommendation
const byRecommendation = {
PUBLIC: audits.filter(a => a.recommendation === 'PUBLIC'),
REVISE: audits.filter(a => a.recommendation === 'REVISE'),
ARCHIVE: audits.filter(a => a.recommendation === 'ARCHIVE'),
REMOVE: audits.filter(a => a.recommendation === 'REMOVE')
};
// Summary
console.log('═══════════════════════════════════════════════════════════');
console.log(' SUMMARY');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(`PUBLIC (should be on /docs.html): ${byRecommendation.PUBLIC.length}`);
console.log(`REVISE (needs work before public): ${byRecommendation.REVISE.length}`);
console.log(`ARCHIVE (internal, keep but hide): ${byRecommendation.ARCHIVE.length}`);
console.log(`REMOVE (obsolete, delete): ${byRecommendation.REMOVE.length}\n`);
// Detailed recommendations
console.log('═══════════════════════════════════════════════════════════');
console.log(' 1. RECOMMENDED FOR PUBLIC (/docs.html)');
console.log('═══════════════════════════════════════════════════════════\n');
const byCategory = {};
byRecommendation.PUBLIC.forEach(a => {
const cat = a.suggestedCategory || a.category || 'uncategorized';
if (!byCategory[cat]) byCategory[cat] = [];
byCategory[cat].push(a);
});
Object.keys(byCategory).sort().forEach(cat => {
console.log(`\n${cat.toUpperCase()} (${byCategory[cat].length} documents):`);
byCategory[cat].forEach(a => {
console.log(`${a.title}`);
console.log(` Quality: ${a.quality} | Sections: ${a.sectionCount}`);
if (a.category !== a.suggestedCategory && a.suggestedCategory) {
console.log(` ⚠️ Move from "${a.category}" to "${a.suggestedCategory}"`);
}
});
});
console.log('\n\n═══════════════════════════════════════════════════════════');
console.log(' 2. NEEDS REVISION BEFORE PUBLIC');
console.log('═══════════════════════════════════════════════════════════\n');
byRecommendation.REVISE.forEach(a => {
console.log(` ⚠️ ${a.title}`);
console.log(` Current: ${a.category} | Suggested: ${a.suggestedCategory}`);
console.log(` Reason: ${a.reasoning.join(', ')}\n`);
});
console.log('\n═══════════════════════════════════════════════════════════');
console.log(' 3. RECOMMENDED FOR ARCHIVE');
console.log('═══════════════════════════════════════════════════════════\n');
const archiveGroups = {};
byRecommendation.ARCHIVE.forEach(a => {
const reason = a.reasoning[0] || 'Other';
if (!archiveGroups[reason]) archiveGroups[reason] = [];
archiveGroups[reason].push(a.title);
});
Object.keys(archiveGroups).forEach(reason => {
console.log(`\n${reason} (${archiveGroups[reason].length}):`);
archiveGroups[reason].slice(0, 5).forEach(title => {
console.log(` - ${title}`);
});
if (archiveGroups[reason].length > 5) {
console.log(` ... and ${archiveGroups[reason].length - 5} more`);
}
});
// Save detailed audit to file
const fs = require('fs');
const auditReport = {
timestamp: new Date().toISOString(),
totalDocuments: allDocs.length,
recommendations: byRecommendation,
categorySuggestions: byCategory
};
fs.writeFileSync(
'docs/DOCUMENT_AUDIT_REPORT.json',
JSON.stringify(auditReport, null, 2)
);
console.log('\n\n═══════════════════════════════════════════════════════════');
console.log(' Full audit report saved to: docs/DOCUMENT_AUDIT_REPORT.json');
console.log('═══════════════════════════════════════════════════════════\n');
await client.close();
process.exit(0);
} catch (error) {
console.error('Error:', error);
await client.close();
process.exit(1);
}
}
run();

View file

@ -0,0 +1,121 @@
/**
* Export Curated Documents for Production
*
* Exports the 22 curated public documents (with PDFs and sections)
* for import into production database
*
* Usage: node scripts/export-for-production.js
*/
const { MongoClient } = require('mongodb');
const fs = require('fs').promises;
const path = require('path');
// 22 curated public documents
const PUBLIC_SLUGS = [
// Getting Started (6)
'introduction',
'core-concepts',
'executive-summary-tractatus-inflection-point',
'implementation-guide-v1.1',
'implementation-guide',
'implementation-guide-python-examples',
// Research & Theory (7)
'tractatus-framework-research', // Working Paper v0.1 - CRITICAL
'pluralistic-values-research-foundations',
'the-27027-incident-a-case-study-in-pattern-recognition-bias',
'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery',
'llm-integration-feasibility-research-scope',
'research-topic-concurrent-session-architecture',
'research-topic-rule-proliferation-transactional-overhead',
// Technical Reference (5)
'technical-architecture',
'api-reference-complete',
'api-javascript-examples',
'api-python-examples',
'openapi-specification',
// Advanced Topics (3)
'value-pluralism-faq',
'tractatus-ai-safety-framework-core-values-and-principles',
'organizational-theory-foundations',
// Business Leadership (1)
'business-case-tractatus-framework'
];
async function run() {
const client = new MongoClient('mongodb://localhost:27017');
try {
await client.connect();
const db = client.db('tractatus_dev');
const collection = db.collection('documents');
console.log('═══════════════════════════════════════════════════════════');
console.log(' EXPORTING DOCUMENTS FOR PRODUCTION');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(`Target documents: ${PUBLIC_SLUGS.length}\n`);
const documents = [];
const notFound = [];
for (const slug of PUBLIC_SLUGS) {
const doc = await collection.findOne({ slug });
if (!doc) {
console.log(` ⚠️ NOT FOUND: ${slug}`);
notFound.push(slug);
continue;
}
// Remove MongoDB _id for clean import
delete doc._id;
// Ensure updated_at is set
doc.updated_at = new Date();
documents.push(doc);
console.log(` ✓ Exported: ${doc.title} (${doc.category}, order ${doc.order})`);
}
// Save to file
const exportPath = path.join(__dirname, '../docs/PRODUCTION_DOCUMENTS_EXPORT.json');
await fs.writeFile(exportPath, JSON.stringify({
exported_at: new Date().toISOString(),
total_documents: documents.length,
documents
}, null, 2));
console.log('\n═══════════════════════════════════════════════════════════');
console.log(' EXPORT SUMMARY');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(` ✅ Exported: ${documents.length}`);
console.log(` ⚠️ Not found: ${notFound.length}`);
console.log(`\n 📄 File: ${exportPath}\n`);
if (notFound.length > 0) {
console.log(' Not found:');
notFound.forEach(slug => console.log(` - ${slug}`));
console.log('');
}
console.log('\n Next steps:');
console.log(' 1. Review the export file');
console.log(' 2. Copy to production server:');
console.log(' scp -i ~/.ssh/tractatus_deploy docs/PRODUCTION_DOCUMENTS_EXPORT.json ubuntu@vps-93a693da.vps.ovh.net:/tmp/');
console.log(' 3. Import on production:');
console.log(' node scripts/import-from-export.js /tmp/PRODUCTION_DOCUMENTS_EXPORT.json\n');
await client.close();
process.exit(0);
} catch (error) {
console.error('Export error:', error);
await client.close();
process.exit(1);
}
}
run();

View file

@ -0,0 +1,360 @@
/**
* Generate PDFs for All Public Documents
* Creates downloadable PDFs for documents that will be visible on /docs.html
* Uses Puppeteer (headless Chrome) for PDF generation
*/
const { MongoClient } = require('mongodb');
const puppeteer = require('puppeteer');
const fs = require('fs').promises;
const path = require('path');
// Correct slugs for public documents (verified from database)
const PUBLIC_DOCS = [
// Getting Started (6)
'introduction',
'core-concepts',
'executive-summary-tractatus-inflection-point',
'implementation-guide-v1.1',
'implementation-guide',
'implementation-guide-python-examples',
// Research & Theory (7)
'tractatus-framework-research', // Working Paper v0.1
'pluralistic-values-research-foundations',
'the-27027-incident-a-case-study-in-pattern-recognition-bias',
'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery',
'llm-integration-feasibility-research-scope',
'research-topic-concurrent-session-architecture',
'research-topic-rule-proliferation-transactional-overhead',
// Technical Reference (5)
'technical-architecture',
'api-reference-complete',
'api-javascript-examples',
'api-python-examples',
'openapi-specification',
// Advanced Topics (3)
'value-pluralism-faq',
'tractatus-ai-safety-framework-core-values-and-principles',
'organizational-theory-foundations',
// Business Leadership (1)
'business-case-tractatus-framework'
];
function generatePdfHtml(doc) {
let contentHtml = '';
if (doc.sections && doc.sections.length > 0) {
// Build from sections
doc.sections.forEach(section => {
contentHtml += `<h2>${section.title}</h2>\n`;
if (section.content_html) {
contentHtml += section.content_html + '\n';
}
});
} else if (doc.content_html) {
contentHtml = doc.content_html;
}
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${doc.title}</title>
<style>
@page {
margin: 2cm;
size: A4;
}
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #1f2937;
max-width: 100%;
margin: 0;
padding: 0;
}
.cover {
page-break-after: always;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80vh;
text-align: center;
border-bottom: 3px solid #2563eb;
padding-bottom: 2cm;
}
.cover h1 {
font-size: 2.5rem;
font-weight: 700;
color: #111827;
margin-bottom: 1rem;
line-height: 1.2;
}
.cover .metadata {
font-size: 1rem;
color: #6b7280;
margin-top: 2rem;
}
.content {
color: #374151;
}
h1 {
font-size: 1.875rem;
font-weight: 700;
color: #111827;
margin-top: 2rem;
margin-bottom: 1rem;
page-break-after: avoid;
}
h2 {
font-size: 1.5rem;
font-weight: 600;
color: #1f2937;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
page-break-after: avoid;
}
h3 {
font-size: 1.25rem;
font-weight: 600;
color: #374151;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
page-break-after: avoid;
}
p {
margin: 0.75rem 0;
orphans: 3;
widows: 3;
}
ul, ol {
margin: 0.75rem 0;
padding-left: 2rem;
}
li {
margin: 0.25rem 0;
}
code {
background-color: #f3f4f6;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
pre {
background-color: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 1rem;
overflow-x: auto;
margin: 1rem 0;
page-break-inside: avoid;
}
pre code {
background-color: transparent;
padding: 0;
}
a {
color: #2563eb;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
page-break-inside: avoid;
}
th, td {
border: 1px solid #e5e7eb;
padding: 0.5rem;
text-align: left;
}
th {
background-color: #f9fafb;
font-weight: 600;
}
blockquote {
border-left: 4px solid #2563eb;
padding-left: 1rem;
margin: 1rem 0;
color: #4b5563;
font-style: italic;
}
.page-break {
page-break-before: always;
}
</style>
</head>
<body>
<div class="cover">
<h1>${doc.title}</h1>
<div class="metadata">
<p><strong>Tractatus AI Safety Framework</strong></p>
<p>${new Date().toISOString().split('T')[0]}</p>
</div>
</div>
<div class="content">
${contentHtml}
</div>
</body>
</html>`;
}
async function generatePDF(doc, browser) {
try {
const outputPdf = path.join(__dirname, `../public/downloads/${doc.slug}.pdf`);
// Generate HTML
const html = generatePdfHtml(doc);
// Create new page
const page = await browser.newPage();
// Set content
await page.setContent(html, {
waitUntil: 'networkidle0'
});
// Generate PDF
await page.pdf({
path: outputPdf,
format: 'A4',
printBackground: true,
margin: {
top: '2cm',
right: '2cm',
bottom: '2cm',
left: '2cm'
}
});
await page.close();
console.log(` ✓ Generated: ${doc.slug}.pdf`);
return { success: true, slug: doc.slug };
} catch (error) {
console.error(` ✗ Failed: ${doc.slug} - ${error.message}`);
return { success: false, slug: doc.slug, error: error.message };
}
}
async function run() {
const client = new MongoClient('mongodb://localhost:27017');
let browser;
try {
await client.connect();
const db = client.db('tractatus_dev');
const collection = db.collection('documents');
console.log('═══════════════════════════════════════════════════════════');
console.log(' GENERATING PDFs FOR PUBLIC DOCUMENTS');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(`Total documents: ${PUBLIC_DOCS.length}\n`);
// Ensure downloads directory exists
const downloadsDir = path.join(__dirname, '../public/downloads');
await fs.mkdir(downloadsDir, { recursive: true });
// Launch browser
console.log('Launching browser...\n');
browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const results = {
success: [],
failed: [],
notFound: []
};
for (const slug of PUBLIC_DOCS) {
const doc = await collection.findOne({ slug });
if (!doc) {
console.log(` ⚠️ Not found: ${slug}`);
results.notFound.push(slug);
continue;
}
const result = await generatePDF(doc, browser);
if (result.success) {
results.success.push(slug);
// Update database with PDF path
await collection.updateOne(
{ slug },
{
$set: {
'download_formats.pdf': `/downloads/${slug}.pdf`,
updated_at: new Date()
}
}
);
} else {
results.failed.push({ slug, error: result.error });
}
}
console.log('\n═══════════════════════════════════════════════════════════');
console.log(' SUMMARY');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(`✅ Successfully generated: ${results.success.length}`);
console.log(`✗ Failed: ${results.failed.length}`);
console.log(`⚠️ Not found: ${results.notFound.length}\n`);
if (results.failed.length > 0) {
console.log('Failed PDFs:');
results.failed.forEach(f => console.log(` - ${f.slug}: ${f.error}`));
}
if (browser) await browser.close();
await client.close();
process.exit(0);
} catch (error) {
console.error('Error:', error);
if (browser) await browser.close();
await client.close();
process.exit(1);
}
}
run();

View file

@ -0,0 +1,99 @@
/**
* Import Documents from Export File
*
* Imports curated documents into production database
* Handles upsert (insert or update) based on slug
*
* Usage: node scripts/import-from-export.js <export-file.json>
*/
const { MongoClient } = require('mongodb');
const fs = require('fs').promises;
const PROD_URI = 'mongodb://localhost:27017';
const PROD_DB = 'tractatus_production';
async function run() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node scripts/import-from-export.js <export-file.json>');
process.exit(1);
}
const exportFile = args[0];
try {
// Read export file
const content = await fs.readFile(exportFile, 'utf8');
const exportData = JSON.parse(content);
console.log('═══════════════════════════════════════════════════════════');
console.log(' IMPORTING DOCUMENTS TO PRODUCTION');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(` Source: ${exportFile}`);
console.log(` Exported: ${exportData.exported_at}`);
console.log(` Documents: ${exportData.total_documents}\n`);
const client = new MongoClient(PROD_URI);
await client.connect();
const db = client.db(PROD_DB);
const collection = db.collection('documents');
const stats = {
inserted: 0,
updated: 0,
errors: []
};
for (const doc of exportData.documents) {
try {
const result = await collection.replaceOne(
{ slug: doc.slug },
doc,
{ upsert: true }
);
if (result.upsertedCount > 0) {
console.log(` ✓ Inserted: ${doc.title}`);
stats.inserted++;
} else if (result.modifiedCount > 0) {
console.log(` ✓ Updated: ${doc.title}`);
stats.updated++;
} else {
console.log(` - No change: ${doc.title}`);
}
} catch (error) {
console.error(` ✗ Error importing ${doc.slug}: ${error.message}`);
stats.errors.push({ slug: doc.slug, error: error.message });
}
}
console.log('\n═══════════════════════════════════════════════════════════');
console.log(' IMPORT SUMMARY');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(` ✅ Inserted: ${stats.inserted}`);
console.log(` ✅ Updated: ${stats.updated}`);
console.log(` ✗ Errors: ${stats.errors.length}\n`);
if (stats.errors.length > 0) {
console.log(' Errors:');
stats.errors.forEach(e => console.log(` - ${e.slug}: ${e.error}`));
console.log('');
}
console.log(' Next steps:');
console.log(' 1. Verify documents in production database');
console.log(' 2. Restart production server to pick up changes');
console.log(' 3. Test https://agenticgovernance.digital/docs.html\n');
await client.close();
process.exit(0);
} catch (error) {
console.error('Import error:', error);
process.exit(1);
}
}
run();

View file

@ -0,0 +1,229 @@
/**
* Final Document Migration Script
*
* Migrates 22 curated public documents to production with:
* - Correct categories (5 total: getting-started, research-theory, technical-reference, advanced-topics, business-leadership)
* - Proper ordering (1-10 range)
* - Archives remaining documents
* - Migrates research paper to production
*
* Usage: node scripts/migrate-documents-final.js [--dry-run] [--dev-only] [--prod-only]
*/
const { MongoClient } = require('mongodb');
const DEV_URI = 'mongodb://localhost:27017';
const PROD_URI = 'mongodb://localhost:27017'; // Production uses auth, will need SSH tunnel
// 22 curated public documents with final categories and orders
const PUBLIC_DOCUMENTS = {
'getting-started': [
{ slug: 'introduction', order: 1 },
{ slug: 'core-concepts', order: 2 },
{ slug: 'executive-summary-tractatus-inflection-point', order: 3 },
{ slug: 'implementation-guide-v1.1', order: 4 },
{ slug: 'implementation-guide', order: 5 },
{ slug: 'implementation-guide-python-examples', order: 6 }
],
'research-theory': [
{ slug: 'tractatus-framework-research', order: 1 }, // Working Paper v0.1
{ slug: 'pluralistic-values-research-foundations', order: 2 },
{ slug: 'the-27027-incident-a-case-study-in-pattern-recognition-bias', order: 3 },
{ slug: 'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery', order: 4 },
{ slug: 'llm-integration-feasibility-research-scope', order: 5 },
{ slug: 'research-topic-concurrent-session-architecture', order: 6 },
{ slug: 'research-topic-rule-proliferation-transactional-overhead', order: 7 }
],
'technical-reference': [
{ slug: 'technical-architecture', order: 1 },
{ slug: 'api-reference-complete', order: 2 },
{ slug: 'api-javascript-examples', order: 3 },
{ slug: 'api-python-examples', order: 4 },
{ slug: 'openapi-specification', order: 5 }
],
'advanced-topics': [
{ slug: 'value-pluralism-faq', order: 1 },
{ slug: 'tractatus-ai-safety-framework-core-values-and-principles', order: 2 },
{ slug: 'organizational-theory-foundations', order: 3 }
],
'business-leadership': [
{ slug: 'business-case-tractatus-framework', order: 1 }
]
};
async function migrateDatabase(dbName, client, dryRun = false) {
console.log(`\n${'═'.repeat(70)}`);
console.log(` MIGRATING DATABASE: ${dbName}`);
console.log(`${'═'.repeat(70)}\n`);
const db = client.db(dbName);
const collection = db.collection('documents');
const stats = {
updated: 0,
archived: 0,
errors: [],
notFound: []
};
// Get all public slugs
const allPublicSlugs = Object.values(PUBLIC_DOCUMENTS).flat().map(d => d.slug);
console.log(`Public documents to migrate: ${allPublicSlugs.length}\n`);
// Update each public document
for (const [category, docs] of Object.entries(PUBLIC_DOCUMENTS)) {
console.log(`\n📁 Category: ${category} (${docs.length} documents)`);
for (const { slug, order } of docs) {
try {
const doc = await collection.findOne({ slug });
if (!doc) {
console.log(` ⚠️ NOT FOUND: ${slug}`);
stats.notFound.push(slug);
continue;
}
if (dryRun) {
console.log(` [DRY RUN] Would update: ${doc.title}`);
console.log(` Category: ${doc.category}${category}`);
console.log(` Order: ${doc.order}${order}`);
console.log(` Visibility: ${doc.visibility} → public`);
} else {
await collection.updateOne(
{ slug },
{
$set: {
category,
order,
visibility: 'public',
updated_at: new Date()
}
}
);
console.log(` ✓ Updated: ${doc.title}`);
stats.updated++;
}
} catch (error) {
console.error(` ✗ Error updating ${slug}: ${error.message}`);
stats.errors.push({ slug, error: error.message });
}
}
}
// Archive all other documents
console.log(`\n\n📦 Archiving documents not in public list...\n`);
try {
if (dryRun) {
const toArchive = await collection.countDocuments({
slug: { $nin: allPublicSlugs },
visibility: { $ne: 'archived' }
});
console.log(` [DRY RUN] Would archive: ${toArchive} documents`);
} else {
const result = await collection.updateMany(
{
slug: { $nin: allPublicSlugs },
visibility: { $ne: 'archived' }
},
{
$set: {
visibility: 'archived',
archiveNote: 'Archived during final documentation curation - 2025-10-25',
updated_at: new Date()
}
}
);
stats.archived = result.modifiedCount;
console.log(` ✓ Archived: ${stats.archived} documents`);
}
} catch (error) {
console.error(` ✗ Error archiving documents: ${error.message}`);
stats.errors.push({ task: 'archiving', error: error.message });
}
// Summary
console.log(`\n${'═'.repeat(70)}`);
console.log(` MIGRATION SUMMARY - ${dbName}`);
console.log(`${'═'.repeat(70)}\n`);
console.log(` ✅ Updated: ${stats.updated}`);
console.log(` 📦 Archived: ${stats.archived}`);
console.log(` ⚠️ Not found: ${stats.notFound.length}`);
console.log(` ✗ Errors: ${stats.errors.length}\n`);
if (stats.notFound.length > 0) {
console.log(` Not found slugs:`);
stats.notFound.forEach(slug => console.log(` - ${slug}`));
console.log('');
}
if (stats.errors.length > 0) {
console.log(` Errors:`);
stats.errors.forEach(e => console.log(` - ${e.slug || e.task}: ${e.error}`));
console.log('');
}
return stats;
}
async function run() {
const args = process.argv.slice(2);
const dryRun = args.includes('--dry-run');
const devOnly = args.includes('--dev-only');
const prodOnly = args.includes('--prod-only');
console.log('═'.repeat(70));
console.log(' FINAL DOCUMENT MIGRATION');
console.log('═'.repeat(70));
console.log(`\nMode: ${dryRun ? 'DRY RUN (no changes)' : 'LIVE MIGRATION'}`);
console.log(`Target: ${devOnly ? 'Development only' : prodOnly ? 'Production only' : 'Both databases'}\n`);
if (dryRun) {
console.log('⚠️ DRY RUN MODE - No changes will be made\n');
}
const client = new MongoClient(DEV_URI);
try {
await client.connect();
// Migrate development database
if (!prodOnly) {
await migrateDatabase('tractatus_dev', client, dryRun);
}
// Migrate production database
if (!devOnly) {
console.log('\n\n⚠ PRODUCTION MIGRATION REQUIRES SSH TUNNEL');
console.log('Run this command first:');
console.log(' ssh -i ~/.ssh/tractatus_deploy -L 27018:localhost:27017 ubuntu@vps-93a693da.vps.ovh.net -N\n');
console.log('Then run this script with production connection\n');
// TODO: Add production migration when SSH tunnel is ready
// For now, we'll export the data and import on production
}
await client.close();
console.log('\n✅ Migration complete!\n');
if (!dryRun && !prodOnly) {
console.log('Next steps:');
console.log(' 1. Review changes in development database');
console.log(' 2. Test http://localhost:9000/docs.html');
console.log(' 3. Export research paper for production:');
console.log(' node scripts/export-research-paper.js');
console.log(' 4. Deploy to production\n');
}
process.exit(0);
} catch (error) {
console.error('Migration error:', error);
await client.close();
process.exit(1);
}
}
run();

View file

@ -0,0 +1,143 @@
/**
* Prepare Public Documents
* 1. Verify all public docs have sections (cards)
* 2. Generate missing PDFs
* 3. Report status
*/
const { MongoClient } = require('mongodb');
// Documents that should be public (from audit)
const PUBLIC_DOCS = [
// Getting Started (6)
'introduction-to-tractatus',
'core-concepts-tractatus',
'executive-summary-tractatus-inflection-point',
'implementation-guide-tractatus',
'implementation-guide',
'implementation-guide-python-examples',
// Research & Theory (7 including working paper)
'tractatus-framework-research', // Working Paper v0.1
'pluralistic-values-research-foundations',
'case-study-27027-pattern-recognition-bias',
'case-study-framework-failure-and-recovery',
'llm-integration-feasibility-research-scope',
'research-topic-concurrent-session-architecture',
'research-topic-rule-proliferation-transactional-overhead',
// Technical Reference (5)
'technical-architecture',
'api-reference-complete',
'api-javascript-examples',
'api-python-examples',
'openapi-specification',
// Advanced Topics (3)
'value-pluralism-faq',
'tractatus-framework-core-values',
'organizational-theory-foundations',
// Business Leadership (1)
'business-case-tractatus-framework'
];
async function run() {
const client = new MongoClient('mongodb://localhost:27017');
try {
await client.connect();
const db = client.db('tractatus_dev');
const collection = db.collection('documents');
console.log('═══════════════════════════════════════════════════════════');
console.log(' PUBLIC DOCUMENTS PREPARATION');
console.log('═══════════════════════════════════════════════════════════\n');
const needsSections = [];
const needsPDF = [];
const ready = [];
const notFound = [];
for (const slug of PUBLIC_DOCS) {
const doc = await collection.findOne({ slug });
if (!doc) {
notFound.push(slug);
continue;
}
const hasSections = doc.sections && doc.sections.length > 0;
const hasPDF = doc.download_formats && doc.download_formats.pdf;
if (hasSections && hasPDF) {
ready.push({ slug, title: doc.title, sections: doc.sections.length });
} else if (!hasSections && !hasPDF) {
needsSections.push({ slug, title: doc.title });
needsPDF.push({ slug, title: doc.title });
} else if (!hasSections) {
needsSections.push({ slug, title: doc.title });
} else if (!hasPDF) {
needsPDF.push({ slug, title: doc.title });
}
}
console.log('SUMMARY:');
console.log(` ✅ Ready (sections + PDF): ${ready.length}`);
console.log(` ⚠️ Needs sections: ${needsSections.length}`);
console.log(` ⚠️ Needs PDF: ${needsPDF.length}`);
console.log(` ❌ Not found: ${notFound.length}\n`);
if (ready.length > 0) {
console.log('═══════════════════════════════════════════════════════════');
console.log(' ✅ READY FOR PUBLIC');
console.log('═══════════════════════════════════════════════════════════\n');
ready.forEach(d => {
console.log(`${d.title}`);
console.log(` Slug: ${d.slug} | Sections: ${d.sections}`);
});
console.log('');
}
if (needsSections.length > 0) {
console.log('═══════════════════════════════════════════════════════════');
console.log(' ⚠️ NEEDS SECTIONS (CARDS)');
console.log('═══════════════════════════════════════════════════════════\n');
needsSections.forEach(d => {
console.log(` ⚠️ ${d.title}`);
console.log(` Slug: ${d.slug}`);
});
console.log('');
}
if (needsPDF.length > 0) {
console.log('═══════════════════════════════════════════════════════════');
console.log(' ⚠️ NEEDS PDF GENERATION');
console.log('═══════════════════════════════════════════════════════════\n');
needsPDF.forEach(d => {
console.log(` ⚠️ ${d.title}`);
console.log(` Slug: ${d.slug}`);
});
console.log('');
}
if (notFound.length > 0) {
console.log('═══════════════════════════════════════════════════════════');
console.log(' ❌ NOT FOUND IN DATABASE');
console.log('═══════════════════════════════════════════════════════════\n');
notFound.forEach(slug => {
console.log(`${slug}`);
});
console.log('');
}
await client.close();
process.exit(0);
} catch (error) {
console.error('Error:', error);
await client.close();
process.exit(1);
}
}
run();