- Create Economist SubmissionTracking package correctly: * mainArticle = full blog post content * coverLetter = 216-word SIR— letter * Links to blog post via blogPostId - Archive 'Letter to The Economist' from blog posts (it's the cover letter) - Fix date display on article cards (use published_at) - Target publication already displaying via blue badge Database changes: - Make blogPostId optional in SubmissionTracking model - Economist package ID: 68fa85ae49d4900e7f2ecd83 - Le Monde package ID: 68fa2abd2e6acd5691932150 Next: Enhanced modal with tabs, validation, export 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
208 lines
9.3 KiB
JavaScript
208 lines
9.3 KiB
JavaScript
/**
|
|
* Add Governance Evolution Banners to Blog Posts
|
|
* Demonstrates framework catching its own content violations
|
|
*/
|
|
|
|
const { MongoClient } = require('mongodb');
|
|
|
|
// Can be overridden by environment variable for production deployment
|
|
const DEV_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
|
const DEV_DB = process.env.MONGODB_DB || 'tractatus_dev';
|
|
|
|
// Banner templates
|
|
const BANNERS = {
|
|
'five-component-tractatus-architecture': `
|
|
<div class="bg-amber-50 border-l-4 border-amber-500 p-4 mb-6">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-5 w-5 text-amber-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<h3 class="text-sm font-medium text-amber-800">Framework Evolution Notice</h3>
|
|
<div class="mt-2 text-sm text-amber-700">
|
|
<p>This blog was written in early October 2025 describing our five-component architecture. When we ran it through updated governance rules in late October 2025, the framework flagged language that violated newly-added instructions:</p>
|
|
<ul class="list-disc ml-5 mt-2 space-y-1">
|
|
<li><strong>inst_017 (No Absolute Assurances):</strong> Detected use of "guarantees" - now requires evidence-based language</li>
|
|
<li><strong>inst_018 (Honest Testing Status):</strong> Flags "production-ready" claims without multi-project validation</li>
|
|
</ul>
|
|
<p class="mt-2"><strong>This is the framework working as designed.</strong> As governance rules evolve, they catch content that previously passed review. Rather than silently revise, we're leaving this as a live demonstration of how architectural governance improves over time. Read our <a href="/blog-post.html?slug=when-frameworks-fail-case-study" class="underline font-semibold hover:text-amber-900">framework failure case study</a> for more on transparent error handling.</p>
|
|
<p class="mt-2 text-xs italic">Note: This blog describes early-stage research (single-project validation). The framework components exist and function, but have not been validated across multiple production deployments.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`,
|
|
|
|
'introducing-tractatus-framework': `
|
|
<div class="bg-amber-50 border-l-4 border-amber-500 p-4 mb-6">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-5 w-5 text-amber-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<h3 class="text-sm font-medium text-amber-800">Framework Evolution Notice</h3>
|
|
<div class="mt-2 text-sm text-amber-700">
|
|
<p>This introductory blog was written in early October 2025. Updated governance rules (inst_017, inst_018) now flag absolute assurance language like "guarantees" and unvalidated production readiness claims.</p>
|
|
<p class="mt-2"><strong>The framework caught its own content.</strong> Rather than silently revise, we're demonstrating transparent governance evolution. The framework components described here exist and function in single-project deployment, but have not been validated at scale.</p>
|
|
<p class="mt-2">See: <a href="/blog-post.html?slug=when-frameworks-fail-case-study" class="underline font-semibold hover:text-amber-900">Framework failure case study</a></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`,
|
|
|
|
'scaling-tractatus-roadmap': `
|
|
<div class="bg-amber-50 border-l-4 border-amber-500 p-4 mb-6">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-5 w-5 text-amber-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<h3 class="text-sm font-medium text-amber-800">Governance Evolution Notice</h3>
|
|
<div class="mt-2 text-sm text-amber-700">
|
|
<p>This roadmap blog was written in mid-October 2025. Updated governance rules (inst_017) now flag absolute assurance language. <strong>The framework detected violations in its own published content</strong> - demonstrating that architectural governance improves through iteration.</p>
|
|
<p class="mt-2">Rather than silently edit, we're preserving this as evidence of transparent evolution. See: <a href="/blog-post.html?slug=when-frameworks-fail-case-study" class="underline font-semibold hover:text-amber-900">Framework failure case study</a></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`,
|
|
|
|
'when-frameworks-fail-case-study': `
|
|
<div class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<h3 class="text-sm font-medium text-blue-800">Educational Content Notice</h3>
|
|
<div class="mt-2 text-sm text-blue-700">
|
|
<p><strong>This blog contains prohibited terms in quoted examples for educational purposes.</strong> Terms like "guarantee", "production-ready", and "battle-tested" appear in:</p>
|
|
<ul class="list-disc ml-5 mt-2 space-y-1">
|
|
<li>Governance rule definitions (inst_017, inst_018)</li>
|
|
<li>Examples of violations to avoid</li>
|
|
<li>Critical analysis of problematic claims</li>
|
|
</ul>
|
|
<p class="mt-2">These educational uses demonstrate <em>what not to do</em> - they are not claims made by the framework itself.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`
|
|
};
|
|
|
|
async function addBannerToBlog(collection, slug, banner) {
|
|
const blog = await collection.findOne({ slug });
|
|
|
|
if (!blog) {
|
|
console.log(` ❌ Blog not found: ${slug}`);
|
|
return false;
|
|
}
|
|
|
|
// Check if banner already exists
|
|
if (blog.content.includes('Framework Evolution Notice') ||
|
|
blog.content.includes('Educational Content Notice')) {
|
|
console.log(` ⚠️ Banner already exists, skipping`);
|
|
return false;
|
|
}
|
|
|
|
let updatedContent;
|
|
|
|
// Check if content is HTML (contains <h1> tag)
|
|
const h1Match = blog.content.match(/(<h1[^>]*>.*?<\/h1>)/);
|
|
|
|
if (h1Match) {
|
|
// HTML content: Insert banner after first <h1> tag
|
|
updatedContent = blog.content.replace(
|
|
h1Match[0],
|
|
h1Match[0] + banner
|
|
);
|
|
console.log(` 📝 Format: HTML`);
|
|
} else {
|
|
// Markdown content: Convert banner to markdown-style notice
|
|
// For markdown blogs, prepend as HTML (it will be rendered in the blog viewer)
|
|
const markdownBanner = '\n' + banner + '\n';
|
|
|
|
// Insert after first line (title) or at the beginning
|
|
const lines = blog.content.split('\n');
|
|
const firstNonEmptyLine = lines.findIndex(line => line.trim().length > 0);
|
|
|
|
if (firstNonEmptyLine >= 0 && lines[firstNonEmptyLine].startsWith('#')) {
|
|
// Insert after markdown title
|
|
lines.splice(firstNonEmptyLine + 1, 0, markdownBanner);
|
|
} else {
|
|
// Prepend to content
|
|
lines.unshift(markdownBanner);
|
|
}
|
|
|
|
updatedContent = lines.join('\n');
|
|
console.log(` 📝 Format: Markdown`);
|
|
}
|
|
|
|
// Update the blog
|
|
const result = await collection.updateOne(
|
|
{ slug },
|
|
{ $set: { content: updatedContent } }
|
|
);
|
|
|
|
if (result.modifiedCount === 1) {
|
|
console.log(` ✅ Banner added successfully`);
|
|
return true;
|
|
} else {
|
|
console.log(` ❌ Failed to update blog`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function addBannersToBlogs() {
|
|
const client = new MongoClient(DEV_URI);
|
|
|
|
try {
|
|
console.log('📋 Adding Governance Evolution Banners to Blogs\n');
|
|
console.log('═'.repeat(70));
|
|
|
|
await client.connect();
|
|
const db = client.db(DEV_DB);
|
|
const collection = db.collection('blog_posts');
|
|
|
|
let successCount = 0;
|
|
const blogSlugs = Object.keys(BANNERS);
|
|
|
|
for (const slug of blogSlugs) {
|
|
console.log(`\n📄 Processing: ${slug}`);
|
|
const success = await addBannerToBlog(collection, slug, BANNERS[slug]);
|
|
if (success) successCount++;
|
|
}
|
|
|
|
console.log('\n' + '═'.repeat(70));
|
|
console.log(`\n✨ Complete: ${successCount}/${blogSlugs.length} blogs updated\n`);
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error:', error.message);
|
|
throw error;
|
|
} finally {
|
|
await client.close();
|
|
}
|
|
}
|
|
|
|
// Run if called directly
|
|
if (require.main === module) {
|
|
addBannersToBlogs()
|
|
.then(() => {
|
|
console.log('✅ Banners added to dev database');
|
|
console.log('\nNext steps:');
|
|
console.log('1. Test blogs locally at http://localhost:9000/blog.html');
|
|
console.log('2. Deploy to production with same script targeting prod DB\n');
|
|
process.exit(0);
|
|
})
|
|
.catch(error => {
|
|
console.error('\n💥 Failed:', error);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { addBannersToBlogs, BANNERS };
|