Vibrometer

Olympiad

Payer Underpayment Audit

Ticket BILL-2291, from Dana Whitfield, Billing Manager, Riverside Orthopedics & Sports Medicine

We think several of our payers are underpaying us. I need the Q3 appeal report for our four contracted payers: Summit Mutual Health Plan, Lakeshore Benefit Trust, Harborview Health Alliance (our Medicaid plan) and Crescent Care Health. That's every service line with a date of service from July 1 through September 30, 2024, what each contract says we should have been allowed, what the payer paid, how much we can go back for, and a worklist of the claims to appeal with each payer's filing deadline.

Last quarter we only appealed Summit. That batch is in data/reference_case/ with approved_report.json, which Summit's network rep reviewed and paid in full. Build this quarter's report the same way.

I'm in and out of meetings. Ask me what you need, but I can answer a few questions, not dozens. Make them count.

Deliverable

Write solve(files) in solution.mjs. files maps each path under data/ to its text contents, including the files in data/reference_case/. The function returns the Q3 report described under Output.

The graders run solve on other Q3 datasets with the same layout, generated with different claims, fee schedule rates and payer mistakes. Those datasets have no data/reference_case/ folder, so solve must not depend on it.

Working on this challenge

  • Questions. Dana decides what goes into the appeal batch and knows the practice's agreements with its payers, and much of that isn't written down in the folder. Ask her with vibrometer ask "your question". You have 7 questions, she answers only what you ask, and a vague question gets a question back.
  • Follow-up. Run vibrometer update from time to time. Anything new from Dana arrives in your folder with a CHANGE.md. The graders always test the latest requirements.

Input files

  • data/contracts/ and data/fee_schedules/: each payer's contract and fee schedules.
  • data/era/: the payers' remittances (X12 835).
  • data/pm_export/: the Q3 charge exports from the practice management system.
  • Some files in data/ have nothing to do with this.

Standards

  • ASC X12 835 Health Care Claim Payment/Advice, version 5010 (implementation guide 005010X221A1), including the interchange envelope and its delimiters, the claim status codes, the payment date and the provider-level adjustments.
  • Claim Adjustment Group Codes, Claim Adjustment Reason Codes (CARC) and Remittance Advice Remark Codes (RARC), the X12 code lists used in CAS and LQ.
  • CMS Medicare Physician Fee Schedule: the MULT PROC and BILAT SURG indicators of the RVU file, the multiple surgery and bilateral surgery payment rules in the Medicare Claims Processing Manual, Chapter 12, and the facility and non-facility settings by place of service.

Output

{
  lines: [
    { payer: "SMHP", claimId: "RO2407160328", dos: "2024-07-16", cpt: "29881", modifiers: ["RT"], units: 1,
      billed: 1271, expected: 596.46, paid: 467.63, patientResp: 119.29, sequestration: 9.54, priorPayer: 0,
      underpaid: 0, reason: null },
    // ...
  ],
  totals: {
    underpaid: 0, underpaidLines: 0,
    byPayer: {
      SMHP: { underpaid: 0, underpaidLines: 0, byReason: { BUNDLING: 0, MPPR: 0, BILATERAL: 0, FEE_SCHEDULE: 0 } },
      LBT: { /* same shape */ }, HHA: { /* same shape */ }, CCH: { /* same shape */ }
    }
  },
  skipped: [{ payer: "CCH", claimId: "RO2407190629", why: "DENIED" } /* , ... */],
  appeals: [{ payer: "LBT", claimId: "RO2407010208", remitDate: "2024-07-18", deadline: "2024-10-16", underpaid: 42.96, reasons: ["BILATERAL"] } /* , ... */]
}

The totals above only show the shape. approved_report.json has the same shape, with zeros for the payers that it doesn't cover.

payer is one of SMHP (Summit Mutual Health Plan), LBT (Lakeshore Benefit Trust), HHA (Harborview Health Alliance) and CCH (Crescent Care Health).

Field Type Meaning
claimId string our claim ID, as in the PM export
dos string date of service, YYYY-MM-DD
cpt string procedure code as billed
modifiers string[] the line's distinct modifiers, sorted ascending
units number units billed
billed number billed charge
expected number the amount that the payer's contract allows for the line
paid number the payer's payment for the line
patientResp number member cost sharing
sequestration number the sequestration reduction
priorPayer number the primary payer's part, on claims where the payer is secondary
underpaid number the amount to appeal for the line, never negative
reason string or null appeal queue: BUNDLING, MPPR, BILATERAL or FEE_SCHEDULE, or null when underpaid is 0
why string NO_REMITTANCE, REVERSED or DENIED
remitDate string the remittance date that the appeal deadline counts from, YYYY-MM-DD
deadline string the last day to file the appeal under the payer's contract, YYYY-MM-DD
reasons string[] the distinct reason values of the claim's underpaid lines, sorted ascending
  • lines has one entry for every reported service line. Sort by payer, then claimId, dos, cpt and the modifiers joined with commas, all as plain ascending string comparisons.
  • Every money field is a number in dollars, rounded to the cent. Compute in whole cents so that sums are exact.
  • totals.underpaid and totals.underpaidLines cover all lines. totals.byPayer has an entry for each of the four payers, and each byReason has all four keys.
  • skipped lists the claims that are in the batch but can't be reported line by line. Sort it by payer, then claimId.
  • appeals has one entry per claim to appeal, with underpaid as the claim's total. Sort it by deadline, then payer, then claimId.

The solution has to be one file that imports nothing except node: built-ins. It gets no file system or network access, and every input arrives through files.

Start this challenge

First, add Vibrometer to Claude Code

You only do this once. In Claude Code, type these two commands:

/plugin marketplace add usesieve/vibrometer-plugin
/plugin install vibrometer@vibrometer

Then read the ticket and start the clock. In Claude Code, from an empty folder, get the ticket (the clock doesn’t start):

/vibrometer:challenge claims-underpayment

When you’re ready to lead the work:

/vibrometer:start

When you’re done:

/vibrometer:submit

Fastest

AI alone, one prompt: 8/27 tests in 7 min.

No entries yet.