high priority low complexity database pending database specialist Tier 0

Acceptance Criteria

A `way_forward_items` table exists in Supabase PostgreSQL with columns: id (uuid PK default gen_random_uuid()), org_id (uuid NOT NULL FK organizations), report_id (uuid FK post_session_reports), peer_mentor_id (uuid NOT NULL FK users), assignee_id (uuid FK users), action_description (text NOT NULL), due_date (date), priority (enum: low/medium/high), status (enum: pending/in_progress/completed/cancelled), created_at (timestamptz), updated_at (timestamptz)
RLS policy: peer mentors can INSERT and SELECT their own items (auth.uid() = peer_mentor_id)
RLS policy: coordinators can SELECT all items where org_id matches their organisation JWT claim — they cannot INSERT, UPDATE, or DELETE items on behalf of mentors via this policy
RLS policy: org admins can perform all CRUD operations on items within their org_id
WayForwardItemRepository Dart class implements create(WayForwardItem), readById(String id), readByReportId(String reportId), update(WayForwardItem), delete(String id) returning typed results or throwing typed exceptions
WayForwardItem Dart model is immutable (freezed or equivalent), serialises to/from JSON matching the Supabase column names exactly
All repository methods are covered by unit tests using a mocked Supabase client
Database migration script is idempotent — running it twice does not throw errors

Technical Requirements

frameworks
Flutter
Supabase Dart SDK
freezed (for immutable model)
flutter_test
apis
Supabase PostgreSQL 15
Supabase Auth (JWT claims for RLS)
data models
assignment
contact
activity
performance requirements
readByReportId query must use an index on (report_id, org_id) — include CREATE INDEX in migration
Bulk fetch of up to 50 way-forward items per report must complete in under 500 ms on a standard Supabase instance
security requirements
Row-Level Security enforced on all tables for multi-tenant isolation — org_id column is mandatory and validated against JWT claim
Service role key never used in the mobile client — all Supabase calls use the anon/user JWT
assignee_id must be validated as belonging to the same org_id before insert — enforce via CHECK constraint or Edge Function
action_description is free text and may contain PII — treat as sensitive, ensure it is within the private RLS boundary

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Follow the existing repository pattern in the codebase — implement an abstract WayForwardItemRepository interface and a SupabaseWayForwardItemRepository concrete class to allow swapping in a fake for tests. Use freezed with @freezed annotation for WayForwardItem and generate copyWith, toJson, fromJson via build_runner. The status and priority fields should use Dart enums with a toJson() that serialises to the string values matching the Postgres enum — add a fromJson factory. Store updated_at as a server-side default (DEFAULT now()) and use a trigger (moddatetime extension) to auto-update it.

The Blindeforbundet formalisert rapportstruktur includes a 'veien videre' (way forward) section — this repository directly supports that feature, so ensure action_description supports multi-line text (text type, not varchar).

Testing Requirements

Write unit tests using a mock SupabaseClient (mockito or fake implementation) covering: create() returns a WayForwardItem with generated id; readById() returns null for unknown id; readByReportId() returns all items for the given report; update() mutates only the provided fields; delete() removes the item. Write a separate integration test (against local Supabase emulator) verifying the RLS policies: peer mentor can read own items, cannot read another mentor's items; coordinator can read all items in their org. Aim for 100% method coverage on the repository class.

Component
Way Forward Item Repository
data low
Epic Risks (3)
high impact medium prob technical

Dynamically rendered form fields built from runtime JSON schema are significantly harder to make accessible than statically declared widgets — Flutter's Semantics tree must be correct for every possible field type and every validation state. Failures here block the entire feature for Blindeforbundet's visually impaired peer mentors.

Mitigation & Contingency

Mitigation: Define WCAG 2.2 AA semantics requirements for each field type before implementation and write widget tests using Flutter's SemanticsController for every type. Include a real-device VoiceOver test session in the acceptance gate for this epic before marking it done.

Contingency: If dynamic semantics prove too difficult to get right generically, implement field-type-specific Semantics wrappers (one per supported field type) instead of a single generic renderer, accepting slightly more code duplication in exchange for reliable accessibility.

high impact medium prob technical

The report-form-orchestrator must manage a complex state machine — schema loading, draft persistence, per-field validation, submission retries, and error recovery — across multiple async operations. Incorrect state transitions could result in lost user data, double submissions, or UI freezes.

Mitigation & Contingency

Mitigation: Define all Bloc states and events explicitly as sealed classes before writing any logic. Use a state machine diagram reviewed by the team before implementation. Write exhaustive Bloc unit tests covering every state transition, including concurrent events and network interruption mid-submission.

Contingency: If Bloc complexity becomes unmanageable, extract draft persistence into a separate DraftManagerCubit and keep report-form-orchestrator focused solely on the submit workflow. The additional granularity makes each component independently testable.

medium impact low prob scope

Organisations may require field types beyond the five currently specified (text, multiline, checkbox group, radio, date). If a new type is discovered during pilot testing, the dynamic-field-renderer must be extended, potentially requiring changes across multiple layers.

Mitigation & Contingency

Mitigation: Design dynamic-field-renderer as a registry of field-type renderers with a clear extension point. Document the pattern for adding a new field type so that it can be done in one file without touching existing renderers.

Contingency: If an unhandled field type is encountered at runtime, dynamic-field-renderer renders a labelled plain-text fallback widget and logs a warning so the missing type is surfaced in monitoring, preventing a crash while making the gap visible.