Answers "which 2.4 radios can we turn OFF given over-coverage, based on AP proximity." Greedy
dominating-set on the AP-to-AP 2.4 SNR layer: disables radios whose area stays covered by a nearby
ACTIVE-2.4 neighbor, maximizing interference-airtime removed without opening a 2.4 hole. Caps per-zone,
guards coverer capacity, flags single-coverer (low-resilience) disables, reports co-channel before/after.
Why separate from optimize-radios: optimize uses band-AGNOSTIC physical adjacency, so it counts an AP
whose ng radio is DISABLED as a "coverer" via its 5/6 GHz (observed: it proposed disabling 127/229/330/428
"covered by 128" — but 128's 2.4 is already disabled => those would be 2.4 holes). coverage-thin uses the
2.4 SNR layer specifically and only counts neighbors whose 2.4 stays ON.
Cascades (live): aggressive MINCOV=1 -> disable 36/76; resilient MINCOV=2 -> disable 34/76 with >=2 active
2.4 coverers each; co-channel ch6 28->13, ch11 25->13, ch1 20->13; ~2400 interference-airtime pts removed.
Read-only; needs NEIGHBOR_JSON. SKILL.md step 3b.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds `radio-usage.sh <site> <band> --ap "<AP name>"`: lists the devices on one AP's band by merging
live clients (stat/sta) with recent association events (wifi_connectivity_event, band-aware), enriched
from ace.user identity. Tags each device steerable vs legacy:
- from events: DUAL (also seen on 5/6 GHz -> steerable) vs NG-ONLY (2.4-only -> legacy/IoT)
- fallback when no event in the (short ~1d) retention window: randomized MAC = modern phone/laptop
(likely 5G/steerable) vs fixed vendor OUI = likely IoT/legacy.
Decision value: steerable -> fix via band-steering/min-RSSI; a legacy/IoT device present argues AGAINST
disabling that 2.4 radio. Needs controller cred for the live BSSID (vap_table) map; honest about the
short event retention. Validated live on Cascades (347, Dining Room).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Answers "is this 2.4 radio actually used?" from accumulated controller stats (ace_stat.stat_daily,
~77d). Reports per-AP time-avg concurrent users (<radio>-num_sta_avg) + peak station snapshot
(<band>-num_sta), distinguishing avg~0/peak>0 (takes bursts -> POWER-DOWN) from peak==0 (genuinely
unused -> disable-safe). With NEIGHBOR_JSON it crosses low-use APs against the AP-to-AP SNR matrix to
emit a defensible safe-to-disable shortlist (low-use AP + strong overlapping neighbor with headroom),
noting mutual-coverage conflicts and deferring conflict-free selection to optimize-radios.
Validated live on Cascades: of 76 APs only 1 has peak==0 over 77d (the offline AP 108); every other
2.4 radio takes real client bursts (peaks 5-58) at very low avg (12 APs <0.5 concurrent). I.e. the
usage history independently CONFIRMS the conservative power-down-not-disable call. Read-only (Mongo
plane). Uses var-assignment to avoid the legacy-mongo REPL echo. SKILL.md documents it as step 2b.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full verification of the skill against Cascades (live):
- All 19 scripts syntax-clean.
- Controller-side read-only validated live: sites, audit-site, switch-audit, live-stats, model-rank,
optimize-radios, monitor-run, gw-audit. Dry-run apply paths validated: apply-radio, apply-wlan,
client-control, device-control. AP-side mechanism validated: SSH auth + /proc/ui_neighbor read on a
sample AP; full neighbor-collect (74-AP SNR sweep) -> channel-plan end-to-end produced a 1/6/11 plan.
Fixes:
- optimize-radios.sh: the `for(k in prof)` loop's numeric completion value was REPL-echoed by the legacy
mongo shell (stray "94.56..." line in output). Terminated the loop body with `void 0` to suppress it.
- ASCII-clean printed output (CLAUDE.md no-non-ASCII): replaced em-dashes / Unicode arrows / § that
reached stdout and rendered as `?`/mojibake on the Windows console, across optimize-radios,
neighbor-collect, survey-collect, dfs-check, audit-site, sites, monitor-run, apply-radio, apply-wlan,
pfsense-backend. (Comment-only non-ASCII left as-is; never printed.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per Howard's decision (2026-06-16, "try what Mike wanted"): Mike's §E open decisions resolved as
REST API package backend + dispatch INSIDE the existing gateway verbs (his lean), not sibling scripts.
- NEW scripts/pfsense-backend.sh: pfSense REST API (pfSense-pkg-RESTAPI v2, X-API-Key) driver exposing
the same verbs as gw-control (audit, pf-list/disable/enable/delete/set-ports, fw-list/disable/enable,
block-ips) + a `setup` helper. Writes --apply-gated with per-object rollback to .claude/tmp + firewall/apply.
- gw-audit.sh: when num_gw=0 and a clients/<slug>/pfsense-api cred is vaulted (or --pfsense <slug>),
appends the pfSense WAN/DHCP/firewall audit; else prints the setup hint. (captures num_gw to gate.)
- gw-control.sh: same-verb auto-dispatch to pfsense-backend when a pfSense cred resolves for the site.
- SKILL.md [PROPOSED]->[SCAFFOLDED]; ROADMAP §E open decisions marked resolved.
STATUS: scaffolded. BLOCKED/setup/no-cred paths tested; gw-audit dispatch validated live (Cascades
num_gw=0 -> hint). Live REST calls pending a reachable pfSense with the API pkg + a vaulted key; v2
endpoint paths must be verified against the installed API version on first live run.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the invoice Message (note) automation into a single reusable helper
set_invoice_note <invoice_id> <customer_id> [pre_billing_prepay]:
- no block (prepay_hours==0) -> "Interested in discounted labor? Ask us about block-rate pricing."
- block, >=4 hrs left -> "Block hours remaining: N."
- block, <4 hrs left -> remaining + renew line, AND tags Winter (<@624666486362996755>)
in #bot-alerts (low-block heads-up; mentions ping, no allowed_mentions)
Pre-billing prepay arg keeps a just-depleted block counted as a block customer (shows renew, not upsell).
Never clobbers a non-empty note.
Wired into billing Step 3 (set_invoice_note "$INVOICE_ID" "$CUST_ID" "$PREPAY"), and a new
"Recurring invoice note sweep" applies the same policy to Syncro's auto-generated recurring invoices
(schedule_id != null, recent, current balance) — idempotent, run after each recurring run.
Branch logic + a real e2e note set/restore validated on the ACG internal test account (#67741); the
<4hr Winter alert was stubbed in testing so no real ping fired.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The on-screen "Invoice Message" text block IS the invoice `note` field, editable via
PUT /invoices/{id} {"note": "..."} (response {"invoice": {...}}). Verified on the ACG
internal test account (#67741: set/verify/restore).
Billing flow now sets a one-line upsell hint on the invoice note — "Interested in
discounted labor? Ask us about block-rate pricing." — ONLY for customers with no prepaid
block (prepay_hours == 0). Block customers (prepay_hours > 0) get no hint; never clobber
a non-empty note.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both surfaced on GND-SERVER (Server 2019 DC), would mis-grade every Windows Server:
1. OS EOL: build numbers are SHARED between client and server SKUs (17763 = Win10
1809 AND Server 2019; 14393 = 1607/Server2016; 26100 = 24H2/Server2025). The map
only had client dates, so Server 2019 (supported to 2029) was flagged EOL-2020 =
false critical. Now branch on SKU ($caption -match 'Server') with a Server EOL map.
2. Stability disk errors: ids 7/51/153 are shared across providers; provider 'disk'
= real I/O error, but 'Microsoft-Windows-Kernel-Boot' id 153 = "VBS disabled" boot
noise. The unfiltered fallback counted that noise as disk errors (false warning on
healthy boxes). Now count only true storage providers, no unfiltered fallback.
Parses clean. Re-run on GND-SERVER should drop from RED to AMBER (both false findings gone).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First onboarding-diagnostic baseline for GND-SERVER (Grabb & Durando DC/file/RRAS box,
gd.local, 192.168.242.200). Grade RED: 2 critical (host firewall OFF on all profiles;
OS-EOL flag — false positive, build 17763 is Server 2019, supported to 2029), 6 warning
(Defender/AV unconfirmed, built-in Administrator enabled, 1 pending update, 2 disk errors
/14d, pending reboot, 2 stopped auto services), plus tempadmin local admin + no confirmed
BitLocker. Immutable JSON + report under onboarding-baselines/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capture the "UniFi APs/switches behind a pfSense gateway" topology (Cascades, our
office, several clients) as a first-class roadmap item: make the gateway verbs
(gw-audit / gw-control / VPN) work against pfSense via a thin driver behind the
same verbs (gw-audit already detects num_gw=0 = third-party firewall).
Includes the verb->pfSense mapping (NAT port-forwards, filter rules,
easyrule block-ips, native OpenVPN/IPsec/WireGuard), ranked backend options
(REST-API pkg vs stock SSH easyrule/pfSsh.php vs diag_command.php vs config.xml),
existing vaulted pfSense creds (Cascades + office), and open decisions. SKILL.md
status block notes the proposed layer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The write companion to gw-audit. Closes/scopes internet-facing port-forwards and
toggles WAN firewall rules at the USG/UXG/UDM via the RW controller REST admin.
Actions: pf-list / pf-disable / pf-enable / pf-delete / pf-set-ports / pf-set-src,
fw-list / fw-disable / fw-enable, block-ips (WAN address-group + WAN_IN drop rule).
Reads via Mongo (no cred); writes via login->CSRF->REST (rest/portforward,
rest/firewallrule, rest/firewallgroup). DRY-RUN default, --apply gated on
infrastructure/uos-server-network-api-rw, rollback saved to .claude/tmp.
Dry-run validated on Grabb & Durando (USG-3P): identifies the live "VPN" forward
(80,443,1723 -> 192.168.242.200) + the "GRE" WAN_IN accept that back an
internet-exposed, brute-forced PPTP. Closes the ROADMAP firewall/port-forward item.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>