v2 : conception ultracode + moteur de formules porté en TS

- 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>
This commit is contained in:
Yvv
2026-08-11 05:59:33 +02:00
co-authored by Claude Fable 5
parent 172eab4c7c
commit 53d8752e40
17 changed files with 5641 additions and 3 deletions
+110
View File
@@ -0,0 +1,110 @@
/**
* 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)
}