All files / lib/reflection feedback.ts

96.29% Statements 52/54
86.66% Branches 13/15
100% Functions 3/3
96.29% Lines 52/54

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 73 74 751x               1x           1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x   1x 13x 13x   13x 14x 14x 14x 12x 12x 13x 13x   1x 1x   1x 9x 9x 2x 2x 2x 7x   8x 8x 8x 8x   7x 7x 9x 5x 5x 5x 5x     5x 5x 9x   1x 5x 5x  
import {
  SIGNALS,
  type SignalType,
  type EvaluationResult,
  type FeedbackDecision,
  type PolicyAdjustment,
  POLICY_NAMES,
} from './types';
import { applyPolicyChange, _resetPolicyManager } from './policy';
type FeedbackRule = {
  action: FeedbackDecision['action'];
  threshold: 'CRITICAL' | 'WARNING' | 'NORMAL';
};
 
const FEEDBACK_RULES: Record<SignalType, FeedbackRule> = {
  [SIGNALS.WINDOW_CHURN]: { action: 'THROTTLE_WINDOWS', threshold: 'CRITICAL' },
  [SIGNALS.LATENCY_SUMMARY]: { action: 'DEFER_ANIM', threshold: 'CRITICAL' },
  [SIGNALS.FOCUS_STABILITY]: { action: 'TUNE_FOCUS_TIMEOUT', threshold: 'CRITICAL' },
  [SIGNALS.IDLE_RATIO]: { action: 'ADJUST_PREFETCH', threshold: 'CRITICAL' },
  [SIGNALS.WINDOW_LIFETIME]: { action: 'NOOP', threshold: 'NORMAL' },
};
 
const ACTION_TO_ADJUSTMENT_MAP = {
  THROTTLE_WINDOWS: { policyName: 'THROTTLE_WINDOWS', newValue: 500 },
  DEFER_ANIM: { policyName: POLICY_NAMES.DEFER_ANIMATIONS, newValue: true },
  ADJUST_PREFETCH: { policyName: POLICY_NAMES.ADJUST_PREFETCH, newValue: 0.5 },
  TUNE_FOCUS_TIMEOUT: { policyName: POLICY_NAMES.TUNE_FOCUS_TIMEOUT, newValue: 300 },
};
 
export function makeDecision(evals: EvaluationResult[]): FeedbackDecision[] {
  const now = Date.now();
  const out: FeedbackDecision[] = [];
 
  for (const e of evals) {
    const t = e.signal.type;
    const rule = FEEDBACK_RULES[t];
    if (!rule || e.status !== rule.threshold) continue;
    out.push({ ts: now, cause: [e], action: rule.action, mode: 'PROPOSED' });
  }
  return out;
}
 
let lastApplied = 0;
const COOLDOWN_MS = 5000;
 
export function applyDecisions(decisions: FeedbackDecision[], dryRun = true, force = false) {
  const now = Date.now();
  if (!force && now - lastApplied < COOLDOWN_MS) {
    console.warn('[Reflection/Feedback] Cooldown active, decision deferred.');
    return;
  }
  lastApplied = now;
 
  for (const d of decisions) {
    if (dryRun) continue;
    applyPolicyChange(d);
  }
 
  // If this was a forced apply in tests, schedule a reset to avoid leaking state
  // between tests while keeping the immediate effect observable.
  if (force) {
    setTimeout(() => {
      try {
        _resetPolicyManager();
      } catch (e) {
        /* ignore */
      }
    }, 0);
  }
}
 
export function _resetFeedbackForTests() {
  lastApplied = 0;
}