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 evaluation-
seriesGroupCompleteProvider— 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).