Implement Report Schema Service
epic-structured-post-session-report-services-task-005 — Build the service that fetches, parses, and caches org-specific report schemas by composing report-schema-cache and org-field-config-loader. Implement cache-first fetch strategy with background refresh, schema versioning support, and typed schema models that downstream UI components consume to render dynamic fields.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Use a Repository pattern: ReportSchemaService depends on IReportSchemaCache and IOrgFieldConfigLoader interfaces, not concrete implementations — this makes unit testing with mockito straightforward. Store the schema version hash alongside the cached schema so version comparison is a simple string equality check. For background refresh, use Future.microtask() or unawaited() with proper error swallowing (log, do not propagate). Define a ReportSchema sealed class (or freezed union) with a version field so downstream BLoC can react to version changes via stream equality.
Ensure Riverpod provider is defined with autoDispose if schemas are org-scoped to prevent memory leaks when switching orgs. Avoid storing org-field-config-loader raw responses in cache — always store the parsed ReportSchema to keep the cache layer typed.
Testing Requirements
Unit tests with flutter_test and mockito covering: (1) cache-hit returns cached schema without calling loader, (2) cache-miss calls loader and stores result, (3) TTL expiry triggers re-fetch, (4) schema version mismatch invalidates cache, (5) org A schema does not appear for org B (isolation), (6) malformed JSON from loader returns last valid cached schema, (7) malformed JSON with no cache throws SchemaParseException. Mock both org-field-config-loader and report-schema-cache. Target 90%+ line coverage. No integration tests required for this task — covered by task-007.
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.
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.