All benefits

Plan Facts Parsing Analysis · v1

Critical Illness

Single-class parse covering ~20 covered conditions (cancer, heart attack, stroke, organ failure, neurological diseases), benefit amount + guarantee issue, age-based reductions (up to 3 tiers), spouse / child rider %, separation periods, pre-existing conditions, and age-banded rates.

Endpoint
POST /criticalIllness
Parse method
parseQuoteCriticalIllness()
Source range
PlanfactService.php:1923–2310
Source pull
plansight - Bit Bucket Pull 4.26

Bottom line

One semantic mapping question, plus three smaller items.

The "Angioplasty" Plan Facts field maps to a Quote field named criticalIllnessCoronaryArteriosclerosis — the field name doesn't match the source. The other items are fragility around enum casing and age-band collapsing.

Per-class handling

Single-class. $class = $data['Critical Illness']['Classes'][0] at line 1930. Age reductions are stored in up to 3 separate tiers (criticalIllnessTier1Age, etc., line 2025).

What's mapped

Plan Facts fieldQuote fieldNotes
Plan NamenameDirect
Participation.[Class].Min Participation %criticalIllnessParticipationRequiredStrip %
Participation.[Class].Min Enrolled LivescriticalIllnessMinimumLivesNumeric
Employee Benefit Schedule.[Class].Benefit MinimumcriticalIllnessIncrementsMinMaxMinCurrency stripped
Employee Benefit Schedule.[Class].Benefit MaximumcriticalIllnessIncrementsMinMaxMaxCurrency stripped
Employee Benefit Schedule.[Class].Benefit IncrementcriticalIllnessHowDoBenefitsPayAppended with "Benefit Increment: " prefix
Employee Benefit Schedule.[Class].Guarantee Issue AmountcriticalIllnessGuaranteedIssueCurrency stripped
Employee Benefit Schedule.[Class].Lifetime MaximumcriticalIllnessHowDoBenefitsPayAppended with "Lifetime Maximum: " prefix
Spouse Benefit Schedule.[Class].% of Employee AmountcriticalIllnessSpouseCoverageStrip %
Child Benefit Schedule.[Class].% of Employee AmountcriticalIllnessDependentCoverageStrip %
Employee Age Reductions.[Class].Ages (up to 3)criticalIllnessTier{1..3}Age + criticalIllnessTier{1..3}Reduction + criticalIllnessNumberOfTiers + criticalIllnessBenefitReduction"By" type inverts (100 - amount); line 2025
Pre-existing Conditions.[Class].Look-back / ExclusioncriticalIllnessPreExistingConditionsMin / criticalIllnessPreExistingConditionsMaxDirect
Separation Period.[Class].Additional OccurrencecriticalIllnessDifferentConditionDaysMonths + …TypeType hardcoded to "Months"
Separation Period.[Class].RecurrencecriticalIllnessSameConditionDaysMonths + …TypeType hardcoded to "Months"
Wellness Benefit.[Class].EmployeecriticalIllnessWellnessBenefitDirect
Heart Attack / Heart Failure / Stroke / Major Organ Failure / End-stage Renal / Coronary Artery Bypass / Coma / Benign Brain Tumor / Invasive Cancer / Non-invasive Cancer / Occupational HIV / Blind / ALS / Alzheimer's / Addison's / Parkinson's / Huntington's / Multiple Sclerosis / Paralysis.[Class].InitialcriticalIllness[ConditionName] + …Type"n/a" → "Not Covered"; else strip % + Type="%"
Angioplasty.[Class].InitialcriticalIllnessCoronaryArteriosclerosisField name doesn't match source — see issue (line 2108)
Skin Cancer.[Class].InitialcriticalIllnessSkinCancer + TypeType inferred from "$" prefix (line 2147)
Childhood conditions.[Class].InitialcriticalIllnessChildhoodConditionsBoolean from presence (line 2161)
Portability.[Class].PortabilitycriticalIllnessPortabilityIncluded / Not Included
Rates.[Class].Employee Unismoker Rates (age-banded, 11+ bands)age-band fields, criticalIllnessRateType, criticalIllnessBenefitEmployeePer-$100 benefit extracted via regex (line 2189)
Rates.[Class].Employee Unismoker Rates.Age BasiscriticalIllnessPremiums"Attained Age" / "Issue Age"
Rates.[Class].Spouse Unismoker Ratesspouse age-band fields (line 2259)Per-$100 benefit extracted

Plan-level / not mapped

Potentially mapped incorrectly

Angioplasty maps to "Coronary Arteriosclerosis" Quote field PlanfactService.php:2108

The Plan Facts field 'Angioplasty' is assigned to criticalIllnessCoronaryArteriosclerosis. These are clinically related but not the same: angioplasty is a procedure; coronary arteriosclerosis is the underlying disease. Either the field name is misleading or the source key is wrong.

