# Demand forecast — action brief

Companion to **AI demand forecasting agent for eCommerce**.

The **statistical model (or ERP forecast) owns the number** when you have one. The agent **explains** the number, names missing signals, and recommends an ops action. This file is a contract, not a MAPE claim.

## Who owns the number

| Source | Owns | Agent may |
| ------ | ---- | --------- |
| Stats / ML forecast service | Units, interval, horizon | Call `getDemandForecast`; quote the payload |
| Promo / launch calendar | Uplift flags | Call `getPromoCalendar`; do not invent a 2× lift |
| Inventory position | Cover vs forecast | Call `getInventoryPosition` |
| Agent (LLM) | Narrative + recommended action | **Must not** replace the forecast API with a guessed qty |

If there is **no** forecast tool, say so. Do not let the model “be the forecast.”

## Inputs

- Historical sales (named window)
- Seasonality flags from the forecast service or calendar
- Promotions, campaigns, launches (`getPromoCalendar`)
- Inventory + open POs
- External signals **only if a tool exists** (weather, marketplace rank — otherwise `unknown`)

## Sample brief (fixture)

```json
{
  "sku": "SKU-TEE-BLU-M",
  "horizon_days": 28,
  "forecast_units": 420,
  "forecast_source": "getDemandForecast",
  "interval": { "p10": 310, "p90": 540 },
  "why": [
    "Base from 8-week seasonally adjusted series (tool)",
    "Promo week of 2026-09-08 flagged on calendar (tool)",
    "No weather tool — not cited"
  ],
  "inventory_cover_days": 5,
  "recommended_action": "Treat as class A reorder; draft PO 240 — buyer HITL",
  "do_not": "Overwrite forecast_units with a chat completion"
}
```

## Eval fails

- Brief cites a lift with no promo/calendar tool.
- `forecast_units` differs from `getDemandForecast` without a labeled override and a human.
- Recommended PO ignores open inbound that lands inside the horizon.
