high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

Every constant in LabelKeyRegistry matches the regex `^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$` (dot-notation, lowercase, no trailing dot)
No two constants in LabelKeyRegistry share the same string value
LabelKeyResolver.resolve(key, orgLabels: {key: 'OrgValue'}) returns 'OrgValue'
LabelKeyResolver.resolve(key, orgLabels: {}, defaultLabels: {key: 'Default'}) returns 'Default'
LabelKeyResolver.resolve(key, orgLabels: {}, defaultLabels: {}) returns a non-empty formatted string derived from the key (e.g. 'activity.title' → 'Activity Title')
resolve(key, orgLabels: {'x': 'Hello {{name}}, you have {{count}} items'}, params: {'name': 'Ada', 'count': '3'}) returns 'Hello Ada, you have 3 items'
All `{{param}}` placeholders in the resolved string are replaced — none remain in the output when matching params are provided
Unmatched `{{param}}` placeholders (no corresponding key in params map) are left as-is or replaced with empty string — behavior is documented and tested explicitly
In debug mode (kDebugMode), validateKey with an unknown key throws an AssertionError
In release mode (simulated by calling the release-mode code path directly), validateKey with an unknown key returns a formatted fallback string without throwing
All tests are grouped by class (LabelKeyRegistry, LabelKeyResolver) using flutter_test group()
Test file passes `flutter test --coverage` with 100% line coverage on the two tested classes

Technical Requirements

frameworks
Flutter
flutter_test
data models
LabelKeyRegistry (const string constants)
LabelKeyResolver (resolve, validateKey methods)
Map<String, String> (orgLabels, defaultLabels, params)
performance requirements
All unit tests must complete in under 2 seconds total — no async operations required
Regex validation of all registry keys must complete synchronously
security requirements
Tests must not contain real org-specific label data — use generic keys like 'activity.title', 'role.coordinator'
No network calls or file I/O in any test

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The most robust way to test all LabelKeyRegistry constants is to use a static list or map of all defined keys (e.g. LabelKeyRegistry.allKeys getter returning a List) and iterate over it with a regex assertion. If reflection is not available in Dart (it is not in Flutter production), the team must expose an `allKeys` static getter on LabelKeyRegistry. For the debug/release mode branching in validateKey, avoid relying on `kDebugMode` directly in tests — instead inject a `bool isDebug` flag or extract a `_validateKeyImpl(key, isDebug)` private function and test both branches.

For parameterized substitution tests, ensure the test covers: single param, multiple params, same param used twice in the string, and no params provided when string has placeholders.

Testing Requirements

Use flutter_test exclusively. Organize tests into two top-level groups: 'LabelKeyRegistry' and 'LabelKeyResolver'. For LabelKeyRegistry, use reflection or a manual list to iterate all constants and assert format. For LabelKeyResolver, use table-driven tests (parameterized with a list of input/output pairs) for the resolution and substitution scenarios.

For debug vs release mode testing: extract the validateKey logic into a pure function that accepts a `bool isDebug` parameter to avoid needing actual build-mode switching. Minimum coverage: 100% lines and branches for both classes. Run with `flutter test test/label_key_resolver_test.dart --coverage`.

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.