---
phase: 03-daily-plan-and-cleanliness
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - lib/features/home/data/daily_plan_dao.dart
  - lib/features/home/data/daily_plan_dao.g.dart
  - lib/features/home/domain/daily_plan_models.dart
  - lib/features/home/presentation/daily_plan_providers.dart
  - lib/core/database/database.dart
  - lib/core/database/database.g.dart
  - lib/l10n/app_de.arb
  - test/features/home/data/daily_plan_dao_test.dart
autonomous: true
requirements:
  - PLAN-01
  - PLAN-02
  - PLAN-03
  - PLAN-05

must_haves:
  truths:
    - "DailyPlanDao.watchAllTasksWithRoomName() returns tasks joined with room name, sorted by nextDueDate ascending"
    - "DailyPlanDao.watchCompletionsToday() returns count of completions recorded today"
    - "dailyPlanProvider categorizes tasks into overdue, today, and tomorrow sections"
    - "Progress total = remaining overdue + remaining today + completedTodayCount (stable denominator)"
    - "Localization keys for daily plan sections and progress text exist in app_de.arb"
  artifacts:
    - path: "lib/features/home/data/daily_plan_dao.dart"
      provides: "Cross-room join query and today's completion count"
      exports: ["DailyPlanDao", "TaskWithRoom"]
    - path: "lib/features/home/domain/daily_plan_models.dart"
      provides: "DailyPlanState data class for categorized daily plan data"
      exports: ["DailyPlanState"]
    - path: "lib/features/home/presentation/daily_plan_providers.dart"
      provides: "Riverpod provider combining task stream and completion stream"
      exports: ["dailyPlanProvider"]
    - path: "test/features/home/data/daily_plan_dao_test.dart"
      provides: "Unit tests for cross-room query, date categorization, completion count"
      min_lines: 80
  key_links:
    - from: "lib/features/home/data/daily_plan_dao.dart"
      to: "lib/core/database/database.dart"
      via: "@DriftAccessor registration"
      pattern: "DailyPlanDao"
    - from: "lib/features/home/presentation/daily_plan_providers.dart"
      to: "lib/features/home/data/daily_plan_dao.dart"
      via: "db.dailyPlanDao.watchAllTasksWithRoomName()"
      pattern: "dailyPlanDao"
---

<objective>
Build the data and provider layers for the daily plan feature: a Drift DAO with cross-room join query, a DailyPlanState model with overdue/today/tomorrow categorization, a Riverpod provider combining task and completion streams, localization keys, and unit tests.

Purpose: Provides the reactive data foundation that the daily plan UI (Plan 02) will consume. Separated from UI to keep each plan at ~50% context.
Output: DailyPlanDao with join query, DailyPlanState model, dailyPlanProvider, localization keys, and passing unit tests.
</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/03-daily-plan-and-cleanliness/3-CONTEXT.md
@.planning/phases/03-daily-plan-and-cleanliness/03-RESEARCH.md

@lib/core/database/database.dart
@lib/features/tasks/data/tasks_dao.dart
@lib/features/tasks/presentation/task_providers.dart
@lib/core/providers/database_provider.dart
@test/features/tasks/data/tasks_dao_test.dart

<interfaces>
<!-- Key types and contracts the executor needs. Extracted from codebase. -->

From lib/core/database/database.dart:
```dart
class Rooms extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text().withLength(min: 1, max: 100)();
  TextColumn get iconName => text()();
  IntColumn get sortOrder => integer().withDefault(const Constant(0))();
  DateTimeColumn get createdAt => dateTime().clientDefault(() => DateTime.now())();
}

class Tasks extends Table {
  IntColumn get id => integer().autoIncrement()();
  IntColumn get roomId => integer().references(Rooms, #id)();
  TextColumn get name => text().withLength(min: 1, max: 200)();
  TextColumn get description => text().nullable()();
  IntColumn get intervalType => intEnum<IntervalType>()();
  IntColumn get intervalDays => integer().withDefault(const Constant(1))();
  IntColumn get anchorDay => integer().nullable()();
  IntColumn get effortLevel => intEnum<EffortLevel>()();
  DateTimeColumn get nextDueDate => dateTime()();
  DateTimeColumn get createdAt => dateTime().clientDefault(() => DateTime.now())();
}

class TaskCompletions extends Table {
  IntColumn get id => integer().autoIncrement()();
  IntColumn get taskId => integer().references(Tasks, #id)();
  DateTimeColumn get completedAt => dateTime()();
}

@DriftDatabase(
  tables: [Rooms, Tasks, TaskCompletions],
  daos: [RoomsDao, TasksDao],
)
class AppDatabase extends _$AppDatabase { ... }
```

