# 03 — Quality Scale Audit Rubric Every file in `docs/audits/` follows this rubric exactly, section for section. The rubric is a faithful mapping of the **official Home Assistant Integration Quality Scale** (developers.home-assistant.io → "Integration Quality Scale", post-2024 revamp; tier definitions ratified via ADR-0007 and its successor docs) onto an auditable checklist. Rule IDs below are the official rule slugs used in `quality_scale.yaml`. ## 1. Audit document structure (mandatory sections) 1. **Integration profile** — domain, popularity band, issue pressure, code-owner liveness, backing library and its maintenance state, current declared scale, proposed target tier with rationale. 2. **Rule-by-rule gap table** — every rule at or below the target tier, with status `DONE` / `GAP` / `PARTIAL` / `EXEMPT-CANDIDATE`, plus evidence. 3. **Focus-area narratives** — prose analysis of the six areas the milestone brief names explicitly: config flow, type hints, diagnostics, reauth, entity naming, test coverage. 4. **Risk notes** — hardware/protocol/library risks specific to this integration. 5. **Re-verification before work starts** — the exact checks to repeat at P0. 6. **Work-item summary** — the items feeding `06-work-item-backlog.md`. ## 2. Evidence standards A rule may be marked `DONE` only with concrete evidence: a file/function that implements it (e.g., "`diagnostics.py` present, redacts `CONF_PASSWORD` via `async_redact_data`") or an explicit `done` entry in the integration's `quality_scale.yaml`. `GAP` entries must state what is missing *and* what the fix looks like. `EXEMPT-CANDIDATE` must cite the official exemption rationale pattern (e.g., `discovery` exempt for cloud-polling integrations with no discoverable device). Vague statuses are not acceptable in audits. Because audits are snapshots (see README caveat), every `DONE`/`GAP` is implicitly "as of snapshot"; §5 of each audit defines re-verification. ## 3. The rule checklist used by every audit ### 3.1 Bronze | Rule ID | Plain meaning | |---|---| | `action-setup` | Service actions registered in `async_setup`, not `async_setup_entry`; validate config entry state in the action | | `appropriate-polling` | Polling interval is sensible for the device class | | `brands` | Brand image assets in `home-assistant/brands` | | `common-modules` | Coordinator in `coordinator.py`, shared base entity in `entity.py` | | `config-flow` | UI-driven setup via config flow; no YAML-only setup | | `config-flow-test-coverage` | 100% test coverage of the config flow (project convention; we hold ourselves to ≥95% line + all flow outcomes) | | `dependency-transparency` | Library is open source, published from public CI, source link in package metadata | | `docs-actions`, `docs-high-level-description`, `docs-installation-instructions`, `docs-removal-instructions` | Baseline docs on home-assistant.io | | `entity-event-setup` | Entities subscribe/unsubscribe to events in `async_added_to_hass` / `async_will_remove_from_hass` | | `entity-unique-id` | Every entity has a stable unique ID | | `has-entity-name` | Entities use `_attr_has_entity_name = True` with proper naming (device name + entity name composition) | | `runtime-data` | Runtime objects stored in `entry.runtime_data` (typed `ConfigEntry[...]`), not `hass.data` | | `test-before-configure` | Config flow validates connectivity/credentials before creating the entry | | `test-before-setup` | `async_setup_entry` raises `ConfigEntryNotReady` / `ConfigEntryAuthFailed` appropriately when the device is unreachable / auth fails | | `unique-config-entry` | Duplicate entries prevented (unique ID or data matching) | ### 3.2 Silver | Rule ID | Plain meaning | |---|---| | `action-exceptions` | Service actions raise `HomeAssistantError`/`ServiceValidationError` on failure | | `config-entry-unloading` | `async_unload_entry` implemented; clean teardown of listeners/sessions | | `docs-configuration-parameters`, `docs-installation-parameters` | Options & setup parameters documented | | `entity-unavailable` | Entities flip to unavailable when the device/service is unreachable | | `integration-owner` | CODEOWNERS entry exists | | `log-when-unavailable` | Log once (info) when unavailable, once when back — no log spam | | `parallel-updates` | `PARALLEL_UPDATES` set appropriately (esp. for non-coordinator polling / action-heavy platforms) | | `reauthentication-flow` | `async_step_reauth` so credential changes don't require delete-and-re-add | | `test-coverage` | ≥ 95% test coverage of the whole integration | ### 3.3 Gold (audited only where the track targets Gold items) `devices`, `diagnostics`, `discovery`, `discovery-update-info`, `docs-data-update`, `docs-examples`, `docs-known-limitations`, `docs-supported-devices`, `docs-supported-functions`, `docs-troubleshooting`, `docs-use-cases`, `dynamic-devices`, `entity-category`, `entity-device-class`, `entity-disabled-by-default`, `entity-translations`, `exception-translations`, `icon-translations`, `reconfiguration-flow`, `repair-issues`, `stale-devices`. Note: although `diagnostics` is formally a Gold rule, we treat it as a **default-include item in every track** because it is cheap (one file) and is the single biggest supportability win for issue triage. The milestone brief names it explicitly. ### 3.4 Platinum (audited opportunistically) | Rule ID | Plain meaning | |---|---| | `async-dependency` | Backing library is asyncio-native | | `inject-websession` | Library accepts HA's shared `aiohttp`/`httpx` session | | `strict-typing` | Full strict typing; integration listed in core's `.strict-typing`; library is PEP-561 (`py.typed`) | ## 4. Focus-area mapping (brief → rules) The milestone brief names six audit areas; they map onto rules as follows, and each audit's §3 narrates them in this order: | Brief area | Primary rules | |---|---| | Config flows | `config-flow`, `test-before-configure`, `unique-config-entry`, `reconfiguration-flow` | | Type hints | `strict-typing`, `runtime-data` (typed entries), library `py.typed` | | Diagnostics | `diagnostics` (+ redaction correctness) | | Reauth | `reauthentication-flow`, `test-before-setup` (`ConfigEntryAuthFailed` path) | | Entity naming | `has-entity-name`, `entity-translations`, `entity-device-class` | | Test coverage | `config-flow-test-coverage`, `test-coverage` | ## 5. Severity and sizing vocabulary used in audits and backlog - **Severity:** `S1` user-blocking (silent auth death, setup failure loops), `S2` user-degrading (log spam, wrong names, no diagnostics), `S3` internal (typing, structure). - **Size:** `XS` ≤ 60 changed lines, `S` ≤ 200, `M` ≤ 400, `L` ≤ 800. Anything that would exceed `L` must be split before it becomes a work item — this feeds the PR-sizing conventions in doc 04 §5. ## 6. How audits were produced (method note) Each audit was produced by reading the integration source under `homeassistant/components//` and `tests/components//` at the snapshot commit, its `manifest.json`/`strings.json`, the backing library's repo and PyPI metadata, the open issue list filtered by the integration label, and the integration's page on home-assistant.io. Coverage figures cited are from the project's coverage tooling at snapshot and are bands (e.g., "~70–80%"), not exact claims, because exact numbers move with every release — the P0 re-verification work item pins exact numbers before any PR is scoped.