All files / src memory-policy.ts

100% Statements 16/16
100% Branches 5/5
100% Functions 2/2
100% Lines 16/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72                              1x                 1x                     1x 9x 9x 32x 6x           3x             1x 8x 25x 5x           3x 11x 1x           2x    
/**
 * 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 };
}