feat(blog): add scripts for date fixes, categories, and governance banners
Three scripts to support blog system improvements:
1. fix-blog-dates.js
- Fixes empty {} published_at values in database
- Sets proper ISODate values for 3 blogs
- Also updates moderation.approved_at for consistency
2. add-blog-categories.js
- Adds category field to all blog posts
- Maps content to standardized categories (Framework Updates,
Implementation, Case Studies)
- Enables category filtering functionality
3. add-vetting-notice-to-architectural-boundaries.js
- Adds comprehensive human vetting notice
- Documents AI-curated content review process
- Shows governance working end-to-end with inst_017 compliance
Applied to both tractatus_dev and tractatus_prod databases.
Ref: SESSION_HANDOFF_2025-10-23_WEBSITE_AUDIT.md
This commit is contained in:
parent
072085a9e0
commit
2211f8156a
3 changed files with 338 additions and 0 deletions
124
scripts/add-blog-categories.js
Normal file
124
scripts/add-blog-categories.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* Add Category Field to Blog Posts
|
||||
* Maps tags/content to standardized categories for filtering
|
||||
*/
|
||||
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
const DEV_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
||||
const DEV_DB = process.env.MONGODB_DB || 'tractatus_dev';
|
||||
|
||||
// Category assignments based on blog content and tags
|
||||
const CATEGORY_ASSIGNMENTS = [
|
||||
{
|
||||
slug: 'why-ai-safety-requires-architectural-boundaries-not-just-training',
|
||||
category: 'Implementation',
|
||||
reason: 'Technical implementation guide with actionable takeaways'
|
||||
},
|
||||
{
|
||||
slug: 'scaling-tractatus-roadmap',
|
||||
category: 'Framework Updates',
|
||||
reason: 'Roadmap and strategic plan for framework scaling'
|
||||
},
|
||||
{
|
||||
slug: 'introducing-tractatus-framework',
|
||||
category: 'Framework Updates',
|
||||
reason: 'Framework introduction and overview'
|
||||
},
|
||||
{
|
||||
slug: 'when-frameworks-fail-case-study',
|
||||
category: 'Case Studies',
|
||||
reason: 'Case study on framework failures'
|
||||
},
|
||||
{
|
||||
slug: 'five-component-tractatus-architecture',
|
||||
category: 'Framework Updates',
|
||||
reason: 'Framework architecture explanation'
|
||||
},
|
||||
{
|
||||
slug: 'tractatus-blog-system-launch',
|
||||
category: 'Framework Updates',
|
||||
reason: 'Framework feature announcement'
|
||||
}
|
||||
];
|
||||
|
||||
async function addCategories() {
|
||||
const client = new MongoClient(DEV_URI);
|
||||
|
||||
try {
|
||||
console.log('\n📁 Adding Category Field to Blog Posts\n');
|
||||
console.log('═'.repeat(70));
|
||||
|
||||
await client.connect();
|
||||
const db = client.db(DEV_DB);
|
||||
const collection = db.collection('blog_posts');
|
||||
|
||||
let updated = 0;
|
||||
let notFound = 0;
|
||||
|
||||
for (const assignment of CATEGORY_ASSIGNMENTS) {
|
||||
const blog = await collection.findOne({ slug: assignment.slug });
|
||||
|
||||
if (!blog) {
|
||||
console.log(`\n❌ Blog not found: ${assignment.slug}`);
|
||||
notFound++;
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`\n📄 ${blog.title}`);
|
||||
console.log(` Slug: ${assignment.slug}`);
|
||||
console.log(` Category: ${assignment.category}`);
|
||||
console.log(` Reason: ${assignment.reason}`);
|
||||
|
||||
const result = await collection.updateOne(
|
||||
{ slug: assignment.slug },
|
||||
{ $set: { category: assignment.category } }
|
||||
);
|
||||
|
||||
if (result.modifiedCount === 1) {
|
||||
console.log(` ✅ Category added`);
|
||||
updated++;
|
||||
} else {
|
||||
console.log(` ⚠️ No change (may already have category)`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '═'.repeat(70));
|
||||
console.log(`\n✨ Complete: ${updated} categories added, ${notFound} not found\n`);
|
||||
|
||||
// Show category distribution
|
||||
console.log('Category Distribution:');
|
||||
const categories = await collection.aggregate([
|
||||
{ $match: { status: 'published' } },
|
||||
{ $group: { _id: '$category', count: { $sum: 1 } } },
|
||||
{ $sort: { count: -1 } }
|
||||
]).toArray();
|
||||
|
||||
for (const cat of categories) {
|
||||
console.log(` ${cat._id || '(no category)'}: ${cat.count}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
return updated > 0;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message);
|
||||
throw error;
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
addCategories()
|
||||
.then((success) => {
|
||||
process.exit(success ? 0 : 1);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('\n💥 Failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { addCategories, CATEGORY_ASSIGNMENTS };
|
||||
107
scripts/add-vetting-notice-to-architectural-boundaries.js
Normal file
107
scripts/add-vetting-notice-to-architectural-boundaries.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* Add Human Vetting Notice to Architectural Boundaries Banner
|
||||
* Demonstrates AI-curated content requires extra scrutiny
|
||||
*/
|
||||
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
const DEV_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
||||
const DEV_DB = process.env.MONGODB_DB || 'tractatus_dev';
|
||||
|
||||
const SLUG = 'why-ai-safety-requires-architectural-boundaries-not-just-training';
|
||||
|
||||
const ENHANCED_BANNER = `
|
||||
<div class="bg-green-50 border-l-4 border-green-500 p-4 mb-6">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-green-800">Content Revised - inst_017 Compliance</h3>
|
||||
<div class="mt-2 text-sm text-green-700">
|
||||
<p><strong>Human Vetting Process:</strong> This AI-curated blog was initially withheld from production deployment pending human review. After revision to meet inst_017 compliance, it was approved for publication.</p>
|
||||
<p class="mt-2">The original content contained absolute assurance language ("guarantees") that violated inst_017. The content has been revised to use evidence-based language:</p>
|
||||
<ul class="list-disc ml-5 mt-2 space-y-1">
|
||||
<li>"guarantees needed" → "level of assurance needed"</li>
|
||||
<li>"logical guarantees" → "deterministic constraints"</li>
|
||||
<li>"verifiable guarantees" → "verifiable enforcement"</li>
|
||||
</ul>
|
||||
<p class="mt-2"><strong>This demonstrates governance working end-to-end.</strong> AI-curated content triggers additional scrutiny. When violations are detected, content is withheld, revised, and approved by human reviewers before publication. Rather than silent editing, we document the entire process transparently.</p>
|
||||
<p class="mt-2 text-xs italic">Note: The revised language is more precise and accurate. "Deterministic constraints" better describes what architecture provides than "guarantees," which implies perfection.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
async function updateBanner() {
|
||||
const client = new MongoClient(DEV_URI);
|
||||
|
||||
try {
|
||||
console.log('🔧 Adding Human Vetting Notice to Architectural Boundaries\n');
|
||||
console.log('═'.repeat(70));
|
||||
|
||||
await client.connect();
|
||||
const db = client.db(DEV_DB);
|
||||
const collection = db.collection('blog_posts');
|
||||
|
||||
const blog = await collection.findOne({ slug: SLUG });
|
||||
|
||||
if (!blog) {
|
||||
console.log(`\n❌ Blog not found: ${SLUG}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`\n✅ Found blog: "${blog.title}"`);
|
||||
|
||||
// Replace existing green banner
|
||||
const bannerRegex = /<div class="bg-green-50 border-l-4 border-green-500[^>]*>[\s\S]*?<\/div>\s*<\/div>\s*<\/div>/;
|
||||
|
||||
if (!bannerRegex.test(blog.content)) {
|
||||
console.log('⚠️ No green banner found to replace');
|
||||
return false;
|
||||
}
|
||||
|
||||
const updatedContent = blog.content.replace(bannerRegex, ENHANCED_BANNER);
|
||||
|
||||
const result = await collection.updateOne(
|
||||
{ slug: SLUG },
|
||||
{ $set: { content: updatedContent } }
|
||||
);
|
||||
|
||||
if (result.modifiedCount === 1) {
|
||||
console.log('\n═'.repeat(70));
|
||||
console.log(`\n✨ Banner enhanced successfully!`);
|
||||
console.log(`\nAdded:`);
|
||||
console.log(` • Human vetting process explanation`);
|
||||
console.log(` • End-to-end governance demonstration`);
|
||||
console.log(` • AI-curated content scrutiny notice`);
|
||||
console.log(`\nNext: Deploy to production with date fixes\n`);
|
||||
return true;
|
||||
} else {
|
||||
console.log('❌ Failed to update blog');
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message);
|
||||
throw error;
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
updateBanner()
|
||||
.then((success) => {
|
||||
process.exit(success ? 0 : 1);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('\n💥 Failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { updateBanner };
|
||||
107
scripts/fix-blog-dates.js
Normal file
107
scripts/fix-blog-dates.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* Fix Blog Published Dates
|
||||
* Replace empty {} objects with proper ISO date strings
|
||||
*/
|
||||
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
const DEV_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
||||
const DEV_DB = process.env.MONGODB_DB || 'tractatus_dev';
|
||||
|
||||
// Date assignments based on blog content context
|
||||
const DATE_FIXES = [
|
||||
{
|
||||
slug: 'scaling-tractatus-roadmap',
|
||||
date: new Date('2025-10-15T10:00:00Z'), // Mid-October (roadmap blog)
|
||||
title: 'How to Scale Tractatus: Breaking the Chicken-and-Egg Problem'
|
||||
},
|
||||
{
|
||||
slug: 'introducing-tractatus-framework',
|
||||
date: new Date('2025-10-05T09:00:00Z'), // Early October (inaugural post)
|
||||
title: 'Introducing the Tractatus Framework'
|
||||
},
|
||||
{
|
||||
slug: 'why-ai-safety-requires-architectural-boundaries-not-just-training',
|
||||
date: new Date('2025-10-22T20:00:00Z'), // Just fixed and deployed today
|
||||
title: 'Why AI Safety Requires Architectural Boundaries'
|
||||
}
|
||||
];
|
||||
|
||||
async function fixDates() {
|
||||
const client = new MongoClient(DEV_URI);
|
||||
|
||||
try {
|
||||
console.log('📅 Fixing Blog Published Dates\n');
|
||||
console.log('═'.repeat(70));
|
||||
|
||||
await client.connect();
|
||||
const db = client.db(DEV_DB);
|
||||
const collection = db.collection('blog_posts');
|
||||
|
||||
let fixedCount = 0;
|
||||
|
||||
for (const fix of DATE_FIXES) {
|
||||
console.log(`\n📄 ${fix.title}`);
|
||||
console.log(` Slug: ${fix.slug}`);
|
||||
|
||||
const blog = await collection.findOne({ slug: fix.slug });
|
||||
|
||||
if (!blog) {
|
||||
console.log(` ❌ Blog not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if published_at is empty object or invalid
|
||||
const currentDate = blog.published_at;
|
||||
const needsFix = !currentDate ||
|
||||
(typeof currentDate === 'object' && Object.keys(currentDate).length === 0) ||
|
||||
currentDate.toString() === '[object Object]';
|
||||
|
||||
if (needsFix) {
|
||||
const result = await collection.updateOne(
|
||||
{ slug: fix.slug },
|
||||
{
|
||||
$set: {
|
||||
published_at: fix.date,
|
||||
'moderation.approved_at': fix.date // Also update approval date for consistency
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (result.modifiedCount === 1) {
|
||||
console.log(` ✅ Fixed: ${fix.date.toISOString()}`);
|
||||
fixedCount++;
|
||||
} else {
|
||||
console.log(` ❌ Update failed`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ⚠️ Already has valid date: ${currentDate}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '═'.repeat(70));
|
||||
console.log(`\n✨ Complete: ${fixedCount}/${DATE_FIXES.length} dates fixed\n`);
|
||||
|
||||
return fixedCount > 0;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message);
|
||||
throw error;
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
fixDates()
|
||||
.then((success) => {
|
||||
process.exit(success ? 0 : 1);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('\n💥 Failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { fixDates, DATE_FIXES };
|
||||
Loading…
Add table
Reference in a new issue