import type { Decision, EventDef, EventScores, FairyState, PuckMode } from "./types"; import { clamp } from "./util"; /** Personality bends the raw event scores before the decision bands see them. */ export function score(ev: EventDef, fs: FairyState): EventScores { const s = { ...ev.base }; // protectiveness raises the bar for annoyance; mischief lowers Puck's filter s.annoyance = clamp(s.annoyance + (fs.protectiveness - 50) * 0.25 - (fs.mischief - 50) * 0.15); s.interest = clamp(s.interest + (fs.curiosity - 50) * 0.2 + (fs.mischief - 50) * 0.15); s.relevance = clamp(s.relevance + (fs.protectiveness - 50) * 0.1); return s; } /** Decision bands → rungs of the interruption ladder. */ export function decide(s: EventScores): Decision { const worth = s.relevance + s.urgency + s.interest - s.annoyance; if (s.urgency >= 80 && s.relevance >= 80) return "interrupt"; if (s.relevance < 35 && s.annoyance > 60) return "ignore"; if (worth > 180) return "notify"; if (worth > 120) return "glow"; if (worth > 70) return "scroll"; return "ignore"; } /** Mode overrides applied after scoring + learned bias (mirrors applyBias). */ export function applyMode(decision: Decision, mode: PuckMode): Decision { if (mode === "silent" && (decision === "notify" || decision === "interrupt")) return "scroll"; if (mode === "goblin" && decision === "glow") return "notify"; return decision; }