diff --git a/billsplit/lib/ui/home_screen.dart b/billsplit/lib/ui/home_screen.dart index 55a26b37..dbf00ea9 100644 --- a/billsplit/lib/ui/home_screen.dart +++ b/billsplit/lib/ui/home_screen.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'dart:convert'; import 'package:billsplit/domain/eparagon_parser.dart'; @@ -26,11 +25,9 @@ class HomeScreen extends StatelessWidget { IconButton( tooltip: 'People & groups', icon: const Icon(Icons.group), - onPressed: () => unawaited( - Navigator.push( - context, - MaterialPageRoute(builder: (_) => const PeopleScreen()), - ), + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => const PeopleScreen()), ), ), ], @@ -70,12 +67,10 @@ class HomeScreen extends StatelessWidget { formatPln(r.itemsTotalGr), style: Theme.of(context).textTheme.titleMedium, ), - onTap: () => unawaited( - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => ReceiptScreen(receiptId: r.id), - ), + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => ReceiptScreen(receiptId: r.id), ), ), ), @@ -146,11 +141,9 @@ class HomeScreen extends StatelessWidget { for (final w in result.warnings) { messenger.showSnackBar(SnackBar(content: Text(w))); } - unawaited( - nav.push( - MaterialPageRoute( - builder: (_) => ReceiptScreen(receiptId: result.receipt.id), - ), + nav.push( + MaterialPageRoute( + builder: (_) => ReceiptScreen(receiptId: result.receipt.id), ), ); } on EParagonParseException catch (e) { @@ -189,11 +182,9 @@ class HomeScreen extends StatelessWidget { if (title == null || title.trim().isEmpty) return; final r = Receipt(id: Ids.next(), title: title.trim()); state.addReceipt(r); - unawaited( - nav.push( - MaterialPageRoute( - builder: (_) => ReceiptScreen(receiptId: r.id), - ), + nav.push( + MaterialPageRoute( + builder: (_) => ReceiptScreen(receiptId: r.id), ), ); } diff --git a/billsplit/test/people_flows_test.dart b/billsplit/test/people_flows_test.dart index 081ceca4..7c297b12 100644 --- a/billsplit/test/people_flows_test.dart +++ b/billsplit/test/people_flows_test.dart @@ -1,6 +1,5 @@ // People screen flows. - import 'package:billsplit/domain/models.dart'; import 'package:billsplit/state/app_state.dart'; import 'package:billsplit/ui/people_screen.dart'; diff --git a/billsplit/test/receipt_flows_test.dart b/billsplit/test/receipt_flows_test.dart index 740b14ba..09f58809 100644 --- a/billsplit/test/receipt_flows_test.dart +++ b/billsplit/test/receipt_flows_test.dart @@ -1,6 +1,5 @@ // Receipt screen flows. - import 'package:billsplit/domain/models.dart'; import 'package:billsplit/state/app_state.dart'; import 'package:billsplit/ui/receipt_screen.dart'; diff --git a/billsplit/test/receipt_items_test.dart b/billsplit/test/receipt_items_test.dart index b48fdc2d..07059e91 100644 --- a/billsplit/test/receipt_items_test.dart +++ b/billsplit/test/receipt_items_test.dart @@ -2,7 +2,6 @@ // Receipt screen flows. - import 'package:billsplit/domain/models.dart'; import 'package:billsplit/state/app_state.dart'; import 'package:billsplit/ui/receipt_screen.dart'; diff --git a/docs/superpowers/contracts/billsplit-ci-green-2026-08-17.json b/docs/superpowers/contracts/billsplit-ci-green-2026-08-17.json new file mode 100644 index 00000000..e9e2afea --- /dev/null +++ b/docs/superpowers/contracts/billsplit-ci-green-2026-08-17.json @@ -0,0 +1,17 @@ +{ + "title": "Make the billsplit CI gates green before touching billsplit", + "objective": "billsplit CI was already failing on main at 6713ef3: flutter analyze --fatal-infos exited 1 with four unnecessary_unawaited issues, and dart format --set-exit-if-changed exited 1 on three test files left unformatted by an earlier split commit. Both are steps in .github/workflows/billsplit-ci.yml. This change fixes exactly those two gates and nothing else, so that the platform-directory deletion that follows can be verified as green-to-green rather than red-to-red.", + "acceptance_criteria": [ + "flutter analyze --fatal-infos exits 0 in billsplit/ (was exit 1, 4 issues)", + "dart format --set-exit-if-changed . exits 0 in billsplit/ (was exit 1, 3 changed files)", + "flutter test still reports exactly 60 passing tests, the same count as at HEAD - proving the unawaited() removals did not alter navigation behaviour in the widget tests", + "No lint suppression, no analysis_options exclusion, and no lowered threshold is used to reach any of the above" + ], + "out_of_scope": [ + "Deleting billsplit/windows, macos, ios or linux - that is the next commit, with its own contract", + "Any change to billsplit behaviour, UI, or test assertions", + "Reformatting Dart files outside billsplit/", + "The 45 bucket_catch eslint errors and the usage_report split work, both tracked separately in this session" + ], + "verifier": "cd billsplit && flutter analyze --fatal-infos && dart format --set-exit-if-changed . && flutter test - the first three steps of .github/workflows/billsplit-ci.yml. Each was run before and after the change and the exit codes are recorded in docs/superpowers/evidence/billsplit-ci-green-2026-08-17.json." +} diff --git a/docs/superpowers/evidence/billsplit-ci-green-2026-08-17.json b/docs/superpowers/evidence/billsplit-ci-green-2026-08-17.json new file mode 100644 index 00000000..a9ad37fc --- /dev/null +++ b/docs/superpowers/evidence/billsplit-ci-green-2026-08-17.json @@ -0,0 +1,38 @@ +{ + "intent": "Restore the two billsplit CI gates that were already failing on main, so that subsequent billsplit changes can be verified against a green baseline instead of a red one.", + "scope": [ + "billsplit/lib/ui/home_screen.dart - remove redundant unawaited() wrappers and the now-unused dart:async import", + "billsplit/test/people_flows_test.dart, receipt_flows_test.dart, receipt_items_test.dart - apply dart format (one trailing blank line each)", + "Non-goal: no behaviour change, no test added or removed, no platform-directory work (that is a separate commit)" + ], + "changes": [ + "Removed four unawaited(...) wrappers around Navigator.push / nav.push at home_screen.dart lines 29, 73, 149, 192. Flutter now annotates Navigator.push with @awaitNotRequired, which makes the wrapper redundant and triggers the unnecessary_unawaited lint. Each removal is a pure unwrapping of the argument expression; no await was introduced and no control flow changed.", + "Removed 'import dart:async' from home_screen.dart, which was only present for unawaited(). Future is exported by dart:core, so the three async methods in the file still analyze clean.", + "Ran dart format on three test files left unformatted by commit a68313f (Split every over-cap billsplit Dart file). Each change is the deletion of a single trailing blank line." + ], + "verification": [ + { + "command": "flutter analyze --fatal-infos (in billsplit/)", + "result": "pass", + "evidence": "Before this change, at HEAD 6713ef3: exit 1, '4 issues found', all four unnecessary_unawaited in lib/ui/home_screen.dart at 29:30, 73:38, 149:7, 192:5. After: exit 0, 'No issues found! (ran in 2.1s)'. This is the exact command .github/workflows/billsplit-ci.yml runs." + }, + { + "command": "dart format --set-exit-if-changed . (in billsplit/)", + "result": "pass", + "evidence": "Before: 'Formatted 29 files (3 changed)' - people_flows_test.dart, receipt_flows_test.dart, receipt_items_test.dart were unformatted at HEAD. After: 'Formatted 29 files (0 changed)', exit 0. This is also a billsplit-ci.yml step." + }, + { + "command": "flutter test (in billsplit/)", + "result": "pass", + "evidence": "60 tests passed both before and after the change. The unchanged count is the discriminating check: removing unawaited() could have altered navigation timing in the widget tests, and did not." + } + ], + "risks": [ + "unawaited() communicates intent to a human reader as well as to the analyzer. Removing it makes the fire-and-forget nature of these navigation calls implicit rather than explicit. Accepted because the @awaitNotRequired annotation on Navigator.push now carries that same information at the API level, and the alternative is a permanently red --fatal-infos gate or a lint suppression.", + "The three formatting-only test edits touch files this task did not otherwise need to change. They are included because dart format runs repo-wide in CI, so the gate cannot be made green without them." + ], + "rollback": [ + "git revert the commit. home_screen.dart returns to using unawaited() with the dart:async import, and the three test files regain their trailing blank lines.", + "After rollback, expect flutter analyze --fatal-infos to exit 1 with 4 unnecessary_unawaited issues and dart format --set-exit-if-changed to exit 1 with 3 changed files - i.e. billsplit CI red again, as it was at 6713ef3. flutter test should still report 60 passing." + ] +}