critical priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

WayForwardItem Dart model class exists with fields: itemId (String), reportId (String), assignedTo (String?), description (String), dueDate (DateTime?), status (WayForwardItemStatus enum: open, inProgress, completed, cancelled), createdAt (DateTime); includes fromJson and toJson methods
WayForwardItemStatus is a Dart enum with values matching the database CHECK constraint
WayForwardItemRepository is a class (not abstract) that accepts a SupabaseClient via constructor injection
createItem(WayForwardItem item) → Future<WayForwardItem> inserts a row and returns the created item with server-assigned item_id and created_at
getItemsByReportId(String reportId) → Future<List<WayForwardItem>> returns all items for the given report ordered by created_at ascending
updateItem(WayForwardItem item) → Future<WayForwardItem> performs a partial update using item_id as the filter and returns the updated row
deleteItem(String itemId) → Future<void> deletes the row by item_id
markComplete(String itemId) → Future<WayForwardItem> sets status to 'completed' and returns the updated row
All methods throw a typed WayForwardItemRepositoryException (or a shared RepositoryException subclass) on Supabase/PostgREST errors, including permission denied (RLS), row not found, and network errors
No raw Supabase error types leak outside the repository class
All methods are async and return typed Futures — no dynamic return types
Repository does not hold any state beyond the injected SupabaseClient

Technical Requirements

frameworks
Flutter
Supabase (supabase_flutter package)
apis
Supabase PostgREST REST API
Supabase Auth (for RLS context)
data models
WayForwardItem
WayForwardItemStatus
way_forward_items (Supabase table)
performance requirements
getItemsByReportId must use .eq('report_id', reportId) filter — never fetch all rows
updateItem must use .update(...).eq('item_id', itemId).select().single() to avoid unnecessary full-table operations
No N+1 queries — all items for a report fetched in a single query
security requirements
Repository relies on Supabase RLS for access control — do not add application-level permission checks that duplicate RLS
Never log sensitive field values (description content) at error level
Typed exceptions must not expose raw Supabase error codes to UI callers

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Follow the repository pattern already established in the codebase — look at existing repositories (e.g., activity or contact repositories) for the error-handling pattern and exception hierarchy before creating new exception types. Use .select().single() after insert and update to get the server-returned row. For markComplete, use .update({'status': 'completed'}).eq('item_id', itemId).select().single() rather than fetching then updating to avoid a double round-trip. The WayForwardItemStatus enum should have a fromString factory that maps database string values and throws on unknown values to catch schema drift early.

Inject SupabaseClient rather than using Supabase.instance directly to keep the class testable.

Testing Requirements

Write unit tests using flutter_test with a mocked SupabaseClient (use mockito or mocktail). Test each method: (1) createItem returns mapped WayForwardItem on success, (2) getItemsByReportId returns correct list and empty list when no items exist, (3) updateItem sends correct fields and returns updated model, (4) deleteItem calls delete with correct item_id filter, (5) markComplete sends status='completed', (6) each method throws WayForwardItemRepositoryException when Supabase throws PostgrestException, (7) each method throws WayForwardItemRepositoryException on network timeout. Test fromJson/toJson round-trip for all WayForwardItem fields including null dueDate and null assignedTo. Aim for 100% method coverage on repository and model.

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

Supabase RLS policies for multi-org report access may be more complex than anticipated — coordinators need cross-peer-mentor access within their org but not across orgs, and draft reports should be invisible to coordinators until submitted. Misconfigured RLS could expose sensitive health data or block legitimate access.

Mitigation & Contingency

Mitigation: Define and test RLS policies in isolation before writing repository code. Create a dedicated SQL migration file with policy definitions and an automated integration test suite that verifies each role's access boundaries using real Supabase auth tokens.

Contingency: If RLS proves too complex to express declaratively, implement application-level access control in the repository layer with explicit org and role checks, and add a security audit task before the feature goes to production.

high impact medium prob integration

The org field config JSON stored in Supabase may lack a stable, versioned schema contract. If different organisations have drifted to different field-definition formats, org-field-config-loader will fail silently or crash, breaking form rendering for those orgs.

Mitigation & Contingency

Mitigation: Define a canonical JSON Schema for field config and validate all existing org configs against it before implementation begins. Store a schema version field in every config record and handle version migrations explicitly in the loader.

Contingency: If existing configs are too heterogeneous, implement a config normalisation pass in org-field-config-loader that coerces known variants to the canonical format, logging warnings for fields that cannot be normalised so operations can fix them in the admin console.

medium impact low prob technical

TTL-based schema cache invalidation may cause peer mentors to use stale field definitions for up to the TTL window after an admin updates the org config, potentially collecting data against outdated field structures.

Mitigation & Contingency

Mitigation: Set a conservative TTL (e.g. 15 minutes) and expose a manual cache-bust mechanism triggered on app foreground-resume. Document the maximum staleness window in the admin console so org admins know to plan config changes outside active reporting windows.

Contingency: If stale schema causes a data quality incident, add a Supabase Realtime subscription to the org config table that invalidates the cache immediately on any config update.