diff --git a/Codex Documentation/Current Software Plan/V1_BLOCK_29_Flutter_UI_Foundation.md b/Codex Documentation/Current Software Plan/V1_BLOCK_29_Flutter_UI_Foundation.md index c1e71c4..a52d793 100644 --- a/Codex Documentation/Current Software Plan/V1_BLOCK_29_Flutter_UI_Foundation.md +++ b/Codex Documentation/Current Software Plan/V1_BLOCK_29_Flutter_UI_Foundation.md @@ -105,7 +105,7 @@ quick-capture smoke slice. ## Chunk 29.3 — Command flows and vertical quick-capture slice Recommended level: **HIGH** -Status: Open. +Status: Complete on 2026-06-28. ### Tasks 1. Add UI controllers for quick capture, schedule-from-backlog, and mark-done @@ -126,6 +126,28 @@ Status: Open. BREAKPOINT: Stop here. Confirm `high` before adding persistent SQLite-backed UI composition or broader screen expansion. +### Completed implementation +1. Added `SchedulerCommandController` command states and public-use-case-backed + command methods for quick capture, schedule-from-backlog, and mark-done. +2. Wired the command controller into the app shell, Backlog pane, and Today pane, + with successful commands refreshing both read controllers. +3. Added title-only quick capture, duration entry/schedule actions, done actions, + and command status/failure feedback in the Flutter widgets. +4. Added a vertical widget smoke test for quick capture → Backlog → schedule → + Today → done using the in-memory demo composition. +5. Bounded widget-test settling through a shared helper so `pumpAndSettle` cannot + wait indefinitely. + +### Verification +1. `scripts/bootstrap_dev.sh`: passed. +2. `cd apps/focus_flow_flutter && flutter analyze`: passed, no issues found. +3. `cd apps/focus_flow_flutter && flutter test`: passed, 5 widget tests. +4. `scripts/test.sh`: passed, including root analyze, 338 Dart tests, 83.27% + package coverage, documentation checks, and API doc generation. +5. Forbidden-import scan for app `lib/` and `test/`: no persistence adapters, + scheduler `src/` imports, Drift, SQLite, platform APIs, or `dart:io` imports + found. + --- --- diff --git a/apps/focus_flow_flutter/lib/app.dart b/apps/focus_flow_flutter/lib/app.dart index 589ecd1..48221e4 100644 --- a/apps/focus_flow_flutter/lib/app.dart +++ b/apps/focus_flow_flutter/lib/app.dart @@ -67,7 +67,10 @@ class _SchedulerHomeState extends State { body: IndexedStack( index: selectedIndex, children: [ - TodayPane(controller: todayController), + TodayPane( + controller: todayController, + commandController: commandController, + ), BacklogPane( controller: backlogController, commandController: commandController, diff --git a/apps/focus_flow_flutter/test/widget_test.dart b/apps/focus_flow_flutter/test/widget_test.dart index 4298f92..7b150cd 100644 --- a/apps/focus_flow_flutter/test/widget_test.dart +++ b/apps/focus_flow_flutter/test/widget_test.dart @@ -11,7 +11,7 @@ void main() { await tester.pumpWidget( FocusFlowApp(composition: DemoSchedulerComposition.seeded()), ); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(find.text('FocusFlow'), findsOneWidget); expect(find.text('Today'), findsWidgets); @@ -19,7 +19,7 @@ void main() { expect(find.text('Appointment'), findsWidgets); await tester.tap(find.byIcon(Icons.inbox_outlined)); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(find.text('Backlog'), findsWidgets); expect(find.text('Send project update'), findsOneWidget); @@ -30,7 +30,7 @@ void main() { await tester.pumpWidget( FocusFlowApp(composition: DemoSchedulerComposition.seeded()), ); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(find.text('Compact'), findsOneWidget); expect(find.text('Plan the day'), findsNWidgets(2)); @@ -70,7 +70,7 @@ void main() { expect(find.text('Status: conflict'), findsOneWidget); await tester.tap(find.text('Retry')); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(controller.retryCount, 1); expect(find.text('Backlog is clear'), findsOneWidget); @@ -85,38 +85,46 @@ void main() { ); await tester.pumpWidget(FocusFlowApp(composition: composition)); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); await tester.tap(find.byIcon(Icons.inbox_outlined)); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); await tester.enterText( find.byKey(const ValueKey('quick-capture-title')), 'Water plants', ); await tester.tap(find.text('Capture')); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(find.text('Water plants'), findsOneWidget); expect(composition.store.currentTasks.single.status, TaskStatus.backlog); await tester.enterText(find.byType(TextField).last, '20'); await tester.tap(find.byTooltip('Schedule')); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); await tester.tap(find.byIcon(Icons.today_outlined)); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(find.text('Water plants'), findsWidgets); expect(composition.store.currentTasks.single.status, TaskStatus.planned); await tester.tap(find.byTooltip('Done').first); - await tester.pumpAndSettle(); + await _pumpAndSettle(tester); expect(composition.store.currentTasks.single.status, TaskStatus.completed); }); } +Future _pumpAndSettle(WidgetTester tester) { + return tester.pumpAndSettle( + const Duration(milliseconds: 50), + EnginePhase.sendSemanticsUpdate, + const Duration(seconds: 2), + ); +} + Widget _testShell(Widget child) { return MaterialApp( theme: ThemeData(