CHANGELOG.md
Rama main · réplica de lectura del repositorio original.
# Changelog
All notable changes to UnifiOptimizer are recorded here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.8.2] — 2026-09-23
### Changed
- The package author and add-on maintainer contact is now a GitHub noreply
address. No code changes.
## [0.8.1] — 2026-09-06
A reliability release. Six failures found by running the daemon against a real
network for a week — not by the test suite — where a large, long-lived store
exposed scheduling and coercion edges that small fixtures never reach. All are
backward compatible.
### Fixed
- The daily detection pass (`detect_daily`) crashed every night with
`ValueError: 'rogue_bss' is not a valid EntityType`. The rogue-AP scanner stores
neighbour BSSes as `rogue_bss` inventory rows that are deliberately not an
`EntityType`; the daily config audit enumerates every entity type and choked
coercing the first such row. The detector's entity view now skips inventory-only
rows (and logs any genuinely unknown type) instead of aborting the whole pass.
- The same crash had a second site in the issue-engine reconciliation step that
only running the real daily pass against a live store surfaced: the
"Foreign AP broadcasting our SSID" issues are keyed to those `rogue_bss` rows,
so the parentage-inhibition lookup (`get_entity`) coerced the type and failed
the pass *after* every detector had already produced findings. An issue
legitimately references its `rogue_bss` entity, so the lookup now returns the
row with its raw type (as the collector stores it) rather than excluding it.
- `sle_minutes` and `anomalies` silently stopped running on a busy daemon. Both
jobs were being dropped by APScheduler's 1-second misfire window whenever a
detect/correlate pass held the single event loop a few seconds past their fire
time — on a large store, every run. Jobs now carry a generous misfire grace, so
a slipped run executes once when the loop frees instead of vanishing.
- `retention_prune` reported `UNKNOWN` in `/api/health` forever: the nightly prune
ran but never recorded a `poll_runs` row, so health could not tell a prune that
ran from one that never fired. It now records each run.
- `/api/health` now reports *why* a job is failing (`last_error` / `last_failure_ts`
from the most recent failed run), and the dashboard's source-health panel shows
that reason inline. Previously the surface said a job was "failing" but the cause
was only readable by shelling into the host and querying the database.
- The container image pinned `mcp>=1.2` with no upper bound, so a fresh build
resolved mcp 2.0 and the `/mcp` mount died at import (`'Server' object has no
attribute 'list_tools'`, a 503). Pinned to `mcp>=1.2,<2` to match the packaging
extra until the server is ported to the 2.x API.
## [0.8.0] — 2026-09-06
A safety and correctness release. Every controller-facing and incident-facing
path was audited and hardened; the changes are backward compatible (the CLI, API,
and on-disk layout are unchanged, and the schema upgrades in place on first run).
### Added
- `netadmin doctor --offline` — a store-only health check that never touches the
controller: it reports whether the database is present and migratable, the schema
version, whether credentials are configured, the age of the last successful poll
per collector, and any detected collection gaps, with stable exit codes for
scripting.
- Incident UI: fix outcomes now render their real state (`applying` / `failed` /
`unknown` / `applied` / `reverted`) with per-step results; the timeline and
collector strip show where the collectors had no visibility (so a gap never
reads as "nothing happened"); a resolved incident shows how long it lasted and
preserves its whole story including cleared symptoms; the change ledger is
filterable and its revert control opens a real approval diff.
### Fixed
Controller safety
- Data collection is strictly GET-only; report/event/session reads that used POST
are GET or reported unavailable, and only the approved, revertible fix writer
ever issues a non-GET.
- A mutation is never auto-retried: an uncertain outcome (lost response, connection
reset, unparseable or timing-out reply) is reported as *unknown*, never replayed
and never mislabeled as failed or applied.
- Authenticated controller requests no longer follow cross-origin redirects, so the
API key cannot leak to another host; concurrent auth failures honor the
controller's `Retry-After` cooldown.
Fixes you apply
- One-way actions (PoE power-cycle, min-RSSI removal) are advisory — surfaced as a
recommendation, never executed — because they cannot be reverted.
- A revert restores only the fields the original change touched, rebuilt from fresh
live state, and refuses if the device drifted; apply and revert serialize per
device and re-check eligibility, so concurrent operators cannot clobber each other.
- Verification arms only for confirmed-successful steps; a failed apply is no longer
recorded as applied.
History you can trust
- An incident keeps its full membership history (append-only, with joined/cleared
timestamps) while its current severity and symptom counts reflect only what is
still open — consistently across the dashboard, issue pages, MCP, and the LLM
dossier.
- Event-based detectors freeze to *unknown* during an event-feed outage instead of
falsely clearing a real issue; event catch-up and metric backfill track observed
coverage so a gap is retried rather than silently lost.
- Four wired root-cause rules (port flapping, bad cable, STP loop, broadcast storm)
that never fired in production now do, because feeder-port topology derived from
GET inventory reaches the correlation graph.
Accuracy
- Historical SLE minutes attribute to the AP a client was actually on at the time,
split correctly across roams.
- Metric history uses the real retention clock and aligns retention tiers so each
interval is counted exactly once.
- First-run setup is serialized, so concurrent requests cannot overwrite
configuration or disclose the owner's token.
### Changed
- Schema migrations `0010`–`0013` run automatically on first start (incident
membership history, the event-source coverage ledger, per-AP SLE attribution, and
event-repair fairness). Upgrades are forward-only and preserve existing data.
[0.8.2]: https://github.com/gneitzke/UnifiOptimizer/releases/tag/v0.8.2
[0.8.1]: https://github.com/gneitzke/UnifiOptimizer/releases/tag/v0.8.1
[0.8.0]: https://github.com/gneitzke/UnifiOptimizer/releases/tag/v0.8.0