Architecture
Cicada's forecasting pipeline processes FHIR input through a series of steps that mirror the CDSi Logic Specification.
Pipeline Overview
FHIR Parameters (input)
│
1. Parse ──────────▶ VaxPatient (demographics, doses, observations)
│
2. Map ────────────▶ Map<String, VaxAntigen> (one per target disease)
│
3. Evaluate ───────▶ Each dose checked against series requirements
│
4. Forecast ───────▶ Series determines need + recommended dates
│
5. Select Best ────▶ VaxGroup scores and picks best series
│
6. Aggregate ──────▶ Vaccine group forecasts (multi-antigen grouping)
│
FHIR Parameters (output)
Step 1: Parse
The PatientForAssessment Riverpod provider extracts a VaxPatient from the FHIR Parameters input. It parses:
- Patient demographics (birth date, sex)
- Immunization history (CVX codes, dates administered)
- Clinical conditions, allergies, observations, procedures, and medications
Each clinical resource is cross-referenced against the observation crosswalk to identify CDSi observation codes (e.g., "patient is immunocompromised", "patient is pregnant").
Step 2: Map
antigenMap() distributes each administered dose to the antigens it targets. A single CVX code (e.g., CVX 94 for MMR-Varicella) may map to multiple antigens (Measles, Mumps, Rubella, Varicella). The CVX-to-antigen mapping comes from the schedule supporting data.
Step 3: Evaluate
Each VaxAntigen contains one or more VaxGroups, each containing one or more VaxSeries. For each series, doses are evaluated in order per CDSi Figure 6-1:
- Inadvertent Administration — Was the vaccine given inadvertently?
- Age Validity — Was the patient within the valid age range?
- Interval — Was the minimum interval from the previous dose met?
- Live Virus Conflict — Was there a conflicting live virus vaccine given too recently?
- Vaccine Type — Was the administered vaccine an allowable type for this dose?
The first failing check determines the evaluation reason. Valid doses are added to the series' evaluated dose list.
Step 4: Forecast
Each series determines its forecast need:
- Check for immunity (clinical evidence or completed series)
- Check for contraindications (both preferable and allowable vaccines)
- Determine series completeness (all target doses satisfied?)
- If not complete, calculate forecast dates using conditional skip logic and interval calculations
Step 5: Select Best Series
VaxGroup scores and prioritizes series using CDSi Table 8-14:
- Complete/Immune series are preferred over in-process
- In-process preferred over zero-dose
- Among ties, standard series preferred over risk series
- Score considers number of valid doses, number of not-valid doses, and closest forecast date
Step 6: Aggregate
Multi-antigen vaccine groups (e.g., DTaP/Tdap/Td covers Diphtheria + Tetanus + Pertussis) aggregate recommendations from constituent antigens. The administerFullVaccineGroup flag determines whether to recommend the full combination or allow individual components.
Model Hierarchy
VaxPatient
└── VaxAntigen (one per target disease: Measles, Tetanus, HepB, etc.)
└── VaxGroup (groups series by vaccine group: MMR, DTaP/Tdap/Td, etc.)
└── VaxSeries (one CDSi series: standard, risk, evaluation-only)
└── VaxDose (one administered dose with evaluation status)
VaxPatient
Parsed patient with demographics, past doses, and observations. Created by the PatientForAssessment provider from FHIR Parameters.
VaxAntigen
One instance per target disease (e.g., Measles). Contains groups, handles immunity and contraindication checks. The set of antigens comes from activeAntigenMap (CDC or WHO depending on ForecastMode).
VaxGroup
Groups related series by vaccine group. Implements the CDSi series scoring and best-series selection algorithm. A vaccine group like MMR contains multiple series (e.g., standard 2-dose MMR, catch-up).
VaxSeries
Tracks one CDSi series definition. Evaluates administered doses against target dose requirements, manages conditional skip logic ("if patient already received X, skip dose Y"), and generates forecast dates with earliest/recommended/past-due/latest.
Series types:
- Standard — Always included for evaluation
- Risk — Only included when patient has a matching observation (e.g., immunocompromised) and falls within the indication age range
- Evaluation-only — Used for dose evaluation but does not generate forecasts
VaxDose
An individual administered dose with its evaluation outcome:
- Valid — Meets all requirements for the target dose
- Not Valid — Fails one or more checks (with specific reason)
- Sub-standard — Partially valid (e.g., grace period)
- Extraneous — Not needed by the series
Key Types
VaxDate
Extends DateTime with CDSi date arithmetic:
final birthDate = VaxDate(2024, 1, 15);
final sixMonths = birthDate.change('6 months'); // 2024-07-15
final dueDate = birthDate.change('6 months 4 days'); // 2024-07-19
// Sentinel values
VaxDate.min(); // 1900-01-01
VaxDate.max(); // 2999-12-31
AntigenSupportingData
Immutable data class containing the complete definition for one antigen: target disease, vaccine group, series definitions (with dose rules, intervals, allowable vaccines), immunity criteria, and contraindications. Deserialized from generated JSON.
ScheduleSupportingData
Global schedule data shared across all antigens: live virus conflicts, vaccine groups, vaccine-group-to-antigen map, CVX-to-antigen map, and observation definitions (clinical condition crosswalk).
State Management
Cicada uses Riverpod (non-Flutter variant) with ProviderContainer for managing shared state during a forecast run:
patientForAssessmentProvider— Parses FHIR Parameters into VaxPatientobservationsProvider— Shared observation state across evaluationseriesGroupCompleteProvider— Tracks series group completion across antigens (used for conditional skip logic)operationOutcomesProvider— Collects OperationOutcome issues (errors/warnings)
A new ProviderContainer is created for each forecast invocation, ensuring thread safety and isolation.
Generated Code
Supporting data files in cicada/lib/generated_files/ are produced by the generator pipeline and should not be hand-edited:
- CDC: One
.dartfile per antigen +schedule_supporting_data.dart+ barrel file - WHO: Same structure in
generated_files/who/withwhoprefix on variable names
The generator reads from CDSi XML/Excel specifications (CDC) or Excel workbooks (WHO) and produces Dart files with AntigenSupportingData.fromJson() and ScheduleSupportingData.fromJson() calls containing the full specification data. WHO Excel source files live in cicada_generator/lib/WHO/antigen/ (22 .xlsx) and WHO/schedule/ (5 .xlsx).