From lib/core/providers/database_provider.dart:
```dart
@Riverpod(keepAlive: true)
AppDatabase appDatabase(Ref ref) { ... }
```

From lib/features/tasks/presentation/task_providers.dart:
```dart
// Manual StreamProvider.family pattern (used because riverpod_generator
// has trouble with drift's generated Task type)
final tasksInRoomProvider =
    StreamProvider.family.autoDispose<List<Task>, int>((ref, roomId) {
  final db = ref.watch(appDatabaseProvider);
  return db.tasksDao.watchTasksInRoom(roomId);
});
```
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: DailyPlanDao with cross-room join query and completion count</name>
  <files>
    lib/features/home/data/daily_plan_dao.dart,
    lib/features/home/data/daily_plan_dao.g.dart,
    lib/features/home/domain/daily_plan_models.dart,
    lib/core/database/database.dart,
    lib/core/database/database.g.dart,
    test/features/home/data/daily_plan_dao_test.dart
  </files>
  <behavior>
    - watchAllTasksWithRoomName returns empty list when no tasks exist
    - watchAllTasksWithRoomName returns tasks with correct room name from join
    - watchAllTasksWithRoomName returns tasks sorted by nextDueDate ascending
    - watchAllTasksWithRoomName returns tasks from multiple rooms with correct room name pairing
    - watchCompletionsToday returns 0 when no completions exist
    - watchCompletionsToday returns correct count of completions recorded today
    - watchCompletionsToday does not count completions from yesterday
  </behavior>
  <action>
    1. Create `lib/features/home/domain/daily_plan_models.dart` with:
       - `TaskWithRoom` class: `final Task task`, `final String roomName`, `final int roomId`, const constructor
       - `DailyPlanState` class: `final List<TaskWithRoom> overdueTasks`, `final List<TaskWithRoom> todayTasks`, `final List<TaskWithRoom> tomorrowTasks`, `final int completedTodayCount`, `final int totalTodayCount`, const constructor

    2. Create `lib/features/home/data/daily_plan_dao.dart` with:
       - `@DriftAccessor(tables: [Tasks, Rooms, TaskCompletions])`
       - `class DailyPlanDao extends DatabaseAccessor<AppDatabase> with _$DailyPlanDaoMixin`
       - `Stream<List<TaskWithRoom>> watchAllTasksWithRoomName()`: innerJoin tasks with rooms on rooms.id.equalsExp(tasks.roomId), orderBy nextDueDate asc, map rows using readTable(tasks) and readTable(rooms)
       - `Stream<int> watchCompletionsToday({DateTime? today})`: count TaskCompletions where completedAt >= startOfDay AND completedAt < endOfDay. Use customSelect with SQL COUNT(*) and readsFrom: {taskCompletions} for proper stream invalidation

    3. Register DailyPlanDao in `lib/core/database/database.dart`:
       - Add import for daily_plan_dao.dart
       - Add `DailyPlanDao` to `@DriftDatabase(daos: [...])` list
       - Run `dart run build_runner build --delete-conflicting-outputs` to regenerate database.g.dart and daily_plan_dao.g.dart

    4. Create `test/features/home/data/daily_plan_dao_test.dart`:
       - Follow existing tasks_dao_test.dart pattern: `AppDatabase(NativeDatabase.memory())`, setUp/tearDown
       - Create 2 rooms and insert tasks with different due dates across them
       - Test all behaviors listed above
       - Use stream.first for single-emission testing (same pattern as existing tests)

    IMPORTANT: The customSelect approach for watchCompletionsToday must use `readsFrom: {taskCompletions}` so Drift knows which table to watch for stream invalidation. Without this, the stream won't re-fire on new completions.
  </action>
  <verify>
    <automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/home/data/daily_plan_dao_test.dart</automated>
  </verify>
  <done>
    - DailyPlanDao registered in AppDatabase, code generation passes
    - watchAllTasksWithRoomName returns tasks joined with room name, sorted by due date
    - watchCompletionsToday returns accurate count of today's completions
    - All unit tests pass
    - TaskWithRoom and DailyPlanState models defined with correct fields
  </done>
</task>

