high priority low complexity infrastructure pending backend specialist Tier 2

Acceptance Criteria

`LabelKeyResolver.validateKey(String key)` returns `true` if the key exists in the registered key set, `false` otherwise
In debug mode (`kDebugMode == true`), calling `resolve()` with an unregistered key throws an `AssertionError` with a message identifying the invalid key
In release mode, calling `resolve()` with an unregistered key logs a warning via Flutter's `debugPrint` (or a logger) and proceeds with the formatted-key fallback — no exception thrown
The valid key set `_validKeys` is a `static const Set<String>` (or `static final`) populated from all constants in LabelKeyRegistry — no runtime reflection
If using code generation, a `build_runner` task generates `label_key_registry.g.dart` containing the `_validKeys` set
If using a static initializer, a manually maintained `allKeys` list in a test helper must match `_validKeys` — a CI test asserts they are equal
`validateKey` is a pure function — no side effects, same result for same input
Unit tests verify: registered key returns true, unregistered key returns false, debug-mode assertion fires for unregistered key, release-mode logs warning without throwing
The assert does not fire in widget tests unless `kDebugMode` is explicitly set — test infrastructure handles this correctly

Technical Requirements

frameworks
Flutter
Dart
build_runner (optional, for code generation)
data models
LabelKeyRegistry (all static const String values)
performance requirements
`Set.contains()` lookup is O(1) — validation adds negligible overhead to resolve()
Key set must be initialized once at class load time, not per call
security requirements
Debug assertions must not expose internal key registry structure in production logs
Warning log in release mode must not include user data — only the invalid key string

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Since Dart/Flutter does not support runtime reflection in release mode, the valid key set must be statically defined. Two approaches: (1) Manual static initializer — add a `static final Set _validKeys = { LabelKeyRegistry.Activity.typeLabel, LabelKeyRegistry.Activity.durationLabel, ... }` block in `LabelKeyResolver`. This is simple but must be kept in sync with LabelKeyRegistry manually.

(2) Code generation — write a `build_runner` builder that reads `label_key_registry.dart` and generates a `_validKeys` set. Approach (1) is recommended for the current scale (low complexity, few keys); approach (2) is better if the registry grows beyond ~100 keys. Integrate `validateKey` into the existing `resolve()` method as the first step — call `assert(validateKey(key), 'Unregistered label key: $key')` at the top of `resolve()`. The `assert` statement is a no-op in release mode, so add an explicit `if (kDebugMode)` block for the warning log to ensure visibility during development without impacting production.

Testing Requirements

Unit tests in `label_key_resolver_test.dart` (extend existing file). Test group 'validateKey': (1) known key returns true, (2) typo key returns false, (3) empty string returns false. Test group 'debug mode assertion': use `expect(() => LabelKeyResolver.resolve('invalid.key', {}), throwsAssertionError)` — this works because `flutter_test` runs in debug mode by default. Test group 'release mode behavior': mock `kDebugMode` to false using a test-injectable flag or test the `validateKey` method directly without calling `resolve` in release mode.

Alternatively, extract the warn-and-fallback logic into a separate method and test it directly. Ensure the valid key set test (`_validKeys` matches `LabelKeyRegistry` constants) runs in CI.

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.