high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

TerminologySyncService is declared as an abstract class (or interface via abstract interface class) in its own file under the terminology service layer
The interface exposes: Future<void> start(), Future<void> stop(), and Stream<SyncEvent> get events
TerminologySyncConfig is an immutable value object (const constructor, all fields final) covering: Duration syncInterval, Duration initialRetryDelay, double retryMultiplier, int maxRetries, Duration maxRetryDelay
TerminologySyncConfig provides a sensible const default factory: syncInterval = 30 min, initialRetryDelay = 2 s, retryMultiplier = 2.0, maxRetries = 4, maxRetryDelay = 60 s
SyncTrigger is a Dart enum with values: appForeground, connectivityRestored, manual
SyncDecision is a sealed class with variants: upToDate() and refreshRequired(DateTime serverUpdatedAt)
SyncEvent is a sealed class exposing sync lifecycle events: syncStarted(SyncTrigger), syncCompleted(SyncDecision), syncFailed(Object error, int attempt)
All new types are exported from a single barrel file for the terminology service layer
No implementation code lives in this task — pure interface, config, and enum/sealed-class definitions only
dart analyze reports zero issues on all new files

Technical Requirements

frameworks
Dart (sealed classes, enums, const constructors)
data models
TerminologySyncConfig
SyncTrigger
SyncDecision
SyncEvent
performance requirements
All config and enum types must be const-constructible to allow compile-time defaults
security requirements
Config values must be validated in the constructor: syncInterval >= 5 min, maxRetries in 1..10, retryMultiplier >= 1.0 — throw ArgumentError on violation

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place all types in lib/src/terminology/sync/ (or equivalent layer path). Use Dart 3 sealed classes for SyncDecision and SyncEvent so exhaustive pattern matching is enforced at call sites. TerminologySyncConfig should use copyWith for easy test overrides — either hand-write or use a code generator if the project already has one. Keep the interface minimal: start/stop/events.

Avoid over-engineering with additional methods at this stage; the concrete implementation task will determine what private helpers are needed. Ensure the barrel file re-exports all public types so consumers import only one file.

Testing Requirements

Minimal unit tests: verify TerminologySyncConfig default factory values match specification; verify ArgumentError is thrown for invalid config values (syncInterval < 5 min, maxRetries = 0, retryMultiplier = 0.5). No behavior tests needed for pure interface/config definitions. All tests in flutter_test.

Component
Terminology Sync Service
service medium
Epic Risks (3)
high impact medium prob technical

When a user switches organization context (e.g., a coordinator with multi-org access), a race condition between the outgoing organization's map disposal and the incoming organization's fetch could briefly expose the wrong organization's terminology to the widget tree.

Mitigation & Contingency

Mitigation: Implement an explicit loading state in OrganizationLabelsNotifier that widgets check before rendering any resolved labels. The provider graph should cancel the previous organization's fetch via Riverpod's ref.onDispose before initiating the next.

Contingency: If the race manifests in production, fall back to English defaults during the transition window and emit a Sentry error event for investigation; the UX impact is a brief English flash rather than wrong-org terminology.

high impact low prob security

Supabase Row Level Security policies on organization_configs may inadvertently restrict the authenticated user from reading their own organization's labels JSONB column, causing silent empty maps that appear as English fallbacks.

Mitigation & Contingency

Mitigation: Write and test explicit RLS policies that grant SELECT on the labels column to any authenticated user whose organization_id matches. Add an integration test that verifies label fetch succeeds for each role (peer mentor, coordinator, admin).

Contingency: If RLS blocks are discovered in production, temporarily escalate label fetch to a service-role edge function while the RLS policy is corrected, ensuring no labels are exposed cross-organization.

medium impact medium prob scope

A peer mentor who installs the app for the first time with no internet connection will have no cached terminology map and will see only English defaults, which may be confusing for organizations like NHF that use Norwegian-specific role names exclusively.

Mitigation & Contingency

Mitigation: Bundle a default fallback terminology map for each known organization as a compile-time asset (Dart asset file) so that even fresh installs without connectivity render correct organizational terminology immediately.

Contingency: If bundled assets are out of date, display a one-time informational banner noting that terminology will update on next connectivity restore, with no functional blocking of the app.