<task type="auto">
  <name>Task 2: Daily plan provider with date categorization, progress tracking, and localization keys</name>
  <files>
    lib/features/home/presentation/daily_plan_providers.dart,
    lib/l10n/app_de.arb
  </files>
  <action>
    1. Create `lib/features/home/presentation/daily_plan_providers.dart` with:
       - Import daily_plan_models.dart, database_provider.dart
       - Define `dailyPlanProvider` as a manual `StreamProvider.autoDispose<DailyPlanState>` (NOT using @riverpod, same pattern as tasksInRoomProvider because drift Task type causes riverpod_generator issues)
       - Inside provider: `ref.watch(appDatabaseProvider)` to get db
       - Watch `db.dailyPlanDao.watchAllTasksWithRoomName()` stream
       - Use `.asyncMap()` on the task stream to:
         a. Get completions today count via `db.dailyPlanDao.watchCompletionsToday().first`
         b. Compute `today = DateTime(now.year, now.month, now.day)`, `tomorrow = today + 1 day`, `dayAfterTomorrow = tomorrow + 1 day`
         c. Partition tasks into: overdue (dueDate < today), todayList (today <= dueDate < tomorrow), tomorrowList (tomorrow <= dueDate < dayAfterTomorrow)
         d. Compute totalTodayCount = overdue.length + todayList.length + completedTodayCount
         e. Return DailyPlanState with all fields

    CRITICAL for progress accuracy: totalTodayCount includes completedTodayCount so the denominator stays stable as tasks are completed. Without this, completing a task would shrink the total (since the task moves to a future due date), making progress appear to go backward.

    2. Add localization keys to `lib/l10n/app_de.arb` (add these AFTER existing keys, before the closing brace):
       ```json
       "dailyPlanProgress": "{completed} von {total} erledigt",
       "@dailyPlanProgress": {
         "placeholders": {
           "completed": { "type": "int" },
           "total": { "type": "int" }
         }
       },
       "dailyPlanSectionOverdue": "\u00dcberf\u00e4llig",
       "dailyPlanSectionToday": "Heute",
       "dailyPlanSectionUpcoming": "Demn\u00e4chst",
       "dailyPlanUpcomingCount": "Demn\u00e4chst ({count})",
       "@dailyPlanUpcomingCount": {
         "placeholders": {
           "count": { "type": "int" }
         }
       },
       "dailyPlanAllClearTitle": "Alles erledigt! \ud83c\udf1f",
       "dailyPlanAllClearMessage": "Keine Aufgaben f\u00fcr heute. Genie\u00dfe den Moment!",
       "dailyPlanNoOverdue": "Keine \u00fcberf\u00e4lligen Aufgaben",
       "dailyPlanNoTasks": "Noch keine Aufgaben angelegt"
       ```

    Note: Use Unicode escapes for umlauts in ARB keys (same pattern as existing keys). The "all clear" title includes a star emoji per the established playful German tone from Phase 1.

    3. Run `flutter gen-l10n` (or `flutter pub get` which triggers it) to regenerate localization classes.

    4. Verify `dart analyze` passes clean on all new files.
  </action>
  <verify>
    <automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/home/ lib/l10n/ && flutter test</automated>
  </verify>
  <done>
    - dailyPlanProvider defined as manual StreamProvider.autoDispose returning DailyPlanState
    - Tasks correctly categorized into overdue (before today), today (today), tomorrow (next day)
    - Progress total is stable: remaining overdue + remaining today + completedTodayCount
    - All 10 new localization keys present in app_de.arb and code-generated without errors
    - dart analyze clean, full test suite passes
  </done>
</task>

</tasks>

<verification>
- `flutter test test/features/home/data/daily_plan_dao_test.dart` -- all DAO tests pass
- `dart analyze lib/features/home/` -- no analysis errors
- `flutter test` -- full suite still passes (no regressions)
- DailyPlanDao registered in AppDatabase daos list
- dailyPlanProvider compiles and references DailyPlanDao correctly
</verification>

<success_criteria>
- DailyPlanDao.watchAllTasksWithRoomName() returns reactive stream of tasks joined with room names
- DailyPlanDao.watchCompletionsToday() returns reactive count of today's completions
- dailyPlanProvider categorizes tasks into overdue/today/tomorrow with stable progress tracking
- All localization keys for daily plan UI are defined
- All existing tests still pass (no regressions from database.dart changes)
</success_criteria>

<output>
After completion, create `.planning/phases/03-daily-plan-and-cleanliness/03-01-SUMMARY.md`
</output>
