Nightly Refresh Runbook¶
What It Does¶
Runs every day at 6am via launchd (com.stickymetrics.daily). Orchestrates the full data pipeline in 10 steps:
- Pull orders (last 7 days)
- Pull FBA restock report 2b. Pull inventory ledger (last 60 days)
- Pull AWD inventory 3b. Pull SoStocked POs 3c. Pull warehouse inventory (HIEX)
- Forecast (Chronos-Bolt-mini, 365-day horizon, lifecycle-aware) 4b. Compute PO recommendations (per-ASIN, MOQ-gated) 4c. Build styled XLSX dashboard
- Push Claude MRP to source sheet
- Push forecast to purchasing sheet
- Push restock raw data 7b. Push forecast feed
- Push AWD raw data
- Score predictions vs actuals
- Self-healing calibration (1st of month only) 10b. Validate dashboard consistency (smoke test, non-blocking)
Logs¶
How to Debug¶
- Check today's log:
cat ~/.stickymetrics/logs/nightly_$(date +%Y-%m-%d).log - If a step failed, run it manually:
cd ~/stickymetrics && python3 scripts/<script>.py --seller ecomhd_us - Common failures:
- SP-API token expired:
lib_sticky.pyauto-refreshes LWA tokens, but if the refresh_token itself is revoked, re-auth in Seller Central - Ads API 425: Duplicate report request - the existing reportId is returned, use it
- Sheet push fails: Usually a quota issue (Google Sheets API limit: 300 req/min). Wait and retry.
- Forecast OOM: Chronos-Bolt-mini needs ~4GB RAM. Check if other processes are hogging memory.
How to Run Manually¶
cd ~/stickymetrics
bash scripts/nightly_refresh.sh
# Or individual steps:
python3 scripts/pull_orders.py --seller ecomhd_us --days 7
python3 forecasts/forecast_all_lifecycle.py --seller ecomhd_us --horizon-days 365 --use-pretrained mini
Key Google Sheets¶
| Sheet | ID | Purpose |
|---|---|---|
| Purchasing Dashboard | 1TmQk3sKrLxS1ooUb1UfliA3-XkQ7gqBVZQQbswZDnb8 |
Main ops dashboard |
| Source/MRP | 1kV6N_vMUqJzT8w2Sbq7DfmJI5Ttadxol8AIu_UaaHow |
Forecast source |
| Restock | 13u0g4ruHyayZ2AA8GvOcccgy1wZ2R175rtQk1zi2avU |
FBA restock data |
| AWD | 1Zj6viurclBiZJwGL6iH4AozuAv1NeBplJmNYt0KMRuQ |
AWD inventory |
Month-End Behavior¶
- Last day of month: tags the forecast run as
month_end_baseline_fornext month - 1st of month: runs
calibrate.py(compares last month's forecast vs actuals, adjusts weights)