/** * 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;