high priority medium complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

The loader fetches the field configuration for the authenticated user's organisation_id from a Supabase table (e.g. organisation_field_configs) and deserialises it into a typed OrganisationFieldConfig model
OrganisationFieldConfig contains a list of FieldConfig entries each with: field_id, label (resolved via org-labels system), is_visible, is_required, display_order, and field_type
Custom labels are resolved by querying the org-labels system and overriding the default label when an org-specific label exists for the field_id
Fields are returned sorted by display_order ascending
Invisible fields (is_visible = false) are excluded from the returned list so consumers never need to filter them
When Supabase returns an error, the loader throws a typed OrgFieldConfigException with a human-readable message and the underlying error code
When no configuration exists for the organisation the loader returns a default fallback config (all standard fields visible, required flags from schema defaults, default labels)
The loader is exposed as a Riverpod FutureProvider or AsyncNotifierProvider, allowing consuming services to await the config before building the report schema
Changes to the org-labels system trigger a re-read of labels without requiring a full config reload
Unit tests achieve 90%+ branch coverage on deserialisation, label resolution, default fallback, and error handling paths

Technical Requirements

frameworks
Flutter
Riverpod
flutter_test
apis
Supabase
data models
OrganisationFieldConfig
FieldConfig
OrgLabel
performance requirements
Config fetch and parse must complete in under 800ms on a standard mobile connection
Label resolution must not issue one Supabase query per field — batch or join the labels fetch
security requirements
Organisation field config must be scoped to the authenticated user's organisation_id via RLS — a user must never receive another organisation's config
Config data must not contain PII; only structural metadata (field IDs, display order, visibility flags) is stored
The loader must validate the response structure before deserialisation to prevent a malformed database row from crashing the app

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Design the Supabase query as a single join or RPC call that fetches both the field config rows and the org-specific label overrides in one round trip — avoid N+1 queries (one per field for label lookup). Model the deserialisation as a static factory method OrganisationFieldConfig.fromJson(Map) that is independently testable. The default fallback config should be defined as a const in a separate DefaultFieldConfigs class so it can be referenced in tests without instantiating the loader. Use Riverpod's ref.watch to invalidate the provider when the authenticated organisation changes (e.g.

on role switch). Keep the loader stateless — it fetches and returns; caching is the responsibility of the upstream ReportSchemaCache layer.

Testing Requirements

Write unit tests using flutter_test with a mocked Supabase client. Cover: (1) successful fetch with custom labels overriding defaults, (2) successful fetch with no org-specific labels (all defaults used), (3) fetch returns null/empty config → fallback config returned, (4) Supabase error → typed OrgFieldConfigException thrown, (5) fields sorted correctly by display_order, (6) invisible fields excluded from output, (7) required flags correctly mapped. Write one integration test against a local Supabase instance that inserts a known config row and asserts the loader returns the expected OrganisationFieldConfig.

Component
Organisation Field Config Loader
infrastructure medium
Epic Risks (2)
medium impact high prob technical

Flutter's speech_to_text package behaviour differs meaningfully between iOS and Android — microphone permission flows, locale availability, background audio session interference, and partial-result timing all vary. Inconsistent behaviour could make voice input unreliable for the primary audience (visually impaired peer mentors on iOS VoiceOver).

Mitigation & Contingency

Mitigation: Test speech-to-text-adapter on physical iOS and Android devices from the start, not just simulators. Write platform-specific test cases for permission flows and locale detection. Design the adapter's public interface to be platform-agnostic so that a native bridge could replace the package if needed.

Contingency: If speech_to_text proves unreliable on a platform, implement a native-speech-api-bridge (already identified in the component catalogue) as a drop-in replacement within the adapter, keeping the external interface unchanged so no UI code needs to change.

medium impact medium prob dependency

The coordinator task queue notification mechanism is not fully specified. If the queue system is owned by another team or uses an external service, way-forward-task-service may block on an undefined integration contract, delaying this epic.

Mitigation & Contingency

Mitigation: Define the task queue notification interface as an abstract Dart interface early in the epic. Implement a stub that writes a flag to the database so coordinator list queries can detect new tasks, deferring the real notification integration to a later epic.

Contingency: If the queue integration remains undefined at implementation time, ship way-forward-task-service with database persistence only and add a TODO-flagged notification hook. Coordinators will still see items on next page load; push notification delivery is deferred.