/** * WoT members threshold formula for binary votes. * * Exact TypeScript port of backend/app/engine/threshold.py, * smith_threshold.py and techcomm_threshold.py (Python is the * reference implementation — same guards, same ceil rounding). * * Core formula: * Result = C + B^W + (M + (1-M) * (1 - (T/W)^G)) * max(0, T - C) * * Where: * C = constantBase * B = baseExponent * W = wotSize (corpus of eligible voters) * T = totalVotes (for + against) * M = majorityRatio (majorityPct / 100) * G = gradientExponent * * Inertia behaviour: * - Low participation (T << W) -> near-unanimity required * - High participation (T -> W) -> simple majority M suffices * * Reference test case: * wotSize=7224, votesFor=97, votesAgainst=23 (total=120) * params M50 B.1 G.2 => threshold=94, adopted (97 >= 94) */ /** * Compute the minimum number of *for* votes required for adoption. * * A vote passes when `votesFor >= wotThreshold(...)`. * * @param wotSize - Size of the eligible voter corpus (WoT members) * @param totalVotes - Number of votes cast (for + against) * @param majorityPct - Majority percentage (0-100). 50 = simple majority at full participation * @param baseExponent - B in the formula; B^W is a vanishingly small offset when W is large * @param gradientExponent - G controls how fast the super-majority decays toward M * @param constantBase - C, a fixed additive floor on the threshold * @returns The ceiling of the computed threshold */ export function wotThreshold( wotSize: number, totalVotes: number, majorityPct: number = 50, baseExponent: number = 0.1, gradientExponent: number = 0.2, constantBase: number = 0.0, ): number { if (wotSize <= 0) { throw new Error('wotSize doit etre strictement positif') } if (totalVotes < 0) { throw new Error('totalVotes ne peut pas etre negatif') } if (majorityPct < 0 || majorityPct > 100) { throw new Error('majorityPct doit etre entre 0 et 100') } const C = constantBase const B = baseExponent const W = wotSize const T = totalVotes const M = majorityPct / 100.0 const G = gradientExponent // Guard: if no votes, threshold is at least ceil(C + B^W) if (T === 0) { return Math.ceil(C + B ** W) } // Core formula const participationRatio = T / W const inertiaFactor = 1.0 - participationRatio ** G const requiredRatio = M + (1.0 - M) * inertiaFactor const result = C + B ** W + requiredRatio * Math.max(0.0, T - C) return Math.ceil(result) } /** * Smith sub-WoT threshold criterion. * * Formula: ceil(smithWotSize ^ S) * * @param smithWotSize - Number of active Smith members (forgerons) * @param exponent - S in the formula * @returns Minimum Smith votes required */ export function smithThreshold(smithWotSize: number, exponent: number = 0.1): number { if (smithWotSize <= 0) { throw new Error('smithWotSize doit etre strictement positif') } return Math.ceil(smithWotSize ** exponent) } /** * Technical Committee threshold criterion. * * Formula: ceil(cotecSize ^ T) * * @param cotecSize - Number of Technical Committee members * @param exponent - T in the formula * @returns Minimum TechComm votes required */ export function techcommThreshold(cotecSize: number, exponent: number = 0.1): number { if (cotecSize <= 0) { throw new Error('cotecSize doit etre strictement positif') } return Math.ceil(cotecSize ** exponent) }