Powerwise

Document

Seasonal refresh cadence

Reference runbook for keeping the GLAX pipeline, schedule clusters, shift watchlist, and website in sync across the school year.

Orchestrator: scripts/run_seasonal_refresh.py
Related: SCHEDULE_VALIDATION_PLAN.md, SCHEDULE_AD_CHEATSHEET.md, VALIDATION.md


Profiles

Profile When to use Typical runtime
quick After roster/stats scrape mid-season; refresh forward 2027 outlook only ~2–5 min
standard Monthly during season; after major data updates ~5–15 min
full End of season; before schedule meetings; after adding tier-A finish file ~30–90 min
cd glax-pipeline

# Default — pipeline + audits + walk-forward + targeted Neon import
python3 scripts/run_seasonal_refresh.py

# Mid-season forward refresh only
python3 scripts/run_seasonal_refresh.py --profile quick

# End-of-year deep refresh (all seasons + tune + full import)
python3 scripts/run_seasonal_refresh.py --profile full --with-tests

# Preview commands without running
python3 scripts/run_seasonal_refresh.py --profile full --dry-run

Each run writes output/seasonal_refresh_report.json (step pass/fail, timestamps).


Calendar cadence

During season (monthly or after scrape)

  1. Ensure scraped stats/rosters are current under stats/ and rosters/.
  2. Run standard refresh (or quick if only forward ranks changed).
  3. Review on website: - /ranking/2027 — board line - /schedule-risk?year=2027 — clusters + shift watchlist - /strength/2027 — margin diagnostics
  4. Check output/sanity_report_2025_26.md for large anchor vs composite splits.

End of season (May–June)

  1. Add hand-curated finish_YYYY.tsv (tier A) for the completed year.
  2. Update docs/finish_ground_truth_manifest.json if ground-truth tier changes.
  3. Promote next season in seasons_registry.json when data is ready.
  4. Run full refresh: bash python3 scripts/run_seasonal_refresh.py --profile full --with-tests
  5. Re-tune shift thresholds if tier-A recall dropped: bash python3 scripts/tune_shift_detection.py python3 scripts/audit_shift_detection.py --forward
  6. Import to Neon (included in full profile, or manually): bash cd ../website uv run python scripts/import_data.py --skip-migrations

Before AD schedule meetings (June–July)

  1. Export and share (already in output/): - schedule_clusters_2027.csv - schedule_shift_watchlist_2027.csv - schedule_risk_matrix_2026_27.csv
  2. Walk ADs through /schedule-guide on the live site.
  3. Confirm cluster validation passed: bash python3 scripts/validate_schedule_clusters.py --forward Target: 5 tiers, median size ~8, blowout rate within tiers ≈ 0%.

Annually (off-season)

Task Script Gate
Walk-forward evidence run_walk_forward_backtest.py Grid CSV updated
Feature signal screen audit_feature_signal.py --subset large Spearman signs stable
Shift threshold tune tune_shift_detection.py Tier-A recall ≥ 0.7
Cluster method check validate_schedule_clusters.py fixed8 still passes
Unit tests python3 -m unittest discover -s tests All green

What each step does

Step Command Outputs
pipeline run_glax_analysis.py --season 2025-26 team_strength_*, projected_power_*, schedule_clusters_*, watchlist, risk matrix, run_meta.json
batch_pipeline batch_run_glax_pipeline.py --skip 2025-26 All output/backtest_* dirs
validate_forward audit_shift_detection.py --forward Refreshed schedule_shift_watchlist_2027.csv
walk_forward run_walk_forward_backtest.py backtest_grid_summary.csv, per-fold reports
validate_clusters validate_schedule_clusters.py --forward cluster_validation_report.md
shift_audit audit_shift_detection.py --min-tier full shift_detection_report.md
feature_signal audit_feature_signal.py feature_signal_report.md
tune_shifts tune_shift_detection.py Updates shift_detection_config.json
import website/scripts/import_data.py --only … Neon tables for live site
tests unittest discover -s tests Regression guard

Website import scope

Quick profile imports: team power/strength, projections, rankings, schedule risk, clusters/watchlist, run_meta.

Standard profile adds: backtest grid, holdout errors, benchmark tiers, markdown docs (cheat sheet), graduation/margin stats.

Full profile runs complete import_data.py (all tables except board edits).

Always set in website/.env:

DATABASE_URL=postgresql://...
SOURCE_DIR=../glax-pipeline

Repeat imports are safe (--skip-migrations after first deploy).


Pre-flight checklist


Post-flight verification


Troubleshooting

Symptom Likely cause Fix
Single cluster (36 teams) Old gap=4.5 export Re-run pipeline; confirm run_meta.cluster_method = fixed8
Empty watchlist Stale projected_power Re-run pipeline + audit_shift_detection.py --forward
Import fails on backtest_grid_folds Migration 018 not applied Run import without --skip-migrations once
Walk-forward fold missing No finish file for next year Add finish TSV or exclude season in registry
Shift recall dropped New season data pattern Run tune_shift_detection.py; review tier-A folds only

Manual one-offs

# Holdout only (2024-25 → 2026)
python3 run_glax_analysis.py --season both

# Single backtest season
python3 scripts/batch_run_glax_pipeline.py --season 2023-24

# Import only schedule artifacts
cd ../website && uv run python scripts/import_data.py \
  --only schedule_shift_watchlist backtest_grid --skip-migrations

Revision log

Date Notes
2026-06-07 Initial runbook + run_seasonal_refresh.py orchestrator

Flag codes reference

Action flags trigger a board conversation; info flags are diagnostic only.

Code Severity Means Follow-up
award_talent_loss action Departing award points >= 10 Real talent loss vs quality returning class
high_returning_still_down action Returning >= 55% but model still moves down >= 3 Is the assist/feeder structure intact?
large_move_vs_prior action Model wants to move them >= 4 slots from last year Confirm the roster turnover is real
legacy_downgrade action Legacy >= 0.82 but model moves down >= 2 Coach interview, off-season program signals
low_returning_heavy_loss action Returning < 35% AND drop >= 17 / loss >= 32% Underclass video; replacement candidates
backtest_large_error info (Backtest only) projection missed by >= 5 Worth a future audit
roster_composite_split info Composite re-sort disagrees by >= 8 slots Diagnostic only - usually composite is wrong