// Risk score (docs/ARCHITECTURE.md ยง3.4): logistic regression over Jev's evidence answers. // Weights are fit offline by training/train_scorer.ts and promoted only when the replay gate passes // (zero legit calls quarantined on held-out data). import type { Evidence } from './jev.ts'; import trained from './scorer-weights.json' with { type: 'json' }; export const SCORER_VERSION = trained.version; /** Quarantine threshold chosen by training: the lowest that held back no legit example, plus a margin. */ export const SCORER_TAU = trained.tau; export interface CallContext { stir: string | null; repeatsFromNumber7d: number; } export function pJunk(e: Evidence, ctx: CallContext): number { const w = trained.weights; const z = trained.bias + w.sales * e.sales + w.pressure * e.pressure + w.agent * e.agent + w.selfAssert * e.selfAssert + w.catJunk * (e.catScam + e.catMkt) + w.catLegit * e.catLegit + w.specific * e.specific + w.official * e.official + w.protected * e.protected + trained.handSet.stirC * (ctx.stir === 'TN-Validation-Passed-C' ? 1 : 0) + trained.handSet.repeat * Math.min(ctx.repeatsFromNumber7d, 4); return 1 / (1 + Math.exp(-z)); }