Files
HouseHoldKeaper/.planning/phases/05-calendar-strip/05-VERIFICATION.md

203 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 05-calendar-strip
verified: 2026-03-16T21:00:00Z
status: human_needed
score: 10/10 must-haves verified
human_verification:
- test: "Launch the app on a device or emulator and confirm the calendar strip renders correctly"
expected: "Horizontal row of day cards with German abbreviations (Mo, Di, Mi...) and date number; today's card is bold with a 2px green underline accent; all cards have a light sage tint; selected card has stronger green background and border"
why_human: "Visual appearance, color fidelity, and card proportions cannot be verified programmatically"
- test: "Tap several day cards and verify the task list below updates"
expected: "Tapping a card selects it (green highlight, centered), and the task list below immediately shows that day's tasks"
why_human: "Interactive tap-to-select flow and reactive list update require a running device"
- test: "Scroll the strip far from today, then tap the floating Today button"
expected: "Floating 'Heute' FAB appears when today is scrolled out of view; tapping it re-centers today's card and resets the task list to today"
why_human: "Visibility toggle of FAB and imperative scroll-back behavior require real scroll interaction"
- test: "Verify month boundary treatment"
expected: "At every month boundary a slightly wider gap appears between the last card of one month and the first card of the next, with a small month label (e.g. 'Mrz', 'Apr') in the gap"
why_human: "Month label rendering and gap width are visual properties that require visual inspection"
- test: "With tasks overdue (nextDueDate before today), view today in the strip"
expected: "An 'Uberfaellig' section header in coral appears above the overdue tasks; switching to yesterday or tomorrow hides the overdue section entirely"
why_human: "Requires a device with test data in the database and navigation between days"
- test: "Complete a task via its checkbox"
expected: "The task slides out with a SizeTransition + SlideTransition animation (300ms); it disappears from the list after the animation"
why_human: "Animation quality and timing require visual observation on a running device"
---
# Phase 5: Calendar Strip Verification Report
**Phase Goal:** Users navigate their tasks through a horizontal date-strip that replaces the stacked daily plan, seeing today's tasks by default and any day's tasks on tap
**Verified:** 2026-03-16T21:00:00Z
**Status:** human_needed — all automated checks pass; 6 visual/interactive behaviors need human confirmation
**Re-verification:** No — initial verification
---
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
|----|-------|--------|---------|
| 1 | Home screen shows a horizontal scrollable strip of day cards with German abbreviation (Mo, Di...) and date number | VERIFIED | `calendar_strip.dart` L265: `DateFormat('E', 'de').format(date)` produces German abbreviations; 181-card `ListView.builder` scroll direction horizontal |
| 2 | Tapping a day card updates the task list below to show that day's tasks | VERIFIED | `_onCardTapped` calls `ref.read(selectedDateProvider.notifier).selectDate(tappedDate)`; `CalendarDayList` watches `calendarDayProvider` which watches `selectedDateProvider` |
| 3 | On app launch the strip auto-scrolls so today's card is centered | VERIFIED | `initState` calls `WidgetsBinding.instance.addPostFrameCallback``_animateToToday()` which calls `_scrollController.animateTo(..., duration: 200ms, curve: Curves.easeOut)` |
| 4 | A subtle wider gap and month label appears at month boundaries | VERIFIED | `_kMonthBoundaryGap = 16.0` vs `_kCardMargin = 4.0`; `_isFirstOfMonth` triggers `DateFormat('MMM', 'de').format(date)` text label; non-boundary cards reserve `SizedBox(height: 16)` to prevent jitter |
| 5 | Overdue tasks appear in a separate coral-accented section when viewing today | VERIFIED | `calendarDayProvider` fetches `watchOverdueTasks` only when `isToday`; `_buildTaskList` renders a coral-colored "Uberfaellig" header via `_overdueColor = Color(0xFFE07A5F)` when `state.overdueTasks.isNotEmpty` |
| 6 | Overdue tasks do NOT appear when viewing past or future days | VERIFIED | `calendarDayProvider`: `isToday` guard — past/future sets `overdueTasks = const []`; 101-test suite includes `does not show overdue section for non-today date` test passing |
| 7 | Completing a task via checkbox triggers slide-out animation | VERIFIED | `_CompletingTaskRow` in `calendar_day_list.dart` implements `SizeTransition` + `SlideTransition` (300ms, `Curves.easeInOut`); `_onTaskCompleted` adds to `_completingTaskIds` and calls `taskActionsProvider.notifier.completeTask` |
| 8 | Floating Today button appears when scrolled away from today, hidden when today is visible | VERIFIED | `CalendarStrip.onTodayVisibilityChanged` callback drives `_showTodayButton` in `HomeScreen`; `_onScroll` computes viewport bounds vs today card position |
| 9 | First-run empty state (no rooms/tasks) still shows the create-room prompt | VERIFIED | `CalendarDayList._buildFirstRunEmpty` shows checklist icon + `l10n.dailyPlanNoTasks` + `l10n.homeEmptyAction` FilledButton.tonal navigating to `/rooms`; gated by `totalTaskCount == 0` |
| 10 | Celebration state shows when all tasks for the selected day are done | VERIFIED | `_buildCelebration` renders `Icons.celebration_outlined` + `dailyPlanAllClearTitle` + `dailyPlanAllClearMessage`; triggered by `isToday && dayTasks.isEmpty && overdueTasks.isEmpty && totalTaskCount > 0` |
**Score: 10/10 truths verified**
---
## Required Artifacts
### Plan 01 Artifacts
| Artifact | Expected | Lines | Status | Details |
|----------|----------|-------|--------|---------|
| `lib/features/home/data/calendar_dao.dart` | Date-parameterized task queries | 87 | VERIFIED | `watchTasksForDate`, `watchOverdueTasks`, `getTaskCount` all implemented; `@DriftAccessor(tables: [Tasks, Rooms, TaskCompletions])` annotation present |
| `lib/features/home/data/calendar_dao.g.dart` | Generated Drift mixin | 25 | VERIFIED | `_$CalendarDaoMixin` generated, part of `calendar_dao.dart` |
| `lib/features/home/domain/calendar_models.dart` | CalendarDayState model | 25 | VERIFIED | `CalendarDayState` with `selectedDate`, `dayTasks`, `overdueTasks`, `totalTaskCount`, `isEmpty` getter |
| `lib/features/home/presentation/calendar_providers.dart` | Riverpod providers | 69 | VERIFIED | `selectedDateProvider` (NotifierProvider), `calendarDayProvider` (StreamProvider.autoDispose) with overdue-today-only logic |
| `test/features/home/data/calendar_dao_test.dart` | DAO unit tests (min 50 lines) | 286 | VERIFIED | 11 tests: 5 for `watchTasksForDate`, 6 for `watchOverdueTasks`; all pass |
### Plan 02 Artifacts
| Artifact | Expected | Lines | Status | Details |
|----------|----------|-------|--------|---------|
| `lib/features/home/presentation/calendar_strip.dart` | Horizontal scrollable date strip (min 100 lines) | 348 | VERIFIED | 181-card ListView, German abbreviations, CalendarStripController, today-visibility callback, month boundary labels |
| `lib/features/home/presentation/calendar_day_list.dart` | Day task list with states (min 80 lines) | 310 | VERIFIED | 5-state machine (loading/first-run/celebration/empty/tasks), overdue section, `_CompletingTaskRow` animation |
| `lib/features/home/presentation/calendar_task_row.dart` | Task row (min 30 lines) | 69 | VERIFIED | Name + room chip + checkbox; `isOverdue` coral styling; no relative date |
| `lib/features/home/presentation/home_screen.dart` | Rewritten HomeScreen (min 40 lines) | 69 | VERIFIED | Stack with Column(CalendarStrip + CalendarDayList) + conditional floating FAB |
---
## Key Link Verification
### Plan 01 Links
| From | To | Via | Status | Details |
|------|----|-----|--------|---------|
| `calendar_dao.dart` | `database.dart` | CalendarDao registered in @DriftDatabase daos | WIRED | `database.dart` L49: `daos: [RoomsDao, TasksDao, DailyPlanDao, CalendarDao]`; `database.g.dart` L1249: `late final CalendarDao calendarDao = CalendarDao(this as AppDatabase)` |
| `calendar_providers.dart` | `calendar_dao.dart` | Provider reads CalendarDao from AppDatabase via `db.calendarDao` | WIRED | `calendar_providers.dart` L46: `db.calendarDao.watchTasksForDate(selectedDate)`; L5354: `db.calendarDao.watchOverdueTasks(selectedDate).first`; L60: `db.calendarDao.getTaskCount()` |
### Plan 02 Links
| From | To | Via | Status | Details |
|------|----|-----|--------|---------|
| `home_screen.dart` | `calendar_strip.dart` | HomeScreen composes CalendarStrip | WIRED | `home_screen.dart` L37: `CalendarStrip(controller: _stripController, ...)` |
| `home_screen.dart` | `calendar_day_list.dart` | HomeScreen composes CalendarDayList | WIRED | `home_screen.dart` L43: `const Expanded(child: CalendarDayList())` |
| `calendar_strip.dart` | `calendar_providers.dart` | Strip reads/writes selectedDateProvider | WIRED | `calendar_strip.dart` L193: `ref.read(selectedDateProvider.notifier).selectDate(tappedDate)`; L199: `ref.watch(selectedDateProvider)` |
| `calendar_day_list.dart` | `calendar_providers.dart` | Day list watches calendarDayProvider | WIRED | `calendar_day_list.dart` L46: `final dayState = ref.watch(calendarDayProvider)` |
| `calendar_day_list.dart` | `task_providers.dart` | Task completion via taskActionsProvider | WIRED | `calendar_day_list.dart` L39: `ref.read(taskActionsProvider.notifier).completeTask(taskId)` |
---
## Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
|-------------|-------------|-------------|--------|---------|
| CAL-01 | Plan 02 | User sees horizontal scrollable date-strip with day abbreviation (Mo, Di...) and date number per card | SATISFIED | `calendar_strip.dart`: 181-card horizontal ListView; `DateFormat('E', 'de')` for German abbreviations; `date.day.toString()` for date number |
| CAL-02 | Plan 01 | User can tap a day card to see that day's tasks in a list below the strip | SATISFIED | `_onCardTapped``selectedDateProvider``calendarDayProvider``CalendarDayList` reactive update |
| CAL-03 | Plan 02 | User sees a subtle color shift at month boundaries for visual orientation | SATISFIED | `_isFirstOfMonth` check triggers `_kMonthBoundaryGap = 16.0` (vs 4px normal) and `DateFormat('MMM', 'de')` month label in `theme.colorScheme.primary` |
| CAL-04 | Plan 02 | Calendar strip auto-scrolls to today on app launch | SATISFIED | `addPostFrameCallback``_animateToToday()``animateTo(200ms, Curves.easeOut)` centered on today's index |
| CAL-05 | Plans 01+02 | Undone tasks carry over to the next day with red/orange color accent | SATISFIED | `watchOverdueTasks` returns tasks with `nextDueDate < today`; `calendarDayProvider` includes them only for `isToday`; `_overdueColor = Color(0xFFE07A5F)` applied to section header and task name text |
**All 5 CAL requirements: SATISFIED**
No orphaned requirements — REQUIREMENTS.md maps CAL-01 through CAL-05 exclusively to Phase 5, all accounted for.
---
## Anti-Patterns Found
No anti-patterns detected. Scan of all 7 phase-created/modified files found:
- No TODO/FIXME/XXX/HACK/PLACEHOLDER comments
- No empty implementations (`return null`, `return {}`, `return []`)
- No stub handlers (`() => {}` or `() => console.log(...)`)
- No unimplemented API routes
---
## Test Results
| Suite | Result | Count |
|-------|--------|-------|
| `flutter test test/features/home/data/calendar_dao_test.dart` | All passed | 11/11 |
| `flutter test` (full suite) | All passed | 101/101 |
| `flutter analyze --no-fatal-infos` | No issues | 0 errors, 0 warnings |
---
## Commit Verification
All 5 commits documented in SUMMARY files confirmed to exist in git history:
| Hash | Description |
|------|-------------|
| `f5c4b49` | test(05-01): add failing tests for CalendarDao |
| `c666f9a` | feat(05-01): implement CalendarDao with date-parameterized task queries |
| `68ba7c6` | feat(05-01): add CalendarDayState model, Riverpod providers, and l10n strings |
| `f718ee8` | feat(05-02): build CalendarStrip, CalendarTaskRow, CalendarDayList widgets |
| `88ef248` | feat(05-02): replace HomeScreen with calendar composition and floating Today button |
---
## Human Verification Required
All automated checks pass. The following items require a device or emulator to confirm:
### 1. Calendar Strip Visual Rendering
**Test:** Launch the app, navigate to the home tab.
**Expected:** Horizontal row of day cards each showing a German day abbreviation (Mo, Di, Mi, Do, Fr, Sa, So) and a date number. All cards have a light sage/green tint background. Today's card has bold text and a 2px green underline accent bar below the date number.
**Why human:** Color fidelity, card proportions, and font weight treatment are visual properties.
### 2. Day Selection Updates Task List
**Test:** Tap several different day cards in the strip.
**Expected:** The tapped card becomes highlighted (stronger green background + border, centered in the strip), and the task list below immediately updates to show that day's scheduled tasks.
**Why human:** Interactive responsiveness and smooth centering animation require a running device.
### 3. Floating Today Button Behavior
**Test:** Scroll the strip well past today (e.g., 30+ days forward). Then tap the floating "Heute" button.
**Expected:** The "Heute" FAB appears when today's card is no longer in the viewport. Tapping it re-centers today's card with a smooth scroll animation and resets the task list to today's tasks. The FAB then disappears.
**Why human:** FAB visibility toggling based on scroll position and imperative scroll-back require real interaction.
### 4. Month Boundary Labels
**Test:** Scroll through a month boundary in the strip.
**Expected:** At the boundary, a small month name label (e.g., "Apr") appears above the first card of the new month, and the gap between the last card of the old month and the first card of the new month is visibly wider than the normal gap.
**Why human:** Gap width and label placement are visual properties.
### 5. Overdue Section Today-Only
**Test:** With at least one task whose nextDueDate is before today in the database, view the home screen on today's date, then tap a past or future date.
**Expected:** On today's view, a coral-colored "Uberfaellig" section header appears above the overdue task(s) with coral-colored task names. Switching to any other day hides the overdue section entirely — only that day's scheduled tasks appear.
**Why human:** Requires real data in the database and navigation between dates.
### 6. Task Completion Slide-Out Animation
**Test:** Tap a checkbox on any task in the day list.
**Expected:** The task row slides out to the right while simultaneously collapsing its height to zero, over approximately 300ms, then disappears from the list.
**Why human:** Animation smoothness, duration, and visual quality require observation on a running device.
---
## Summary
Phase 5 goal is **fully achieved at the code level**. The horizontal calendar strip replaces the stacked daily plan, the data layer correctly handles date-parameterized queries and overdue isolation, all UI widgets are substantive and properly wired, all key links are connected, all 5 CAL requirements are satisfied, and the full 101-test suite passes with zero analysis issues.
The `human_needed` status reflects that 6 visual and interactive behaviors (strip appearance, tap selection, Today button scroll-back, month boundary labels, overdue section isolation, and task completion animation) require a running device to confirm their real-world quality. No code gaps were found.
---
_Verified: 2026-03-16T21:00:00Z_
_Verifier: Claude (gsd-verifier)_