---
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"
---

<objective>
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.
</objective>

<execution_context>
@/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md
@/home/jlmak/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.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

<interfaces>
<!-- Existing code contracts the executor needs -->

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() { ... }
}
```
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Define Drift tables, migration, DAOs, and domain models</name>
  <files>
    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
  </files>
  <behavior>
    --- 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
  </behavior>
  <action>
    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<IntervalType>), intervalDays (int, default 1), anchorDay (int nullable), effortLevel (intEnum<EffortLevel>), 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<List<Room>>` ordered by sortOrder
    - `watchRoomWithStats()` -> `Stream<List<RoomWithStats>>` 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<int>`
    - `updateRoom(Room)` -> `Future<bool>`
    - `deleteRoom(int roomId)` -> `Future<void>` with transaction: delete completions for room's tasks, delete tasks, delete room
    - `reorderRooms(List<int> roomIds)` -> `Future<void>` with transaction updating sortOrder
    - `getRoomById(int id)` -> `Future<Room>`

    **lib/features/tasks/data/tasks_dao.dart**: `@DriftAccessor(tables: [Tasks, TaskCompletions])` with:
    - `watchTasksInRoom(int roomId)` -> `Stream<List<Task>>` ordered by nextDueDate ASC
    - `insertTask(TasksCompanion)` -> `Future<int>`
    - `updateTask(Task)` -> `Future<bool>`
    - `deleteTask(int taskId)` -> `Future<void>` (delete completions first, then task)
    - `completeTask(int taskId, {DateTime? now})` -> `Future<void>` 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<int>`

    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.
  </action>
  <verify>
    <automated>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</automated>
  </verify>
  <done>
    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.
  </done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: Create task template data and template tests</name>
  <files>
    lib/features/templates/data/task_templates.dart,
    test/features/templates/task_templates_test.dart
  </files>
  <behavior>
    - 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
  </behavior>
  <action>
    **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<String, List<TaskTemplate>>` 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.
  </action>
  <verify>
    <automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/templates/task_templates_test.dart</automated>
  </verify>
  <done>
    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.
  </done>
</task>

</tasks>

<verification>
```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.
</verification>

<success_criteria>
- 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
</success_criteria>

<output>
After completion, create `.planning/phases/02-rooms-and-tasks/02-01-SUMMARY.md`
</output>
