mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* feat(resilience): language/source-density normalization for informationCognitive (Phase 2 T2.9) Add English-language media coverage factor that normalizes social velocity and news threat scores before they enter the informationCognitive dimension scorer. Countries with low English-media coverage (e.g. CN at 0.4, minimal-tier at 0.2) get their raw signal amplified to compensate for under-representation in English-language sources. RSF press freedom index passes through unchanged (already globally normalized in its methodology). With normalization in place, promote all three informationCognitive indicators from Enrichment back to Core tier, fulfilling the T2.2a demotion contract. Tier counts: 7 primary, 37 secondary, 46 limited, default minimal. * fix(resilience): ISO2 case-normalization + test improvements (#2992 review) - Add .toUpperCase() to getLanguageCoverageFactor() preventing lowercase ISO2 codes from falling through to 'minimal' tier (5x score error) - Replace tautological RSF test with real scorer exercise proving RSF component is equal across language tiers - Add velocity cap test matching real scorer cap of 1000 (was 100)
89 lines
3.6 KiB
TypeScript
89 lines
3.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { describe, it } from 'node:test';
|
|
|
|
import { INDICATOR_REGISTRY } from '../server/worldmonitor/resilience/v1/_indicator-registry.ts';
|
|
|
|
const CORE_MIN_COVERAGE = 180;
|
|
|
|
describe('signal tiering registry (Phase 2 T2.2a)', () => {
|
|
it('every indicator has tier, coverage, license populated', () => {
|
|
for (const entry of INDICATOR_REGISTRY) {
|
|
assert.ok(
|
|
entry.tier === 'core' || entry.tier === 'enrichment' || entry.tier === 'experimental',
|
|
`${entry.id} missing or invalid tier`,
|
|
);
|
|
assert.ok(
|
|
Number.isFinite(entry.coverage) && entry.coverage > 0,
|
|
`${entry.id} missing or non-positive coverage`,
|
|
);
|
|
assert.ok(
|
|
typeof entry.license === 'string' && entry.license.length > 0,
|
|
`${entry.id} missing license`,
|
|
);
|
|
}
|
|
});
|
|
|
|
it('Core indicators have coverage >= 180 countries (Phase 2 A4 invariant)', () => {
|
|
const offending = INDICATOR_REGISTRY
|
|
.filter((e) => e.tier === 'core' && e.coverage < CORE_MIN_COVERAGE)
|
|
.map((e) => `${e.id} (${e.coverage} countries, dimension=${e.dimension})`);
|
|
assert.deepEqual(
|
|
offending,
|
|
[],
|
|
`Core indicators must cover at least ${CORE_MIN_COVERAGE} countries. Demote to Enrichment or fix coverage: ${offending.join(', ')}`,
|
|
);
|
|
});
|
|
|
|
it('Core indicators use a license compatible with commercial use', () => {
|
|
const commercialOk = new Set(['public-domain', 'open-data', 'open-attribution']);
|
|
const offending = INDICATOR_REGISTRY
|
|
.filter((e) => e.tier === 'core' && !commercialOk.has(e.license))
|
|
.map((e) => `${e.id} (license=${e.license})`);
|
|
// Known exceptions the plan allows: GPI (IEP non-commercial, already
|
|
// demoted to Enrichment) and UCDP (research-only, kept Core because it
|
|
// is the canonical global conflict event source). These stay on the
|
|
// allowlist until the Phase 2 A9 Licensing & Legal Review workstream
|
|
// resolves carve-outs.
|
|
const KNOWN_EXCEPTIONS = new Set<string>([
|
|
// UCDP global conflict events: research-only license, kept Core per
|
|
// parent plan section "Signal tiering". Tracked in Phase 2 A9.
|
|
'ucdpConflict',
|
|
// UCDP reused in recovery-capacity stateContinuity dimension.
|
|
'recoveryConflictPressure',
|
|
]);
|
|
const unexcused = offending.filter((s) => {
|
|
const id = s.split(' ')[0];
|
|
return !KNOWN_EXCEPTIONS.has(id);
|
|
});
|
|
assert.deepEqual(
|
|
unexcused,
|
|
[],
|
|
`Core indicators with incompatible licenses must be demoted or added to KNOWN_EXCEPTIONS: ${unexcused.join(', ')}`,
|
|
);
|
|
});
|
|
|
|
it('informationCognitive dimension indicators are Core (promoted in T2.9 after language normalization)', () => {
|
|
const infoCogIndicators = INDICATOR_REGISTRY.filter((e) => e.dimension === 'informationCognitive');
|
|
assert.ok(infoCogIndicators.length > 0, 'expected informationCognitive indicators in registry');
|
|
for (const e of infoCogIndicators) {
|
|
assert.equal(
|
|
e.tier,
|
|
'core',
|
|
`${e.id}: informationCognitive indicators must be 'core' now that T2.9 language normalization has landed.`,
|
|
);
|
|
}
|
|
});
|
|
|
|
it('reports unknown-license count for the licensing audit workstream', () => {
|
|
const unknown = INDICATOR_REGISTRY.filter((e) => e.license === 'unknown');
|
|
// This is a visibility report, not a failure. Phase 2 A9 (Licensing &
|
|
// Legal Review) chases these.
|
|
if (unknown.length > 0) {
|
|
console.warn(
|
|
`[T2.2a] ${unknown.length} indicators have license='unknown': ${unknown.map((e) => e.id).join(', ')}`,
|
|
);
|
|
}
|
|
assert.ok(unknown.length < INDICATOR_REGISTRY.length, 'every indicator has unknown license');
|
|
});
|
|
});
|