Cursor prompt
In app/Services/PlanfactService.php at line 2108, inside parseQuoteCriticalIllness(), the Plan Facts field "Angioplasty" is being assigned to a Quote field named criticalIllnessCoronaryArteriosclerosis.

These are clinically related but distinct: angioplasty is a procedure, coronary arteriosclerosis is the underlying condition.

Please:
1. Show me the line.
2. Look at the form template — what label is shown for criticalIllnessCoronaryArteriosclerosis?
3. Check whether Plan Facts also returns a separate "Coronary Arteriosclerosis" key (or similar) that's currently unmapped.
4. Recommend either:
   (a) renaming criticalIllnessCoronaryArteriosclerosis → criticalIllnessAngioplasty end-to-end, or
   (b) confirming the mapping is intentional and adding a code comment explaining why.
5. Don't change anything yet — show the analysis.
Childhood conditions binary mapping is fragile PlanfactService.php:2161–2168

The Childhood Conditions field is set to "Yes" if the Plan Facts source is present, "No" if absent. If Plan Facts ever returns an explicit "No" or "Not Included" value, the parse will incorrectly set "Yes" because the field is technically present.

Cursor prompt
In app/Services/PlanfactService.php at lines 2161–2168, inside parseQuoteCriticalIllness(), the Childhood Conditions Quote field (criticalIllnessChildhoodConditions) is set to "Yes" or "No" based on presence of the source field, not its value.

This is fragile: if Plan Facts returns { "Initial": "No" } or { "Initial": "Not Included" }, the field will be present and the parser will incorrectly set criticalIllnessChildhoodConditions = "Yes".

Please:
1. Show me the current logic.
2. Update it to check the actual value: "Yes" only if the value is truthy AND not "No" / "Not Included" / "Not Covered" (case-insensitive); otherwise "No".
3. Show me the proposed change.
Skin Cancer Type inference fragile PlanfactService.php:2147–2157

Skin Cancer Type is determined by checking if the value starts with "$" (then "%"). If a carrier returns "25% of benefit" or anything not starting with "$" or a bare percentage, the Type field may be wrong or unset.

Cursor prompt
In app/Services/PlanfactService.php at lines 2147–2157, inside parseQuoteCriticalIllness(), the Skin Cancer Type is inferred from whether the value starts with "$":
- starts with "$" → Type = "$"
- otherwise → Type = "%"

This is fragile if a carrier returns "25% of benefit" or any value with mixed format.

Please:
1. Show me the current logic.
2. Strengthen it: check for "$" anywhere in the value, OR check for "%" suffix. Default to a sensible value if neither matches.
3. Show me the proposed change.
Age band <20 and 20-24 collapse to the same field PlanfactService.php:2195–2208

Both "<20" and "20-24" age bands map to criticalIllnessLess25Employee. If a carrier returns separate rates for those bands, one overwrites the other. Same in the Spouse section (line 2263–2276).

Cursor prompt
In app/Services/PlanfactService.php at lines 2195–2208 (Employee) and 2263–2276 (Spouse), inside parseQuoteCriticalIllness(), the age bands "<20" and "20-24" both map to the same Quote field (criticalIllnessLess25Employee / criticalIllnessLess25FamilySpouse).

If Plan Facts returns separate rates for those two bands, one overwrites the other.

Please:
1. Show me the current age-band mapping.
2. Confirm whether Plan Facts ever returns separate rates for <20 and 20-24 in practice (a quick grep of any test fixtures may help).
3. If yes, recommend splitting into two Quote fields. If no, add a code comment confirming the collapse is intentional and the values are equivalent in practice.

Manual-entry-only fields

Recommendations

  1. Verify Angioplasty → Coronary Arteriosclerosis mapping Hour

    Either rename the Quote field, or confirm intentional and add a comment.

  2. Tighten Childhood Conditions logic 15 minutes

    Check the value, not just presence.

  3. Strengthen Skin Cancer Type detection 15 minutes

    Match "%" or "$" anywhere in the string, not just at the start.

  4. Document or split the <20 / 20-24 age band collapse 15 minutes

    Quick code comment, or split into two fields if rates do differ.

Code references

FileLinesPurpose
app/Services/PlanfactService.php246Critical Illness endpoint config
app/Services/PlanfactService.php463Dispatch to parseQuoteCriticalIllness
app/Services/PlanfactService.php1923–2310Full parser
app/Services/PlanfactService.php2025–2054Age reduction tiers
app/Services/PlanfactService.php2108Angioplasty → CoronaryArteriosclerosis
app/Services/PlanfactService.php2147–2157Skin Cancer Type inference
app/Services/PlanfactService.php2161–2168Childhood Conditions boolean
app/Services/PlanfactService.php2189–2310Employee + Spouse age-band rates