Security — enforce write authorization server-side (was UI/RPC-only): - it_service_requests RLS: block cross-office read/edit + self-approve (QA-015) - pass_slips RLS: owner can complete but not self-approve (QA-046) - swap_requests RLS: scope select/update to participants + admin (QA-047) - storage: tighten it_service_attachments + task_attachments write/delete (QA-027) - admin_user_management edge function: allow programmers to manage users (QA-016) Fixes: - workforce generator "uncovered shifts" false alarms (QA-043/044) - network-map VLAN + New-location dialog validation, disabled-until-valid (QA-048) - de-flake time-of-day-dependent dashboard metrics test (QA-045) Toolchain: - upgrade to Flutter 3.47.5 / Dart 3.13.4; font_awesome_flutter 11.0.0, flutter_quill 11.6.0, pdfrx 2.6.5; clear resulting deprecations (QA-002) analyze clean; 139 tests pass; web build succeeds. Report + evidence in docs/qa/. Note: also carries the in-progress Brick model cleanup already present in the working tree. QA-001 (AI keys public in the build) is deferred by owner decision. Co-Authored-By: claude-flow <ruv@ruv.net>
25 KiB
TasQ QA Report
Status: In progress (loop round 3).
- Round 1 covered everything reachable without logging in.
- Round 2 covered the standard role end-to-end at 1440×900 and 390×844.
- Round 3: logged in as admin (via
ADMIN_USERNAME/ADMIN_PASSWORDin.env), then used User Management to set one dispatcher, it_staff and programmer account each to the shared test password. All five roles now have working logins and saved sessions. Verified role landing, route gating, network-map write gating, several earlier fixes, and dark mode.
Round-3 confirmations (browser, live):
- QA-004 landing routes for all five roles: admin/dispatcher/programmer →
/dashboard, it_staff →/tasks, standard →/tickets. - QA-005 deep links no longer bounce authorized roles; QA-006 gating is consistent (dispatcher/it_staff blocked from
/settings/*, programmer allowed). - QA-032 (dates instead of "4490h ago") and QA-035 (staff names, not initials) on the Dashboard pulse.
- Network-map write gating: dispatcher read-only (no "Add site"), admin has it.
- QA-016 reproduced live: as programmer, User Management fires a 403 per row and shows every email "Unknown".
- Dark mode clean on Dashboard, User Management, Reports.
Last updated: 2026-09-25
1. Environment
| Item | Value |
|---|---|
| Flutter / Dart | 3.47.5 / 3.13.4 on the host at ~/flutter (upgraded from 3.41.9 per QA-002; the two blocking packages were bumped so the project now builds on current stable). |
| Browser driver | agent-browser 0.27.0 with system Chrome 152, driving the app through Flutter's semantics tree |
| App under test | flutter run -d web-server in debug mode at 127.0.0.1:8787 |
| Backend | Hosted test Supabase project pwbxgsuskvqwwaejxutj (from .env). Email confirmation is off. |
| Test account | A self-registered standard user, "QA Standard User" (office: Accounting). All QA data is prefixed [QA-standard]. |
2. Static baseline
| Check | Before | Now |
|---|---|---|
flutter analyze |
0 issues | 0 issues |
flutter test |
126 pass / 8 fail / 1 skip. The same 8 fail on clean HEAD. |
139 pass / 0 fail / 1 skip |
Side effects on the working tree:
flutter pub getrefreshedpubspec.lock(dropsdio) and added build and platform folders toanalysis_options.yaml.- An accidental
dart formatrun was reverted by a verified 3-way merge. The one exception islib/screens/tasks/task_detail_screen.dart, which stays formatted. Its behaviour is identical; the approved plan called for formatting it anyway.
3. Coverage
| Area | Desktop | Mobile | Dark | Roles checked |
|---|---|---|---|---|
| Auth: login and sign-up | ✅ | ✅ | ✅ | signed out |
| Shell: rail, bottom nav, More sheet, gated URLs | ✅ | ✅ | – | standard |
| Dashboard | ✅ | ✅ | – | standard |
| Tickets: list, filters, create, detail, chat, status menu | ✅ | ✅ | – | standard |
| Tasks: list, tabs, table columns | ✅ | – | – | standard |
| IT service requests: list, create dialog | ✅ | ✅ | – | standard. Create is blocked on the backend (QA-026). |
| Announcements, Workforce, Notifications, Profile | ✅ | ✅ | – | standard |
| Server security probes (RLS) | ✅ | standard token | ||
| Attendance (geofence detection verified; check-in RPC needs a real device — see below) | ✅ partial | – | – | admin |
| Whereabouts, Settings | ✅ | – | dark ok | admin/all |
| Reports: RPC data + all 14 widgets render, Export PDF generates (print stub caught, no hang) | ✅ | admin | ||
| Network Map: overview/sites/devices, VLAN validation (QA-048), write RLS admin/it_staff-only (verified secure) | ✅ | admin | ||
| Cross-role: task create → assign → notify → transition | ✅ | admin+it_staff | ||
| Workforce: schedule generation (preview → warnings → commit → render → conflict guard → delete) | ✅ | admin | ||
| Announcements: create + role targeting + banner + notification fan-out + cross-role RLS visibility + delete (cascade) | ✅ | admin + standard | ||
| Approval-chain RLS audit: leave (secure), pass-slip (QA-046 fixed+verified), swap (QA-047 fixed+verified) | ✅ audit + live probes | inspection + REST (admin/it_staff tokens) | ||
| Live approval workflows (file → approve → notify in-browser), PDF export | ⛔ not yet |
4. Issues
Severity:
- P0: build, crash, data loss or security
- P1: feature broken
- P2: UX, visual or accessibility defect
- P3: polish
"Verified" means checked in the browser; "unit/smoke" means checked by the test suite.
| ID | Sev | Area | Issue | Root cause | Status |
|---|---|---|---|---|---|
| QA-001 | P0 | Security | Anyone can download the whole .env from the web build at /assets/.env, and it ships inside the APK. It contains GEMINI_API_KEY, DEEPSEEK_API_KEY and the commented production credentials. |
.env is bundled as a Flutter asset; ai_service.dart reads the keys on the client |
Accepted / deferred (owner decision 2026-09-26: leave the Gemini/DeepSeek keys as-is, no rotation). ⚠️ Noted for a future security pass: these keys remain publicly readable in every web build and APK. When revisited, the fix is an ai_proxy edge function that holds the keys as server secrets so they stop shipping to clients (and rotate them then). No code changed. |
| QA-015 | P0 | Security | Any signed-in user could read every office's IT service requests, edit any of them, approve their own, and assign or unassign staff through the REST API. | USING (true) policies in 20260308090000_add_it_service_requests.sql |
Verified fixed. standard reads 0 other-office ISRs (was 2); cross-office edit/self-approve → rows=0; own self-approve → 403 "Only admins can approve" (trigger); admin approve → 200; creator can still edit own pending request. Migration 20260924090000_isr_rls_hardening.sql. |
| QA-039 | P1 | Backend | The QA-015 SELECT tightening broke standard-user ISR creation: insert_it_service_request_with_number computes MAX(request_number) under the caller's RLS, so a non-privileged creator (who can't see others' rows) regenerated ISR-2026-0001 and hit the unique constraint (23505). Privileged roles (it_staff verified) create fine. |
The function isn't SECURITY DEFINER |
Verified fixed. Standard create now succeeds (ISR-2026-0005, forced pending_approval, creator = the user). Function is SECURITY DEFINER, creator pinned, status clamped. |
| QA-040 | P2 | Backend | Migration 20260604090000 added an 8-arg insert_it_service_request_with_number via CREATE OR REPLACE without dropping the original 7-arg one, leaving two overloads (PGRST203). |
Overload not dropped | Verified fixed (single function; standard create resolves cleanly) |
| QA-041 | P3 | Tasks | On the task detail page the Details panel (assignees, signatories, attachments, type) is collapsed by default, and on wide screens Chat occupies the prominent right pane — so the assignment control is two clicks deep. | Default-collapsed section | Open (minor UX) |
| QA-042 | P2 | Attendance | A Dart console error Too many positional arguments. Expected: 1 Actual: 2 fires when the Check-In geofence status refreshes (seen with and without a stubbed location). No visible impact, but it's an uncaught error worth a look. |
Unknown (no stack trace captured); likely a geolocation/position callback arity mismatch | Open — needs investigation on a device |
| QA-043 | P2 | Workforce | Schedule Generator "Uncovered shifts" panel ignored already-committed schedules. After committing a schedule, regenerating the same range produced an empty preview (the weekly-hours cap correctly blocks new drafts) and falsely flagged every required shift as uncovered — directly contradicting the Schedule tab. This also made it impossible to use the generator to fill a genuine gap after a partial commit. | _buildWarnings computed the day's "covered" set only from freshly generated drafts, never from existing dutySchedulesProvider rows |
Verified fixed (browser + debug trace). _buildWarnings now also counts committed schedules for each day. workforce_screen.dart |
| QA-044 | P2 | Workforce | Every Saturday and Sunday in a preview permanently reported "missing On Call", even in the first generation, although a weekend on-call shift was generated and committed. | The coverage check requires a generic on_call each day, but the generator emits the weekend variants on_call_saturday / on_call_sunday, which never matched. _normalizeShiftType (intended to fold variants) was an identity no-op and is load-bearing for template/generation, so it couldn't be changed globally |
Verified fixed (debug trace: Sat/Sun required={on_call,normal} now satisfied by available={normal,on_call}). Added a coverage-only _coverageShiftType that folds the weekend variants to on_call; generation/template code untouched. workforce_screen.dart |
| QA-045 | P3 | Tests | dashboard_metrics_provider_test.dart → "rejected leave should not affect schedule status" was time-of-day dependent: it hard-coded an 08:00–16:00 shift and asserted the status isn't "off", so it passed only when the suite ran during the day and failed (correctly reading "Off duty") at night. Production status logic is correct. |
Fixture used a fixed daytime window instead of anchoring the shift around now |
Fixed. Shift now spans now-1h…now+4h (matching the leave-window style already in the same test); suite is green regardless of clock. test/dashboard_metrics_provider_test.dart |
| QA-046 | P1 | Security | Pass-slip self-approval bypass. The pass_slips_update policy has a USING of user_id = auth.uid() OR admin/dispatcher and no WITH CHECK, so Postgres reuses USING as the check — the slip owner can PATCH their own row to status='approved' (plus approved_by/approved_at) via REST, self-approving their own duty excusal. Approval is only gated in the UI. Same class as QA-015. Confirmed by policy inspection (not live-probed: pass_slips has no DELETE policy and duty_schedule_id is RESTRICT-referenced, so a probe would leave un-removable test data). |
Missing WITH CHECK in 20260306090200_pass_slips.sql:38 |
Verified fixed (migration applied + live REST probes with an it_staff owner token): owner self-approve (pending→approved) → 0 rows; owner approved→rejected → 42501 RLS violation; admin approve → 200; owner approved→completed → 200. supabase/migrations/20260926090000_pass_slip_rls_hardening.sql. Probe left two un-deletable rows (pass_slips has no DELETE policy, duty_schedule_id is RESTRICT): one completed [QA] slip + one 2027-02-01 duty schedule — benign, removable only via SQL. |
| QA-048 | P2 | Network map | Two dialogs accepted the Create click unconditionally (Navigator.pop(ctx, true)) and validated only after the dialog closed, with no else — so invalid input silently closed the dialog and created nothing, with no feedback: (1) New VLAN — out-of-range ID (0, 4095), non-numeric, or empty name; (2) New location (device edit) — empty name. |
Validation ran post-close; no guard on the button | Fixed (both). Create is now disabled until input is valid (same pattern as QA-029). VLAN verified in-browser (empty/0/4095/"abc" → disabled, 20 → enabled); New location fix is identical (analyze clean). network_map_vlan_screen.dart, network_map_device_edit_screen.dart. |
| QA-047 | P1 | Security | Shift-swap authorization bypass (confirmed live). The RPCs (respond_shift_swap) correctly guard accept/reject by identity, but the swap_requests table has no scoped RLS, so the RPCs are bypassable via direct PostgREST: (a) a requester PATCHed their own swap straight to status='accepted', forging the recipient's acceptance (200, 1 row); (b) a non-participant it_staff PATCHed someone else's swap (200, 1 row); (c) SELECT is unscoped too — any it_staff reads all 60 swaps, incl. 48 they're not in. |
swap_requests base-table policies (out-of-repo) allow any authenticated user to read/update any row |
Verified fixed (migration applied + re-probed via MCP-confirmed policies and it_staff REST token): non-participant SELECT now 0 (was 48); requester direct status write → 0 rows; non-participant direct write → 0 rows; legitimate request_shift_swap create still succeeds. supabase/migrations/20260926100000_swap_requests_rls_hardening.sql. |
| QA-027 | P1 | Security | The it_service_attachments storage bucket is public, and any signed-in user can delete any attachment |
Storage DELETE policy was bucket_id = 'it_service_attachments' for all authenticated users |
Fixed + applied (per your decision: keep bucket public, tighten delete). DELETE now requires owner = auth.uid() OR an ISR-privileged role (admin/programmer/dispatcher/it_staff). Reads/uploads unchanged. supabase/migrations/20260926110000_isr_attachment_delete_hardening.sql, applied to the test backend. Related (also fixed + applied): the separate task_attachments bucket had INSERT/UPDATE/DELETE open to the public role (unauthenticated users could upload/overwrite/delete). Writes are now authenticated-only, with UPDATE/DELETE scoped to uploader-or-privileged; reads unchanged. supabase/migrations/20260926120000_task_attachment_write_hardening.sql. |
| QA-026 | P1 | Backend | Creating an IT service request failed with PGRST202; network_devices.status was missing (42703). |
Test backend was missing migrations 20260604090000, 20260605100000, 20260605110000 |
Migrations applied. it_staff ISR create now works and lands on the detail page. Standard create still blocked by QA-039 until the re-apply. |
| QA-002 | P1 | Build | Doesn't compile on Flutter ≥ 3.44 | font_awesome_flutter 10.12 (extends now-final IconData) and flutter_quill 11.5.0 (missing TextInputClient.onFocusReceived) |
Fixed + verified (per your decision: upgrade). Moved to Flutter 3.47.5 / Dart 3.13.4; font_awesome_flutter → 11.0.0, flutter_quill → 11.6.0, and pdfrx → 2.6.5 / pdfrx_engine → 0.6.1 (0.3.9 failed native compile under Dart 3.13 — would have broken the APK). pubspec Dart floor ^3.10.7 → ^3.12.0. Cleared the 4 resulting deprecations (axisAlignment, onReorder×2, dismissPdfiumWasmWarnings). Result: flutter analyze 0 issues, 139 tests pass, flutter build web succeeds, app runs (icons + Quill render, no console errors). |
| QA-003 | P1 | Build (Android) | The uncommitted diff dropped the flutter_keyboard_visibility: ^6.0.0 override |
pubspec.yaml |
Fixed |
| QA-004 | P1 | Auth | Every login landed on /tickets instead of the role's home route |
login_screen.dart forced context.go('/tickets') |
Verified across all 5 roles |
| QA-005 | P1 | Routing | Opening a gated route directly bounced even an admin while the profile loaded | app_router.dart treated a still-loading role as "no access" |
Verified |
| QA-006 | P1 | Shell | Dispatchers saw Settings items and standard users saw Reports, both of which the router blocks | Nav rules didn't match the router | Verified (standard, dispatcher, it_staff, programmer) |
| QA-007 | P1 | Notifications | In-app notification banners never appeared. Two latent bugs were behind that: the wrong item, and false banners on startup and resume. | NotificationBridge used a messenger with no Scaffold, and .last on a newest-first list |
Fixed + pipeline verified live. admin assigns task → notifications row (201) → it_staff sees "ihomp assigned you … Just now" + 99+ bell → tap routes to the task → it_staff transitions it to in_progress (204). Banner-selection logic is unit-tested. |
| QA-008 | P1 | Data | Lists came back reversed across the app: pickers Z→A, ports and VLANs descending, comments and chat ordered differently online and offline | postgrest .order() defaults to descending |
Fixed. Verified (order=name.asc) |
| QA-009 | P1 | Tasks | The Tasks table hid Status on almost every desktop width | The list-width check was measured against a screen breakpoint | Fixed. Verified at 1440 (screenshots/QA-009-…) |
| QA-028 | P1 | Tickets | Standard users could file a ticket for any office. It then vanished from their own list, because they only see their own offices' tickets. | The create dialog used the all-offices provider | Fixed. Verified: only the user's office is offered |
| QA-033 | P1 | Performance | Every client re-downloaded the whole duty_schedules table, and swap_requests, every 3 s on every page: about 40 requests a minute per user |
A 3 s "safety-net" Timer.periodic in workforce_provider.dart |
Fixed. Now 60 s; realtime remains the primary path |
| QA-025 | P2 | UX | Error messages showed raw exception text, e.g. AuthApiException(message: …, statusCode: 400…). This affected 64 places. |
Callers put '$e' straight into the message |
Fixed once in snackbar.dart (humanizeErrorText, with a unit test). Verified |
| QA-029 | P2 | IT service requests, Workforce | "New Request" closed on an empty name and discarded what the user had entered. The "Add holiday" dialog silently did nothing on an empty name. | Validation ran after the dialog closed | Fixed. The button stays disabled until valid. Verified |
| QA-030 | P2 | Tickets | The table showed 36-character UUIDs, which pushed Status off-screen at 1440 | Raw ticket.id |
Fixed. Now an 8-character ID with the full ID in a tooltip. Verified |
| QA-031 | P2 | Web | The mobile-only "Never miss an update" dialog, whose "Open settings" does nothing on web, appeared on web | No platform check | Fixed. Skipped on web |
| QA-032 | P2 | Dashboard | Stale times read "Last seen outside · 4490h ago" | Relative times were only ever shown in hours | Verified (shows dates); AppTime.relative() added, 2 dup helpers removed |
| QA-034 | P2 | Shell | The rail highlighted Dashboard while on Profile and Notifications | The index lookup fell back to 0 | Fixed. Verified |
| QA-035 | P2 | Dashboard (mobile) | IT Staff Pulse showed initials-only avatars, with names only in a long-press tooltip | A mobile-specific branch | Verified. Names shown, ellipsized |
| QA-010 | P2 | Web | Every page load logged "Could not navigate to initial route" | A wrapper MaterialApp on web |
Fixed. Verified |
| QA-011 | P2 | Attendance | Platform.isAndroid threw on web |
No kIsWeb guard |
Fixed |
| QA-012 | P2 | Detail pages | The "Assigned IT Staff" header overflowed by 13 px | Fixed-width text with a Spacer |
Fixed (smoke test) |
| QA-013 | P2 | Accessibility | Password show/hide buttons had no label | No tooltip | Fixed. Verified |
| QA-014 | P2 | Tests | 8 tests were failing before this work, and 1 passed by accident | Stale expectations, and a smoke-test helper whose screen size didn't reach MediaQuery |
Fixed (139/139) |
| QA-016 | P1 | Settings | Programmers can open User Management, but every row 403s and emails show "Unknown" | The edge function allows admin only | Fixed + verified + deployed (per your decision: programmers may manage users). admin_user_management/index.ts now allows admin or programmer; deployed to the test backend (v12). Verified: a programmer token's list_users returns 200 (was 403). |
| QA-017 | P2 | Detail pages | Detail routes have no app-bar title | _routeToTitle |
Open. No double app bar on ticket detail; network map not yet checked |
| QA-018 | P2 | Startup | Up to about 40 s of blank screen on a slow network | Startup timeouts add up | Open |
| QA-019 | P2 | Accessibility | No Semantics or semanticLabel anywhere. The ticket detail back button has no label. |
App-wide | Open |
| QA-020 | P3 | Web | Warning about the viewport <meta> on every load |
web/index.html |
Open |
| QA-021 | P3 | Auth | Validation errors stay visible after the field is fixed | autovalidateMode |
Open |
| QA-022 | P3 | Data | Duplicate office names ("dddd", "dawd" ×3) | No unique constraint | Open |
| QA-023 | P3 | Web | The connectivity check sends a HEAD every 5 s |
connectivity_provider.dart |
Open |
| QA-024 | P3 | Auth | No "forgot password" flow | Not built | Reported only |
| QA-036 | P3 | Copy | The Workforce empty state tells everyone to "Use the Generator tab", which non-admins don't have. The nav says "Announcement" but the page says "Announcements". The mobile label "IT Service Requests" wraps. The Pulse columns (Tickets, Tasks, Events) don't say they count "closed today". | Copy | Open |
| QA-037 | P3 | Privacy | Standard users can see IT staff location status ("Last seen outside…") on the Dashboard, even though Whereabouts is blocked for them | The dashboard is identical for all roles | Open. Product decision |
| QA-038 | P3 | Data | Two separate accounts share the display name "Paola Ross Liberato" (paolaliberato92@gmail.com = standard, pao@mail.com = programmer). Duplicate display names make User Management and pickers ambiguous. |
No name uniqueness; likely a real re-registration | Open. Test-backend data, but the UI should disambiguate (show email) |
5. Server-side probes (standard user token, test backend)
| Table | Rows the standard user can read | Verdict |
|---|---|---|
| tickets | 8, all from the user's office | ✅ scoped |
| tasks | 48, all from the user's office | ✅ scoped |
| it_service_requests | 2, both from other offices | ❌ QA-015 |
| profiles | 21 (the full directory) | Acceptable |
| user_offices | 1 (own) | ✅ |
| it_service_requests PATCH on another office's row | HTTP 200, 1 row | ❌ QA-015 |
6. Blockers and decisions needed
Admin login is now resolved (round 3). All five role sessions exist. Remaining:
All prior decisions have been resolved by the owner (2026-09-26):
QA-015/039/040— fixed + verified (ISR RLS).QA-016— programmers may manage users → fixed, deployed, verified.QA-027— keep buckets public, tighten deletes → done (it_service + task_attachments).QA-002— upgrade → done (Flutter 3.47.5).- QA-001 — leave the AI keys, noted for a future security enhancement (server-side
ai_proxy). This is the one remaining known exposure, deliberately deferred.
6b. Harness limits (need a real device / manual)
- Attendance check-in / overtime RPC: headless Chrome doesn't grant geolocation, and the in-handler
Geolocator.getCurrentPositiondoesn't resolve with a stubbed position, so the check-in RPC can't be driven here. Geofence detection is verified (screen flips to "Within geofence" with a stubbed location). The check-in, face verification, and PDF print-dialog flows need on-device testing. - QA-046/047 probe residue — cleaned up. The pass-slip and swap probes left rows that REST couldn't delete (
pass_slips/swap_requestshave no DELETE policy; the schedule was RESTRICT-referenced). Once the Supabase MCP reconnected these were removed with service access:pass_slips 55046efa…,swap_requests 514c97ed…/befed0d0…,duty_schedules 5fe10a17…— all confirmed gone (0 rows). The only remaining trace is a cosmeticupdated_atbump on real long-rejected swapebb4b9e5…from the non-destructive PROBE C (status unchanged).- Minor observation: neither
pass_slipsnorswap_requestshas a DELETE policy, so erroneous rows can't be removed through the app — likely intentional (immutable records), noted for awareness.
- Minor observation: neither
7. Files changed by QA
lib/
main.dartrouting/app_router.dartservices/notification_bridge.dartutils/:app_time.dart,device_security.dart,snackbar.dartwidgets/:app_shell.dart,tasq_adaptive_list.dart,task_assignment_section.dartscreens/auth/login_screen.dart,auth/signup_screen.dartdashboard/dashboard_screen.darttickets/tickets_list_screen.dartit_service_requests/it_service_requests_list_screen.dartworkforce/rotation_settings_dialog.dart,workforce/workforce_screen.dart(QA-043/044: coverage warnings)announcements/announcements_screen.dart,announcements/announcement_comments_section.dart
providers/chat,profile,announcements,workforce,tickets,services,user_officesandteamsprovidersnetwork_map/{network_devices,network_sites}_provider.dartnetwork_map/network_map_vlan_screen.dart,network_map/network_map_device_edit_screen.dart(QA-048: dialog validation)
QA-002 upgrade also touched: pubspec.yaml/pubspec.lock (Flutter 3.47.5 + font_awesome 11 + flutter_quill 11.6 + pdfrx 2.6.5), lib/main.dart (dropped deprecated pdfrx flag), lib/screens/network_map/widgets/topology_legend.dart (axisAlignment→alignment), lib/screens/workforce/rotation_settings_dialog.dart (onReorder→onReorderItem), lib/providers/stream_recovery.dart (lint suppression).
Everything else:
pubspec.yamlsupabase/migrations/20260924090000_isr_rls_hardening.sql(new)supabase/migrations/20260926090000_pass_slip_rls_hardening.sql(new — QA-046)supabase/migrations/20260926100000_swap_requests_rls_hardening.sql(new — QA-047)- tests:
notification_bridge_test.dart(new),snackbar_humanize_test.dart(new),layout_smoke_test.dart,offline_sync_test.dart,dashboard_metrics_provider_test.dart(QA-045: de-flaked)