high priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

Cache-first fetch: service returns cached schema without network call when cache is valid (TTL not expired)
Cache-miss path: service fetches schema from org-field-config-loader and stores result in report-schema-cache on cache miss
Background refresh: after returning cached schema, service triggers async refresh if TTL is within refresh window (e.g., last 20% of TTL)
Schema versioning: service compares schema version from loader against cached version and invalidates cache on version mismatch
Multi-org isolation: fetching schema for org A never returns or pollutes schema for org B
Typed schema model: service returns a strongly-typed ReportSchema Dart class (not raw Map) consumed by downstream UI components
Parse error handling: malformed schema from loader logs an error and returns last valid cached schema if available, or throws a typed SchemaParseException
Service is injectable via Riverpod provider with correct scoping (per-org or global singleton as appropriate)
All public methods return Future or Stream with explicit error types documented

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
apis
org-field-config-loader (internal)
report-schema-cache (internal)
data models
ReportSchema
OrgFieldConfig
SchemaVersion
performance requirements
Cache-hit path must complete in under 5ms (in-memory lookup only)
Network fetch must not block UI thread — use compute() or Isolate if parsing is CPU-intensive
Background refresh must not delay rendering of cached schema
security requirements
Org ID must be validated against authenticated user's org membership before fetching schema
Cached schemas must not be accessible across org boundaries — cache keys must include org ID
No sensitive field values (PII) should be stored in the schema cache

Execution Context

Execution Tier
Tier 1

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.

Component
Report Schema 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.