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
| Name | Required | Description | Default |
|---|---|---|---|
| minOccurrences | No | ||
| outputPath | No |
Implementation Reference
- scripts/feedback-loop.js:1097-1113 (handler)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 }; } - scripts/feedback-loop.js:973-1095 (helper)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'); } - adapters/mcp/server-stdio.js:384-387 (handler)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))); }