lantern

mw-scenario

Scenario Modifier Middleware

Applies scenario adjustments to base forecasts - enabling what-if analysis and business planning.

Config

# Input: expects 'data' array from previous pipeline step (forecasts)
# Each record should have: item_id, location_id, week, forecast_quantity, lower_bound, upper_bound
scenario: base
custom_adjustments: {}
ttl_seconds: 1800

Scenarios

Built-in scenario profiles:

Scenario Description Adjustment
base No adjustment 1.0x
optimistic Growth assumptions 1.15x
pessimistic Conservative outlook 0.85x
promotional Planned promotions 1.30x during promo weeks
disruption Supply chain issues 0.70x
custom User-defined Per config

Fetch

import json
from datetime import datetime

# Config is unified: node config + pipeline params + session context
# PipelineProvider loads and merges these automatically
scenario_name = config.get('scenario', 'base')
custom_adj = config.get('custom_adjustments', {})

# Input data comes from previous pipeline step (accumulated context)
input_data = config.get('data', [])
if not input_data:
    raise ValueError("No input data - mw-scenario requires 'data' from previous pipeline step")

# Built-in scenario multipliers
SCENARIOS = {
    'base': lambda week, item: 1.0,
    'optimistic': lambda week, item: 1.15,
    'pessimistic': lambda week, item: 0.85,
    'promotional': lambda week, item: 1.30 if week % 4 == 0 else 1.0,  # Promo every 4th week
    'disruption': lambda week, item: 0.70,
}

def get_multiplier(scenario, week, item_id):
    """Get scenario multiplier for a given week and item."""
    if scenario == 'custom':
        # Custom allows per-item or per-week overrides
        item_mult = custom_adj.get('items', {}).get(item_id, 1.0)
        week_mult = custom_adj.get('weeks', {}).get(str(week), 1.0)
        return item_mult * week_mult
    return SCENARIOS.get(scenario, SCENARIOS['base'])(week, item_id)

# Apply scenario adjustments
scenario_forecasts = []
for record in input_data:
    adj_record = record.copy()
    
    item_id = record.get('item_id')
    week = record.get('horizon_period', 1)
    multiplier = get_multiplier(scenario_name, week, item_id)
    
    # Store original forecast
    adj_record['base_forecast'] = record.get('forecast_quantity', 0)
    adj_record['base_lower'] = record.get('lower_bound', 0)
    adj_record['base_upper'] = record.get('upper_bound', 0)
    
    # Apply scenario adjustment
    adj_record['scenario'] = scenario_name
    adj_record['scenario_multiplier'] = multiplier
    adj_record['adjusted_forecast'] = round(record.get('forecast_quantity', 0) * multiplier)
    adj_record['adjusted_lower'] = round(record.get('lower_bound', 0) * multiplier)
    adj_record['adjusted_upper'] = round(record.get('upper_bound', 0) * multiplier)
    
    scenario_forecasts.append(adj_record)

# Build result with full provenance
result = {
    'data': scenario_forecasts,
    'meta': {
        'generated_at': datetime.utcnow().isoformat(),
        'stage': 'scenario',
        'scenario': scenario_name,
        'input_count': len(input_data),
        'output_count': len(scenario_forecasts),
        'source': 'mw-scenario'
    }
}

print(json.dumps(result, indent=2))

❌ Fence Execution Error: No input data - mw-scenario requires 'data' from previous pipeline step Traceback (most recent call last): File "/app/oculus/providers/python_provider.py", line 468, in execute exec(fence_content, exec_globals, exec_locals) File "", line 12, in ValueError: No input data - mw-scenario requires 'data' from previous pipeline step

Usage

Base Scenario (Pass-through)

oculus exec scenario

Optimistic Planning

scenario: optimistic

Custom Adjustments

scenario: custom
custom_adjustments:
  items:
    ITEM-001: 1.20  # 20% boost for specific item
    ITEM-002: 0.90  # 10% reduction
  weeks:
    "5": 1.50       # 50% boost in week 5 (planned sale)
    "10": 0.60      # 40% reduction in week 10 (supplier issue)

Diffing Scenarios

Run multiple scenarios and compare:

# Generate base and optimistic
oculus exec scenario --config '{"scenario": "base"}' > base.json
oculus exec scenario --config '{"scenario": "optimistic"}' > optimistic.json

# Diff becomes an edge
diff base.json optimistic.json

The diff between scenario outputs reveals:

  • Impact magnitude: How much does the scenario change things?
  • Risk exposure: Which items are most sensitive?
  • Planning buffer: Range between pessimistic and optimistic

Pipeline Position

Raw Data → Seasonality → TrendForecast → [Scenario] → Output
                                               ↑
                                          You are here

This is the final middleware in the core pipeline. Downstream consumers get scenario-adjusted forecasts with full provenance chain.

Provenance

Document

  • Status: 🔴 Unverified

Changelog

  • 2026-01-25 19:11: Node created by mcp - Fourth middleware node completing the forecast pipeline - applies scenario adjustments (optimistic/pessimistic/promotional/custom) to base forecasts

North

slots:
- slug: mw-forecast
  context:
  - 'Pipeline flow: forecast feeds into scenario modifier'
↑ northmw-forecast