tractatus/src/models/GovernanceRule.model.js
TheFlow 29f50124b5 fix: MongoDB persistence and inst_016-018 content validation enforcement
This commit implements critical fixes to stabilize the MongoDB persistence layer
and adds inst_016-018 content validation to BoundaryEnforcer as specified in
instruction history.

## Context
- First session using Anthropic's new API Memory system
- Fixed 3 MongoDB persistence test failures
- Implemented BoundaryEnforcer inst_016-018 trigger logic per user request
- All unit tests now passing (61/61 BoundaryEnforcer, 25/25 BlogCuration)

## Fixes

### 1. CrossReferenceValidator: Port Regex Enhancement
- **File**: src/services/CrossReferenceValidator.service.js:203
- **Issue**: Regex couldn't extract port from "port 27017" (space-delimited format)
- **Fix**: Changed `/port[:=]\s*(\d{4,5})/i` to `/port[:\s=]\s*(\d{4,5})/i`
- **Result**: Now matches "port: X", "port = X", and "port X" formats
- **Tests**: 28/28 CrossReferenceValidator tests passing

### 2. BlogCuration: MongoDB Method Correction
- **File**: src/services/BlogCuration.service.js:187
- **Issue**: Called non-existent `Document.findAll()` method
- **Fix**: Changed to `Document.list({ limit: 20, skip: 0 })`
- **Result**: BlogCuration can now fetch existing documents for topic generation
- **Tests**: 25/25 BlogCuration tests passing

### 3. MemoryProxy: Optional Anthropic API Integration
- **File**: src/services/MemoryProxy.service.js
- **Issue**: Treated Anthropic Memory Tool API as mandatory, causing errors without API key
- **Fix**: Made Anthropic client optional with graceful degradation
- **Architecture**: MongoDB (required) + Anthropic API (optional enhancement)
- **Result**: System functions fully without CLAUDE_API_KEY environment variable

### 4. AuditLog Model: Duplicate Index Fix
- **File**: src/models/AuditLog.model.js:132
- **Issue**: Mongoose warning about duplicate timestamp index
- **Fix**: Removed inline `index: true`, kept TTL index definition at line 149
- **Result**: No more Mongoose duplicate index warnings

### 5. BlogCuration Tests: Mock API Correction
- **File**: tests/unit/BlogCuration.service.test.js
- **Issue**: Tests mocked non-existent `generateBlogTopics()` function
- **Fix**: Updated mocks to use actual `sendMessage()` and `extractJSON()` methods
- **Result**: All 25 BlogCuration tests passing

## New Features

### 6. BoundaryEnforcer: inst_016-018 Content Validation (MAJOR)
- **File**: src/services/BoundaryEnforcer.service.js:508-580
- **Purpose**: Prevent fabricated statistics, absolute guarantees, and unverified claims
- **Implementation**: Added `_checkContentViolations()` private method
- **Enforcement Rules**:
  - **inst_017**: Blocks absolute assurance terms (guarantee, 100% secure, never fails)
  - **inst_016**: Blocks statistics/ROI/$ amounts without sources
  - **inst_018**: Blocks production claims (production-ready, battle-tested) without evidence
- **Mechanism**: All violations classified as VALUES boundary violations (honesty/transparency)
- **Tests**: 22 new comprehensive tests in tests/unit/BoundaryEnforcer.test.js
- **Result**: 61/61 BoundaryEnforcer tests passing

### Regex Pattern for inst_016 (Statistics Detection):
```regex
/\d+(\.\d+)?%|\$[\d,]+|\d+x\s*roi|payback\s*(period)?\s*of\s*\d+|\d+[\s-]*(month|year)s?\s*payback|\d+(\.\d+)?m\s*(saved|savings)/i
```

### Detection Examples:
-  BLOCKS: "This system guarantees 100% security"
-  BLOCKS: "Delivers 1315% ROI without sources"
-  BLOCKS: "Production-ready framework" (without testing_evidence)
-  ALLOWS: "Research shows 85% improvement [source: example.com]"
-  ALLOWS: "Validated framework with testing_evidence provided"

## MongoDB Models (New Files)
- src/models/AuditLog.model.js - Audit log persistence with TTL
- src/models/GovernanceRule.model.js - Governance rules storage
- src/models/SessionState.model.js - Session state tracking
- src/models/VerificationLog.model.js - Verification logs
- src/services/AnthropicMemoryClient.service.js - Optional API integration

## Test Results
- BoundaryEnforcer: 61/61 tests passing (22 new inst_016-018 tests)
- BlogCuration: 25/25 tests passing
- CrossReferenceValidator: 28/28 tests passing

## Framework Compliance
-  Implements inst_016, inst_017, inst_018 enforcement
-  Addresses 2025-10-09 framework failure (fabricated statistics on leader.html)
-  All content generation now subject to honesty/transparency validation
-  Human approval required for statistical claims without sources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 00:17:03 +13:00

300 lines
6.8 KiB
JavaScript

