diff --git a/scripts/generate-markdown-pdfs.js b/scripts/generate-markdown-pdfs.js new file mode 100644 index 00000000..2fca94aa --- /dev/null +++ b/scripts/generate-markdown-pdfs.js @@ -0,0 +1,96 @@ +/** + * Quick PDF Generator for Markdown Files + * Uses Puppeteer to generate PDFs directly from markdown files + */ + +const puppeteer = require('puppeteer'); +const marked = require('marked'); +const fs = require('fs').promises; +const path = require('path'); + +async function generatePdfFromMarkdown(markdownPath, outputPath, title) { + const markdown = await fs.readFile(markdownPath, 'utf-8'); + const html = marked.parse(markdown); + + const fullHtml = ` + + + + + ${title} + + + +

${title}

+ ${html} + + + `; + + const browser = await puppeteer.launch({ + headless: true, + args: ['--no-sandbox', '--disable-setuid-sandbox'] + }); + + const page = await browser.newPage(); + await page.setContent(fullHtml, { waitUntil: 'networkidle0' }); + + await page.pdf({ + path: outputPath, + format: 'A4', + printBackground: true, + margin: { top: '2cm', right: '2cm', bottom: '2cm', left: '2cm' } + }); + + await browser.close(); + console.log(`āœ“ Generated: ${path.basename(outputPath)}`); +} + +async function main() { + const files = [ + { + input: 'docs/research/pluralistic-values-research-foundations.md', + output: 'public/downloads/pluralistic-values-research-foundations.pdf', + title: 'Pluralistic Values: Research Foundations' + }, + { + input: 'docs/value-pluralism-faq.md', + output: 'public/downloads/value-pluralism-faq.pdf', + title: 'Value Pluralism in Tractatus: FAQ' + }, + { + input: 'docs/pluralistic-values-deliberation-plan-v2.md', + output: 'public/downloads/pluralistic-values-deliberation-plan-v2.pdf', + title: 'Pluralistic Values Deliberation Enhancement Plan' + } + ]; + + for (const file of files) { + await generatePdfFromMarkdown(file.input, file.output, file.title); + } + + console.log('\nāœ“ All PDFs generated successfully'); +} + +main().catch(console.error); diff --git a/scripts/load-inst-035.js b/scripts/load-inst-035.js new file mode 100644 index 00000000..c791f183 --- /dev/null +++ b/scripts/load-inst-035.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node + +/** + * Load inst_035 (Precedent Database Design) into MongoDB + * This resolves the startup warning about missing inst_035 + */ + +const mongoose = require('mongoose'); +const config = require('../src/config/app.config'); + +const instructionData = { + id: "inst_035", + text: "Precedent database stores past deliberations as INFORMATIVE (not binding) precedents. Each entry documents: decision context, moral frameworks in tension, stakeholders consulted, values prioritized/deprioritized, moral remainder, dissenting views, justification, precedent applicability SCOPE (not universal rule), review date. When similar case arises: (1) CrossReferenceValidator identifies relevant precedents, (2) Human reviews for context similarity, (3) Precedent INFORMS new deliberation but doesn't dictate outcome, (4) Document why following or departing from precedent. Precedents are PROVISIONAL - reviewable when context changes, scale shifts, new evidence emerges. Prevent precedent creep into rigid hierarchy.", + timestamp: "2025-10-12T14:35:00Z", + quadrant: "OPERATIONAL", + persistence: "HIGH", + temporal_scope: "PERMANENT", + verification_required: "MANDATORY", + explicitness: 1.0, + source: "user", + session_id: "2025-10-12-value-pluralism-implementation", + parameters: { + precedent_type: "informative_not_binding", + precedent_fields: [ + "context", + "frameworks_in_tension", + "stakeholders", + "values_prioritized", + "values_deprioritized", + "moral_remainder", + "dissent", + "justification", + "applicability_scope", + "review_date" + ], + precedent_matching: "CrossReferenceValidator identifies similar cases", + human_review_required: "Context similarity assessment", + precedent_role: "Informs, doesn't dictate", + departure_documentation: "Explain why not following precedent", + provisional_nature: "Reviewable when context/scale/evidence changes", + prevent: "Precedent creep into universal rules", + related_component: [ + "PluralisticDeliberationOrchestrator", + "CrossReferenceValidator" + ] + }, + active: true, + notes: "CORE VALUE PLURALISM IMPLEMENTATION 2025-10-12 - Precedent database design prevents rigid hierarchy while enabling learning from past deliberations. Precedents are PROVISIONAL (Gutmann & Thompson) - decisions aren't final, they're revisable. Key distinction: precedent = 'in similar past case we did X' NOT 'therefore you must do X'. Context matters: scale changes (1000 users → 87 million users = re-deliberate), new evidence (theoretical harm now documented = re-deliberate), changed circumstances = review. Git-like versioning tracks how thinking evolved over time." +}; + +async function loadInstruction() { + try { + console.log('šŸ”Œ Connecting to MongoDB...'); + await mongoose.connect(config.mongodb.uri, config.mongodb.options); + console.log('āœ… Connected to MongoDB'); + + const db = mongoose.connection.db; + const collection = db.collection('governanceRules'); // Must match model collection name + + // Check if inst_035 already exists + const existing = await collection.findOne({ id: 'inst_035' }); + + if (existing) { + console.log('āš ļø inst_035 already exists in database'); + console.log(' Updating with latest version...'); + + await collection.updateOne( + { id: 'inst_035' }, + { $set: instructionData } + ); + + console.log('āœ… inst_035 updated successfully'); + } else { + console.log('šŸ“ Inserting inst_035...'); + await collection.insertOne(instructionData); + console.log('āœ… inst_035 inserted successfully'); + } + + // Verify insertion + const inserted = await collection.findOne({ id: 'inst_035' }); + console.log('\nšŸ“‹ Verification:'); + console.log(` ID: ${inserted.id}`); + console.log(` Quadrant: ${inserted.quadrant}`); + console.log(` Persistence: ${inserted.persistence}`); + console.log(` Text: ${inserted.text.substring(0, 80)}...`); + console.log(` Related Components: ${inserted.parameters.related_component.join(', ')}`); + + await mongoose.connection.close(); + console.log('\nāœ… Done! MongoDB connection closed.'); + process.exit(0); + + } catch (error) { + console.error('āŒ Error loading instruction:', error); + await mongoose.connection.close(); + process.exit(1); + } +} + +loadInstruction(); diff --git a/scripts/migrate-value-pluralism-docs.js b/scripts/migrate-value-pluralism-docs.js new file mode 100644 index 00000000..76cd7333 --- /dev/null +++ b/scripts/migrate-value-pluralism-docs.js @@ -0,0 +1,200 @@ +/** + * Migrate Value Pluralism Documents to MongoDB + * Adds three new value pluralism documents to the documents collection + */ + +const mongoose = require('mongoose'); +const marked = require('marked'); +const fs = require('fs').promises; +const path = require('path'); +const config = require('../src/config/app.config'); + +// Document structure from existing documents +const documentsToMigrate = [ + { + file: 'docs/research/pluralistic-values-research-foundations.md', + title: 'Pluralistic Values: Research Foundations', + slug: 'pluralistic-values-research-foundations', + quadrant: 'STRATEGIC', + persistence: 'HIGH', + category: 'research', + audience: ['researcher', 'technical'], + tags: ['value-pluralism', 'research', 'deliberative-democracy', 'ethics', 'philosophy'], + description: 'Supporting research material for PluralisticDeliberationOrchestrator implementation, covering deliberative democracy, value pluralism theory, and cross-cultural communication' + }, + { + file: 'docs/value-pluralism-faq.md', + title: 'Value Pluralism in Tractatus: Frequently Asked Questions', + slug: 'value-pluralism-faq', + quadrant: null, + persistence: 'HIGH', + category: 'documentation', + audience: ['general'], + tags: ['value-pluralism', 'faq', 'documentation', 'ethics'], + description: 'Accessible explanation of how Tractatus handles moral disagreement without imposing hierarchy' + }, + { + file: 'docs/pluralistic-values-deliberation-plan-v2.md', + title: 'Pluralistic Values Deliberation Enhancement Plan', + slug: 'pluralistic-values-deliberation-plan-v2', + quadrant: 'OPERATIONAL', + persistence: 'HIGH', + category: 'implementation-guide', + audience: ['implementer', 'researcher'], + tags: ['value-pluralism', 'implementation', 'deliberation', 'planning'], + description: 'Technical design document for implementing non-hierarchical moral reasoning in the Tractatus Framework' + } +]; + +/** + * Generate table of contents from markdown + */ +function generateToC(markdown) { + const toc = []; + const lines = markdown.split('\n'); + + for (const line of lines) { + const match = line.match(/^(#{1,6})\s+(.+)$/); + if (match) { + const level = match[1].length; + const title = match[2].trim(); + const slug = title + .toLowerCase() + .replace(/[^\w\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .trim(); + + toc.push({ level, title, slug }); + } + } + + return toc; +} + +/** + * Generate search index (lowercase, no markdown formatting) + */ +function generateSearchIndex(markdown) { + return markdown + .toLowerCase() + .replace(/```[\s\S]*?```/g, '') // Remove code blocks + .replace(/`[^`]+`/g, '') // Remove inline code + .replace(/[#*_\[\]()]/g, '') // Remove markdown formatting + .replace(/\n+/g, '\n') // Collapse multiple newlines + .trim(); +} + +/** + * Migrate a single document + */ +async function migrateDocument(docInfo, db) { + console.log(`\nMigrating: ${docInfo.title}`); + + // Read markdown file + const markdownPath = path.join(__dirname, '..', docInfo.file); + const markdown = await fs.readFile(markdownPath, 'utf-8'); + + // Convert to HTML + const html = marked.parse(markdown); + + // Generate ToC + const toc = generateToC(markdown); + console.log(` āœ“ Generated ToC (${toc.length} headings)`); + + // Generate search index + const searchIndex = generateSearchIndex(markdown); + + // Create document + const document = { + title: docInfo.title, + slug: docInfo.slug, + quadrant: docInfo.quadrant, + persistence: docInfo.persistence, + content_html: html, + content_markdown: markdown, + toc: toc, + metadata: { + author: 'System', + date_created: new Date(), + date_updated: new Date(), + version: '1.0', + document_code: null, + related_documents: [], + tags: docInfo.tags, + category: docInfo.category, + audience: docInfo.audience, + description: docInfo.description + }, + translations: {}, + search_index: searchIndex + }; + + // Check if document already exists + const existing = await db.collection('documents').findOne({ slug: docInfo.slug }); + + if (existing) { + console.log(` ⚠ Document already exists, updating...`); + await db.collection('documents').updateOne( + { slug: docInfo.slug }, + { $set: document } + ); + console.log(` āœ“ Updated`); + } else { + await db.collection('documents').insertOne(document); + console.log(` āœ“ Inserted`); + } + + return docInfo.slug; +} + +/** + * Main migration function + */ +async function main() { + console.log('=== Value Pluralism Documents Migration ===\n'); + + let client; + + try { + // Connect to MongoDB + console.log('Connecting to MongoDB...'); + client = await mongoose.connect(config.mongodb.uri, config.mongodb.options); + const db = mongoose.connection.db; + console.log('āœ“ Connected\n'); + + // Migrate each document + const migratedSlugs = []; + for (const docInfo of documentsToMigrate) { + const slug = await migrateDocument(docInfo, db); + migratedSlugs.push(slug); + } + + // Summary + console.log('\n=== Migration Complete ===\n'); + console.log(`āœ“ Migrated ${migratedSlugs.length} documents:`); + migratedSlugs.forEach(slug => console.log(` - ${slug}`)); + console.log('\nDocuments are now available in docs.html'); + console.log('PDFs available at:'); + migratedSlugs.forEach(slug => + console.log(` - /downloads/${slug}.pdf`) + ); + + } catch (error) { + console.error('\nāœ— Migration failed:', error.message); + console.error(error.stack); + process.exit(1); + } finally { + if (client) { + await mongoose.connection.close(); + console.log('\nāœ“ Database connection closed'); + } + } +} + +// Run migration +if (require.main === module) { + main(); +} + +module.exports = { migrateDocument, generateToC, generateSearchIndex };