18 KiB
VERDICT BUGS AUDIT — Verified with Redis + PostgreSQL Data
Data: 2026-02-26 Status: Auditat si confirmat cu date reale din Redis si PostgreSQL Fisiere afectate:
src/components/pipeline/verdict-calculator.ts— buguri #1, #2, #6, #7src/components/techniques/executor.ts— bug #3src/components/claims/executor.ts— bug #4src/config/analysisLimits.ts— context pentru bug #4 (MIN_CLAIMS_TEXT_LENGTH = 100)
Sesiuni de referinta (din PostgreSQL bos_analysis)
Sesiunea 7cc2cca8 — DOVADA BUG #1
risk_score = 90 | risk_category = RELIABLE | risk_level = CRITICAL | severity = CRITICAL
score_manipulation = 87 | score_claims = 75 | score_ai = 0 | score_source = NULL
override_applied = true | override_type = SYNERGY | override_adjustment = 20
override_reason = "2 components with high risk (+10%); 2 severe techniques (+10%)"
input_text = "UK and France plan to send nuclear weapons to Ukraine to fight Russian..."
components_run = {ai_tampered, techniques, claims}
Sesiunea d07266ca — DOVADA BUG #3
risk_score = 68 | risk_category = QUESTIONABLE | risk_level = HIGH
manipulation_score = 100 | techniques_count = 1 | total_severity = 60 | dimensions_affected = {D8}
credibility_score = 50 | total_claims = 2 | verified_false = 0
input_text = "In februarie 2026 in bucuresti a nins masiv si la 4 dimineata autoritatile au tr..."
Sesiunea 2adf7ace — DOVADA BUG #4 si #5
risk_score = 0 | risk_category = RELIABLE | risk_level = VERY_LOW
score_manipulation = 0 | score_claims = NULL | score_ai = 0 | score_source = NULL
manipulation_score = 0 | techniques_count = 0 | dimensions_affected = {D7, D8}
credibility_score = NULL | interpretation = "Text too short for claims analysis (54 chars)"
input_text = "Uk and France plan to send nuclear weapons to ukraine." (54 chars)
Sesiunea 4fa38b53 — Context BUG #3 si LLM false positives
risk_score = 58 | risk_category = QUESTIONABLE | risk_level = HIGH | severity = MEDIUM
manipulation_score = 100 | techniques_count = 3 | total_severity = 240 | dimensions_affected = {D1}
credibility_score = 92 | total_claims = 10 | verified_true = 9 | verified_false = 0
override_applied = true | override_adjustment = 10 (CRITICAL: severe techniques)
input_type = url | input_text = "Title: Nancy Guthrie's family offers $1m reward..."
score_source = 30 (domain trusted)
Sesiunea ee58753d — DOVADA BUG #2
risk_score = 80 | risk_category = UNRELIABLE | risk_level = VERY_HIGH | severity = MEDIUM
manipulation_score = 100 | techniques_count = 13 | total_severity = 820 | dimensions_affected = {D1,D3,D7,D8}
credibility_score = 45 | total_claims = 7 | verified_false = 1
override_applied = true | override_adjustment = 10
input_text = "Un studiu secret al Universitatii Harvard arata ca 80% dintre romani vor ramane..."
Severity=MEDIUM la risk_score=80 e gresit. Conform severity_assessment: 80 e in range CRITICAL (80-100). Floatul a fost probabil ~79.5 care a cazut in gap intre HIGH (60-79) si CRITICAL (80-100).
Date framework din Redis SI PostgreSQL (identice in ambele)
Verdict Categories (bos_parammgmt.verdict_category)
RELIABLE start=0 end=15 color=green
MOSTLY_RELIABLE start=16 end=30 color=lightgreen
MIXED start=31 end=55 color=yellow
QUESTIONABLE start=56 end=75 color=orange
UNRELIABLE start=76 end=90 color=red
DISINFORMATION start=91 end=100 color=darkred
GAP-URI FLOAT: 15.x, 30.x, 55.x, 75.x, 90.x — niciun range nu acopera aceste valori.
Risk Mappings (bos_parammgmt.risk_mapping) — 6 nivele, NU 4 ca in cod defaults
VERY_LOW start=0 end=10 level=1 color=green
LOW start=11 end=25 level=2 color=lightgreen
MEDIUM start=26 end=50 level=3 color=yellow
HIGH start=51 end=75 level=4 color=orange
VERY_HIGH start=76 end=90 level=5 color=red
CRITICAL start=91 end=100 level=6 color=darkred
GAP-URI FLOAT: 10.x, 25.x, 50.x, 75.x, 90.x
Cod defaults au 4 nivele (LOW/MODERATE/HIGH/CRITICAL), Redis are 6 (VERY_LOW/LOW/MEDIUM/HIGH/VERY_HIGH/CRITICAL).
Fallback in cod: risk_mappings[length - 1] = ultimul element = CRITICAL (cel mai sever).
Severity Assessments (bos_parammgmt.severity_assessment)
LOW start=0 end=29
MEDIUM start=30 end=59
HIGH start=60 end=79
CRITICAL start=80 end=100
GAP-URI FLOAT: 29.x, 59.x, 79.x
Fallback in cod: severity_assessments[length - 1] = ultimul element.
NOTA: Redis are trailing whitespace pe valori ("CRITICAL "), codul face .trim() la linia 318.
Component Weights (bos_parammgmt.component_weight)
manipulation = 35 (cod default: 0.35)
claims = 25 (cod default: 0.25)
source = 20 (cod default: 0.20)
ai = 10 (cod default: 0.10)
context = 10 (cod default: 0.10)
Unitati diferite (35 vs 0.35) dar FARA impact — calculateWeightedRisk imparte la totalWeight, deci se normalizeaza.
Multipliers (doar in Redis, cod defaults = array gol)
Topic (type 1):
elections = 150 (intentie: 1.5x)
security = 140 (intentie: 1.4x)
public_health = 130
dimension_d4 = 130
dimension_d8 = 130
dimension_d5 = 120
dimension_d7 = 110
dimension_d6 = 90
Temporal (type 2):
election_window = 140
crisis_window = 130
Reach (type 3):
high_reach = 125
medium_reach = 110
low_reach = 100
Verdict Config (Redis didi:config:pipeline:v1:verdict_config)
synergy: enabled=true, threshold=70, bonus_per_component=5, max_bonus=15
false_claims: enabled=true, threshold=3, bonus_per_claim=5, max_bonus=20
severe_techniques: enabled=true, threshold=2, bonus=10
undisclosed_ai: enabled=true, bonus=15
untrusted_domain: enabled=true, untrusted_bonus=20, suspicious_bonus=10, blacklisted_bonus=25
domain_red_flags: enabled=true, threshold=2, bonus_per_flag=5, max_bonus=15
confidence: base_per_component=12.5, domain_strong=15, domain_weak=8, techniques_max=12
ai_high=12, ai_medium=8, ai_low=4, claims_max=11
confidence_levels: HIGH=75, MEDIUM=50, LOW=0
Scenarii redistribuire (doar Redis, ignorate de cod)
Full Analysis: manipulation=35, claims=25, source=20, ai=10, context=10
No Source: manipulation=44, claims=31, source=0, ai=12, context=13
No Claims: manipulation=44, claims=0, source=25, ai=12, context=19
Text Only: manipulation=47, claims=33, source=0, ai=0, context=20
Minimal: manipulation=70, claims=0, source=0, ai=0, context=30
Techniques Scoring Config (Redis didi:config:techniques:v3:scoring_config)
manipulation_levels: LOW=0-0.25, MEDIUM=0.25-0.5, HIGH=0.5-0.75, CRITICAL=0.75-1.0
bonuses:
count_bonus: threshold_3=+5%, threshold_5=+10%
dimension_bonus: per_dimension=2%, max_bonus=15%
intensity_bonus: formula = (avgIntensity - 1) * 0.05
BUGURI CONFIRMATE — DETALII TEHNICE
BUG #1 — CRITIC: Float gap in mapToVerdictCategory
Fisier: src/components/pipeline/verdict-calculator.ts
Linii: 292 (mapare), 313 (rotunjire), 568-573 (functia)
Problema: Scorul e un float (ex: 90.4). Categoriile au ranguri integer (UNRELIABLE: 76-90, DISINFORMATION: 91-100). Valoarea 90.4 nu se potriveste nicaieri. Fallback = categories[0] = RELIABLE.
Cod buguit:
// Linia 292 — mapare pe float NEROTUNJIT
const riskCategory = this.mapToVerdictCategory(riskScore);
const riskLevel = this.mapToRiskLevel(riskScore);
const severity = this.mapToSeverity(riskScore);
// Linia 313 — rotunjire DUPA mapare
risk_score: Math.round(riskScore),
// Linia 568-573 — functia cu fallback RELIABLE
private mapToVerdictCategory(score: number): VerdictCategory {
for (const cat of this.verdicts.categories) {
if (score >= cat.start_range && score <= cat.end_range) return cat;
}
return this.verdicts.categories[0]; // RELIABLE — cel mai bun!
}
Fallback-uri contradictorii:
- mapToVerdictCategory:
categories[0]= RELIABLE (cel mai bun) - mapToRiskLevel:
risk_mappings[last]= CRITICAL (cel mai rau) - mapToSeverity:
severity_assessments[last]= CRITICAL (cel mai rau)
Rezultat posibil: risk_category=RELIABLE + risk_level=CRITICAL simultan.
Fix:
// INAINTE de liniile 292-294, adauga:
const roundedScore = Math.round(riskScore);
// Apoi foloseste roundedScore in loc de riskScore:
const riskCategory = this.mapToVerdictCategory(roundedScore);
const riskLevel = this.mapToRiskLevel(roundedScore);
const severity = this.mapToSeverity(roundedScore);
// La linia 313 foloseste roundedScore:
risk_score: roundedScore,
Dovada din PG: Sesiunea 7cc2cca8: risk_score=90, risk_category=RELIABLE, risk_level=CRITICAL.
BUG #2 — CRITIC: Severity float gap (aceeasi cauza ca #1)
Fisier: src/components/pipeline/verdict-calculator.ts
Linii: 582-587
Dovada din PG: Sesiunea ee58753d: risk_score=80, severity=MEDIUM. Ar trebui CRITICAL (80-100).
Fix: Acelasi ca #1 — rotunjire inainte de mapare.
Nota: Ordinea elementelor in array-ul severity_assessments din Redis poate afecta fallback-ul. Trebuie verificat ordinea exacta daca se vrea un fallback inteligent.
BUG #3 — MARE: manipulation_score=100 pe 1 tehnica
Fisier: src/components/techniques/executor.ts
Linii: 575-612
Problema: Formula baseScore = sum(severity * confidence/100) / sum(severity) se simplifica la confidence/100 cand e o singura tehnica (severity se anuleaza). Cu confidence=95% si bonusuri: 0.95 + 0.02 + 0.10 = 1.07 → clamped la 1.0 → manipulation_score=100.
Cod buguit:
private calculateManipulationScore(techniques: DetectedTechnique[], config: any): number {
if (techniques.length === 0) return 0;
let severitySum = 0;
let weightedSum = 0;
for (const t of techniques) {
severitySum += t.severity;
weightedSum += t.severity * (t.confidence / 100);
}
let baseScore = severitySum > 0 ? weightedSum / severitySum : 0;
// Cu 1 tehnica: baseScore = (S * C/100) / S = C/100
// ...bonusuri...
const finalScore = baseScore + dimensionBonus + countBonus + intensityBonus;
return Math.round(Math.max(0, Math.min(1, finalScore)) * 100);
}
Fix propus — Scalare bazata pe numar tehnici (cap maxim per count):
// Dupa calculul baseScore, inainte de bonusuri:
// Penalizare count mic: o singura tehnica nu poate justifica scor maxim
const countScaler = Math.min(1.0, techniques.length / 3);
// 1 tehnica -> max 33%, 2 tehnici -> max 67%, 3+ -> 100%
baseScore = baseScore * countScaler;
Alternativ, un cap per count: 1 tehnica → max 50, 2 tehnici → max 70, 3+ → max 100.
Dovada din PG: Sesiunea d07266ca: techniques_count=1, total_severity=60, manipulation_score=100.
BUG #4 — MARE: Claims credibility_score=NULL pierde ponderea
Fisiere:
src/components/claims/executor.tslinii 695-716 (buildSkippedResult)src/components/pipeline/verdict-calculator.tslinii 358-360 (extractComponentScores)src/config/analysisLimits.tslinia 21 (MIN_CLAIMS_TEXT_LENGTH = 100)
Problema: Text intre 50-99 chars trece validarea hard (MIN_TEXT_LENGTH=50) dar claims e skip-uit (MIN_CLAIMS_TEXT_LENGTH=100). buildSkippedResult returneaza credibility_score: null. VerdictCalculator: null → -1 → component indisponibil → weight redistribuit.
Diferenta comportament:
- Text < 100 chars: claims skip → credibility=NULL → weight redistribuit (claims ignorat)
- Text >= 100 chars, 0 claims: buildEmptyResult → credibility=50 → participa la scor
- Text >= 100 chars, claims gasite: credibility calculat 0-100
Cod relevant:
// claims/executor.ts linia 695
private buildSkippedResult(startTime: number, textLength: number): ClaimsResult {
return {
credibility_score: null, // <-- NULL
interpretation: `Text too short for claims analysis (${textLength} chars)`,
// ...
};
}
// claims/executor.ts linia 672
private buildEmptyResult(...): ClaimsResult {
return {
credibility_score: 50, // <-- DEFAULT 50
interpretation: 'No claims to verify',
// ...
};
}
// verdict-calculator.ts linia 358
const claimsRisk = claims && claims.credibility_score != null && claims.credibility_score >= 0
? Math.round(100 - claims.credibility_score)
: -1; // -1 = indisponibil, weight redistribuit
Fix propus: In buildSkippedResult, returneaza credibility_score: 50 in loc de null (neutral, ca si 0 claims). Sau: in verdict-calculator, trateaza null ca 50 in loc de -1.
Dovada din PG: Sesiunea 2adf7ace: credibility_score=NULL, interpretation="Text too short for claims analysis (54 chars)".
BUG #5 — MARE: Text scurt vs lung inconsistenta dramatica
Cauza: Combinatie buguri #3 + #4 + dampening din verdict-calculator.
Mecanism:
- Text scurt (54 chars) → screening LLM nu detecteaza tehnici → manipulation=0
- 54 < 100 → claims skip → credibility=NULL → weight redistribuit
- dampenBenignContent (verdict-calculator.ts:455-469): techniques=0, ai=0 → maxStrongSignal=0 < 15 → cap = 0 + 15 = 15
- Rezultat: risk_score capeat la max 15 → in practica 0
Cod dampening:
// verdict-calculator.ts linia 455
private dampenBenignContent(rawScore: number, scores: ComponentScores): number {
const BENIGN_THRESHOLD = 15;
const strongSignals = [scores.manipulation, scores.ai].filter(s => s >= 0);
if (strongSignals.length === 0) return rawScore;
const maxStrongSignal = Math.max(...strongSignals);
if (maxStrongSignal >= BENIGN_THRESHOLD) return rawScore;
const cap = maxStrongSignal + BENIGN_THRESHOLD;
return Math.min(rawScore, cap);
}
Dovada din PG:
- 2adf7ace (54 chars): risk_score=0, RELIABLE
- 7cc2cca8 (aceeasi info, ~200+ chars): risk_score=90, RELIABLE (bug #1, ar trebui UNRELIABLE)
BUG #6 — MARE (LATENT): Multiplicatori ×150 in loc de ×1.5
Fisier: src/components/pipeline/verdict-calculator.ts
Linii: 279-289
Problema: Redis stocheaza multiplicatori ca procente (elections=150 = 1.5x). Codul aplica direct: riskScore * 150 in loc de riskScore * 1.5.
Cod buguit:
// Linia 284
if (options?.topic) {
const topicMultiplier = this.getMultiplier('topic', options.topic);
if (topicMultiplier) {
riskScore = Math.min(100, riskScore * topicMultiplier.multiplier);
// riskScore * 150 = scor × 150, nu × 1.5
// Math.min(100, ...) salveaza de catastrofa dar orice scor > 0.67 devine 100
}
}
Date Redis:
elections=150, security=140, public_health=130, dimension_d4=130, dimension_d8=130
election_window=140, crisis_window=130, high_reach=125, medium_reach=110, low_reach=100
Fix:
riskScore = Math.min(100, riskScore * (topicMultiplier.multiplier / 100));
Status: Bug latent — multiplicatorii se aplica doar daca options.topic e setat, ceea ce pare sa nu fie folosit activ momentan. Dar cand se va activa, va fi catastrofal.
BUG #7 — MIC: Scenarii Redis ignorate
Fisier: src/components/pipeline/verdict-calculator.ts
Linia: 385
Problema: Parametrul _scenario (cu underscore = nefolosit) in calculateWeights. Codul face redistribuire proportionala proprie in loc sa foloseasca scenariile pre-definite din Redis.
Redis contine 5 scenarii:
Full Analysis: manipulation=35, claims=25, source=20, ai=10, context=10
No Source: manipulation=44, claims=31, source=0, ai=12, context=13
No Claims: manipulation=44, claims=0, source=25, ai=12, context=19
Text Only: manipulation=47, claims=33, source=0, ai=0, context=20
Minimal: manipulation=70, claims=0, source=0, ai=0, context=30
Impact: Redistribuirea proportionala din cod poate diferi de scenariile gandite de admin. Nu e un bug functional, dar e un feature ignorat.
BUG #8 — MIC: coupling_context flags din screening, nu deep analysis
Fisier: src/components/techniques/executor.ts
Linii: 664-666
Problema: dimensionsAffected vine din screening (linia 544), nu din tehnicile confirmate in deep analysis. Daca screening detecteaza D7/D8 dar deep analysis nu gaseste tehnici → coupling_context flags raman true cu 0 tehnici.
Cod buguit:
// Linia 664-666 in buildCouplingContext
for_context_analysis: {
narrative_manipulation_detected: dimensionsAffected.includes('D7'), // din SCREENING
context_manipulation_detected: dimensionsAffected.includes('D8'), // din SCREENING
amplification_detected: dimensionsAffected.includes('D6'),
suspicious_patterns: warningFlags,
},
Fix: Foloseste dimensiunile din tehnicile efectiv detectate (deep analysis) in loc de cele din screening:
const confirmedDimensions = new Set(techniques.map(t => t.dimension));
// apoi: confirmedDimensions.has('D7') in loc de dimensionsAffected.includes('D7')
Dovada din PG: Sesiunea 2adf7ace: dimensions_affected={D7,D8}, techniques_count=0.
PRIORITIZARE FIX-URI
| Prioritate | Bug | Efort | Impact |
|---|---|---|---|
| 1 | #1 + #2: Rotunjire inainte de mapare | 1 linie | Elimina bug CRITIC (RELIABLE la risk 90) |
| 2 | #3: Formula manipulation_score | ~10 linii | Elimina scor 100% pe 1 tehnica |
| 3 | #6: Multiplicatori /100 | 1 linie | Previne bug catastrofal cand se activeaza topics |
| 4 | #4: Claims null → 50 | 1 linie | Elimina pierdere ponderi pe text scurt |
| 5 | #8: coupling_context flags | ~5 linii | Corecteaza metadata inconsistenta |
| 6 | #7: Scenarii Redis | ~20 linii | Feature, nu bug critic |
REFERINTE FISIERE
verdict-calculator.ts — src/components/pipeline/verdict-calculator.ts
Linia 292-294: mapare categorii (BUG #1, #2)
Linia 279-289: multiplicatori (BUG #6)
Linia 313: Math.round output
Linia 358-360: claims null handling (BUG #4)
Linia 385: _scenario ignorat (BUG #7)
Linia 455-469: dampenBenignContent (context BUG #5)
Linia 568-573: mapToVerdictCategory cu fallback RELIABLE
Linia 575-579: mapToRiskLevel cu fallback CRITICAL (ultimul)
Linia 582-587: mapToSeverity cu fallback ultimul
techniques/executor.ts — src/components/techniques/executor.ts
Linia 575-612: calculateManipulationScore (BUG #3)
Linia 664-666: coupling_context flags (BUG #8)
claims/executor.ts — src/components/claims/executor.ts
Linia 695-716: buildSkippedResult credibility=null (BUG #4)
Linia 672-693: buildEmptyResult credibility=50
analysisLimits.ts — src/config/analysisLimits.ts
Linia 14: MIN_TEXT_LENGTH = 50
Linia 21: MIN_CLAIMS_TEXT_LENGTH = 100