fix(intelligence): region-scope signals and chokepoint evidence#2952
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes two correctness bugs on the MENA/SSA regional snapshot pipeline: (1) broad display labels emitted by the cross-source signal seed ( Confidence Score: 5/5Safe to merge — both bugs are correctly fixed, tests are comprehensive, and no P0/P1 issues remain. All findings are P2 (pre-existing data model inconsistency in THEATERS.corridorIds, theoretical false-positive risk from the short 'eu' alias). The core logic of isSignalInRegion and the regionChokepointIds allowlist is correct, the type declarations match, and the 15 unit tests directly cover the regression cases. shared/geography.js — the THEATERS.corridorIds inconsistency for horn-of-africa and caribbean is worth cleaning up to avoid misleading future developers. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Cross-source signal / Chokepoint event] --> B{Signal type?}
B -- cross-source signal --> C[isSignalInRegion theater region]
C --> D{theater matches region.theaters? kebab to space normalized}
D -- Yes --> G[Include in output]
D -- No --> E{theater matches region.signalAliases? e.g. middle east sub-saharan africa}
E -- Yes --> G
E -- No --> H[Skip - silent drop fixed]
B -- chokepoint event --> I[Build regionChokepointIds from getRegionCorridors regionId]
I --> J{cpId in regionChokepointIds?}
J -- No --> K[Skip - cross-region leak fixed]
J -- Yes --> L{threatLevel != normal or empty?}
L -- No --> K
L -- Yes --> G
G --> M[evidence-collector.mjs collectEvidence output]
G --> N[balance-vector.mjs computeCoercivePressure drivers]
|
| forecastLabel: 'Europe', | ||
| wbCode: 'ECS', | ||
| theaters: ['eastern-europe', 'western-europe', 'baltic', 'arctic'], | ||
| signalAliases: ['europe', 'eu'], |
There was a problem hiding this comment.
Short alias
"eu" risks substring false-positives
The matching in isSignalInRegion checks t.includes(alias), meaning any theater string that contains "eu" as a substring would match the Europe region. Two-character aliases like "eu" are particularly broad — e.g., a hypothetical theater label "Neutral Zone" or the word "fuel" would satisfy "neutral zone".includes("eu") → true. In practice the seed emits well-formed geographic labels, so this likely won't fire incorrectly, but it's worth either replacing "eu" with "european union" (which maps to a real seed label) or switching short aliases to exact equality instead of substring matching.
| signalAliases: ['europe', 'eu'], | |
| signalAliases: ['europe', 'european union'], |
Two review findings on PR #2940 caused MENA/SSA snapshots to silently drop broad cross-source signals and leak foreign chokepoint events into every region's evidence chain. P1 - theater label matching seed-cross-source-signals.mjs normalizes raw values to broad display labels like "Middle East" and "Sub-Saharan Africa". The consumer side compared these against region.theaters kebab IDs (levant, persian-gulf, horn-of-africa). "middle east" does not substring-match any of those, so every MENA signal emitted with the broad label was silently dropped from coercive_pressure and the evidence chain. Same for SSA. Added signalAliases per region and a shared isSignalInRegion helper in shared/geography.js. Both balance-vector.mjs and evidence-collector.mjs now route signals through the helper, which normalizes both sides and matches against theater IDs or region aliases. P2 - chokepoint region leak evidence-collector.mjs:62 iterated every chokepoint in the payload without filtering by regionId, so Taiwan Strait, Baltic, and Panama threat events surfaced in MENA and SSA evidence chains. Now derives the allowed chokepoint ID set from getRegionCorridors(regionId) and skips anything not owned by the region. Added 15 unit tests covering: broad-label matching, kebab/spaced input, cross-region rejection, and the chokepoint filter for MENA/East Asia/ Europe/SSA.
Two P2 findings from Greptile on the region-scoping PR.
1) Drop 'eu' short alias from europe.signalAliases
`isSignalInRegion` uses substring matching, and bare 'eu' would match
any theater label containing those two letters ('fuel', 'neutral zone',
'feudal'). Replaced with 'european union' which is long enough to be
unambiguous. No seed currently emits a bare 'eu' label, so this is
pure hardening.
2) Make THEATERS.corridorIds live data via getRegionCorridors union
horn-of-africa declared corridorIds: ['babelm'] and caribbean declared
corridorIds: ['panama'], but `getRegionCorridors` only consulted
CORRIDORS.theaterId — so those entries were dead data. After yesterday's
region-scoped chokepoint filter, Bab el-Mandeb threat events landed
ONLY in MENA evidence (via the primary red-sea theater), never in SSA,
even though the corridor physically borders Djibouti/Eritrea. Same for
Panama missing from LatAm.
`getRegionCorridors` now unions direct theater membership (via
CORRIDORS.theaterId) with indirect claims (via THEATERS.corridorIds),
de-duplicated by corridor id. This reflects geopolitical reality:
- MENA + SSA both see babelm threat events
- NA + LatAm both see panama threat events
Scoring impact: SSA maritime_access now weighs babelm (weight 0.9),
LatAm maritime_access now weighs panama (weight 0.6). These were
missing buffers under the pre-fix model.
Added regression tests for both new paths. The existing
"SSA evidence has no chokepoints" test was inverted to assert SSA now
DOES include babelm (and excludes MENA/East Asia corridors).
051c73f to
868da6c
Compare
Summary
Fixes two review findings on PR #2940 that caused MENA/SSA snapshots to silently drop broad cross-source signals and leak foreign chokepoint events into every region's evidence chain.
P1 — theater label matching
scripts/seed-cross-source-signals.mjsnormalizes raw values to broad display labels like"Middle East"and"Sub-Saharan Africa". The consumer side (balance-vector.mjs:70andevidence-collector.mjs:28) compared these againstregion.theaterskebab IDs (levant,persian-gulf,horn-of-africa,sahel, etc.)."middle east"does not substring-match any of those even after kebab-to-space transform, so every MENA signal emitted with the broad label was silently dropped fromcoercive_pressureand the evidence chain. Same for SSA's"Sub-Saharan Africa".Added
signalAliasesper region and a sharedisSignalInRegionhelper inshared/geography.js. Bothbalance-vector.mjsandevidence-collector.mjsnow route signals through the helper, which normalizes both sides (lowercase, trim, collapse dashes) and matches against theater IDs or region aliases.P2 — chokepoint region leak
evidence-collector.mjs:62iterated every chokepoint in the payload without filtering byregionId, so Taiwan Strait, Baltic, and Panama threat events surfaced in MENA and SSA evidence chains. Now derives the allowed chokepoint ID set fromgetRegionCorridors(regionId)and skips anything not owned by the region.Testing
tests/regional-snapshot.test.mjs:isSignalInRegion: theater IDs, broad aliases, case/whitespace tolerance, cross-region rejection, empty/null handling, region-object acceptedcollectEvidence region scoping: MENA/East Asia/Europe get only their own chokepoints, SSA gets none (tier-3 Cape only), normal-threat skip preserved, broad MENA/SSA cross-source signals land in evidencenpm run typecheckandnpm run typecheck:api: cleantsc -p scripts/jsconfig.json: clean for touched files (remaining errors are pre-existing inseed-forecasts.mjs/_r2-storage.mjs)Post-Deploy Monitoring & Validation
Test plan
isSignalInRegioncollectEvidencechokepoint region filter