Files
worldmonitor/todos/154-complete-p1-result-nonnull-assertion-without-guard.md
Elie Habib 60e727679c feat(supply-chain): Sprint E — scenario visual completion + service parity (#2910)
* feat(supply-chain): Sprint E — scenario visual completion + service parity

- E1: fetchSectorDependency exported from supply-chain service index
- E2: PRO gate + all-renderer dispatch in MapContainer.activateScenario
- E3: scenario summary banner in SupplyChainPanel (dismiss wired)
- E4: "Simulate Closure" trigger button in expanded chokepoint cards
- E5: affectedIso2s heat layer in DeckGLMap (GeoJsonLayer, red tint)
- E6: SVG renderer setScenarioState (best-effort iso2 fill)
- E7: Globe renderer scenario polygons via flushPolygons
- E8: integration tests for scenario run/status endpoints

* fix(supply-chain): address PR #2910 review findings (P1 + P2 + P3)

- Wire setOnScenarioActivate + setOnDismissScenario in panel-layout.ts (todo #155)
- Rename shadow variable t→tmpl in SCENARIO_TEMPLATES.find (todo #152)
- Add statusResp.ok guard in scenario polling loop (todo #153)
- Replace status.result! non-null assertion with shape guard (todo #154)
- Add AbortController to prevent concurrent polling races (todo #162)
- Add polygonStrokeColor scenario branch (transparent) in GlobeMap (todo #156)
- Re-export SCENARIO_TEMPLATES via src/config/scenario-templates.ts (todo #157)
- Cache affectedIso2Set in DeckGLMap.setScenarioState (todo #158)
- Add scenario paths to PREMIUM_RPC_PATHS for auth injection (todo #160)
- Show template name in scenario banner instead of raw ID (todo #163)

* fix(supply-chain): address PR #2910 review findings

- Add auth headers to scenario fetch calls in SupplyChainPanel
- Reset button state on scenario dismiss
- Poll status immediately on first iteration (no 2s delay)
- Pre-compute scenario polygons in GlobeMap.setScenarioState
- Use scenarioId for DeckGL updateTriggers precision

* fix(supply-chain): wire panel instance to MapContainer, stop button click propagation

- Call setSupplyChainPanel() in panel-layout.ts so scenario banner renders
- Add stopPropagation() to Simulate Closure button to prevent card collapse
2026-04-10 21:31:26 +04:00

2.4 KiB

status, priority, issue_id, tags, dependencies
status priority issue_id tags dependencies
complete p1 154
code-review
quality
supply-chain
type-safety
153

Non-Null Assertion on Optional result Without Guard in Polling Loop

Problem Statement

In SupplyChainPanel.ts:679, result = status.result! uses a non-null assertion on a field typed as optional. If the server sends { status: 'done' } without result (version mismatch or worker bug), undefined is silently passed to onScenarioActivate, which crashes in activateScenario when it calls result.topImpactCountries.map(...). Additionally, impactPct * 100 in showScenarioSummary could produce NaN if the upstream type changes (prior art: feedback_pizzint_spike_magnitude_type.md).

Findings

  • File: src/components/SupplyChainPanel.ts, line 679
  • Code: if (status.status === 'done') { result = status.result!; break; }
  • status.result is typed as optional (result?: ScenarioResult)
  • If status.result is absent, undefined propagates to activateScenario → crash in topImpactCountries.map(...)
  • impactPct is numeric but upstream could change to string (see MEMORY: feedback_pizzint_spike_magnitude_type.md)

Proposed Solutions

if (status.status === 'done') {
  const r = status.result;
  if (!r || !Array.isArray(r.topImpactCountries)) throw new Error('done without valid result');
  result = r;
  break;
}

Pros: Explicit, catches server bugs, type-safe without ! Cons: Slight verbosity Effort: Small | Risk: None

Option B: Minimal guard only

if (status.status === 'done') {
  if (!status.result) throw new Error('done without result');
  result = status.result;
  break;
}

Protects against undefined but not malformed topImpactCountries. Effort: Small | Risk: Low

Apply Option A — adds one guard line, eliminates the ! assertion.

Technical Details

  • Affected files: src/components/SupplyChainPanel.ts
  • Lines: 679

Acceptance Criteria

  • Non-null assertion ! removed from status.result
  • topImpactCountries presence verified before use
  • npm run typecheck passes without ! assertion

Work Log

  • 2026-04-10: Identified by kieran-typescript-reviewer during PR #2910 review

Resources

  • PR: #2910
  • MEMORY: feedback_pizzint_spike_magnitude_type.md