critical priority low complexity database pending backend specialist Tier 3

Acceptance Criteria

A concrete class LocalOrgPersistenceRepository extends OrgPersistenceRepository and is placed under lib/infrastructure/repositories/
Constructor accepts a LocalStorageAdapter via dependency injection (no direct instantiation of storage inside the class)
getSelectedOrganizationId() calls LocalStorageAdapter.getString() using the namespaced key constant from task-001 and returns null if the key is absent
saveSelectedOrganizationId(String orgId) calls LocalStorageAdapter.setString() with the same namespaced key and the provided orgId
No raw SharedPreferences or Hive API calls appear directly — all storage goes through LocalStorageAdapter
The class does not define its own key string — it references the constant from the infrastructure constants file (task-001)
Dart analyzer reports zero errors and zero warnings
The class is exported from the infrastructure barrel file

Technical Requirements

frameworks
Flutter
Dart
Riverpod
data models
Organization
performance requirements
Storage read/write must complete in under 50ms on target devices
No blocking synchronous calls — all methods are async/await
security requirements
Store only the organization ID string, never sensitive org metadata
Use the namespaced key (e.g., 'eircodex.selected_org_id') to avoid key collisions with other app data

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

File location: lib/infrastructure/repositories/local_org_persistence_repository.dart. Follow constructor injection pattern: final LocalStorageAdapter _adapter; const LocalOrgPersistenceRepository(this._adapter);. Use the key constant as LocalStorageKeys.selectedOrgId (or whatever constant name task-001 defines) — import only from the infrastructure constants file. Keep both methods to 2-3 lines each; any added complexity is a sign the responsibility boundary is wrong.

This class should be a thin delegation layer — if business logic appears here, it belongs in a use case instead.

Testing Requirements

Unit tests are covered in task-008. For this task, verify manually or via a simple integration smoke test that saving and reading an org ID round-trips correctly through the real LocalStorageAdapter on device/emulator. Ensure the class can be instantiated with a mock adapter without any errors.

Epic Risks (2)
medium impact medium prob technical

SharedPreferences behaves differently on iOS (NSUserDefaults) vs Android (SharedPreferences) for edge cases such as first-launch cold reads, storage quota exceeded, or process kill mid-write. If the adapter does not account for these differences, the persistence layer can silently return null on one platform while returning a stale value on the other, causing incorrect routing decisions downstream.

Mitigation & Contingency

Mitigation: Write platform-specific integration tests using flutter_test device runners for both iOS and Android. Document known platform delta in the adapter's inline comments and encode defensive fallback for null returns at the repository boundary.

Contingency: If platform delta causes persistent issues, replace SharedPreferences with flutter_secure_storage for this key — the LocalStorageAdapter abstraction makes this a single-file swap with no impact on the repository or service layer.

high impact low prob dependency

The shared_preferences Flutter plugin may have a version conflict with other plugins already in the project pubspec.yaml. A conflict discovered late in the epic blocks all downstream epics.

Mitigation & Contingency

Mitigation: Resolve and pin the shared_preferences version in pubspec.yaml as the very first task of this epic before writing any implementation code. Run flutter pub get and resolve any version conflicts immediately.

Contingency: If version pinning is impossible due to transitive conflicts, implement LocalStorageAdapter using path_provider + dart:io for JSON file storage as an alternative — the interface contract remains unchanged.