fix(docs): require document_type and audience before publishing

Documents could be set to visibility: 'public' without document_type,
audience, or status fields — either via bulk migration scripts or the
upload-document.js script. This allowed internal session logs to appear
in the public docs UI.

Safeguards added:
- Document.publish() now rejects if document_type or audience is missing
- Document.publish() now sets status: 'current' automatically
- upload-document.js requires --type and --category flags (was optional)
- upload-document.js sets status: 'current' and document_type on insert

Also archived 2 internal Phase 5 PoC session documents that were
incorrectly public, and set status: 'current' on 4 legitimate public
documents that were missing it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
TheFlow 2026-02-22 18:48:48 +13:00
parent 4c6c72847d
commit 4557f4b420
2 changed files with 40 additions and 1 deletions

View file

@ -41,8 +41,10 @@ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
Usage: node scripts/upload-document.js <markdown-file> [options] Usage: node scripts/upload-document.js <markdown-file> [options]
Options: Options:
--category <cat> Category: getting-started, technical-reference, research-theory, --category <cat> REQUIRED. Category: getting-started, technical-reference, research-theory,
advanced-topics, case-studies, business-leadership advanced-topics, case-studies, business-leadership
--type <type> REQUIRED. Document type: working-paper, case-study, technical-report,
guide, reference, brief
--audience <aud> Audience: general, researcher, implementer, leader, advocate, developer --audience <aud> Audience: general, researcher, implementer, leader, advocate, developer
--title <title> Override document title (extracted from H1 if not provided) --title <title> Override document title (extracted from H1 if not provided)
--author <author> Document author (default: Agentic Governance Research Team) --author <author> Document author (default: Agentic Governance Research Team)
@ -67,12 +69,14 @@ Examples:
# Upload research paper # Upload research paper
node scripts/upload-document.js docs/research/my-paper.md \\ node scripts/upload-document.js docs/research/my-paper.md \\
--category research-theory \\ --category research-theory \\
--type working-paper \\
--audience researcher \\ --audience researcher \\
--tags "ai-safety,governance,research" --tags "ai-safety,governance,research"
# Upload technical guide (no PDF) # Upload technical guide (no PDF)
node scripts/upload-document.js docs/guides/setup.md \\ node scripts/upload-document.js docs/guides/setup.md \\
--category getting-started \\ --category getting-started \\
--type guide \\
--audience developer \\ --audience developer \\
--no-pdf --no-pdf
@ -96,6 +100,7 @@ if (!mdFilePath) {
const options = { const options = {
category: null, category: null,
audience: 'general', audience: 'general',
document_type: null,
title: null, title: null,
author: 'Agentic Governance Research Team', author: 'Agentic Governance Research Team',
tags: [], tags: [],
@ -115,6 +120,9 @@ for (let i = 1; i < args.length; i++) {
case '--audience': case '--audience':
options.audience = args[++i]; options.audience = args[++i];
break; break;
case '--type':
options.document_type = args[++i];
break;
case '--title': case '--title':
options.title = args[++i]; options.title = args[++i];
break; break;
@ -481,6 +489,17 @@ async function uploadDocument() {
try { try {
console.log('\n=== Tractatus Document Upload ===\n'); console.log('\n=== Tractatus Document Upload ===\n');
// SECURITY: Require --category and --type for public documents
const validTypes = ['working-paper', 'case-study', 'technical-report', 'guide', 'reference', 'brief'];
if (!options.category) {
console.error('❌ Error: --category is required. Available: getting-started, resources, research-theory, technical-reference, advanced-topics, business-leadership');
process.exit(1);
}
if (!options.document_type || !validTypes.includes(options.document_type)) {
console.error(`❌ Error: --type is required. Available: ${validTypes.join(', ')}`);
process.exit(1);
}
// Verify markdown file exists // Verify markdown file exists
const mdPath = path.resolve(mdFilePath); const mdPath = path.resolve(mdFilePath);
try { try {
@ -559,7 +578,9 @@ async function uploadDocument() {
quadrant: null, quadrant: null,
persistence: 'HIGH', persistence: 'HIGH',
audience: options.audience, audience: options.audience,
document_type: options.document_type,
visibility: 'public', visibility: 'public',
status: 'current',
category: options.category, category: options.category,
licence: options.licence, licence: options.licence,
order: options.order, order: options.order,

View file

@ -214,6 +214,23 @@ class Document {
return { success: false, message: 'Document must have content before publishing' }; return { success: false, message: 'Document must have content before publishing' };
} }
// SECURITY: Require document_type for public docs
const docType = doc.document_type;
if (!docType) {
return {
success: false,
message: 'Document must have a document_type before publishing. Set document_type to one of: working-paper, case-study, technical-report, guide, reference, brief'
};
}
// SECURITY: Require audience for public docs
if (!doc.audience) {
return {
success: false,
message: 'Document must have an audience before publishing. Set audience to one of: researcher, implementer, leader, general'
};
}
// SECURITY: Require valid category for public docs // SECURITY: Require valid category for public docs
const category = options.category || doc.category; const category = options.category || doc.category;
if (!category || category === 'none') { if (!category || category === 'none') {
@ -241,6 +258,7 @@ class Document {
{ {
$set: { $set: {
visibility: 'public', visibility: 'public',
status: 'current',
category, category,
order: options.order !== undefined ? options.order : doc.order, order: options.order !== undefined ? options.order : doc.order,
workflow_status: 'published', workflow_status: 'published',