Skip to main content
Glama

prevention_rules

Generate rules to prevent repeated mistakes by analyzing patterns of errors and creating actionable guidelines for improvement.

Instructions

Generate prevention rules from repeated mistake patterns

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
minOccurrencesNo
outputPathNo

Implementation Reference

  • The implementation of the `prevention_rules` tool handler, which triggers the generation and registration of prevention rules.
    function writePreventionRules(filePath, minOccurrences = 2) {
      const { PREVENTION_RULES_PATH } = getFeedbackPaths();
      const outPath = filePath || PREVENTION_RULES_PATH;
      const markdown = buildPreventionRules(minOccurrences);
      ensureDir(path.dirname(outPath));
      fs.writeFileSync(outPath, `${markdown}\n`);
    
      const contextFs = getContextFsModule();
      if (contextFs && typeof contextFs.registerPreventionRules === 'function') {
        try {
          contextFs.registerPreventionRules(markdown, { minOccurrences, outputPath: outPath });
        } catch {
          // Non-critical
        }
      }
      return { path: outPath, markdown };
    }
  • The logic for building the prevention rules content based on logged mistakes and diagnostic entries.
    function buildPreventionRules(minOccurrences = 2, options = {}) {
      const { MEMORY_LOG_PATH, DIAGNOSTIC_LOG_PATH } = getFeedbackPaths();
      const memories = readJSONL(MEMORY_LOG_PATH).filter((m) => m.category === 'error');
      const diagnosticEntries = readDiagnosticEntries(DIAGNOSTIC_LOG_PATH);
      if (memories.length === 0) {
        if (diagnosticEntries.length === 0) {
          return '# Prevention Rules\n\nNo mistake memories recorded yet.';
        }
      }
    
      // Time-weighted decay: recent mistakes count more (#202)
      const decayHalfLifeDays = options.decayHalfLifeDays || 7;
      const lambda = Math.LN2 / decayHalfLifeDays;
      const now = Date.now();
    
      function decayWeight(memory) {
        const ts = memory.timestamp ? new Date(memory.timestamp).getTime() : now;
        const daysSince = (now - ts) / (24 * 60 * 60 * 1000);
        return Math.exp(-lambda * daysSince);
      }
    
      const buckets = {};
      const rubricBuckets = {};
      const diagnosisBuckets = {};
      const repeatedViolationBuckets = {};
      for (const m of memories) {
        const key = (m.tags || []).find((t) => !['feedback', 'negative', 'positive'].includes(t)) || 'general';
        if (!buckets[key]) buckets[key] = { items: [], weightedCount: 0 };
        const w = decayWeight(m);
        buckets[key].items.push(m);
        buckets[key].weightedCount += w;
    
        const failed = m.rubricSummary && Array.isArray(m.rubricSummary.failingCriteria)
          ? m.rubricSummary.failingCriteria
          : [];
        failed.forEach((criterion) => {
          if (!rubricBuckets[criterion]) rubricBuckets[criterion] = [];
          rubricBuckets[criterion].push(m);
        });
    
        if (m.diagnosis && m.diagnosis.rootCauseCategory) {
          if (!diagnosisBuckets[m.diagnosis.rootCauseCategory]) diagnosisBuckets[m.diagnosis.rootCauseCategory] = [];
          diagnosisBuckets[m.diagnosis.rootCauseCategory].push(m);
        }
    
        (m.diagnosis && Array.isArray(m.diagnosis.violations) ? m.diagnosis.violations : []).forEach((violation) => {
          const key = violation.constraintId || violation.message;
          if (!key) return;
          if (!repeatedViolationBuckets[key]) repeatedViolationBuckets[key] = [];
          repeatedViolationBuckets[key].push(m);
        });
      }
    
      for (const entry of diagnosticEntries) {
        const diagnosis = entry && entry.diagnosis ? entry.diagnosis : null;
        if (!diagnosis || !diagnosis.rootCauseCategory) continue;
        if (!diagnosisBuckets[diagnosis.rootCauseCategory]) diagnosisBuckets[diagnosis.rootCauseCategory] = [];
        diagnosisBuckets[diagnosis.rootCauseCategory].push(entry);
    
        (Array.isArray(diagnosis.violations) ? diagnosis.violations : []).forEach((violation) => {
          const key = violation.constraintId || violation.message;
          if (!key) return;
          if (!repeatedViolationBuckets[key]) repeatedViolationBuckets[key] = [];
          repeatedViolationBuckets[key].push(entry);
        });
      }
    
      const lines = ['# Prevention Rules', '', 'Generated from negative feedback memories (time-weighted, half-life: ' + decayHalfLifeDays + 'd).'];
    
      Object.entries(buckets)
        .sort((a, b) => b[1].weightedCount - a[1].weightedCount)
        .forEach(([domain, { items, weightedCount }]) => {
          const effectiveOccurrences = Math.round(weightedCount);
          if (effectiveOccurrences < minOccurrences) return;
          const latest = items[items.length - 1];
          const avoid = (latest.content || '').split('\n').find((l) => l.toLowerCase().startsWith('how to avoid:')) || 'How to avoid: Investigate and prevent recurrence';
          lines.push('');
          lines.push(`## ${domain}`);
          lines.push(`- Recurrence count: ${items.length} (weighted: ${weightedCount.toFixed(1)})`);
          lines.push(`- Rule: ${avoid.replace(/^How to avoid:\s*/i, '')}`);
          lines.push(`- Latest mistake: ${latest.title}`);
        });
    
      const rubricEntries = Object.entries(rubricBuckets)
        .sort((a, b) => b[1].length - a[1].length)
        .filter(([, items]) => items.length >= minOccurrences);
      if (rubricEntries.length > 0) {
        lines.push('');
        lines.push('## Rubric Failure Dimensions');
        rubricEntries.forEach(([criterion, items]) => {
          lines.push(`- ${criterion}: ${items.length} failures`);
        });
      }
    
      const diagnosisEntries = Object.entries(diagnosisBuckets)
        .sort((a, b) => b[1].length - a[1].length)
        .filter(([, items]) => items.length >= minOccurrences);
      if (diagnosisEntries.length > 0) {
        lines.push('');
        lines.push('## Root Cause Categories');
        diagnosisEntries.forEach(([category, items]) => {
          lines.push(`- ${category}: ${items.length} failures`);
        });
      }
    
      const repeatedViolationEntries = Object.entries(repeatedViolationBuckets)
        .sort((a, b) => b[1].length - a[1].length)
        .filter(([, items]) => items.length >= minOccurrences);
      if (repeatedViolationEntries.length > 0) {
        lines.push('');
        lines.push('## Repeated Failure Constraints');
        repeatedViolationEntries.forEach(([constraintId, items]) => {
          lines.push(`- ${constraintId}: ${items.length} failures`);
        });
      }
    
      if (lines.length === 3) {
        lines.push('');
        lines.push(`No domain has reached the threshold (${minOccurrences}) yet.`);
      }
    
      return lines.join('\n');
    }
  • The tool registration/dispatch handler in the MCP server adapter that calls `writePreventionRules`.
    case 'prevention_rules': {
      const outputPath = args.outputPath ? resolveSafePath(args.outputPath) : undefined;
      return toTextResult(writePreventionRules(outputPath, Number(args.minOccurrences || 2)));
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/IgorGanapolsky/mcp-memory-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server