high priority low complexity integration pending integration specialist Tier 0

Acceptance Criteria

ReceiptImagePickerIntegration is an abstract class (not a mixin) in lib/data/receipt/receipt_image_picker_integration.dart
pickFromGallery() returns Future<ReceiptImageResult?> with null representing user cancellation
pickFromCamera() returns Future<ReceiptImageResult?> with null representing user cancellation or camera unavailability
ReceiptImageResult is an immutable value object with fields: Uint8List bytes (non-null, non-empty) and String mimeType (e.g. 'image/jpeg', 'image/png')
No import of image_picker, dart:io, or any platform-specific package in the abstract class file
A Riverpod Provider<ReceiptImagePickerIntegration> is defined in the same file with a default implementation that throws UnimplementedError to force explicit platform registration
ReceiptImageResult implements equality and toString for testability
Public API surface documented with dartdoc comments explaining null return semantics

Technical Requirements

frameworks
Flutter
Riverpod
dart
performance requirements
Interface definition must impose no runtime overhead — pure abstract contract
security requirements
Interface contract must specify that returned bytes must not contain EXIF metadata — stripping responsibility delegated to implementation or compressor
MIME type must be validated against an allowlist (image/jpeg, image/png, image/heic) in ReceiptImageResult constructor

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Keep this file to under 60 lines — it is a pure interface definition. Use @immutable on ReceiptImageResult and provide a const constructor where possible (though Uint8List is not const, so use final fields). Define the Riverpod provider as a Provider using a family or standard provider — do not use a global variable. Consider using freezed for ReceiptImageResult if the project already uses it, otherwise a hand-written value object is sufficient given the simplicity.

The UnimplementedError default forces each platform entry point to register the correct implementation, preventing silent fallback to a no-op.

Testing Requirements

Unit tests using flutter_test. Verify: (1) ReceiptImageResult can be constructed with valid bytes and MIME type, (2) ReceiptImageResult constructor throws AssertionError or ArgumentError for empty bytes, (3) ReceiptImageResult constructor throws for unsupported MIME type, (4) equality operator returns true for two ReceiptImageResult instances with identical bytes and MIME type, (5) a mock implementation of ReceiptImagePickerIntegration can be created inline (verifying the abstract contract is implementable). No platform channel calls in these tests.

Component
Receipt Image Picker Integration
infrastructure low
Epic Risks (3)
high impact medium prob security

Supabase Storage RLS policies using org/user/claim path scoping may not enforce correctly if claim ownership is not present in the JWT or if path segments are constructed differently at upload vs. read time, leading to data leakage or access denial for legitimate users.

Mitigation & Contingency

Mitigation: Define and test RLS policies in isolation before wiring to app code. Write integration tests that assert cross-org and cross-user access is denied. Use service-role key only in edge functions, never in client code.

Contingency: If client-side RLS proves insufficient, route all storage reads through a Supabase Edge Function that validates ownership before generating signed URLs, adding a controlled server-side enforcement layer.

high impact medium prob technical

Aggressive image compression may reduce receipt legibility below the threshold required for financial auditing, causing claim rejections or compliance failures despite technically successful uploads.

Mitigation & Contingency

Mitigation: Define minimum legibility requirements with HLF finance team before implementation. Set compression targets conservatively (e.g., max 1MB, min 80% JPEG quality) and validate with sample receipt images. Provide compression statistics in verbose/debug mode.

Contingency: If post-compression quality is disputed by auditors, increase the quality floor at the cost of larger file sizes, and add a manual override allowing users to skip compression for PDFs and high-quality scans.

medium impact medium prob dependency

The Flutter image_picker package behaves differently on iOS 17+ (PHPicker) vs older Android (Intent-based), particularly for file types, permission flows, and PDF selection, which may cause platform-specific failures not caught in development.

Mitigation & Contingency

Mitigation: Test image picker integration on physical devices for both platforms early in the sprint. Pin the image_picker package version and review changelogs before updates. Write widget tests using mock file results for each platform branch.

Contingency: If PHPicker or Android Intent differences cause blocking issues, implement separate platform-specific picker delegates behind the unified interface, allowing platform-specific fixes without breaking the shared API.