forked from yvv/decision
- Blueprint définitif (docs/dev/BLUEPRINT-V2.md + JSON) : panel 4 visions + 3 juges, corpus doctrinal (Mon nom est personne, Une économie du don, SejeteralO), 4 critiques d'intégration, 3 vérificateurs, réparations - Corpus d'intention + lectures brutes archivés - Moteur porté Python→TS pur : threshold (inertie WoT, Smith, ComTech), nuancé 6 niveaux, DSL modeParams — 45 tests vitest, parité croisée vérifiée (Forgeron W=7224 T=120 ⇒ 94) - Contrat de domaine v2 (types/domain.ts) + lexique UI (lexicon.ts) - vitest + idb-keyval en dépendances Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
/**
|
|
* 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)
|
|
}
|