--- phase: 02-rooms-and-tasks plan: 01 type: execute wave: 1 depends_on: [] files_modified: - lib/core/database/database.dart - lib/features/rooms/data/rooms_dao.dart - lib/features/tasks/data/tasks_dao.dart - lib/features/tasks/domain/scheduling.dart - lib/features/tasks/domain/frequency.dart - lib/features/tasks/domain/effort_level.dart - lib/features/tasks/domain/relative_date.dart - lib/features/rooms/domain/room_icons.dart - lib/features/templates/data/task_templates.dart - test/features/rooms/data/rooms_dao_test.dart - test/features/tasks/data/tasks_dao_test.dart - test/features/tasks/domain/scheduling_test.dart - test/features/templates/task_templates_test.dart autonomous: true requirements: - ROOM-01 - ROOM-02 - ROOM-03 - ROOM-04 - ROOM-05 - TASK-01 - TASK-02 - TASK-03 - TASK-04 - TASK-05 - TASK-06 - TASK-07 - TASK-08 - TMPL-01 - TMPL-02 must_haves: truths: - "Room CRUD operations (insert, update, delete with cascade, reorder) work correctly at the database layer" - "Task CRUD operations (insert, update, delete, sorted by due date) work correctly at the database layer" - "Task completion records a timestamp and auto-calculates next due date using the scheduling utility" - "All 11 preset frequency intervals and custom intervals produce correct next due dates" - "Calendar-anchored intervals clamp to last day of month with anchor memory" - "Catch-up logic advances past-due dates to the next future occurrence" - "All 14 room types have bundled German-language task templates" - "Overdue detection correctly identifies tasks with nextDueDate before today" artifacts: - path: "lib/core/database/database.dart" provides: "Rooms, Tasks, TaskCompletions table definitions, schema v2 migration, foreign keys PRAGMA" contains: "schemaVersion => 2" - path: "lib/features/rooms/data/rooms_dao.dart" provides: "Room CRUD + stream queries + reorder + cascade delete" exports: ["RoomsDao"] - path: "lib/features/tasks/data/tasks_dao.dart" provides: "Task CRUD + stream queries + completion transaction" exports: ["TasksDao"] - path: "lib/features/tasks/domain/scheduling.dart" provides: "calculateNextDueDate, catchUpToPresent pure functions" exports: ["calculateNextDueDate", "catchUpToPresent"] - path: "lib/features/tasks/domain/frequency.dart" provides: "IntervalType enum, FrequencyInterval model" exports: ["IntervalType", "FrequencyInterval"] - path: "lib/features/tasks/domain/effort_level.dart" provides: "EffortLevel enum (low, medium, high)" exports: ["EffortLevel"] - path: "lib/features/tasks/domain/relative_date.dart" provides: "formatRelativeDate German formatter" exports: ["formatRelativeDate"] - path: "lib/features/rooms/domain/room_icons.dart" provides: "Curated list of ~25 household Material Icons with name+IconData pairs" exports: ["curatedRoomIcons", "mapIconName"] - path: "lib/features/templates/data/task_templates.dart" provides: "Static Dart constant template data for all 14 room types" exports: ["TaskTemplate", "roomTemplates", "detectRoomType"] key_links: - from: "lib/features/tasks/data/tasks_dao.dart" to: "lib/features/tasks/domain/scheduling.dart" via: "completeTask calls calculateNextDueDate + catchUpToPresent" pattern: "calculateNextDueDate|catchUpToPresent" - from: "lib/core/database/database.dart" to: "lib/features/rooms/data/rooms_dao.dart" via: "database registers RoomsDao" pattern: "daos.*RoomsDao" - from: "lib/core/database/database.dart" to: "lib/features/tasks/data/tasks_dao.dart" via: "database registers TasksDao" pattern: "daos.*TasksDao" - from: "lib/features/tasks/domain/frequency.dart" to: "lib/core/database/database.dart" via: "IntervalType enum used as intEnum column in Tasks table" pattern: "intEnum.*IntervalType" --- Build the complete data layer for rooms and tasks: Drift tables with schema v2 migration, DAOs with stream queries and cascade operations, pure scheduling utility, domain enums/models, template data, and comprehensive unit tests. Purpose: This is the foundation everything else in Phase 2 builds on. All UI plans (rooms, tasks, templates) depend on having working DAOs, domain models, and tested scheduling logic. Output: Database schema v2 with Rooms/Tasks/TaskCompletions tables, two DAOs, scheduling utility, template data, and passing unit test suite. @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-rooms-and-tasks/2-CONTEXT.md @.planning/phases/02-rooms-and-tasks/02-RESEARCH.md @.planning/phases/02-rooms-and-tasks/02-VALIDATION.md From lib/core/database/database.dart: ```dart @DriftDatabase(tables: []) class AppDatabase extends _$AppDatabase { AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection()); @override int get schemaVersion => 1; } ``` From lib/core/providers/database_provider.dart: ```dart @Riverpod(keepAlive: true) AppDatabase appDatabase(Ref ref) { final db = AppDatabase(); ref.onDispose(db.close); return db; } ``` From lib/core/theme/theme_provider.dart (pattern reference for AsyncNotifier): ```dart @riverpod class ThemeNotifier extends _$ThemeNotifier { @override ThemeMode build() { ... } } ``` Task 1: Define Drift tables, migration, DAOs, and domain models lib/features/tasks/domain/frequency.dart, lib/features/tasks/domain/effort_level.dart, lib/features/tasks/domain/scheduling.dart, lib/features/tasks/domain/relative_date.dart, lib/features/rooms/domain/room_icons.dart, lib/core/database/database.dart, lib/features/rooms/data/rooms_dao.dart, lib/features/tasks/data/tasks_dao.dart, test/features/rooms/data/rooms_dao_test.dart, test/features/tasks/data/tasks_dao_test.dart, test/features/tasks/domain/scheduling_test.dart --- Scheduling (scheduling_test.dart) --- - calculateNextDueDate: daily adds 1 day - calculateNextDueDate: everyNDays(3) adds 3 days - calculateNextDueDate: weekly adds 7 days - calculateNextDueDate: biweekly adds 14 days - calculateNextDueDate: monthly from Jan 15 gives Feb 15 - calculateNextDueDate: monthly from Jan 31 gives Feb 28 (clamping) - calculateNextDueDate: monthly from Feb 28 (anchor 31) gives Mar 31 (anchor memory) - calculateNextDueDate: quarterly from Jan 15 gives Apr 15 - calculateNextDueDate: yearly from 2026-03-15 gives 2027-03-15 - calculateNextDueDate: everyNMonths(2) adds 2 months - catchUpToPresent: skips past occurrences to reach today or future - catchUpToPresent: returns date unchanged if already in future - formatRelativeDate: today returns "Heute" - formatRelativeDate: tomorrow returns "Morgen" - formatRelativeDate: 3 days from now returns "in 3 Tagen" - formatRelativeDate: 1 day overdue returns "Uberfaellig seit 1 Tag" - formatRelativeDate: 5 days overdue returns "Uberfaellig seit 5 Tagen" --- RoomsDao (rooms_dao_test.dart) --- - insertRoom returns a valid id - watchAllRooms emits rooms ordered by sortOrder - updateRoom changes name and iconName - deleteRoom cascades to associated tasks and completions - reorderRooms updates sortOrder for all rooms - watchRoomWithStats emits room with due task count and cleanliness ratio --- TasksDao (tasks_dao_test.dart) --- - insertTask returns a valid id - watchTasksInRoom emits tasks sorted by nextDueDate ascending - updateTask changes name, description, interval, effort - deleteTask removes the task - completeTask records completion and updates nextDueDate - completeTask with overdue task catches up to present - tasks with nextDueDate before today are detected as overdue 1. Create domain models FIRST (these are the contracts): **lib/features/tasks/domain/frequency.dart**: Define `IntervalType` enum with values in this EXACT order (for intEnum stability): `daily, everyNDays, weekly, biweekly, monthly, everyNMonths, quarterly, yearly`. Add index comments. Create `FrequencyInterval` class with `intervalType` and `days` fields, plus a `label()` method returning German display strings ("Taeglich", "Alle 2 Tage", "Woechentlich", "Alle 2 Wochen", "Monatlich", "Alle 2 Monate", "Vierteljaehrlich", "Halbjaehrlich", "Jaehrlich", "Alle N Tage/Wochen/Monate" for custom). Include the full preset list as a static const list per TASK-04: daily, every 2 days, every 3 days, weekly, biweekly, monthly, every 2 months, quarterly, every 6 months, yearly. **lib/features/tasks/domain/effort_level.dart**: Define `EffortLevel` enum: `low, medium, high` (this exact order, with index comments). Add `label()` extension returning German: "Gering", "Mittel", "Hoch". **lib/features/tasks/domain/scheduling.dart**: Implement `calculateNextDueDate()` and `catchUpToPresent()` as top-level pure functions per the RESEARCH.md Pattern 5. Accept `DateTime today` parameter (not `DateTime.now()`) for testability. Day-count intervals use `Duration(days: N)`. Calendar-anchored intervals use `_addMonths()` helper with anchor day clamping. Catch-up uses a while loop advancing until `nextDue >= today`. **lib/features/tasks/domain/relative_date.dart**: Implement `formatRelativeDate(DateTime dueDate, DateTime today)` returning German strings: "Heute", "Morgen", "in X Tagen", "Uberfaellig seit 1 Tag", "Uberfaellig seit X Tagen". Accept `today` parameter for testability. **lib/features/rooms/domain/room_icons.dart**: Define `curatedRoomIcons` as a const list of records `({String name, IconData icon})` with ~25 household Material Icons (kitchen, bathtub, bed, living, weekend, door_front_door, desk, garage, balcony, local_laundry_service, stairs, child_care, single_bed, dining, yard, grass, home, inventory_2, window, cleaning_services, iron, microwave, shower, chair, door_sliding). Add `IconData mapIconName(String name)` function that maps stored string names back to IconData. 2. Update database with tables and migration: **lib/core/database/database.dart**: Add three Drift table classes (`Rooms`, `Tasks`, `TaskCompletions`) above the database class. Import `IntervalType` and `EffortLevel` for `intEnum` columns. Register tables and DAOs in `@DriftDatabase` annotation. Increment `schemaVersion` to 2. Add `MigrationStrategy` with `onCreate` calling `m.createAll()`, `onUpgrade` that creates all three tables when upgrading from v1, and `beforeOpen` that runs `PRAGMA foreign_keys = ON`. Table columns per RESEARCH.md Pattern 1: - Rooms: id (autoIncrement), name (text 1-100), iconName (text), sortOrder (int, default 0), createdAt (dateTime clientDefault) - Tasks: id (autoIncrement), roomId (int, references Rooms#id), name (text 1-200), description (text nullable), intervalType (intEnum), intervalDays (int, default 1), anchorDay (int nullable), effortLevel (intEnum), nextDueDate (dateTime), createdAt (dateTime clientDefault) - TaskCompletions: id (autoIncrement), taskId (int, references Tasks#id), completedAt (dateTime) 3. Create DAOs: **lib/features/rooms/data/rooms_dao.dart**: `@DriftAccessor(tables: [Rooms, Tasks, TaskCompletions])` with: - `watchAllRooms()` -> `Stream>` ordered by sortOrder - `watchRoomWithStats()` -> `Stream>` joining rooms with task counts and cleanliness ratio. Create a `RoomWithStats` class (room, totalTasks, dueTasks, overdueCount, cleanlinessRatio). The cleanliness ratio = (totalTasks - overdueCount) / totalTasks (1.0 when no overdue, 0.0 when all overdue). Use `customSelect` or join query to compute these counts. - `insertRoom(RoomsCompanion)` -> `Future` - `updateRoom(Room)` -> `Future` - `deleteRoom(int roomId)` -> `Future` with transaction: delete completions for room's tasks, delete tasks, delete room - `reorderRooms(List roomIds)` -> `Future` with transaction updating sortOrder - `getRoomById(int id)` -> `Future` **lib/features/tasks/data/tasks_dao.dart**: `@DriftAccessor(tables: [Tasks, TaskCompletions])` with: - `watchTasksInRoom(int roomId)` -> `Stream>` ordered by nextDueDate ASC - `insertTask(TasksCompanion)` -> `Future` - `updateTask(Task)` -> `Future` - `deleteTask(int taskId)` -> `Future` (delete completions first, then task) - `completeTask(int taskId, {DateTime? now})` -> `Future` with transaction: get task, insert completion, calculate next due via `calculateNextDueDate`, catch up via `catchUpToPresent`, update task's nextDueDate. Accept optional `now` for testability (defaults to `DateTime.now()`). - `getOverdueTaskCount(int roomId, {DateTime? today})` -> `Future` 4. Run `dart run build_runner build --delete-conflicting-outputs` to generate all .g.dart files. 5. Run `dart run drift_dev make-migrations` to capture drift_schema_v2.json. 6. Write unit tests (RED phase first, then make GREEN): **test/features/tasks/domain/scheduling_test.dart**: Test all behaviors listed above for calculateNextDueDate and catchUpToPresent and formatRelativeDate. Use fixed dates (no DateTime.now()). **test/features/rooms/data/rooms_dao_test.dart**: Use `AppDatabase(NativeDatabase.memory())` pattern from Phase 1. Test insert, watchAll, update, delete with cascade, reorder, watchRoomWithStats. **test/features/tasks/data/tasks_dao_test.dart**: Same in-memory DB pattern. Test insert, watchTasksInRoom (verify sort order), update, delete, completeTask (verify completion record + nextDueDate update), overdue detection. 7. Run `flutter test` to verify all tests pass. cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/tasks/domain/scheduling_test.dart test/features/rooms/data/rooms_dao_test.dart test/features/tasks/data/tasks_dao_test.dart All domain models defined with correct enum ordering. Drift database at schema v2 with Rooms, Tasks, TaskCompletions tables. Foreign keys enabled via PRAGMA. RoomsDao and TasksDao with full CRUD + stream queries + cascade delete + completion transaction. Scheduling utility correctly handles all 8 interval types, anchor memory, and catch-up logic. All unit tests passing. Task 2: Create task template data and template tests lib/features/templates/data/task_templates.dart, test/features/templates/task_templates_test.dart - roomTemplates map contains exactly 14 room type keys - Each room type key maps to a non-empty list of TaskTemplate objects - Every TaskTemplate has a non-empty German name - Every TaskTemplate has a valid IntervalType - Every TaskTemplate has a valid EffortLevel - detectRoomType("Kueche") returns "kueche" - detectRoomType("Mein Badezimmer") returns "badezimmer" - detectRoomType("Bad") returns "badezimmer" (alias) - detectRoomType("Random Name") returns null - detectRoomType is case-insensitive - All 14 room types from TMPL-02 are present: kueche, badezimmer, schlafzimmer, wohnzimmer, flur, buero, garage, balkon, waschkueche, keller, kinderzimmer, gaestezimmer, esszimmer, garten **lib/features/templates/data/task_templates.dart**: Create `TaskTemplate` class with const constructor (name, description nullable, intervalType, intervalDays defaults to 1, effortLevel). Create `roomTemplates` as `const Map>` with 4-6 templates per room type for all 14 types. Use realistic German household task names with appropriate frequencies and effort levels. Room types and sample tasks: - kueche: Abspuelen (daily/low), Herd reinigen (weekly/medium), Kuehlschrank reinigen (monthly/medium), Backofen reinigen (monthly/high), Muell rausbringen (everyNDays 2/low) - badezimmer: Toilette putzen (weekly/medium), Spiegel reinigen (weekly/low), Dusche reinigen (weekly/medium), Waschbecken reinigen (weekly/low), Badezimmerboden wischen (weekly/medium) - schlafzimmer: Bettwasche wechseln (biweekly/medium), Staubsaugen (weekly/medium), Staub wischen (weekly/low), Fenster putzen (monthly/medium) - wohnzimmer: Staubsaugen (weekly/medium), Staub wischen (weekly/low), Kissen aufschuetteln (daily/low), Fenster putzen (monthly/medium) - flur: Boden wischen (weekly/medium), Schuhe aufraumen (weekly/low), Garderobe aufraeumen (monthly/low) - buero: Schreibtisch aufraeumen (weekly/low), Staubsaugen (weekly/medium), Bildschirm reinigen (monthly/low), Papierkorb leeren (weekly/low) - garage: Boden fegen (monthly/medium), Aufraeumen (quarterly/high), Werkzeug sortieren (quarterly/medium) - balkon: Boden fegen (weekly/low), Pflanzen giessen (everyNDays 2/low), Gelaender reinigen (monthly/medium), Moebel reinigen (monthly/medium) - waschkueche: Waschmaschine reinigen (monthly/medium), Trockner reinigen (monthly/medium), Boden wischen (weekly/medium), Waschmittel auffuellen (monthly/low) - keller: Staubsaugen (monthly/medium), Aufraeumen (quarterly/high), Luftentfeuchter pruefen (monthly/low) - kinderzimmer: Spielzeug aufraeumen (daily/low), Staubsaugen (weekly/medium), Bettwasche wechseln (biweekly/medium), Staub wischen (weekly/low) - gaestezimmer: Staubsaugen (biweekly/medium), Bettwasche wechseln (monthly/medium), Staub wischen (biweekly/low), Lueften (weekly/low) - esszimmer: Tisch abwischen (daily/low), Staubsaugen (weekly/medium), Stuehle reinigen (monthly/medium) - garten: Rasen maehen (weekly/high), Unkraut jaeten (weekly/medium), Hecke schneiden (quarterly/high), Laub harken (weekly/medium in autumn context), Pflanzen giessen (everyNDays 2/low) Create `detectRoomType(String roomName)` function: lowercase + trim the input, check if it contains any room type key or its aliases. Aliases map: badezimmer -> [bad, wc, toilette], buero -> [arbeitszimmer, office], waschkueche -> [waschraum], garten -> [terrasse, aussenbereich, hof], gaestezimmer -> [gaeste], kinderzimmer -> [kinder], schlafzimmer -> [schlafraum], kueche -> [kitchen], esszimmer -> [essbereich]. **test/features/templates/task_templates_test.dart**: Test all behaviors listed above. Verify exact room type count (14), non-empty template lists, valid fields on every template, detectRoomType matching and null cases. cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/templates/task_templates_test.dart All 14 room types have 3-6 German-language task templates with valid names, intervals, and effort levels. detectRoomType correctly identifies room types from names and aliases. Template test suite passing. ```bash cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/rooms/ test/features/tasks/ test/features/templates/ && flutter test ``` All Phase 2 data layer tests pass. Full existing test suite (Phase 1 + Phase 2) passes. `dart analyze` shows no new errors. - Database schema v2 with Rooms, Tasks, TaskCompletions tables - RoomsDao: CRUD + watchAll + watchWithStats + cascade delete + reorder - TasksDao: CRUD + watchInRoom (sorted by due date) + completeTask transaction + overdue detection - Scheduling utility: all 8 interval types, anchor memory, catch-up logic - Domain models: IntervalType, EffortLevel, FrequencyInterval, curatedRoomIcons, formatRelativeDate - Template data: 14 room types with German task templates, detectRoomType - All unit tests green After completion, create `.planning/phases/02-rooms-and-tasks/02-01-SUMMARY.md`