high priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

getPrioritizedMatch(matchingRules) returns the single ScenarioRule with the highest explicit priority value when multiple rules match
When two rules share the same explicit priority, getPrioritizedMatch returns the rule with the higher specificity score
Specificity scoring: each non-wildcard condition present on a rule adds 1 to its specificity score (contactType != '*' = +1, wellbeingFlags non-empty = +1, durationRange defined = +1, delayWindow defined = +1); maximum score is 4
When both priority and specificity are equal, getPrioritizedMatch returns the rule with the lower insertionOrder value (earlier-inserted rule wins)
getPrioritizedMatch returns null when called with an empty list without throwing
getPrioritizedMatch returns the single element when called with a list of one rule without applying sorting overhead
evaluate() in ScenarioRuleEngine now collects all matching rules first and then delegates to getPrioritizedMatch() to select the winner
EvaluationResult.matchedRule reflects only the single winner returned by getPrioritizedMatch
EvaluationResult includes a candidateRules list showing all rules that matched before priority resolution, for debugging purposes
The priority ordering algorithm is deterministic: identical inputs always produce identical output regardless of the initial list order

Technical Requirements

frameworks
Flutter
flutter_test
data models
ScenarioRule (priority: int, insertionOrder: int fields required)
EvaluationResult (candidateRules: List<ScenarioRule> added)
performance requirements
getPrioritizedMatch() must complete in O(n log n) time for sorting n candidate rules
For the typical case of 1-5 matching rules, getPrioritizedMatch() must complete in under 1ms
security requirements
Priority values must be validated as non-negative integers at the ScenarioRule model level; negative priorities should be rejected during rule validation in ScenarioConfigurationManager

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Implement getPrioritizedMatch(List candidates) as a pure synchronous method — no async, no repository calls. Use Dart's List.sort() with a custom Comparator that applies the three-tier ordering: (1) descending by rule.priority, (2) descending by computeSpecificityScore(rule), (3) ascending by rule.insertionOrder. Extract computeSpecificityScore(ScenarioRule) as a private static method for independent testability. Do not add the specificity score to the ScenarioRule data model — compute it on the fly to keep the model a pure data class.

Update the evaluate() method from task-005 to collect all matching rules into a List candidates, then call getPrioritizedMatch(candidates) instead of returning the first match found. Store candidates in EvaluationResult.candidateRules before applying priority. If ScenarioRule does not yet have a priority field, add it now with a default of 0 and document the convention that higher values mean higher priority.

Testing Requirements

Write unit tests using flutter_test. Tests must cover: (1) single rule returns immediately, (2) two rules with different explicit priorities, (3) two rules with equal priority but different specificity scores, (4) two rules with equal priority and equal specificity but different insertion orders, (5) all three tiebreakers needed in sequence, (6) empty list returns null, (7) determinism test — shuffle input and assert same winner across 10 random orderings, (8) specificity score calculation for all 16 permutations of the 4 binary condition dimensions. Verify that evaluate() in task-005 correctly pipes all matching rules through getPrioritizedMatch and that EvaluationResult.candidateRules is populated correctly.

Component
Scenario Rule Engine
service high
Epic Risks (2)
high impact medium prob scope

The Rule Engine must support a flexible JSON rule schema that can express compound conditions (e.g., contact_type AND wellbeing_flag AND delay_days). Underestimating schema expressiveness may require breaking changes to the rule format after coordinators have already configured rules.

Mitigation & Contingency

Mitigation: Define and freeze the rule JSON schema (trigger_type enum, metadata_conditions structure, delay logic) before any implementation begins; validate schema against all known HLF scenarios documented in the feature spec.

Contingency: If schema changes are needed after deployment, implement a schema version field and a migration utility that upgrades stored rules to the new format without coordinator intervention.

medium impact medium prob technical

Deep-link navigation to the activity wizard with pre-filled arguments may fail if the user's session has expired or if the wizard route is not yet mounted in the navigator stack, causing unhandled navigation exceptions.

Mitigation & Contingency

Mitigation: Implement session state check before navigation; if session is expired, redirect to biometric/login screen and store the pending deep-link URI for post-auth redirect using go_router's redirect mechanism.

Contingency: If post-auth redirect proves unreliable, fall back to navigating to the home screen with a visible action banner that re-triggers the wizard with pre-filled arguments.