Files
2026-04-03 21:55:55 +02:00

176 lines
15 KiB
Markdown

---
phase: 11-issue-3-tasks-management-allow-task-checking-anytime-and-pre-populate-recurring-tasks
verified: 2026-04-03T00:00:00Z
status: human_needed
score: 8/8 must-haves verified
re_verification: false
human_verification:
- test: "Open the app on a day that is NOT a task due date for a weekly task (e.g. 3 days before due). Confirm the task appears in a 'Demnächst' section with reduced opacity."
expected: "Task visible with ~55% opacity in a muted 'Demnächst' section below the regular day tasks."
why_human: "Visual rendering, Opacity widget behavior, and section layout cannot be verified without running the app on a device."
- test: "Navigate to a future calendar day for a weekly task and check its checkbox. Verify the task disappears from that day and does not reappear as pre-populated on other days in the same period."
expected: "Completion recorded, task removed from pre-populated list on all remaining days within the current interval window."
why_human: "Reactive stream behavior (watchCompletionsInRange filtering prePopulatedTasks) requires a live app session to validate end-to-end."
- test: "Check that an overdue task still shows its coral/terracotta accent colour when viewed on today's date."
expected: "Overdue tasks render task name in _overdueColor (0xFFE07A5F) with no opacity reduction."
why_human: "Visual color rendering requires a live device/simulator."
- test: "Run flutter test and dart analyze in a Flutter-capable environment."
expected: "All tests pass; dart analyze reports zero issues."
why_human: "Flutter SDK and dart CLI are not available in the verification shell environment. The SUMMARY notes the same constraint for Plan 01. Both plans document this as an environment limitation, not a code issue."
---
# Phase 11: Allow Task Checking Anytime and Pre-Populate Recurring Tasks — Verification Report
**Phase Goal:** Users can complete tasks on any day regardless of schedule, and recurring tasks appear on all applicable days within their interval window — making the app feel like a consistent checklist rather than a rigid scheduler
**Verified:** 2026-04-03
**Status:** human_needed (all automated checks pass; 4 items require live-app or Flutter SDK)
**Re-verification:** No — initial verification
---
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | Checkboxes always enabled on all calendar days and in room task lists | ✓ VERIFIED | `isFuture` guard absent from both `calendar_day_list.dart` and `task_row.dart`; all `canComplete` calls pass `true`; `task_row.dart` `onChanged` is unconditional |
| 2 | Completing a task on a non-due day recalculates nextDueDate from today | ✓ VERIFIED | `tasks_dao.dart:64``final baseDate = todayStart == taskDueDay ? task.nextDueDate : todayStart;` — explicit today-base path; `calculateNextDueDate` receives `baseDate` |
| 3 | A weekly task appears on all 7 days leading up to its due date | ✓ VERIFIED | `_isInCurrentIntervalWindow` checks `selected.isAfter(prevDay) && selected.isBefore(dueDate)`; `_calculatePreviousDueDate` subtracts 7 days for weekly; provider feeds result into `prePopulatedTasks` |
| 4 | A monthly task appears on all days within its current month interval | ✓ VERIFIED | `_calculatePreviousDueDate` uses `_subtractMonths` with corrected total-month arithmetic for monthly/quarterly/yearly intervals |
| 5 | Tasks already completed in the current interval do not reappear as pre-populated | ✓ VERIFIED | `watchCompletionsInRange(taskId, prevDue, nextDue+1day)` called per candidate task; non-empty result skips the task |
| 6 | Pre-populated tasks have muted (reduced opacity) visual appearance | ✓ VERIFIED | `calendar_task_row.dart:92``return isPrePopulated ? Opacity(opacity: 0.55, child: tile) : tile;` |
| 7 | Overdue tasks retain coral accent styling | ✓ VERIFIED | `CalendarTaskRow` applies `_overdueColor` to task name when `isOverdue: true`; pre-populated tasks are never overdue (excluded by `overdueIds` set in providers) |
| 8 | Tests written for all key behaviors | ✓ VERIFIED | 4 new tests in `tasks_dao_test.dart` (on-due-day, before-due-day, daily non-due, monthly-early-with-anchor); 9 new tests in `calendar_dao_test.dart` covering `watchAllActiveRecurringTasks`, `watchAllActiveRecurringTasksInRoom`, `watchCompletionsInRange` |
**Score:** 8/8 truths verified
---
### Required Artifacts
#### Plan 01 Artifacts
| Artifact | Expected | Level 1 Exists | Level 2 Substantive | Level 3 Wired | Status |
|----------|----------|---------------|---------------------|---------------|--------|
| `lib/features/home/presentation/calendar_day_list.dart` | Checkbox always enabled for all day tasks | ✓ | ✓ — `canComplete: true` on all three call sites (overdue, day, pre-pop loops) | ✓ — consumed by `CalendarTaskRow` widget | ✓ VERIFIED |
| `lib/features/home/presentation/calendar_task_row.dart` | CalendarTaskRow with always-enabled checkbox | ✓ | ✓ — `canComplete` param present, defaults `true`; `isPrePopulated` param added | ✓ — rendered by `calendar_day_list.dart` | ✓ VERIFIED |
| `lib/features/tasks/presentation/task_row.dart` | TaskRow with always-enabled checkbox | ✓ | ✓ — `onChanged: (_) { ref.read(...).completeTask(task.id); }` unconditional; `isFuture` absent | ✓ — widget used in room task list | ✓ VERIFIED |
| `lib/features/tasks/data/tasks_dao.dart` | completeTask with today-based recalculation | ✓ | ✓ — `baseDate` logic at lines 62-67; `calculateNextDueDate(currentDueDate: baseDate, ...)` | ✓ — called by `taskActionsProvider` | ✓ VERIFIED |
#### Plan 02 Artifacts
| Artifact | Expected | Level 1 Exists | Level 2 Substantive | Level 3 Wired | Status |
|----------|----------|---------------|---------------------|---------------|--------|
| `lib/features/home/data/calendar_dao.dart` | `watchAllActiveRecurringTasks` + `watchCompletionsInRange` queries | ✓ | ✓ — three new methods: `watchAllActiveRecurringTasks` (line 173), `watchAllActiveRecurringTasksInRoom` (line 193), `watchCompletionsInRange` (line 214) | ✓ — called by `calendar_providers.dart` | ✓ VERIFIED |
| `lib/features/home/presentation/calendar_providers.dart` | Pre-population logic with `isPrePopulated` filtering | ✓ | ✓ — `_isInCurrentIntervalWindow`, `_calculatePreviousDueDate`, `_subtractMonths` helper functions; full pre-pop loop in both providers | ✓ — `CalendarDayState.prePopulatedTasks` populated and passed to `CalendarDayList` | ✓ VERIFIED |
| `lib/features/home/domain/calendar_models.dart` | `CalendarDayState` with `prePopulatedTasks` field | ✓ | ✓ — `final List<TaskWithRoom> prePopulatedTasks` at line 13; `isEmpty` updated at line 30 | ✓ — consumed by `calendar_day_list.dart` | ✓ VERIFIED |
| `lib/features/home/presentation/calendar_task_row.dart` | Visual distinction for pre-populated tasks | ✓ | ✓ — `isPrePopulated` param (line 29); `Opacity(opacity: 0.55)` wrapper (line 92) | ✓ — `isPrePopulated: true` passed from `calendar_day_list.dart` pre-pop loop | ✓ VERIFIED |
| `lib/features/home/presentation/calendar_day_list.dart` | Renders pre-populated section with muted styling | ✓ | ✓ — "Demnächst" section header (line 280); `isPrePopulated: true` in loop (line 290); cleanup loop covers `prePopulatedTasks` (line 62) | ✓ — reads from `state.prePopulatedTasks` | ✓ VERIFIED |
---
### Key Link Verification
| From | To | Via | Status | Evidence |
|------|----|-----|--------|----------|
| `calendar_day_list.dart` | `CalendarTaskRow` | `canComplete: true` always passed | ✓ WIRED | Lines 262, 273, 289 all pass `canComplete: true` |
| `tasks_dao.dart` | `scheduling.dart` | `calculateNextDueDate` uses `baseDate` | ✓ WIRED | Line 66-71: `currentDueDate: baseDate` |
| `calendar_providers.dart` | `calendar_dao.dart` | `watchAllActiveRecurringTasks` stream | ✓ WIRED | Line 156: `db.calendarDao.watchAllActiveRecurringTasks()` |
| `calendar_providers.dart` | `scheduling.dart` | `calculateNextDueDate` determines window boundary | ✓ WIRED | `_calculatePreviousDueDate` is the inverse of `calculateNextDueDate` logic; same interval arithmetic applied in reverse |
| `calendar_day_list.dart` | `CalendarTaskRow` | `isPrePopulated` flag passed | ✓ WIRED | Line 290: `isPrePopulated: true` in pre-pop loop; line 337: propagated in `_buildAnimatedTaskRow` |
---
### Data-Flow Trace (Level 4)
| Artifact | Data Variable | Source | Produces Real Data | Status |
|----------|---------------|--------|--------------------|--------|
| `calendar_day_list.dart` (pre-pop section) | `state.prePopulatedTasks` | `calendarDayProvider` / `roomCalendarDayProvider``watchAllActiveRecurringTasks()` DB stream filtered through `_isInCurrentIntervalWindow` and `watchCompletionsInRange` | Yes — real DB queries; no hardcoded fallback | ✓ FLOWING |
| `calendar_task_row.dart` (opacity) | `isPrePopulated` boolean | Passed from `_buildAnimatedTaskRow` with `isPrePopulated: true` for pre-pop section | Yes — prop set from real `state.prePopulatedTasks` list | ✓ FLOWING |
| `tasks_dao.dart` (baseDate) | `baseDate` | `currentTime` (injectable `now` param) vs `task.nextDueDate` from DB | Yes — real DB task record | ✓ FLOWING |
---
### Behavioral Spot-Checks
Flutter SDK and `dart` CLI unavailable in the verification shell environment. Static code verification performed in lieu of running tests.
| Behavior | Method | Result | Status |
|----------|--------|--------|--------|
| `isFuture` guard removed from `calendar_day_list.dart` | `grep -n "isFuture"` | No matches | ✓ PASS |
| `isFuture` guard removed from `task_row.dart` | `grep -n "isFuture"` | No matches | ✓ PASS |
| `canComplete: true` present in all day-task loops | `grep -n "canComplete"` | Lines 262, 273, 289 all `true` | ✓ PASS |
| `baseDate` logic in `completeTask` | `grep -n "baseDate"` | Lines 62-67 match plan spec exactly | ✓ PASS |
| 4 new TDD tests present in `tasks_dao_test.dart` | Content search | Lines 320, 338, 356, 374 contain all 4 test cases | ✓ PASS |
| 9 new DAO tests present in `calendar_dao_test.dart` | Content search | `watchAllActiveRecurringTasks` (3 tests), `watchAllActiveRecurringTasksInRoom` (2 tests), `watchCompletionsInRange` (4 tests) | ✓ PASS |
| All 5 plan commits present in git log | `git log --oneline` | `b00806a`, `3398aca`, `c5ab052`, `9a67c51`, `8cbe989` all found | ✓ PASS |
| `flutter test` / `dart analyze` | Not runnable in shell | Flutter SDK absent | ? SKIP |
---
### Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
|-------------|------------|-------------|--------|----------|
| TM-01 | 11-01 | Checkboxes never disabled for future dates | ✓ SATISFIED | `isFuture` removed from `calendar_day_list.dart` and `task_row.dart`; `canComplete: true` hardcoded |
| TM-02 | 11-01 | nextDueDate recalculated from today on non-due-day completion | ✓ SATISFIED | `baseDate = todayStart` when `todayStart != taskDueDay` in `tasks_dao.dart:64` |
| TM-03 | 11-02 | Recurring tasks pre-populated on all days in interval window | ✓ SATISFIED | `_isInCurrentIntervalWindow` + `_calculatePreviousDueDate` in `calendar_providers.dart`; all interval types covered |
| TM-04 | 11-02 | Pre-populated tasks already completed in current period hidden | ✓ SATISFIED | `watchCompletionsInRange` check in provider loop; task skipped when `completions.isNotEmpty` |
| TM-05 | 11-02 | Pre-populated tasks have muted visual distinction | ✓ SATISFIED | `Opacity(opacity: 0.55)` wrapping `ListTile` in `calendar_task_row.dart:92` |
All 5 requirements satisfied. No orphaned requirements detected (all TM-01 through TM-05 appear in plan frontmatter and are covered by code).
---
### Anti-Patterns Found
No anti-patterns detected:
- No `TODO`, `FIXME`, `HACK`, or placeholder comments in any modified file.
- No `return null`, `return []`, or empty-body handlers that block goal.
- No `isFuture` guards remaining anywhere in the modified UI files.
- `onChanged: (_) { ... }` in `task_row.dart` is unconditional with real `completeTask` call.
- The "Completing tasks always show full styling" comment at `calendar_day_list.dart:327` is a documented design decision, not a stub.
---
### Human Verification Required
#### 1. Pre-populated task visual rendering
**Test:** Open the app and navigate to a calendar day that is within the interval window of a weekly task (e.g. 3 days before the task's due date). Scroll to see if the task appears below any due-today tasks.
**Expected:** Task visible in a "Demnächst" section with roughly 55% opacity — noticeably dimmer than regular tasks. The section header text "Demnächst" appears in a muted colour.
**Why human:** `Opacity(0.55)` correctness and the "Demnächst" section header layout can only be confirmed visually in a running app.
#### 2. End-to-end pre-population filtering after completion
**Test:** On a calendar day within a weekly task's interval window (e.g. Monday, task due Friday), check the task's checkbox. Then navigate to Tuesday through Thursday for the same week.
**Expected:** The task does NOT reappear as pre-populated on any remaining day in the same interval window after being completed.
**Why human:** Requires a live Drift database session with reactive `watchCompletionsInRange` streams to verify filtering propagates correctly across day navigation.
#### 3. Overdue task coral accent retention
**Test:** With an overdue task (nextDueDate before today), open today's calendar view. Confirm the overdue task's name text uses the coral/terracotta colour.
**Expected:** Overdue task name in `Color(0xFFE07A5F)` with no opacity reduction. Pre-populated tasks (if any) appear below with reduced opacity — no style cross-contamination.
**Why human:** Colour rendering and visual separation between sections require live rendering.
#### 4. All tests pass and dart analyze clean
**Test:** In a shell with Flutter SDK: `flutter test && dart analyze`
**Expected:** Exit code 0 for both commands. No analyzer warnings or errors.
**Why human:** Flutter SDK (`flutter`) and Dart CLI (`dart`) are absent from the CI/verification shell. Both SUMMARY documents note this as an environment constraint. Code was verified correct through static inspection; test logic is substantive and matches the plan's specified expected values exactly.
---
### Gaps Summary
No gaps found. All 8 observable truths are verified through static code analysis and commit presence checks. The phase goal is structurally achieved: checkboxes are unconditionally enabled, the today-base recalculation is implemented and tested, the pre-population provider logic is wired end-to-end from DAO queries through provider filtering to UI rendering, and the muted visual distinction is in place.
The 4 human verification items are confirmatory (not gap-closing) — they validate rendering quality and reactive behavior that cannot be checked without a running Flutter app.
---
_Verified: 2026-04-03_
_Verifier: Claude (gsd-verifier)_