// Weighted scoring composition for ranked candidates (Epic #177, Issue #182).
// Pure function: signal vector × weight vector → clamped unit score. The default positive
// weights sum to 0.95 and the generated penalty weight is 0.30; the filter layer's
// `omitGenerated` default keeps generated files OUT of the kept set regardless of score,
// so the scoring penalty is a secondary defence (a fully-positive generated file scores
// 0.65). Callers may override weights to tune ring-specific behaviour; never uses
// parseFloat or .toFixed.
export const DEFAULT_SCORING_WEIGHTS = {
    provenanceBestScore: 0.35,
    provenanceCount: 0.1,
    anchorOverlap: 0.25,
    pathDepthAffinity: 0.1,
    testPairBonus: 0.1,
    stacktracePositionBonus: 0.05,
    generatedPenalty: 0.3,
};
const SIGNAL_WEIGHT_KEYS = {
    "provenance-best-score": "provenanceBestScore",
    "provenance-count": "provenanceCount",
    "anchor-overlap": "anchorOverlap",
    "path-depth-affinity": "pathDepthAffinity",
    "test-pair-bonus": "testPairBonus",
    "stacktrace-position-bonus": "stacktracePositionBonus",
    "generated-penalty": "generatedPenalty",
};
function clampUnit(value) {
    return Math.max(0, Math.min(1, value));
}
export function computeScore(signals, weights = DEFAULT_SCORING_WEIGHTS) {
    let raw = 0;
    for (const signal of signals.signals) {
        const key = SIGNAL_WEIGHT_KEYS[signal.name];
        if (key === undefined) {
            continue;
        }
        raw += signal.value * weights[key];
    }
    return clampUnit(raw);
}
