Reconcile dev from main

Reconcile dev from main (hotfixes and release commits)
This commit was merged in pull request #141.
This commit is contained in:
2026-09-21 13:54:50 -04:00
9 changed files with 96 additions and 16 deletions
+2 -2
View File
@@ -13,14 +13,14 @@ NestQuest helps households coordinate daily life, chore tracking, custody schedu
1. Ensure [HACS (Home Assistant Community Store)](https://hacs.xyz/) is installed. 1. Ensure [HACS (Home Assistant Community Store)](https://hacs.xyz/) is installed.
2. In Home Assistant, open **HACS** > **Integrations**. 2. In Home Assistant, open **HACS** > **Integrations**.
3. Select the three-dots menu in the top right and click **Custom repositories**. 3. Select the three-dots menu in the top right and click **Custom repositories**.
4. Enter the repository URL: `https://github.com/talon2king/NestQuest` 4. Enter the repository URL: `https://github.com/Maniacs-Mansion/NestQuest`
5. Select category **Integration** and click **Add**. 5. Select category **Integration** and click **Add**.
6. Search for **NestQuest** and click **Download**. 6. Search for **NestQuest** and click **Download**.
7. Restart Home Assistant. 7. Restart Home Assistant.
> **Note:** Development happens in the private Gitea repository > **Note:** Development happens in the private Gitea repository
> (`https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest`). The GitHub > (`https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest`). The GitHub
> repository is a public mirror maintained for HACS distribution. > repository is a private mirror maintained for HACS distribution.
### Manual Installation ### Manual Installation
+82
View File
@@ -1,5 +1,87 @@
# NestQuest Release Notes # NestQuest Release Notes
## Version 0.5.0 — 2026-09-18
### Scope
Fifth release of NestQuest, promoting Features 06, 07, and 08 from `dev` onto 0.4.0 (quest model, children registry, presence engine):
- **Feature 06 — quest definitions and assignment:** business layer `quest_definitions.py` over the multi-assignee / multi-window model (D-008). Create/edit/deactivate definitions; assign/unassign children; query helpers for materialization. `ScheduleRule` ⇄ `schedule_rules` storage mapping in the DAO layer.
- **Feature 07 — instance materialization:** `materialize.py` fans out one instance per (definition, child, window, firing date) when the recurrence rule fires and the child is present. Idempotent on `(definition_id, child_id, due_date, window)`; skips completed instances; never generates past dates. HA-local `today` and configured `horizon_days` threaded end-to-end. Regeneration on definition/assignee/presence changes; daily rollover listener; startup backfill; `nestquest.regenerate` service.
- **Feature 08 — completion and event log:** business layer `completion.py` wrapping the existing append-only `CompletionEventsDao`. `complete_instance` / `uncomplete_instance` (reversal event, original row untouched); state derived from the latest event; HA-local `was_on_time`; derived `missed` (never stored). Actor shapes `'user'` / `'panel'` with `actor_child_id` (D-008). Permission gating of un-complete is Feature 09.
### Requirements
- Minimum Home Assistant version: **2024.6.0**.
### Breaking changes
- None at the schema layer. Schema remains **version 6** (advanced in 0.4.0). This release adds application code only.
### Known Limitations
- No permission decorator yet (Feature 09): `complete_instance` / `uncomplete_instance` are Python APIs, not gated HA services. The only HA service in this release is `nestquest.regenerate`.
- No entity or event surface (Feature 10): no sensors, no `nestquest_quest_completed` bus events.
- No panel or admin Lovelace cards (Features 12/13).
- Un-complete is implemented but not admin-gated until Feature 09.
- Single-instance only.
### Rollback
Schema version is unchanged from 0.4.0 (still 6). A code downgrade to 0.4.0 does not require a database restore for schema compatibility, but **back up `nestquest.db` (plus `-wal`/`-shm`) before upgrading** — generated instances and completion events written by 0.5.0 remain in the file after downgrade; 0.4.0 will not generate or complete them.
**HACS installs:**
1. Downgrade via HACS (HACS > Integrations > NestQuest > Redownload > 0.4.0).
2. Restart Home Assistant.
**Manual installs:**
1. Replace `custom_components/nestquest/` with the 0.4.0 tree.
2. Restart Home Assistant.
**Repository operators:** git-revert the 0.5.0 release merge on `main`.
## Version 0.4.0 — 2026-09-15
### Scope
Fourth release of NestQuest, shipping three completed features on top of the data layer and recurrence engine:
- **Feature 14 — quest domain model (BREAKING rename, D-007/D-008):** `task_definitions`/`task_instances` are now `quest_definitions`/`quest_instances` everywhere; definitions are multi-assignee (`quest_definition_assignees`, no single child column) and multi-window (`quest_definition_windows` — morning/afternoon/evening, clock ranges in `const.py`); the instance key widens to `(definition_id, child_id, due_date, window)` so twice-daily and shared quests materialise one instance per child per window per day; `completion_events` gains `actor_child_id` (the tapped panel profile; admin events stay NULL) with a second actor-pair CHECK. Schema version advances to **6**.
- **Feature 03 — children registry & admin allowlist:** typed business layer for child profiles (create/edit with trimmed required names and duplicate-name warnings, whole-table reorder, deactivation as the only removal path — history is never touched); the admin allowlist with a fail-closed `is_admin` resolver (empty allowlist denies everyone; the last admin cannot be removed); automatic seeding on first setup (the config-flow user is persisted in entry data, with the options-flow list and HA owner accounts as fallbacks) so the owner is never locked out; the options flow gains a multi-select picker of existing Home Assistant users.
- **Feature 05 — presence & custody engine:** a pure `PresenceSchedule`/`PresenceOverride` model with total validation and lossless round-trips; anchor-date cycle arithmetic (D-004 — never ISO week numbers or parity); `PresenceEngine.is_present` with the always-present default for schedule-free children; date overrides beat the repeating pattern; a `next_present_dates` preview for the admin schedule UI; a full-year regression guard walking 2020 (366 days, ISO week 53) and 2015 (365 days, ISO week 53) proving the rotation never inverts at a year boundary.
### Requirements
- Minimum Home Assistant version: **2024.6.0**.
### Breaking changes
- The database schema advances from version 1 to version 6. Upgrades are automatic and data-preserving (legacy `task_*` tables are renamed; the definitions table is rebuilt for multi-assignee; legacy instances pin to the morning window; panel events backfill `actor_child_id`). **Back up `nestquest.db` before upgrading** — see Rollback.
- Dev installs from before 2026-09-15 migrate automatically on first start.
### Known Limitations
- Still no entities, services, calendar platforms, or panel cards: nothing is user-visible in Home Assistant yet (Features 06-10 land the business layer, materialisation, permission gate, and entity surface).
- No materialisation yet: instances are not generated from definitions + presence yet (Feature 07).
- Single-instance only.
### Rollback
**Back up `nestquest.db` (plus `-wal`/`-shm` sidecars) BEFORE upgrading.** This release migrates the database to schema version 6 with renamed tables; an older NestQuest build refuses a version-6 database ("newer than this integration understands") and cannot read renamed tables at all — a plain downgrade is NOT possible once the database has been migrated.
**Rollback procedure (after a migrated database):**
1. Stop Home Assistant.
2. Restore the pre-upgrade `nestquest.db` backup (and sidecars) you made before upgrading.
3. Downgrade the integration (HACS > Integrations > NestQuest > Redownload > 0.3.0) or replace `custom_components/nestquest/` manually.
4. Restart Home Assistant.
Without a backup, the only path is staying on 0.4.0 (recommended) or deleting the integration and its database to start fresh (Settings > Devices & Services > NestQuest > Delete, then remove `nestquest.db`).
**Repository operators:** instead of uninstalling, git-revert the release merge on `main` and restore the pre-upgrade database.
## Version 0.3.0 — 2026-09-14 ## Version 0.3.0 — 2026-09-14
### Scope ### Scope
+3 -3
View File
@@ -9,10 +9,10 @@
"frontend", "frontend",
"http" "http"
], ],
"documentation": "https://github.com/talon2king/NestQuest", "documentation": "https://github.com/Maniacs-Mansion/NestQuest",
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"issue_tracker": "https://github.com/talon2king/NestQuest/issues", "issue_tracker": "https://github.com/Maniacs-Mansion/NestQuest/issues",
"requirements": [], "requirements": [],
"version": "0.3.0" "version": "0.5.0"
} }
-2
View File
@@ -25,7 +25,6 @@ from collections.abc import Callable, Iterable
from typing import Any from typing import Any
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity, SensorEntity,
SensorStateClass, SensorStateClass,
) )
@@ -280,7 +279,6 @@ class NestQuestCompletionPctTodaySensor(_NestQuestChildDaySensor):
"""The child's completion percentage for today (100 on a zero-quest day).""" """The child's completion percentage for today (100 on a zero-quest day)."""
_attr_state_class = SensorStateClass.MEASUREMENT _attr_state_class = SensorStateClass.MEASUREMENT
_attr_device_class = SensorDeviceClass.PERCENTAGE
_attr_native_unit_of_measurement = "%" _attr_native_unit_of_measurement = "%"
_name_suffix = "completion pct today" _name_suffix = "completion pct today"
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "nestquest" name = "nestquest"
version = "0.3.0" version = "0.5.0"
description = "NestQuest Home Assistant Integration" description = "NestQuest Home Assistant Integration"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
+2 -2
View File
@@ -67,9 +67,9 @@ def test_readme_exists_and_contains_required_sections() -> None:
def test_readme_hacs_points_at_public_github_mirror() -> None: def test_readme_hacs_points_at_public_github_mirror() -> None:
"""Verify README directs HACS users to the public GitHub mirror, not the private Gitea repo.""" """Verify README directs HACS users to the public GitHub mirror, not the private Gitea repo."""
content = README_PATH.read_text(encoding="utf-8") content = README_PATH.read_text(encoding="utf-8")
assert "https://github.com/talon2king/NestQuest" in content assert "https://github.com/Maniacs-Mansion/NestQuest" in content
assert "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest" in content assert "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest" in content
assert content.index("https://github.com/talon2king/NestQuest") < content.index( assert content.index("https://github.com/Maniacs-Mansion/NestQuest") < content.index(
"https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest" "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest"
) )
+3 -3
View File
@@ -58,19 +58,19 @@ def test_manifest_requirements(manifest: dict) -> None:
def test_manifest_documentation_url_is_public_github_mirror(manifest: dict) -> None: def test_manifest_documentation_url_is_public_github_mirror(manifest: dict) -> None:
"""Verify documentation URL points at the public GitHub mirror.""" """Verify documentation URL points at the public GitHub mirror."""
assert manifest.get("documentation") == "https://github.com/talon2king/NestQuest" assert manifest.get("documentation") == "https://github.com/Maniacs-Mansion/NestQuest"
def test_manifest_issue_tracker_url_is_public_github_mirror(manifest: dict) -> None: def test_manifest_issue_tracker_url_is_public_github_mirror(manifest: dict) -> None:
"""Verify issue_tracker URL points at the public GitHub mirror.""" """Verify issue_tracker URL points at the public GitHub mirror."""
assert manifest.get("issue_tracker") == "https://github.com/talon2king/NestQuest/issues" assert manifest.get("issue_tracker") == "https://github.com/Maniacs-Mansion/NestQuest/issues"
@pytest.mark.parametrize("key", ["documentation", "issue_tracker"]) @pytest.mark.parametrize("key", ["documentation", "issue_tracker"])
def test_manifest_urls_use_github_com(manifest: dict, key: str) -> None: def test_manifest_urls_use_github_com(manifest: dict, key: str) -> None:
"""Verify manifest URLs use github.com rather than the private Gitea host.""" """Verify manifest URLs use github.com rather than the private Gitea host."""
url = manifest.get(key, "") url = manifest.get(key, "")
assert url.startswith("https://github.com/talon2king/NestQuest") assert url.startswith("https://github.com/Maniacs-Mansion/NestQuest")
assert "cubecraftlabs" not in url assert "cubecraftlabs" not in url
+1 -1
View File
@@ -143,7 +143,7 @@ async def test_every_active_child_gets_five_sensors_with_documented_identity(
assert entity.native_unit_of_measurement is None assert entity.native_unit_of_measurement is None
assert pct_entity.state_class == "measurement" assert pct_entity.state_class == "measurement"
assert pct_entity.native_unit_of_measurement == "%" assert pct_entity.native_unit_of_measurement == "%"
assert pct_entity.device_class == "percentage" assert pct_entity.device_class is None
device_info = due_entity.device_info device_info = due_entity.device_info
assert device_info.name == f"NestQuest {name}" assert device_info.name == f"NestQuest {name}"
assert device_info.manufacturer == "NestQuest" assert device_info.manufacturer == "NestQuest"
Generated
+1 -1
View File
@@ -108,7 +108,7 @@ wheels = [
[[package]] [[package]]
name = "nestquest" name = "nestquest"
version = "0.3.0" version = "0.5.0"
source = { virtual = "." } source = { virtual = "." }
[package.dev-dependencies] [package.dev-dependencies]