Commit Graph
13 Commits
Author SHA1 Message Date
overseer 43f68fb7a3 Address PR #142 review: core isolation invariant and shim fixes
- Point recurrence purity scan at custom_components.nestquest.core.recurrence
  (P2.1); the shim only re-exports, so the real model was no longer scanned.
- Make test_const/test_config_flow/test_dao_matrix module scans recursive
  (rglob) so custom_components/nestquest/core/ is covered (P2.2, P2.3);
  const.py name exclusion retained.
- Add test_core_isolation: AST-scan every core module for homeassistant
  imports, relative imports climbing out of core (level > 1), absolute
  imports reaching custom_components.nestquest.<not core>, and bare
  'import core'/'from core' (P2.4).
- Drop the private re-export of _month_end/_nth_weekday_of_month from the
  recurrence shim; those are test-only, so move the two test imports to
  custom_components.nestquest.core.recurrence (P2.1/P3.1).
- Fix shim docstrings and core/__init__.py to reference
  custom_components.nestquest.core.* instead of the unimportable 'core.*'
  paths (P3.2).
- Note the migrations logger namespace move to
  custom_components.nestquest.core.migrations in RELEASE_NOTES.md (P3.3).
2026-09-21 14:43:43 -04:00
overseer fb3c6601c1 Bundle core inside the integration
HACS and the manual install copy only custom_components/nestquest, so
the integration shipped with a top-level 'core' package that was never
deployed and failed with ModuleNotFoundError: No module named 'core'.

Move core/ into custom_components/nestquest/core/, repoint the
re-export shims to relative '.core.X' imports, and update tests
(import paths, monkeypatch targets, caplog logger) to the new
'custom_components.nestquest.core.X' location. The integration is now
self-contained: nothing under custom_components/nestquest reaches
outside its own tree, the migrations logger becomes
custom_components.nestquest.core.migrations, and the SQL-leak guard
allowlists point at the new core/ paths.

uv run pytest -q: 1496 passed.
2026-09-21 14:28:49 -04:00
overseer 518f8b80ae Extract the storage layer into core 2026-09-21 14:20:51 -04:00
overseer 9836a6f524 Core generation walk: assignees x windows fan-out with presence (Feature 07, task 1) (#75) 2026-09-17 14:01:20 -04:00
overseer fa40e21dc2 Fix feature-integration review findings F06-001 and F06-002 (#74) 2026-09-17 12:06:47 -04:00
overseer 4606d7ddd0 Assign and unassign children on a quest definition (Feature 06, task 4) (#69) 2026-09-17 09:21:21 -04:00
overseer 802258d431 Edit a quest definition's metadata, schedule rule, and windows (Feature 06, task 3) (#68) 2026-09-17 09:06:11 -04:00
overseer d9cdac87d7 Create a quest definition with rule, assignees, and windows (Feature 06, task 2) (#67) 2026-09-17 08:41:56 -04:00
overseer cbd29f5ab7 Map ScheduleRule models to and from schedule_rules storage (Feature 06, task 1) (#66) 2026-09-17 08:11:19 -04:00
Joshua King ebc2064222 Quest windows via quest_definition_windows (D-008)
Per IMPLEMENTATION-ORDER.md 1.4: a quest_definition_windows table
(definition_id, window, due_time) declares which day windows a
definition spans, one row per definition+window via the composite
primary key; window is CHECK-constrained to morning/afternoon/evening.

- const.py: WINDOW_MORNING/AFTERNOON/EVENING, QUEST_WINDOWS canonical
  order, WINDOW_CLOCK_RANGES with the documented boundaries and the
  boundary-to-later-window rule documented for Feature 07.
- schema.py: windows DDL joins SCHEMA_V1_STATEMENTS; due_time is
  caller-validated strict HH:MM (DAO layer), matching the date policy.
- migrations.py: migration 4 is a plain CREATE IF NOT EXISTS statement
  list (no rebuild needed for a new table).
- dao_rules.py: QuestDefinitionsDao gains upsert_window (idempotent
  per window, due_time refresh in place), list_windows (const order),
  remove_window; _validate_time/_validate_window helpers.
- tests: windows DDL shape/constraints (CHECK matrix, duplicate,
  unknown definition), DAO round-trips, const range shapes; leak
  guards extended to the new table.
2026-09-15 10:05:43 -04:00
Joshua King 25f0a43e16 Multi-assignee definitions via quest_definition_assignees (D-008)
Per IMPLEMENTATION-ORDER.md 1.3: a quest_definition_assignees table
(definition_id, child_id) with a composite primary key and FKs to
quest_definitions and children replaces the single child_id column on
quest_definitions.

- schema.py: definitions DDL loses child_id; new assignees table joins
  SCHEMA_V1_STATEMENTS; the definitions table body is extracted to
  QUEST_DEFINITIONS_TABLE_SQL so the rebuild migration cannot drift
  from the canonical shape.
- migrations.py: callable migrations now own their transaction and
  stamp via stamp_schema_version(database, version), because the
  rebuild must bracket itself with PRAGMA foreign_keys OFF/ON. New
  migration 3 creates the assignees table and, when a pre-D-008
  quest_definitions still carries child_id, rebuilds the table via
  SQLite's canonical procedure (create, copy, move assignees, drop,
  rename), verifies PRAGMA foreign_key_check inside the transaction,
  and stamps. Migration 2 converted to the new callable contract.
- dao_rules.py: QuestDefinitionsDao.create drops the child_id argument
  and applies assignee_child_ids atomically; set_assignee is replaced
  by add_assignee (idempotent, active-child validated under the
  connection lock), remove_assignee and list_assignees; list_by_child
  reads through the assignees table.
- dao_instances.py: upsert validates the child against
  quest_definition_assignees instead of the definitions column.
- tests: schema shape tests for the assignees table (columns, PK, FKs,
  duplicate/unknown constraints); DAO tests rewritten for the new
  assignment API; migration tests for the v2 -> v3 rebuild (rows and
  instances survive, child_id gone, foreign_key_check clean).
2026-09-15 09:59:29 -04:00
Joshua King c988f6508b Rename task_* tables to quest_* with legacy-dev migration path (D-007)
Per design/IMPLEMENTATION-ORDER.md 1.1 and decision D-007: the domain
model takes the quest vocabulary pre-release. SCHEMA_V1 now creates
quest_definitions/quest_instances directly; DAO classes, records and
error messages follow (QuestDefinitionsDao, QuestInstancesDao). The
migration runner gains migration 2, an async-callable step that detects
surviving task_* tables on dev machines and ALTER TABLE RENAMES them
inside the migration transaction, with FK clauses rewritten by SQLite.
Migration 1's content rewrite is legal because nothing was ever
shipped; dev databases stamped version 1 ride migration 2 up to 2.
2026-09-15 09:35:21 -04:00
orchestrator-agent 3e2860b2f1 Sync dev with main (release-merge backport) (#36) 2026-09-14 15:19:28 -04:00