Codifies the scan-first/data-driven workflow proven on Cascades (where the baked-in
non-DFS bias picked the congested channels and a data-driven DFS plan halved 5GHz retry):
- NEW survey-report.py: rolls survey-collect JSON into the fleet per-channel/per-band-group
measured busy% table + cleanest/dirtiest ranking + a suggested clean 40MHz palette. The
decision-driver that was missing (we built it by hand).
- channel-plan.sh: na palette is now DATA-DRIVEN, not hardcoded non-DFS. Adds --channels
(explicit palette) + --dfs ok|avoid|only; default considers ALL 40MHz primaries and lets
measured busy% choose. Adds load-balancing + a local-search pass -> strong co-channel to 0.
- survey-collect.sh: per-AP "cleanest" report no longer pre-filters out DFS (DFS is usually
cleanest here); marks DFS with *, points at survey-report.
- SKILL.md: documents the mandatory scan -> survey-report -> channel-plan --channels -> apply
-> validate order + the Cascades lesson.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>