feat(supply-chain): energy flow estimates per chokepoint (mb/d card row) (#2780)

* feat(supply-chain): energy flow estimates per chokepoint (mb/d card row)

- Add FlowEstimate proto message + ChokepointInfo field 15; regenerate stubs
- Add baselineId mapping to _chokepoint-ids.ts (7 of 13 chokepoints)
- Add relayId to seed-chokepoint-baselines.mjs CHOKEPOINTS entries
- New seed-chokepoint-flows.mjs: reads portwatch + baselines, computes
  7d tanker avg vs 90d baseline, outputs flow_ratio and current_mbd;
  prefers DWT (capTanker) when available; flags disruption if last 3 days
  each below 0.85 threshold; writes energy:chokepoint-flows:v1 (TTL 3d)
- get-chokepoint-status.ts: parallel-reads flows key, attaches flowEstimate
- SupplyChainPanel: compact card gains mb/d row (red <85%, amber <95%)
- 19 new unit tests for flow computation and seeder contract

* fix(chokepoint-flows): base useDwt on 90d baseline window, not recent 7 days

Zero recent capTanker is the disruption signal, not a reason to fall back
to vessel counts. Switching metrics during peak disruption caused the seeder
to report a higher (less accurate) flow estimate exactly when oil-flow
collapse is most acute. useDwt is now locked to whether the baseline window
has DWT data -- stable across disruption events.

Adds regression test covering DWT-collapse scenario.

* fix(chokepoint-flows): require majority DWT coverage in baseline before activating DWT mode

capBaselineSum > 0 would activate DWT on a single non-zero day during
partial data roll-out, pulling down the baseline average via zero-filled
gaps. Now requires >= ceil(prev90.length / 2) days with DWT data.
ArcGIS data is all-or-nothing per chokepoint in practice, so this
guard catches edge cases without affecting normal operation.
This commit is contained in:
Elie Habib
2026-04-07 12:43:54 +04:00
committed by GitHub
parent 6a56a9830a
commit a09f49ff9c
11 changed files with 451 additions and 22 deletions

View File

@@ -18,6 +18,16 @@ message ShippingIndex {
bool spike_alert = 8;
}
message FlowEstimate {
double current_mbd = 1;
double baseline_mbd = 2;
double flow_ratio = 3;
bool disrupted = 4;
string source = 5;
string hazard_alert_level = 6;
string hazard_alert_name = 7;
}
message ChokepointInfo {
string id = 1;
string name = 2;
@@ -33,6 +43,7 @@ message ChokepointInfo {
repeated string directions = 12;
repeated DirectionalDwt directional_dwt = 13 [deprecated = true];
TransitSummary transit_summary = 14;
FlowEstimate flow_estimate = 15;
}
message DirectionalDwt {