Skip to content

Undisclosed algorithmic bias: hardcoded country event multipliers (up to 10x) and arbitrary formula coefficients in CII risk scores #3725

Description

@tg12

Summary

computeStrategicRisks() in server/worldmonitor/intelligence/v1/get-risk-scores.ts produces a "global threat score" and per-country CII scores using hardcoded formula coefficients (* 0.7 + 15, positional decay 1 - i * 0.15, event multipliers up to 3.0×) that are never disclosed to users. The scores are presented as objective intelligence metrics with no methodological footnotes, making them impossible to reproduce or audit.

Affected code

1. Strategic risk formula — arbitrary linear transform

// get-risk-scores.ts
const score = Math.min(100, Math.round((weightedSum / totalWeight) * 0.7 + 15));

The * 0.7 dampening factor and + 15 floor are not documented anywhere. They ensure the minimum output is 15 and the maximum weighted sum maps to 85, compressing the entire scale into a 15–85 band. Users see a number out of 100 with no indication it can never fall below 15.

2. Global threat score — undisclosed positional decay

const sorted = scores.sort((a, b) => b.score - a.score).slice(0, 5);
const globalScore = sorted.reduce((sum, s, i) => {
  return sum + s.score * (1 - i * 0.15);
}, 0) / sorted.reduce((sum, _, i) => sum + (1 - i * 0.15), 0);

The top-5 country scores are weighted by 1 - i * 0.15 (rank 1 = 1.0×, rank 5 = 0.40×). This decay is purely positional — it has no stated geopolitical rationale and is not disclosed.

3. Per-country event multipliers — undisclosed political judgment

const EVENT_MULTIPLIER: Record<string, number> = {
  US: 0.3, RU: 2.0, CN: 2.5, UA: 0.8, IR: 2.0, IL: 0.7, TW: 1.5, KP: 3.0, ...
};

The same number of ACLED battle events in Ukraine produces a CII score 10× higher for North Korea than for the United States (KP = 3.0×, US = 0.3×). These multipliers encode subjective political threat assessments as hidden formula constants. ACLED event counts for China produce 8× the score impact vs. equal events in Ukraine.

4. Baseline risk — hardcoded with no source

const BASELINE_RISK: Record<string, number> = {
  US: 5, RU: 35, CN: 25, UA: 50, IR: 40, IL: 45, TW: 30, KP: 45, ...
};

These are political judgments embedded as code constants with no attribution to any published risk framework (e.g. Global Peace Index, IISS Military Balance, Freedom House).

Impact

Users relying on CII scores for operational decisions (e.g. travel safety, investment risk, security posture) cannot:

  • Reproduce a score from raw ACLED data
  • Know the score has a hard floor of 15
  • Know China events are amplified 8× vs. Ukraine events
  • Know the global score over-weights the single highest-risk country

This is undisclosed algorithmic bias in a product marketed as objective intelligence.

Suggested remediation

  1. Add a methodology disclosure page explaining baseline values, event multipliers, formula coefficients, and their sources or rationale.
  2. Add a methodology field to the API response documenting the formula version and key parameters used.
  3. Separate editorially-assigned multipliers from empirically-derived weights and label them accordingly in the UI.
  4. Expose the BASELINE_RISK and EVENT_MULTIPLIER tables as configuration (or at minimum as documented constants in a methodology file) rather than inline code literals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions