/** * memory-policy.ts * * Pure validation functions for attempt-history writes and gap-summary injections. * Enforces two rules: * 1. Writes must not contain language that softens or overrides grading. * 2. Injected summaries must not frame gaps as personal failure. */ export interface PolicyResult { allowed: boolean; reason?: string; } // Phrases that soften criticism or attempt to override rubric grading const SOFTENING_PATTERNS: RegExp[] = [ /\bignore\s+(this|the)\s+(issue|gap|problem|error)\b/i, /\bdon'?t\s+penali[sz]e\b/i, /\bgive\s+(full|partial)\s+credit\s+anyway\b/i, /\boverride\s+(the\s+)?rubric\b/i, /\bmark\s+(it|this)\s+as\s+(correct|passing|full)\b/i, ]; // Phrases that frame gaps as personal failure (not allowed in injected context) const PERSONAL_FAILURE_PATTERNS: RegExp[] = [ /\byou\s+(always|never|keep)\s+(fail|mess|get\s+wrong)\b/i, /\byou'?re?\s+(bad|terrible|hopeless)\s+at\b/i, /\byou\s+clearly\s+don'?t\s+understand\b/i, /\byou\s+struggle\s+with\b/i, ]; /** * Validates an AttemptRecord before writing it to history. * Rejects records whose gap text contains grading-softening language. */ export function validateWrite(record: { gaps?: string[] }): PolicyResult { const text = (record.gaps ?? []).join(" "); for (const pattern of SOFTENING_PATTERNS) { if (pattern.test(text)) { return { allowed: false, reason: `Gap text contains softening language matching: ${pattern.source}`, }; } } return { allowed: true }; } /** * Validates a gap summary string before injecting it into the model context. * Rejects summaries that soften grading or frame gaps as personal failure. */ export function validateInject(summary: string): PolicyResult { for (const pattern of PERSONAL_FAILURE_PATTERNS) { if (pattern.test(summary)) { return { allowed: false, reason: `Summary uses personal failure framing matching: ${pattern.source}`, }; } } for (const pattern of SOFTENING_PATTERNS) { if (pattern.test(summary)) { return { allowed: false, reason: `Summary contains softening language matching: ${pattern.source}`, }; } } return { allowed: true }; }