high priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

ReceiptStoragePathBuilder is a pure Dart class with no Flutter or Supabase dependencies
buildPath(orgId, userId, claimId, filename) returns a deterministic string in the format '{orgId}/{userId}/{claimId}/{filename}'
parsePath(path) correctly extracts orgId, userId, claimId, and filename from a valid path string
parsePath returns null or throws a typed exception for malformed paths (missing segments, empty strings)
All path segments are validated as non-empty, non-null UUIDs where applicable
ReceiptStorageRepository exclusively uses ReceiptStoragePathBuilder for all path construction — no inline string interpolation remains
Unit tests cover happy path, empty segment, null segment, and malformed path cases
No static state or singleton pattern used — class is instantiable and injectable via Riverpod

Technical Requirements

frameworks
Flutter
Riverpod
dart
data models
assignment
performance requirements
Path construction must be synchronous and complete in <1ms
No memory allocation beyond the returned string
security requirements
Path segments must not allow directory traversal characters (../, ./)
UUIDs must conform to RFC 4122 format before inclusion in path
No PII included in path beyond opaque UUIDs

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement as a simple final class in lib/data/receipt/receipt_storage_path_builder.dart. Use a private const separator '/' and validate each UUID segment with a lightweight regex or the uuid package. The parse method should return a typed record or value object (ReceiptStoragePath) rather than a raw Map to keep callers type-safe. Register as a Riverpod Provider so ReceiptStorageRepository can depend on it via ref.watch.

Avoid making it a singleton to preserve testability. The key risk is developers bypassing the builder — enforce this with a linter rule or a code comment in ReceiptStorageRepository explaining the constraint.

Testing Requirements

Unit tests using flutter_test. Cover: (1) valid path construction with well-formed UUIDs, (2) round-trip parsePath(buildPath(...)) equality, (3) parsePath on malformed inputs (too few segments, empty segment, null), (4) directory traversal injection attempt rejected, (5) ReceiptStorageRepository unit test asserting it delegates path construction to ReceiptStoragePathBuilder rather than inline strings. Target 100% branch coverage on the builder class.

Component
Receipt Storage Repository
data medium
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.