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:
parent
a86f9777bd
commit
a04521713a
9 changed files with 3597 additions and 31 deletions
2337
docs/DOCUMENT_AUDIT_REPORT.json
Normal file
2337
docs/DOCUMENT_AUDIT_REPORT.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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',
|
||||
|
|
|
|||
45
scripts/analyze-categories.js
Normal file
45
scripts/analyze-categories.js
Normal 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);
|
||||
});
|
||||
243
scripts/comprehensive-document-audit.js
Normal file
243
scripts/comprehensive-document-audit.js
Normal 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();
|
||||
121
scripts/export-for-production.js
Normal file
121
scripts/export-for-production.js
Normal 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();
|
||||
360
scripts/generate-public-pdfs.js
Normal file
360
scripts/generate-public-pdfs.js
Normal 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();
|
||||
99
scripts/import-from-export.js
Normal file
99
scripts/import-from-export.js
Normal 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();
|
||||
229
scripts/migrate-documents-final.js
Normal file
229
scripts/migrate-documents-final.js
Normal 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();
|
||||
143
scripts/prepare-public-docs.js
Normal file
143
scripts/prepare-public-docs.js
Normal 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();
|
||||
Loading…
Add table
Reference in a new issue