high priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

On report submission, all way-forward items in the report are iterated and persisted via way-forward-item-repository
Each way-forward item results in exactly one coordinator task record with a due date and coordinator assignment metadata
Task queue receives a signal for each newly created coordinator task
Idempotency: re-submitting the same report (same report_id) does not create duplicate tasks — existing tasks are returned without re-insertion
Idempotency key is derived from report_id + way_forward_item_index (or stable item identifier) to guarantee per-item uniqueness
If way-forward-item-repository throws on persist, the entire batch is rolled back (no partial state)
Due date calculation follows the configured business rule (e.g., submission date + org-configured follow-up days)
Coordinator assignment uses the report's linked coordinator_id, falling back to org default coordinator if unset
Service is exposed as a Riverpod provider or injectable Dart class consumed by the report submission BLoC

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
apis
way-forward-item-repository (internal)
task-queue (internal)
Supabase (for persistence and idempotency key lookup)
data models
WayForwardItem
CoordinatorTask
Report
TaskQueueSignal
performance requirements
Batch insert all way-forward items in a single Supabase transaction to avoid N+1 inserts
Idempotency check must be a single indexed lookup by idempotency key, not a full table scan
security requirements
Only authenticated users with 'peer_mentor' or 'coordinator' role may trigger way-forward task creation
Coordinator task records must be scoped to the correct org — cross-org assignment must be rejected
Way-forward item content must be sanitized before persistence to prevent stored XSS if rendered in web admin

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement idempotency using a composite unique constraint in Supabase on (report_id, item_index) — the database enforces uniqueness at the insert level, avoiding race conditions from concurrent retries. In the service, use an upsert with onConflict: DoNothing and check affected rows to distinguish insert vs. already-exists. Wrap the full persist + task-queue signal in a Supabase database transaction or a logical transaction in Dart (persist all items first, then signal queue only on full success).

Define a WayForwardTaskCreationResult sealed class (created / alreadyExisted / failed) to give callers precise feedback. Due date calculation should be extracted into a pure function (daysToAdd: int, from: DateTime) → DateTime for easy unit testing. Note: this service is invoked synchronously within the report submission flow — keep it fast. If task queue signalling is slow, consider fire-and-forget with unawaited().

Testing Requirements

Unit tests with flutter_test and mockito covering: (1) successful creation of one coordinator task per way-forward item, (2) idempotency — second call with same report_id returns existing tasks without inserting, (3) repository failure causes full rollback with no partial records, (4) task queue signal is emitted exactly once per new task, (5) due date calculated correctly from submission date + configured offset, (6) coordinator assignment falls back to org default when report has no coordinator_id. Mock way-forward-item-repository and task queue. Validate that no real Supabase calls are made in unit tests. Target 90%+ line coverage.

Component
Way Forward Task Service
service 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.