Multi-component stratifier loses components that share a scalar value#1045
Merged
lukedegruchy merged 4 commits intoJun 4, 2026
Conversation
…(CDO-789) Captures the bug where a stratifier with multiple components silently drops components whose value equals another component's value for the same row key, because MeasureMultiSubjectEvaluator keys its assembly table on value alone. - New fixture CohortBooleanStratSameValueComponents.json with three components (two using "Gender Stratification", one using "Age"). - Integration test in MeasureStratifierTest asserting each stratum carries all three components when two share a value. - Unit tests in MeasureMultiSubjectEvaluatorTest (DuplicateValueAcrossComponents) pinning the evaluator-level behavior directly. - MultiMeasure variant in MultiMeasureServiceTest reusing the same fixture via the multi-measure orchestration path. All four tests fail on the current branch; the fix follows in a subsequent commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MeasureMultiSubjectEvaluator's subject-results table was keyed on StratumValueWrapper alone, so when two components of the same stratifier resolved to equal values for the same row key (e.g. Race and Ethnicity both AskedButNoAnswer), the second put() overwrote the first cell and the losing component silently vanished from the MeasureReport stratum. Introduce a private StratumTableColumnKey(component, value) record and use it as the table's column key. Two distinct components with equal values now land in distinct cells; same-component idempotent puts still collapse. The change is fully encapsulated inside MeasureMultiSubjectEvaluator — no callers of the existing public surface are affected. Also: align integration test M/F expectations with the actual fixture data (females are 38, males are 35 against the default 2024 measurement period) and adopt spotless formatting on the Phase 1 tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Formatting check succeeded! |
…r-multi-component-patient-same-value-diff-expressions
brynrhodes
approved these changes
Jun 3, 2026
|
lukedegruchy
added a commit
that referenced
this pull request
Jun 11, 2026
Integrates the latest main (incl. the multi-component-stratifier fixes #1042 and #1045) into the update-cql-4-7-0 CQL-5 refactor. Conflict resolution (MeasureMultiSubjectEvaluator.java): - Re-applied #1042's collectAlignmentRowKeys generalization (function + iterable alignment) using the refactor's typed wrapper API (asFunctionResultAccumulator / isIterable) instead of raw instanceof Map. - Adopted #1045's StratumTableColumnKey column-key type. Completing the incomplete refactor's stratifier value/grouping path so the full cqf-fhir-cr suite passes (was "measures are broken again" on update-cql-4-7-0; ~18 pre-existing MeasureStratifierTest failures): - R4StratifierBuilder: render stratum value via getValueClass/getValueAsString instead of StratumValueWrapper.toString. - StratumValueWrapper: normalize CQL-5 ClassInstance values to FHIR R4. - CqlExpressionValue + MeasureMultiSubjectEvaluator: handle function results arriving as raw Map as well as FunctionResultAccumulator; handle MEASUREOBSERVATION ObservationAccumulator/Map inputs. - StratifierRowValue: normalize CQL-5 ClassInstance FHIR-resource keys. - PopulationBasisValidator: allow CQL runtime primitive value types. cqf-fhir-cr: 1777 tests, 0 failures, 0 errors (18 skipped). CQL v5 SNAPSHOT dependency unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes a silent data-loss bug in measure-report assembly where stratifier components could be dropped from the output stratum when two of them happened to evaluate to the same scalar value for the same subject. The bug was reported against a real-world RDM measure where a three-component
RaceEthnicitystratifier (ProductLine,Race,Ethnicity) produced strata missing theRacecomponent wheneverRaceandEthnicityboth resolved toAskedButNoAnswerfor the same patient. The CQL output already contained all values; the loss happened insideMeasureMultiSubjectEvaluatorbecause its accumulation table was keyed on the value alone, so the secondputfor the same(row, value)cell overwrote the first.StratumValueWrapperto a smallStratumTableColumnKey(component, value)record so that two distinct components emitting equal values for the same row key land in distinct cells instead of overwriting each other.MeasureStratifierTest(driven by a newCohortBooleanStratSameValueComponentsmeasure fixture), two evaluator-level unit tests inMeasureMultiSubjectEvaluatorTest, and a defense-in-depth check inMultiMeasureServiceTestto confirm the multi-measure orchestration path inherits the fix.Example: the RDM-Reporting scenario end-to-end
The snippets below are the colleague's original reproduction for patient
2025.hpdi.0.115003, walking from CQL output → Measure definition → buggy MeasureReport → corrected MeasureReport. They show why the assembly-layer key change matters: the CQL already computes all three component values; the loss is purely inMeasureMultiSubjectEvaluator.1. CQL results for the patient (input to assembly)
Note the collision:
RaceandEthnicityare distinct stratifier components but both resolve to the same scalarAskedButNoAnswerfor this patient.Product linesis multi-valued, so each value yields its own stratum (PPOandPOS).2. Measure stratifier definition (RDM-Reporting.json, three components)
3. Before the fix — MeasureReport stratum is missing the
RacecomponentRaceis silently absent from both strata because, insidebuildSubjectResultsTable, the cell(rowKey, "AskedButNoAnswer") → Racegot overwritten by(rowKey, "AskedButNoAnswer") → Ethnicitywhen only the value was used as the column key.4. After the fix — all three components survive in each stratum