high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

OrgCompressionConfig is an immutable data class with fields: maxDimensionPx (int, default 800), jpegQuality (int 1–100, default 75), maxFileSizeBytes (int, default 512000 = 500KB)
ReceiptImageCompressor.compress() accepts an optional OrgCompressionConfig and uses it instead of hardcoded defaults when provided
When the compressed output exceeds maxFileSizeBytes, the compressor iteratively reduces jpegQuality by 10 points until the size constraint is met or quality reaches a minimum of 30
A Riverpod Provider<OrgCompressionConfig> is defined and can be overridden per org in ProviderScope
The default config Provider reads the org config from the org settings repository (injected) and falls back to const OrgCompressionConfig() if unavailable
Passing jpegQuality outside the range 1–100 throws an ArgumentError with a descriptive message
Passing maxDimensionPx less than 100 throws an ArgumentError (prevents unusable thumbnails)
All existing task-014 acceptance criteria remain satisfied after this extension

Technical Requirements

frameworks
Flutter
Riverpod
data models
OrgCompressionConfig
CompressionConfig
CompressionResult
performance requirements
The iterative quality reduction loop must not exceed 5 iterations to bound worst-case compression time
Each iteration reuses the already-resized pixel buffer — do not re-decode the original image
security requirements
Config values from remote org settings must be clamped to valid ranges before use — never trust raw remote values

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Define OrgCompressionConfig as a const-constructible immutable class using the @immutable annotation. Use copyWith() for override scenarios. The iterative size reduction loop should be clearly commented with the termination condition. Clamp all incoming config values at the point of use: jpegQuality.clamp(1, 100), maxDimensionPx.clamp(100, 4096).

Expose the config provider as a family if multiple orgs can be active simultaneously: compressionConfigProvider.family(orgId). Register the default config in the app's root ProviderScope. File: lib/features/receipts/domain/org_compression_config.dart.

Testing Requirements

Extend task-016 tests to cover: custom maxDimensionPx (e.g., 400px) produces output within that dimension; jpegQuality override produces measurably different file sizes; maxFileSizeBytes constraint triggers iterative quality reduction and final output is within the limit; invalid config values (quality=0, dimension=50) throw ArgumentError. Test the Riverpod provider override mechanism with ProviderContainer.

Component
Receipt Image Compressor
service 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.