critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

`LabelKeyResolver` is a class with only static methods — no instance state, no constructor
`resolve(String key, Map<String, String> labels, {Map<String, String>? params})` returns a non-null String in all cases
Fallback chain: (1) `labels[key]` if present and non-empty → (2) default English string from a built-in defaults map → (3) key formatted as human-readable string (e.g., `'activity.type_label'` → `'Activity Type Label'`)
Default English labels map is a private static const Map<String, String> inside LabelKeyResolver populated for all keys defined in LabelKeyRegistry
Parameterized substitution: if the resolved label contains `{{paramName}}`, each occurrence is replaced with `params['paramName']` — missing params leave the placeholder as-is (do not throw)
Empty string in `labels[key]` is treated as missing — falls through to next fallback
All methods are pure: same inputs always produce same outputs, no I/O, no side effects
Unit tests achieve 100% branch coverage on the fallback chain
File located at `lib/core/labels/label_key_resolver.dart`
Works correctly with all keys defined in LabelKeyRegistry for all four organizations

Technical Requirements

frameworks
Flutter
Dart
data models
LabelKeyRegistry (compile-time key source)
organization_configs labels map (runtime input)
performance requirements
resolve() must be O(1) for the happy path (org-specific label found)
No regex compilation at call time — compile regex patterns as static const if needed
Parameterized substitution must handle 0–10 parameters without performance degradation
security requirements
Parameterized substitution must escape HTML entities in param values when resolving labels for web-rendered content — provide a `resolveHtml()` variant or a `escapeHtml` flag
Do not evaluate params as code — pure string replacement only

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

The default English labels map should be populated by copying the constant values from LabelKeyRegistry and providing human-readable English defaults. Do not use LabelKeyRegistry constants as the default value — the constant is a key, not a label. Maintain a separate `_defaultLabels` map. For the formatted-key fallback, implement a simple `_formatKey(String key)` helper that splits on `.` and `_`, capitalizes each word, and joins with spaces — this ensures the UI is always readable even during development before all labels are configured.

For parameterized substitution, use `String.replaceAllMapped(RegExp(r'\{\{(\w+)\}\}'), (m) => params?[m.group(1)] ?? m.group(0)!)` — compile the RegExp as a static field to avoid re-compilation. Keep this class free of any Flutter imports — it is pure Dart and must be testable without a Flutter environment.

Testing Requirements

Unit tests in `label_key_resolver_test.dart` using `flutter_test`. Group by behavior: (1) fallback chain — test each of the 3 fallback levels independently, (2) parameterized substitution — test single param, multiple params, missing param leaves placeholder, null params map treated as empty, (3) edge cases — empty string in labels map falls through, null-safe behavior with empty maps, key not in LabelKeyRegistry returns formatted fallback. Use `const` test data to keep tests fast and deterministic. Assert exact output strings, not just non-null.

Target: all 3 branches of the fallback chain explicitly tested.

Component
Label Key Resolver Service
service 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.