critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

OrgHierarchyAccessResolver class is implemented with a single public method: Future<List<ExportScope>> resolvePermittedScopes(String userId)
Method queries the authenticated user's role (admin, coordinator, peer_mentor) and org-unit membership from Supabase
Scope eligibility rules are applied: national admins → [national, region, localChapter]; regional coordinators → [region, localChapter]; local coordinators → [localChapter]; peer_mentor → throws UnauthorisedExportScopeException
UnauthorisedExportScopeException is a typed exception class with a message field — it must NOT be a generic Exception
Returned list is ordered from broadest to narrowest scope: national first, local_chapter last
Method is side-effect free (read-only Supabase queries) — it must not modify any database state
Method handles Supabase network errors by wrapping them in a typed ExportAccessResolutionException with the original error as cause
Unit tests cover: national admin gets all three scopes, regional coordinator gets two scopes, local coordinator gets one scope, peer mentor throws exception, Supabase error is wrapped correctly
Integration test verifies the method against the Supabase test database with a seeded user for each role type

Technical Requirements

frameworks
Supabase Flutter client SDK
Dart async/await
apis
Supabase REST API — user_roles table or RPC function for role lookup
Supabase REST API — org_unit_memberships table for org-unit lookup
data models
UserRole
OrgUnitMembership
ExportScope
UnauthorisedExportScopeException
ExportAccessResolutionException
performance requirements
resolvePermittedScopes must complete in under 500ms for a single user lookup
Must use a single combined Supabase query or RPC where possible — avoid N+1 query patterns
security requirements
Must validate that the userId parameter matches the currently authenticated Supabase user — reject mismatched IDs to prevent privilege escalation
Supabase Row Level Security (RLS) must be the final enforcement layer — this resolver is a UX optimisation, not the sole security gate
Never cache resolved scopes in memory across sessions — re-resolve on each export flow initiation

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Check the existing role-based access control patterns in the codebase (auth and RBAC may already have utility methods for role lookup — reuse these rather than writing new Supabase queries). If the project already has a UserRole enum, use it directly rather than duplicating values. Consider implementing resolvePermittedScopes via a Supabase RPC (stored function) to keep the scope-eligibility rules server-side and auditable — this is preferable if the Supabase project already uses RPCs for other access checks. The exception hierarchy should extend a base AppException class if one exists in the project.

Place the resolver in lib/features/bufdir_export/services/org_hierarchy_access_resolver.dart.

Testing Requirements

Write unit tests using flutter_test with Mockito or mocktail to mock the Supabase client. Test all role → scope mappings as separate test cases. Test the UnauthorisedExportScopeException is thrown (not just a generic exception) for peer_mentor. Test that Supabase errors are wrapped in ExportAccessResolutionException.

Write one integration test using the Supabase test database that seeds a user for each role and calls the real implementation — verify the returned scope list matches expectations. Aim for 100% branch coverage on the role-to-scope mapping logic.

Component
Export Scope Selector
ui medium
Epic Risks (2)
high impact medium prob security

The scope selector must accurately reflect each coordinator's access rights within the org hierarchy. If a coordinator can select a scope broader than their authorised access, the edge function's RLS enforcement must catch the attempt — but a permissive RLS policy or a bug in the scope resolver could allow unauthorised data to be exported.

Mitigation & Contingency

Mitigation: Implement permission enforcement at two independent layers: (1) the scope selector only renders options permitted by the user's role record, and (2) the edge function re-validates the requested scope against the user's JWT claims before executing any queries. Write integration tests that attempt to invoke the edge function with a scope beyond the user's permissions and assert rejection.

Contingency: If a permission bypass is discovered post-launch, immediately disable the export feature via the org-level feature flag while the fix is deployed. Review all audit records for exports that may have included out-of-scope data and notify affected organisations.

medium impact medium prob technical

The export workflow has 7+ discrete states (idle, scope selected, period selected, preview loading, preview ready, confirming, exporting, complete, failed) and several conditional transitions. An incomplete BLoC state machine could allow duplicate submissions, stale preview data to be confirmed, or error states to be unrecoverable without a restart.

Mitigation & Contingency

Mitigation: Model the state machine explicitly as a sealed class hierarchy before coding. Review the state diagram against all user story acceptance criteria. Write bloc unit tests for every valid and invalid state transition, including the happy path and all documented error states.

Contingency: If the BLoC grows too complex to test reliably, decompose it into two cooperating blocs: one for configuration (scope + period selection) and one for execution (preview + confirm + export), linked by a coordinator object.