high priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

`TerminologyCacheBackend` abstract class (or interface) defines `Future<void> write(String orgId, Map<String, String> labels)`, `Future<Map<String, String>?> read(String orgId)`, and `Future<void> clear(String orgId)`
`SharedPreferencesBackend` implements `TerminologyCacheBackend` — encapsulates the existing SharedPreferences logic from task-002
`HiveBackend` implements `TerminologyCacheBackend` — uses a Hive box named `'terminology_cache'` with orgId as key
`TerminologyCacheAdapter` accepts a `TerminologyCacheBackend` via constructor injection and delegates all calls to it
HiveBackend stores `Map<String, String>` directly using Hive's native map support — no manual JSON serialization required
HiveBackend handles box-not-open state: opens the box lazily on first access if not already open
Both backends pass the same unit test suite (parameterized or duplicated per backend)
Corrupt/missing Hive data handled gracefully: `read` returns null without throwing
Hive box is opened with a type adapter for `Map<String, String>` if required, or uses dynamic typing with explicit cast
Class remains at `lib/core/labels/terminology_cache_adapter.dart`; interface at `lib/core/labels/terminology_cache_backend.dart`

Technical Requirements

frameworks
Flutter
Dart
hive package
hive_flutter package
shared_preferences package
data models
LabelMap (Map<String, String>)
performance requirements
Hive read must be faster than SharedPreferences read for maps with 100+ entries on low-end Android
Box open operation must be idempotent — no double-open errors
Both backends must be non-blocking (async)
security requirements
Hive box does not use encryption (label strings are not sensitive)
Box name must be a compile-time constant to avoid typo-based data loss

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

The strategy pattern here is constructor injection — `TerminologyCacheAdapter(TerminologyCacheBackend backend)`. A factory constructor or static factories can provide `TerminologyCacheAdapter.withSharedPreferences()` and `TerminologyCacheAdapter.withHive()` as convenience constructors. Hive stores `Map` by default when using `dynamic` typed boxes — cast carefully: `(box.get(orgId) as Map?)?.cast()`. Avoid Hive type adapters for this use case since the map structure is simple and type adapters add code generation overhead.

The decision of which backend to use at runtime should be made at the DI layer (Riverpod provider), not inside the adapter itself. Document in a comment that Hive is recommended when the label map exceeds ~50 entries per organization, as this is the threshold where Hive's binary format outperforms SharedPreferences JSON.

Testing Requirements

Write tests for both backends separately. Use `MockSharedPreferences` for SharedPreferencesBackend. For HiveBackend, use `hive_test` package which provides an in-memory Hive implementation — call `Hive.init(Directory.systemTemp.path)` in setUp and `Hive.deleteBoxFromDisk('terminology_cache')` in tearDown. Test cases (run for both backends): (1) write then read returns same map, (2) read on empty returns null, (3) multi-org isolation, (4) clear removes only target.

Additionally test HiveBackend-specific: (5) lazy box open succeeds on first read without pre-initialization, (6) concurrent writes to the same orgId do not corrupt data.

Component
Terminology Local Cache Adapter
data low
Epic Risks (2)
high impact medium prob integration

The labels JSONB column in organization_configs may lack a consistent schema across organizations, causing deserialization failures or silent missing keys when a new organization is onboarded with a differently structured map.

Mitigation & Contingency

Mitigation: Define and enforce a canonical JSONB schema via a Supabase check constraint and a migration script. Validate the schema in TerminologyRepository at parse time and emit structured errors for any key-type mismatches.

Contingency: If schema drift is discovered post-deployment, LabelKeyResolver's fallback logic ensures the app continues to function with English defaults while a data migration is prepared to normalize the offending organization's config.

medium impact low prob technical

Device local storage corruption or platform-specific SharedPreferences serialization bugs could render a cached terminology map unreadable, causing the app to fall back to English defaults unexpectedly for an organization with custom terminology.

Mitigation & Contingency

Mitigation: Wrap all cache reads in try/catch, validate the deserialized map against a minimum-required-keys check, and evict corrupted entries automatically before re-fetching from Supabase.

Contingency: Surface a non-blocking in-app warning to the coordinator that terminology may be in default English until the next sync completes; trigger an immediate background sync.