critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

loadOrganizations() returns a non-null List<Organization> for an authenticated user with at least one membership
Result list is capped at 5 organizations for NHF multi-chapter members
An empty list is returned (not an error) when the authenticated user has no organization memberships
A typed domain exception (e.g., OrgLoadException) is thrown on network failure, not a raw SupabaseException
Each returned Organization model contains at minimum: id, name, logoUrl, and partnerType (nhf | blindeforbundet | hlf | barnekreftforeningen)
Method is invoked only while a valid Supabase auth session is active; calling it unauthenticated throws AuthRequiredException
Result is not cached by default — each call queries Supabase to reflect membership changes
Unit tests pass with both a mock returning 0, 1, 3, and 5 organizations
Unit tests pass with a mock simulating a network timeout

Technical Requirements

frameworks
Flutter
Riverpod
Supabase Dart SDK
apis
Supabase PostgREST REST API — user_organizations or memberships join table
Supabase Auth — current user UID for row-level security filter
data models
Organization
UserOrganizationMembership
performance requirements
Query must complete within 2 seconds on a 4G connection
Query must use a selective column projection (no SELECT *) to minimize payload
security requirements
Row-level security (RLS) on the Supabase table must restrict results to the authenticated user's memberships
Service must not expose raw Supabase error messages to callers — wrap in domain exceptions
No organization data should be logged at INFO level or above

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Inject OrgRepository (not the raw Supabase client) into OrgSelectionService via constructor — this keeps the service testable. The repository should call `.from('user_organizations').select('organization_id, organizations(id, name, logo_url, partner_type)').eq('user_id', userId).limit(5)`. Map the nested Supabase response to typed Organization models in the repository layer, not in the service. The service only calls the repository and handles domain-level error translation.

Use a sealed Result pattern or simple try/catch that rethrows domain exceptions — avoid returning nullable or dynamic types.

Testing Requirements

Write unit tests using flutter_test and mockito (or mocktail). Mock the OrgRepository dependency. Test cases: (1) successful fetch returning 1–5 orgs, (2) empty membership returns empty list, (3) Supabase network error is caught and re-thrown as OrgLoadException, (4) unauthenticated call throws AuthRequiredException. Aim for 100% branch coverage on loadOrganizations.

No integration tests required at this stage.

Component
Organization Selection Service
service low
Epic Risks (2)
high impact medium prob technical

The OrgLabelsProvider must be fully initialized before any UI widget renders organization-specific text. If the service triggers label initialization asynchronously and the UI builds before the labels stream emits, widgets will briefly display raw label keys instead of human-readable text, which constitutes a WCAG 2.2 AA failure for screen readers that announce whatever text is present at render time.

Mitigation & Contingency

Mitigation: Design the OrgSelectionService.select() method to await OrgLabelsProvider initialization before completing its Future — callers receive a resolved future only after labels are ready. Use an AsyncValue loading state to gate UI rendering via Riverpod's when() pattern.

Contingency: If awaiting initialization causes perceivable UI lag, implement an optimistic render with a skeleton loading state that is announced by the live-region-announcer as 'Loading organization content' so screen readers do not announce raw keys.

medium impact medium prob integration

If the label definitions loaded from the backend for each organization do not match the label key registry used in the UI, widgets will fall back to raw keys silently. This is particularly harmful for Blindeforbundet users relying on VoiceOver, where a raw key announced by a screen reader is incomprehensible.

Mitigation & Contingency

Mitigation: Define a compile-time label key registry (enum or const strings) and assert at OrgLabelsProvider initialization that all required keys are present in the loaded map. Log a structured warning for any missing key and substitute a human-readable fallback string rather than the raw key.

Contingency: If a label schema mismatch reaches production, the OrgLabelsProvider fallback mechanism ensures users see a reasonable English default rather than a raw key. A backend label patch can be deployed without an app release.