/**
* GovernanceRule Model
*
* Stores Tractatus governance instructions (inst_001, inst_016, etc.)
* Replaces filesystem-based .claude/instruction-history.json
*
* Benefits over filesystem:
* - Fast indexed queries by ID, quadrant, persistence
* - Atomic updates (no race conditions)
* - Aggregation for analytics
* - Built-in replication/backup
* - Transaction support
*/
const mongoose = require('mongoose');
const governanceRuleSchema = new mongoose.Schema({
// Rule identification
id: {
type: String,
required: true,
unique: true,
index: true,
description: 'Unique rule identifier (e.g., inst_016, inst_017)'
},
// Rule content
text: {
type: String,
required: true,
description: 'The governance instruction text'
},
// Classification
quadrant: {
type: String,
required: true,
enum: ['STRATEGIC', 'OPERATIONAL', 'TACTICAL', 'SYSTEM', 'STORAGE'],
index: true,
description: 'Tractatus quadrant classification'
},
persistence: {
type: String,
required: true,
enum: ['HIGH', 'MEDIUM', 'LOW'],
index: true,
description: 'Persistence level - how long this rule remains active'
},
// Metadata
category: {
type: String,
enum: ['content', 'security', 'privacy', 'technical', 'process', 'values', 'other'],
default: 'other',
index: true,
description: 'Category for filtering and organization'
},
priority: {
type: Number,
default: 50,
min: 0,
max: 100,
description: 'Priority level (100 = highest, 0 = lowest)'
},
// Temporal scope
temporalScope: {
type: String,
enum: ['IMMEDIATE', 'SESSION', 'PROJECT', 'PERMANENT'],
default: 'PERMANENT',
description: 'How long this rule applies (IMMEDIATE = one-time, SESSION = this conversation, PROJECT = this project, PERMANENT = always)'
},
expiresAt: {
type: Date,
default: null,
description: 'When this rule expires (null = never)'
},
// Status
active: {
type: Boolean,
default: true,
index: true,
description: 'Whether this rule is currently enforced'
},
// Source tracking
source: {
type: String,
enum: ['user_instruction', 'framework_default', 'automated', 'migration', 'test'],
default: 'framework_default',
description: 'How this rule was created'
},
createdBy: {
type: String,
default: 'system',
description: 'Who created this rule'
},
// Enforcement statistics
stats: {
timesChecked: {
type: Number,
default: 0,
description: 'How many times this rule has been evaluated'
},
timesViolated: {
type: Number,
default: 0,
description: 'How many times this rule was violated'
},
lastChecked: {
type: Date,
default: null,
description: 'When this rule was last evaluated'
},
lastViolated: {
type: Date,
default: null,
description: 'When this rule was last violated'
}
},
// Additional context
examples: {
type: [String],
default: [],
description: 'Example scenarios where this rule applies'
},
relatedRules: {
type: [String],
default: [],
description: 'IDs of related rules (e.g., inst_016 relates to inst_017)'
},
notes: {
type: String,
default: '',
description: 'Additional notes or clarifications'
}
}, {
timestamps: true, // Adds createdAt and updatedAt automatically
collection: 'governanceRules'
});
// Indexes for common queries
governanceRuleSchema.index({ quadrant: 1, persistence: 1 });
governanceRuleSchema.index({ active: 1, priority: -1 });
governanceRuleSchema.index({ category: 1, active: 1 });
governanceRuleSchema.index({ expiresAt: 1 }, { sparse: true }); // Sparse index for expiry queries
// Virtual for checking if rule is expired
governanceRuleSchema.virtual('isExpired').get(function() {
if (!this.expiresAt) return false;
return new Date() > this.expiresAt;
});
// Static methods
/**
* Find all active rules
*/
governanceRuleSchema.statics.findActive = function(options = {}) {
const query = { active: true };
// Filter out expired rules
query.$or = [
{ expiresAt: null },
{ expiresAt: { $gt: new Date() } }
];
return this.find(query)
.sort({ priority: -1, id: 1 })
.limit(options.limit || 0);
};
/**
* Find rules by quadrant
*/
governanceRuleSchema.statics.findByQuadrant = function(quadrant, activeOnly = true) {
const query = { quadrant };
if (activeOnly) {
query.active = true;
query.$or = [
{ expiresAt: null },
{ expiresAt: { $gt: new Date() } }
];
}
return this.find(query).sort({ priority: -1, id: 1 });
};
/**
* Find rules by persistence level
*/
governanceRuleSchema.statics.findByPersistence = function(persistence, activeOnly = true) {
const query = { persistence };
if (activeOnly) {
query.active = true;
query.$or = [
{ expiresAt: null },
{ expiresAt: { $gt: new Date() } }
];
}
return this.find(query).sort({ priority: -1, id: 1 });
};
/**
* Find rule by ID
*/
governanceRuleSchema.statics.findByRuleId = function(ruleId) {
return this.findOne({ id: ruleId, active: true });
};
/**
* Get rule statistics summary
*/
governanceRuleSchema.statics.getStatistics = async function() {
const stats = await this.aggregate([
{ $match: { active: true } },
{
$group: {
_id: null,
totalRules: { $sum: 1 },
byQuadrant: {
$push: {
quadrant: '$quadrant',
count: 1
}
},
byPersistence: {
$push: {
persistence: '$persistence',
count: 1
}
},
totalChecks: { $sum: '$stats.timesChecked' },
totalViolations: { $sum: '$stats.timesViolated' }
}
}
]);
return stats[0] || null;
};
/**
* Increment check counter
*/
governanceRuleSchema.methods.incrementChecked = async function() {
this.stats.timesChecked += 1;
this.stats.lastChecked = new Date();
return this.save();
};
/**
* Increment violation counter
*/
governanceRuleSchema.methods.incrementViolated = async function() {
this.stats.timesViolated += 1;
this.stats.lastViolated = new Date();
return this.save();
};
/**
* Deactivate rule (soft delete)
*/
governanceRuleSchema.methods.deactivate = async function() {
this.active = false;
return this.save();
};
/**
* Activate rule
*/
governanceRuleSchema.methods.activate = async function() {
this.active = true;
return this.save();
};
// Pre-save hook to validate expiration
governanceRuleSchema.pre('save', function(next) {
// If expiresAt is in the past, deactivate the rule
if (this.expiresAt && this.expiresAt < new Date()) {
this.active = false;
}
next();
});
const GovernanceRule = mongoose.model('GovernanceRule', governanceRuleSchema);
module.exports = GovernanceRule;