high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

ReportSchemaCache is a plain Dart class (not a Flutter widget or BLoC) instantiable with an optional Duration ttl parameter (default Duration(minutes: 15))
Internal storage uses a Map<String, _CacheEntry> where _CacheEntry holds the cached FieldConfigSchema and the DateTime it was inserted
get(String orgId) → FieldConfigSchema? returns null if key absent or entry has exceeded TTL; stale entry is removed from the map on access
set(String orgId, FieldConfigSchema schema) stores the schema with the current timestamp
invalidate(String orgId) removes the entry for the given orgId if present; no-op if absent
invalidateAll() clears all entries from the cache
Cache does not use any async mechanism — all methods are synchronous
Cache is safe to call from the main Dart isolate (Flutter app) — no isolate communication required; document that it is not intended for use across isolates
FieldConfigSchema is a typed Dart class (or data class) with fields: orgId (String), fields (List<FieldConfig>), version (int), fetchedAt (DateTime)
FieldConfig is a typed Dart class with fields: fieldKey (String), fieldType (FieldType enum), label (String), required (bool), visible (bool), validationRules (List<String>), options (List<String>), displayOrder (int)
FieldType is a Dart enum with values: text, number, boolean, select, date
Unit tests cover: get returns null for missing key, get returns null for expired entry and removes it, get returns value for valid (non-expired) entry, set overwrites existing entry, invalidate removes entry, invalidateAll empties cache, TTL boundary (entry at exactly TTL age is considered stale)

Technical Requirements

frameworks
Flutter (Dart)
flutter_test
data models
FieldConfigSchema
FieldConfig
FieldType
performance requirements
All cache operations complete in O(1) average time
Cache must not grow unboundedly — stale entries are eagerly evicted on get()
A periodic purge method purgeExpired() may optionally be exposed for callers to call on app resume to prevent memory accumulation
security requirements
Cache must not persist to disk — in-memory only to avoid exposing field configuration data in plaintext storage
Cache must not be shared across user sessions — invalidateAll() must be called on logout

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

To make TTL tests deterministic, accept an optional DateTime Function() nowProvider parameter in the constructor defaulting to () => DateTime.now(). This allows tests to inject a controlled clock. Keep the cache as a simple class — do not reach for a third-party cache library. A lazy eviction strategy (evict on get) is sufficient; a background timer for proactive eviction is unnecessary complexity for the current scale.

Place the cache class in a dedicated file under the appropriate layer (e.g., src/processing/ or the infrastructure layer of the feature). The FieldConfig and FieldConfigSchema types defined here will be shared with OrgFieldConfigLoader (task-005), so define them in a shared types file rather than inside the cache file to avoid circular imports.

Testing Requirements

Write unit tests using flutter_test (no Flutter widget test environment needed — plain Dart test). Cover: (1) empty cache returns null for any key, (2) set then get within TTL returns schema, (3) set then get after TTL returns null and entry is removed, (4) invalidate removes specific key without affecting others, (5) invalidateAll removes all keys, (6) custom TTL duration is respected, (7) overwriting an existing key resets its TTL timestamp, (8) FieldConfig and FieldConfigSchema fromJson/toJson round-trip if serialization is added. Use fake DateTime injection (pass a clock function or DateTime provider) to make TTL tests deterministic without sleeping.

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