161 lines
9.5 KiB
Markdown
161 lines
9.5 KiB
Markdown
# Project Retrospective
|
|
|
|
*A living document updated after each milestone. Lessons feed forward into future planning.*
|
|
|
|
## Milestone: v1.0 — MVP
|
|
|
|
**Shipped:** 2026-03-16
|
|
**Phases:** 4 | **Plans:** 13
|
|
|
|
### What Was Built
|
|
- Complete room-based household chore app with auto-scheduling task management
|
|
- Daily plan home screen with overdue/today/tomorrow sections and progress tracking
|
|
- Bundled German task templates for 14 room types
|
|
- Daily summary notifications with configurable time and Android permission handling
|
|
- 89 tests covering DAOs, scheduling logic, providers, and widget behavior
|
|
|
|
### What Worked
|
|
- Bottom-up phase structure (foundation -> data -> UI -> polish) kept each phase clean with minimal rework
|
|
- TDD approach for providers and services caught several issues early (async race conditions, API mismatches)
|
|
- Verification gates at the end of Phase 2, 3, and 4 confirmed all requirements before moving on
|
|
- Calendar-anchored scheduling with anchor memory was designed right the first time — no rework needed
|
|
- ARB localization from Phase 1 meant adding German strings was frictionless throughout
|
|
|
|
### What Was Inefficient
|
|
- riverpod_generator InvalidTypeException with drift Task type required workaround (manual StreamProvider) in 3 separate plans — should have been caught in Phase 1 research
|
|
- Some plan specifications referenced outdated API patterns (flutter_local_notifications positional parameters removed in v20+) — research needs to verify exact current API signatures
|
|
- Phase 4 plan checkboxes in ROADMAP.md weren't updated to [x] by executor — minor bookkeeping gap
|
|
|
|
### Patterns Established
|
|
- `@Riverpod(keepAlive: true)` AsyncNotifier with SharedPreferences for persistent settings (ThemeNotifier, NotificationSettingsNotifier)
|
|
- Manual StreamProvider.family/autoDispose for drift type compatibility
|
|
- DailyPlanDao innerJoin pattern for cross-table queries
|
|
- ConsumerStatefulWidget for screens with async callbacks requiring `mounted` guards
|
|
- Provider override pattern in widget tests for database isolation
|
|
|
|
### Key Lessons
|
|
1. Research phase should verify exact current package API signatures — breaking changes between major versions cause plan deviations
|
|
2. Drift + riverpod_generator type incompatibility is a known issue — plan for manual providers from the start when using drift
|
|
3. Verification gates add minimal time (~2 min) but catch integration issues — keep them for all phases
|
|
4. Progressive disclosure (AnimatedSize) is a clean pattern for conditional settings UI
|
|
|
|
### Cost Observations
|
|
- Model mix: orchestrator on opus, researchers/planners/executors/checkers on sonnet
|
|
- Total execution: ~1.3 hours for 13 plans across 4 phases
|
|
- Notable: Verification gates averaged 2 min — very efficient for the confidence they provide
|
|
|
|
---
|
|
|
|
## Milestone: v1.1 — Calendar & Polish
|
|
|
|
**Shipped:** 2026-03-16
|
|
**Phases:** 3 | **Plans:** 5
|
|
|
|
### What Was Built
|
|
- Horizontal 181-day calendar strip replacing the stacked daily plan HomeScreen
|
|
- CalendarDao with date-parameterized reactive Drift streams for day tasks and overdue tasks
|
|
- Task completion history bottom sheet with per-task reverse-chronological log
|
|
- Alphabetical, interval, and effort sort options with SharedPreferences persistence
|
|
- SortDropdown widget in both HomeScreen and TaskListScreen AppBars
|
|
|
|
### What Worked
|
|
- Phase dependency ordering (5 → 6+7 parallel-capable) meant calendar strip was stable before building features on top
|
|
- TDD red-green cycle continued smoothly — every plan had failing tests before implementation
|
|
- Auto-advance mode enabled rapid phase chaining with minimal manual intervention
|
|
- Existing patterns from v1.0 (DAO, provider, widget test) were reused directly — no new patterns invented unnecessarily
|
|
- CalendarStripController (VoidCallback holder) was simpler than GlobalKey approach — good architecture call
|
|
|
|
### What Was Inefficient
|
|
- StateProvider removal in Riverpod 3.x was discovered during execution rather than research — same category of issue as v1.0's riverpod_generator problem
|
|
- ROADMAP.md plan checkboxes still not auto-checked by executor (same bookkeeping gap as v1.0)
|
|
- Phase 5 plan split (data layer + UI) could have been a single plan given the small scope — overhead of 2 separate plans wasn't justified for ~13 min total
|
|
|
|
### Patterns Established
|
|
- CalendarStripController: VoidCallback holder for parent-to-child imperative scroll communication
|
|
- CalendarDayList state machine: first-run → celebration → emptyDay → hasTasks (5 states)
|
|
- In-memory sort via stream.map after DB stream emit — sort preference changes without re-querying
|
|
- SortPreferenceNotifier: sync default + async _loadPersisted() — matches ThemeNotifier pattern
|
|
- Nested Scaffold pattern for per-tab AppBars in StatefulShellRoute.indexedStack
|
|
|
|
### Key Lessons
|
|
1. Riverpod API surface changes (StateProvider removal) should be caught during phase research, not during execution — pattern repeats from v1.0
|
|
2. Plans under ~5 min execution can be merged into a single plan to reduce orchestration overhead
|
|
3. In-memory sort is the right approach when sort criteria don't affect DB queries — avoids re-streaming
|
|
4. Bottom sheets for one-shot modals (history) don't need dedicated Riverpod providers — ref.read() in ConsumerWidget is sufficient
|
|
|
|
### Cost Observations
|
|
- Model mix: orchestrator on opus, executors/checkers on sonnet
|
|
- Total execution: ~26 min for 5 plans across 3 phases
|
|
- Notable: Each plan averaged ~5 min — significantly faster than v1.0's ~6 min average due to established patterns
|
|
|
|
---
|
|
|
|
## Milestone: v1.2 — Polish & Task Management
|
|
|
|
**Shipped:** 2026-04-03
|
|
**Phases:** 4 | **Plans:** 6
|
|
|
|
### What Was Built
|
|
- Smart task delete: hard-delete for unused tasks, soft-delete (isActive flag) for tasks with history
|
|
- Reworked frequency picker: 4 shortcut chips + freeform "Every N unit" interface
|
|
- Dead code cleanup: 3 orphaned v1.0 files removed, zero regressions
|
|
- Anytime task completion: checkboxes always enabled, nextDueDate recalculated from today
|
|
- Recurring task pre-population: virtual instances on all interval days with 0.55 opacity muted styling
|
|
- "Demnächst" (upcoming) section in calendar day view for pre-populated tasks
|
|
|
|
### What Worked
|
|
- Drift schema migration pattern (v2→v3 with BoolColumn.withDefault) was clean — existing rows auto-migrated
|
|
- Query-time virtual instances for pre-population avoided a schema migration entirely — provider-layer only
|
|
- Phase 10 cleanup was surgical: 3 files deleted, DailyPlanDao preserved, all 144 tests passed
|
|
- TDD continued to work well — 9 new DAO tests for pre-population queries caught edge cases
|
|
- Plan 11-01 was small and focused (remove restrictions + fix recalculation) — executed quickly
|
|
|
|
### What Was Inefficient
|
|
- Phase 11 split across two sessions due to scope — could have been planned as a single phase with tighter scope
|
|
- Flutter/dart not available in CI-less environment — verification gate always needs human testing for visual/runtime items
|
|
- _subtractMonths year-boundary bug was caught during execution — the plan's formula used Dart truncation division which fails for negative month values
|
|
|
|
### Patterns Established
|
|
- isActive BoolColumn.withDefault(true) for soft-delete with auto-migration
|
|
- _ShortcutFrequency enum with bidirectional toPickerValues()/fromPickerValues() for picker ↔ shortcut sync
|
|
- Interval-window pre-population: query all recurring tasks, filter by `_isInCurrentIntervalWindow`, exclude completed-in-period
|
|
- Total-month arithmetic for _subtractMonths: `totalMonths = year*12 + month - N` avoids Dart truncation division pitfall
|
|
|
|
### Key Lessons
|
|
1. Provider-layer virtual instances are a powerful pattern for showing derived data without schema changes
|
|
2. Dart's integer truncation division (`~/`) behaves differently from floor division for negative values — always test boundary cases
|
|
3. Soft-delete with BoolColumn.withDefault is the cleanest Drift migration pattern — no backfill needed
|
|
4. Small focused plans (11-01: 2 tasks, 4 files) execute faster and more reliably than large plans
|
|
|
|
### Cost Observations
|
|
- Model mix: orchestrator on opus, executors/verifiers on sonnet
|
|
- Sessions: 2 (plan 01 in prior session, plan 02 + verification in this session)
|
|
- Notable: Plan 11-02 was the most complex single plan in v1.2 — 6 files, 3 new DAO methods, provider rewrite, UI changes
|
|
|
|
---
|
|
|
|
## Cross-Milestone Trends
|
|
|
|
### Process Evolution
|
|
|
|
| Milestone | Phases | Plans | Key Change |
|
|
|-----------|--------|-------|------------|
|
|
| v1.0 | 4 | 13 | Initial project — established all patterns |
|
|
| v1.1 | 3 | 5 | Reused v1.0 patterns — faster execution, auto-advance mode |
|
|
| v1.2 | 4 | 6 | Schema migration, provider-layer virtual instances, soft-delete pattern |
|
|
|
|
### Cumulative Quality
|
|
|
|
| Milestone | Tests | LOC (total) | Key Metric |
|
|
|-----------|-------|-------------|------------|
|
|
| v1.0 | 89 | 7,773 (lib) | dart analyze clean, 0 issues |
|
|
| v1.1 | 108 | 9,051 (lib) | dart analyze clean, 0 issues |
|
|
| v1.2 | 117+ | 13,232 (lib+test) | Drift schema v3, 14 requirements |
|
|
|
|
### Top Lessons (Verified Across Milestones)
|
|
|
|
1. **Research must verify current package API signatures** — v1.0 hit riverpod_generator type incompatibility, v1.1 hit StateProvider removal. Same root cause: outdated API assumptions in plans.
|
|
2. **Established patterns compound** — v1.1 plans averaged ~5 min vs v1.0's ~6 min. v1.2 reused all established patterns seamlessly.
|
|
3. **Verification gates are cheap insurance** — Consistently ~2 min per phase, caught regressions in all milestones.
|
|
4. **Provider-layer transformations avoid schema migrations** — v1.2's pre-population used query-time virtual instances, proving complex derived views can live entirely in the provider layer.
|