Which MOR Is This? A Surprisingly Expensive Question
by Dr. Prince Baawuah, Head of Medicare Advantage Solutions
TL;DR: CMS ships the monthly and the annual final Model Output Report with byte-identical record layouts — the only thing distinguishing them is the dataset name. Filenames don't survive being parsed and handed downstream, so anyone working past the parse — a provider organization receiving plan extracts, or an analytics team inside the plan itself — routinely ends up with MOR data they cannot identify. Stack it together and you double-count risk scores. Deduplicate it and you may delete the settled year-end scores outright. Both failures are silent, and both distort the PMPM benchmarks the arrangement is priced on. The fix is two date fields read in the right direction — and capturing provenance at ingest so nobody downstream has to reconstruct it.
You think a MOR is a MOR.
But the Model Output Report isn't one file. It's a family of files that look exactly alike on the inside, and the only thing that tells them apart is a name — a name that rarely survives the trip downstream. By the time the data reaches whoever actually has to act on it — a provider group, a business user inside an MA plan, the analytics developer building the model — that name is usually gone. Whether an internal IT team or an outside vendor did the parsing makes no difference. The records survive the parse. The filename doesn't.
The data still loads. The dashboards still render. The numbers are just quietly wrong.
Everything Is Named "mor"
Working with client data, we run into this: a folder of Model Output Report extracts, delivered by the health plan. Already parsed. Already cleaned. Columns instead of fixed-width positions, which is exactly what you want when you're trying to get to work.
No filenames. No dataset names. Every extract carrying the same label: mor.
The obvious move is to stack them into one table and start analyzing. That move produces a number. That's the problem — it produces a plausible number, one that reconciles against itself and survives a first review.
The Layouts Are Identical. That's Not a Bug.
First, what's actually inside one.
The MOR is CMS's answer to a specific question: which conditions did this beneficiary get credit for? Each detail record carries the HCC-level disease coefficients from the CMS-HCC risk adjustment model along with the demographic factors — age and sex bucket, disability status, ESRD status — that sum into that beneficiary's risk score. It isn't a report about the risk score. It's the components the score is built from, as CMS itself calculated them.
Which means when two MORs disagree, they aren't disagreeing about a number. They're disagreeing about which conditions counted. Teams that recompute HCCs independently — with an open-source implementation like mimilabs's hccinfhir, which maps diagnosis codes to HCCs and applies the CMS models directly — run into this same disagreement from the other direction: their own computed RAF score doesn't match the MOR's, and the gap is a run, not a bug.
Here's the piece that stays invisible until it costs you.
CMS produces MORs on more than one cadence. There's a monthly file, and there's a final yearly file that lands after the payment year closes. Different runs, different purposes, genuinely and potentially different HCC flags for the same beneficiary.
And they share a record layout. Byte for byte.
Same header record. Same detail record types. Same trailer — CMS's own Model Output Report resource guide lays out that structure directly, down to the run date and payment month fields in the header. The Plan Communications User Guide is where the file layouts themselves live, and the yearly variants share layouts with their monthly counterparts, differing only in dataset naming. HCCMOFD is the yearly Part C file; HCCMODD is the monthly. On the Part D side, PTDMOFD and PTDMODD.
Read that again. The file's identity lives in its name, not in its contents. There is no run-type field. There is no frequency flag. Nothing inside the record announces "I am the annual final."
That design is perfectly reasonable when you're CMS, transmitting a named dataset to a contracted plan. It stops being reasonable a few hops later.
Identity Dies at Every Hand-Off
Follow the data.
CMS to the plan. Filename intact. HCCMOFD and HCCMODD arrive as separate, unambiguous datasets.
The plan parses it. The fixed-width file becomes tables — the header record with its contract, run date, and payment month; the detail records with their HCC flags and coefficients. That's the right call, not a mistake; it's the only way to make the file queryable. But a filename isn't a record. There's nothing in the parse for it to become, so it's the one piece of the file with nowhere to land.
Whoever parsed it, to whoever uses it. A clean, well-formed extract — handed to a provider organization, or an internal analytics team, or a business owner who just needs the numbers. Called mor.
Nobody did anything wrong at any step. Every hop kept what it needed for its own purposes and discarded what it didn't. But metadata that lives outside the rows — in a filename, a folder, a tab label — does not survive being handed to someone else.
By the last hop, the only thing standing between you and a silently double-counted risk score is which columns somebody else's parser happened to carry down.
And the hand-off doesn't have to cross a company boundary.
The version above crosses from one organization to another, but the same chain runs entirely inside a single health plan. IT or data engineering receives the raw files from CMS and parses them into warehouse tables — that's the job, and they do it correctly. The analytics team queries those tables. A business team builds PMPM models on top of them. Nobody in that chain ever sees a filename, because by the time the data reaches them it lives in a warehouse, not a folder.
Same loss, without ever leaving the building, and no vendor to call and ask. Being close to the source doesn't protect you. What matters is which side of the parse you're on — and most of the people who analyze this data are on the far side of it, by design.
What Survived: Two Dates and a Direction
The header record survived the parse, and it carries two fields that matter:
- Run Date — when the Risk Adjustment System actually created the file
- Payment Year and Month — which payment month the model run applies to
Attach those to the detail rows — which you have to do anyway to make the data usable — and every beneficiary record carries the identity of the run it came from.
Neither field announces the file type. Taken alone, neither is diagnostic. The answer is in the direction between them.
Monthly files. The run date sits behind the payment month. Month-end processing in month M produces the file for payment month M+1, so the run always precedes the month it's paying for. Line up a set of monthly extracts and the payment months walk forward, one at a time.
Annual final files. The payment year and month is December of the payment year — YYYY12 — and it's the same across every header in the set, because a final run is a single point-in-time snapshot rather than a rolling one. The run date sits ahead of it, because the final run can only happen once the payment year has closed.
| Payment Year/Month | Run Date | Across a file set | |
|---|---|---|---|
| Monthly (MODD) | Walks forward | Before the payment month | Many consecutive months |
| Annual final (MOFD) | YYYY12, constant | After the payment month | One snapshot |
Top tip:
YYYY12alone will not identify an annual file. The December monthly extract also carriesYYYY12. What separates them is the run date — the December monthly was created in November, while the annual final for that same year was created the following year or later. The run-date comparison is the load-bearing half of the test, and it's the half people drop.A second trap: don't reach for "same calendar year means monthly." A monthly file created in December 2026 for payment month January 2027 has a payment year ahead of its run year. Compare the full
YYYYMMand read the direction of the inequality. That holds across the December/January boundary, where a year-equality check quietly breaks.
Why the Wrong Answer Looks Right
This is the part worth sitting with.
Stack a year-end final snapshot on top of twelve monthly snapshots and you don't get an error. You get the same beneficiaries appearing repeatedly, carrying legitimately different HCC flags — and therefore different risk scores — from legitimately different model runs. No constraint is violated. Nothing fails to parse. No row is malformed.
Member counts inflate. Averages drift toward whichever run happens to be over-represented in the pile. Year-over-year comparisons pick up a shift that has nothing to do with the population and everything to do with which files someone happened to send.
The output is a dashboard that renders cleanly, reconciles against itself, and is wrong.
For anyone carrying risk on those scores, that's not a data hygiene footnote. Risk scores are the input to the economics. A distorted score distorts the PMPM benchmark the whole arrangement is priced against — the same benchmark that gets skewed when misallocated dollars sit on the wrong side of the ledger. A silent double-count isn't a reporting problem. It's a mispriced arrangement nobody has a reason to question, because nothing ever broke.
Silent errors are the expensive ones. The loud ones get fixed on Tuesday.
And Deduplicating Makes It Worse
There's a second version of this mistake, and it's the more destructive one.
Once the December monthly and the annual final are sitting in the same table, a beneficiary appears twice with the same contract and the same payment year and month — YYYY12 on both rows. That is exactly what a duplicate looks like. Every instinct a data team has says clean it up.
So somebody writes a dedup. Partition by beneficiary and payment month, keep one row, move on.
They are not duplicates. They are the same beneficiary in the same payment month, observed from two different model runs — one an in-progress December snapshot, one the settled year-end result. Both are real. Both are correct. They disagree because they are supposed to disagree: between those two runs, late-submitted diagnoses land, deletes get processed, and hierarchies resolve against a complete year of data. The HCC flags themselves can differ, not only the score they add up to.
The grain of the data isn't beneficiary plus payment month. It's beneficiary plus payment month plus run. Leave the run out of your key and legitimate records look like noise.
What makes this worse than double-counting is the direction of the damage. Double-counting adds, and inflation is at least visible in aggregate to anyone watching totals. Deduplication removes — and depending on which row the tiebreak happens to favor, the row it removes may be the annual final. That's CMS's settled determination of which conditions counted for that beneficiary and that payment year: the version the money was actually paid on. Once it's dropped from the working table, nothing left in the data tells you it was ever there.
Deduplication is the one destructive operation most teams perform without review. It reads as hygiene. Nobody audits it.
If both files are in scope, the run date belongs in the key — not as a tiebreak for picking a winner, but as part of what makes the row unique in the first place.
How We're Thinking About This
Identifying a file after the fact is the recoverable version of this problem. The better goal is never landing there. A few things worth holding onto:
Provenance is a data element, not a filing convention. When a file's identity lives in its name, the name is part of the record. It belongs in a column at ingest, captured while you still have it — not reconstructed months later by someone reading date fields sideways. One column, written once, costs nothing. Recovering it later costs a forensic exercise, and sometimes it isn't recoverable at all.
The natural-looking key is usually not the grain. Beneficiary plus payment month reads like a primary key. It isn't. Grain errors don't announce themselves — they produce tables that join cleanly and aggregate wrong, which is the hardest class of error to find because every downstream check passes.
The operations that read as hygiene deserve the most scrutiny. Deduplication, coalescing, "cleaning up" — these get written quickly and reviewed rarely, precisely because they sound safe. Nothing fails when they're wrong.
Ask upstream before you reverse-engineer. The party that parsed the file usually still has the original dataset names sitting in a log or a landing folder. A question is faster and more certain than inference.
If you're the one receiving, look for what survived. Date fields are data, and data survives hand-offs in a way that filenames don't. The answer is often still sitting in the extract you already have — just not where you'd think to look for it.
Your Data Is Only Identified If Someone Chose to Identify It
Risk adjustment data moves through more hands than most teams picture. CMS to plan, plan to vendor, vendor to provider organization — and inside any one of those, raw files to the team that parses them, parsed tables to the team that analyzes them. Each hop is a chance to lose the context that made the data interpretable, and none of those losses announce themselves. The hop that costs you is rarely the one you were watching.
The question isn't whether your files are labeled. It's whether the labels are inside them.
Everything needed to tell these files apart was sitting in the extract the whole time. It just took someone knowing which two columns to read, and in which direction.