--- phase: 08-task-delete plan: 01 type: execute wave: 1 depends_on: [] files_modified: - lib/core/database/database.dart - lib/features/tasks/data/tasks_dao.dart - lib/features/home/data/calendar_dao.dart - lib/features/home/data/daily_plan_dao.dart - lib/features/rooms/data/rooms_dao.dart - test/features/tasks/data/tasks_dao_test.dart autonomous: true requirements: [DEL-02, DEL-03] must_haves: truths: - "Active tasks appear in all views (calendar, room task lists, daily plan)" - "Deactivated tasks are hidden from all views" - "Hard delete removes task and completions from DB entirely" - "Soft delete sets isActive to false without removing data" - "Existing tasks default to active after migration" artifacts: - path: "lib/core/database/database.dart" provides: "isActive BoolColumn on Tasks table, schema v3, migration" contains: "isActive" - path: "lib/features/tasks/data/tasks_dao.dart" provides: "softDeleteTask, getCompletionCount, isActive filter on watchTasksInRoom" exports: ["softDeleteTask", "getCompletionCount"] - path: "lib/features/home/data/calendar_dao.dart" provides: "isActive=true filter on all 6 task queries + getTaskCount" contains: "isActive" - path: "lib/features/home/data/daily_plan_dao.dart" provides: "isActive=true filter on watchAllTasksWithRoomName and count queries" contains: "isActive" - path: "lib/features/rooms/data/rooms_dao.dart" provides: "isActive=true filter on task queries in watchRoomWithStats" contains: "isActive" - path: "test/features/tasks/data/tasks_dao_test.dart" provides: "Tests for softDeleteTask, getCompletionCount, isActive filtering" key_links: - from: "lib/core/database/database.dart" to: "All DAOs" via: "Tasks table schema with isActive column" pattern: "BoolColumn.*isActive.*withDefault.*true" - from: "lib/features/tasks/data/tasks_dao.dart" to: "lib/features/tasks/presentation/task_providers.dart" via: "softDeleteTask and getCompletionCount methods" pattern: "softDeleteTask|getCompletionCount" --- Add isActive column to the Tasks table and filter all DAO queries to exclude deactivated tasks. Purpose: Foundation for smart task deletion — the isActive column enables soft-delete behavior where completed tasks are hidden but preserved for statistics, while hard-delete removes tasks with no history entirely. Output: Schema v3 with isActive column, all DAO queries filtering active-only, softDeleteTask and getCompletionCount DAO methods, passing tests. @/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/08-task-delete/08-CONTEXT.md From lib/core/database/database.dart (current schema): ```dart 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()(); IntColumn get intervalDays => integer().withDefault(const Constant(1))(); IntColumn get anchorDay => integer().nullable()(); IntColumn get effortLevel => intEnum()(); DateTimeColumn get nextDueDate => dateTime()(); DateTimeColumn get createdAt => dateTime().clientDefault(() => DateTime.now())(); } // Current schema version int get schemaVersion => 2; // Current migration strategy MigrationStrategy get migration { return MigrationStrategy( onCreate: (Migrator m) async { await m.createAll(); }, onUpgrade: (Migrator m, int from, int to) async { if (from < 2) { await m.createTable(rooms); await m.createTable(tasks); await m.createTable(taskCompletions); } }, beforeOpen: (details) async { await customStatement('PRAGMA foreign_keys = ON'); }, ); } ``` From lib/features/tasks/data/tasks_dao.dart (existing methods): ```dart class TasksDao extends DatabaseAccessor with _$TasksDaoMixin { Stream> watchTasksInRoom(int roomId); Future insertTask(TasksCompanion task); Future updateTask(Task task); Future deleteTask(int taskId); // hard delete with cascade Future completeTask(int taskId, {DateTime? now}); Stream> watchCompletionsForTask(int taskId); Future getOverdueTaskCount(int roomId, {DateTime? today}); } ``` From lib/features/home/data/calendar_dao.dart (6 queries needing filter): ```dart class CalendarDao extends DatabaseAccessor with _$CalendarDaoMixin { Stream> watchTasksForDate(DateTime date); Stream> watchTasksForDateInRoom(DateTime date, int roomId); Stream> watchOverdueTasks(DateTime referenceDate); Stream> watchOverdueTasksInRoom(DateTime referenceDate, int roomId); Future getTaskCount(); Future getTaskCountInRoom(int roomId); } ``` From lib/features/home/data/daily_plan_dao.dart (3 queries needing filter): ```dart class DailyPlanDao extends DatabaseAccessor with _$DailyPlanDaoMixin { Stream> watchAllTasksWithRoomName(); Future getOverdueAndTodayTaskCount({DateTime? today}); Future getOverdueTaskCount({DateTime? today}); } ``` From lib/features/rooms/data/rooms_dao.dart (task query in watchRoomWithStats): ```dart // Inside watchRoomWithStats: final taskList = await (select(tasks) ..where((t) => t.roomId.equals(room.id))) .get(); ``` Test pattern from test/features/tasks/data/tasks_dao_test.dart: ```dart late AppDatabase db; late int roomId; setUp(() async { db = AppDatabase(NativeDatabase.memory()); roomId = await db.roomsDao.insertRoom( RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'), ); }); tearDown(() async { await db.close(); }); ``` Task 1: Add isActive column, migration, and new DAO methods lib/core/database/database.dart, lib/features/tasks/data/tasks_dao.dart, test/features/tasks/data/tasks_dao_test.dart - Test: softDeleteTask sets isActive to false (task remains in DB but isActive == false) - Test: getCompletionCount returns 0 for task with no completions - Test: getCompletionCount returns correct count for task with completions - Test: watchTasksInRoom excludes tasks where isActive is false - Test: getOverdueTaskCount excludes tasks where isActive is false - Test: existing hard deleteTask still works (removes task and completions) 1. In database.dart Tasks table, add: `BoolColumn get isActive => boolean().withDefault(const Constant(true))();` 2. Bump schemaVersion to 3. 3. Update migration onUpgrade — add `from < 3` block: ```dart if (from < 3) { await m.addColumn(tasks, tasks.isActive); } ``` This uses Drift's addColumn which handles the ALTER TABLE and the default value for existing rows. 4. In tasks_dao.dart, add isActive filter to watchTasksInRoom: ```dart ..where((t) => t.roomId.equals(roomId) & t.isActive.equals(true)) ``` 5. In tasks_dao.dart, add isActive filter to getOverdueTaskCount task query: ```dart ..where((t) => t.roomId.equals(roomId) & t.isActive.equals(true)) ``` 6. Add softDeleteTask method to TasksDao: ```dart Future softDeleteTask(int taskId) { return (update(tasks)..where((t) => t.id.equals(taskId))) .write(const TasksCompanion(isActive: Value(false))); } ``` 7. Add getCompletionCount method to TasksDao: ```dart Future getCompletionCount(int taskId) async { final count = taskCompletions.id.count(); final query = selectOnly(taskCompletions) ..addColumns([count]) ..where(taskCompletions.taskId.equals(taskId)); final result = await query.getSingle(); return result.read(count) ?? 0; } ``` 8. Run `dart run build_runner build --delete-conflicting-outputs` to regenerate Drift code. 9. Write tests in tasks_dao_test.dart following existing test patterns (NativeDatabase.memory, setUp/tearDown). cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/tasks/data/tasks_dao_test.dart --reporter compact - Tasks table has isActive BoolColumn with default true - Schema version is 3 with working migration - softDeleteTask sets isActive=false without removing data - getCompletionCount returns accurate count - watchTasksInRoom only returns active tasks - getOverdueTaskCount only counts active tasks - All new tests pass, all existing tests pass Task 2: Add isActive filters to CalendarDao, DailyPlanDao, and RoomsDao lib/features/home/data/calendar_dao.dart, lib/features/home/data/daily_plan_dao.dart, lib/features/rooms/data/rooms_dao.dart 1. In calendar_dao.dart, add `& tasks.isActive.equals(true)` to the WHERE clause of ALL 6 query methods: - watchTasksForDate: add to existing `query.where(...)` expression - watchTasksForDateInRoom: add to existing `query.where(...)` expression - watchOverdueTasks: add to existing `query.where(...)` expression - watchOverdueTasksInRoom: add to existing `query.where(...)` expression - getTaskCount: add `..where(tasks.isActive.equals(true))` to selectOnly - getTaskCountInRoom: add `& tasks.isActive.equals(true)` to existing where 2. In daily_plan_dao.dart, add isActive filter to all 3 query methods: - watchAllTasksWithRoomName: add `query.where(tasks.isActive.equals(true));` after the join - getOverdueAndTodayTaskCount: add `& tasks.isActive.equals(true)` to existing where - getOverdueTaskCount: add `& tasks.isActive.equals(true)` to existing where 3. In rooms_dao.dart watchRoomWithStats method, filter the task query to active-only: ```dart final taskList = await (select(tasks) ..where((t) => t.roomId.equals(room.id) & t.isActive.equals(true))) .get(); ``` 4. Run `dart run build_runner build --delete-conflicting-outputs` to regenerate if needed. 5. Run `dart analyze` to confirm no issues. cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test --reporter compact && dart analyze --fatal-infos - All 6 CalendarDao queries filter by isActive=true - All 3 DailyPlanDao queries filter by isActive=true - RoomsDao watchRoomWithStats only counts active tasks - All 137+ existing tests still pass - dart analyze reports zero issues - Schema version is 3, migration adds isActive column with default true - softDeleteTask and getCompletionCount methods exist on TasksDao - Every query across TasksDao, CalendarDao, DailyPlanDao, and RoomsDao that returns tasks filters by isActive=true - Hard deleteTask (cascade) still works unchanged - All tests pass: `flutter test --reporter compact` - Code quality: `dart analyze --fatal-infos` reports zero issues - Deactivated tasks (isActive=false) are excluded from ALL active views: calendar day tasks, overdue tasks, room task lists, daily plan, room stats - Existing tasks default to active after schema migration - New DAO methods (softDeleteTask, getCompletionCount) are available for the UI layer - All 137+ tests pass, new DAO tests pass After completion, create `.planning/phases/08-task-delete/08-01-SUMMARY.md`