12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 08-task-delete | 01 | execute | 1 |
|
true |
|
|
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.
<execution_context> @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/08-task-delete/08-CONTEXT.mdFrom lib/core/database/database.dart (current schema):
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())();
}
// 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):
class TasksDao extends DatabaseAccessor<AppDatabase> with _$TasksDaoMixin {
Stream<List<Task>> watchTasksInRoom(int roomId);
Future<int> insertTask(TasksCompanion task);
Future<bool> updateTask(Task task);
Future<void> deleteTask(int taskId); // hard delete with cascade
Future<void> completeTask(int taskId, {DateTime? now});
Stream<List<TaskCompletion>> watchCompletionsForTask(int taskId);
Future<int> getOverdueTaskCount(int roomId, {DateTime? today});
}
From lib/features/home/data/calendar_dao.dart (6 queries needing filter):
class CalendarDao extends DatabaseAccessor<AppDatabase> with _$CalendarDaoMixin {
Stream<List<TaskWithRoom>> watchTasksForDate(DateTime date);
Stream<List<TaskWithRoom>> watchTasksForDateInRoom(DateTime date, int roomId);
Stream<List<TaskWithRoom>> watchOverdueTasks(DateTime referenceDate);
Stream<List<TaskWithRoom>> watchOverdueTasksInRoom(DateTime referenceDate, int roomId);
Future<int> getTaskCount();
Future<int> getTaskCountInRoom(int roomId);
}
From lib/features/home/data/daily_plan_dao.dart (3 queries needing filter):
class DailyPlanDao extends DatabaseAccessor<AppDatabase> with _$DailyPlanDaoMixin {
Stream<List<TaskWithRoom>> watchAllTasksWithRoomName();
Future<int> getOverdueAndTodayTaskCount({DateTime? today});
Future<int> getOverdueTaskCount({DateTime? today});
}
From lib/features/rooms/data/rooms_dao.dart (task query in watchRoomWithStats):
// 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:
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(); });
-
Bump schemaVersion to 3.
-
Update migration onUpgrade — add
from < 3block: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.
-
In tasks_dao.dart, add isActive filter to watchTasksInRoom:
..where((t) => t.roomId.equals(roomId) & t.isActive.equals(true)) -
In tasks_dao.dart, add isActive filter to getOverdueTaskCount task query:
..where((t) => t.roomId.equals(roomId) & t.isActive.equals(true)) -
Add softDeleteTask method to TasksDao:
Future<void> softDeleteTask(int taskId) { return (update(tasks)..where((t) => t.id.equals(taskId))) .write(const TasksCompanion(isActive: Value(false))); } -
Add getCompletionCount method to TasksDao:
Future<int> 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; } -
Run
dart run build_runner build --delete-conflicting-outputsto regenerate Drift code. -
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
-
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
- watchAllTasksWithRoomName: add
-
In rooms_dao.dart watchRoomWithStats method, filter the task query to active-only:
final taskList = await (select(tasks) ..where((t) => t.roomId.equals(room.id) & t.isActive.equals(true))) .get(); -
Run
dart run build_runner build --delete-conflicting-outputsto regenerate if needed. -
Run
dart analyzeto 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
<success_criteria>
- 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 </success_criteria>