critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

OrgRateConfig is an immutable Dart class (use `final` fields) with at minimum: `perKmRate` (double), `autoApprovalThresholdKm` (double), and `orgId` (String) fields
OrgRateConfig implements `==` and `hashCode` based on all fields, enabling value equality checks in tests and BLoC state comparisons
OrgRateConfig has a `copyWith()` method for producing modified copies without mutation
The abstract repository interface `OrgRateConfigRepository` defines a method `Stream<OrgRateConfigResult> watchConfig({required String orgId})` returning a reactive stream
A sealed class (or equivalent sum type) `OrgRateConfigResult` is defined with at least two variants: `OrgRateConfigLoaded(OrgRateConfig config)` and `OrgRateConfigError(Object error, StackTrace? stackTrace)`
Dart typedef `OrgRateConfigStream = Stream<OrgRateConfigResult>` is exported from the domain layer
All types are placed under `lib/domain/mileage/` (or equivalent domain layer path) — not in data/ or presentation/
The file compiles without errors: `dart analyze lib/domain/mileage/`
No Supabase or Flutter-specific imports appear in the domain model or interface files — the domain layer must remain infrastructure-agnostic

Technical Requirements

frameworks
Dart
data models
OrgRateConfig
OrgRateConfigResult (sealed)
performance requirements
Domain model instantiation must be allocation-only (no I/O or async) — sub-millisecond
security requirements
OrgRateConfig must not expose mutable setters — all fields are final to prevent accidental mutation of shared config state

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use Dart's `sealed class` (available since Dart 3.0) for OrgRateConfigResult to get exhaustive pattern matching: `sealed class OrgRateConfigResult {}` with `final class OrgRateConfigLoaded extends OrgRateConfigResult { final OrgRateConfig config; ... }` and `final class OrgRateConfigError extends OrgRateConfigResult { final Object error; ... }`. This enables callers to use `switch (result) { case OrgRateConfigLoaded(:final config) => ..., case OrgRateConfigError(:final error) => ...

}` with compile-time exhaustiveness. For OrgRateConfig itself, consider using `package:freezed` if the project already uses it for other models — this auto-generates copyWith, ==, and hashCode. If not using freezed, implement them manually following the standard pattern. Keep this file strictly in the domain layer with no external package dependencies beyond Dart core.

Testing Requirements

Write pure unit tests for OrgRateConfig value equality: (1) two instances with same field values are equal via ==, (2) copyWith() returns a new instance with the changed field and all other fields preserved, (3) hashCode is consistent across two equal instances. No async or I/O required. Tests should live in test/domain/mileage/org_rate_config_test.dart. Also verify that a mock implementation of OrgRateConfigRepository compiles against the interface, confirming the interface contract is correct.

Epic Risks (2)
medium impact medium prob integration

If OrgRateConfigRepository caches the per-km rate aggressively and an admin updates the rate mid-session, ongoing form interactions will show the old rate until the Stream emits. This could result in the UI showing a rate that differs from what is stored when the claim is submitted, causing confusion or disputes.

Mitigation & Contingency

Mitigation: Subscribe to a Supabase Realtime channel for the org_configuration table so config changes propagate within seconds. Document the eventual-consistency window in code comments.

Contingency: If Realtime subscription proves unreliable in test, add a polling fallback with a configurable interval (default 5 minutes) and display a 'rate updated' toast when the stream emits a changed value.

medium impact medium prob scope

The correction window within which a claim can be deleted or voided is not explicitly specified in the feature documentation. Implementing the wrong window (e.g. 24 hours vs 7 days) could lock users out of corrections or allow inappropriate post-approval modifications.

Mitigation & Contingency

Mitigation: Raise the correction window definition as a blocking question to the HLF product owner before implementing the delete/void path in MileageClaimRepository. Implement the window duration as an org-level configuration value rather than a hardcoded constant.

Contingency: If the question cannot be resolved before implementation, default to 24 hours as the most conservative option and flag the value for review in the first user-acceptance test.