The scoring engine lives in
lib/orbitScoreEngine.ts and runs client-side. Results are cached to surgeon_scorecards and refreshed nightly via pg_cron. Both web and iOS platforms read from the cached table.Four pillars
| Pillar | Weight | What it measures |
|---|---|---|
| Profitability | 30% | Profit per case vs. peers in the same procedure cohort |
| Consistency | 25% | Coefficient of variation of case duration per procedure type |
| Schedule Adherence | 25% | Actual vs. booked case duration — did the day go as planned? |
| Availability | 20% | Prep-to-incision gap and surgeon delay rate |
MAD-based scoring (Pillars 1 & 2)
Profitability and Consistency use Median Absolute Deviation instead of standard deviation:Key parameters
| Parameter | Value | Purpose |
|---|---|---|
MAD_BAND | 3 | MADs from median to reach floor/ceiling |
MIN_MAD_PERCENT | 5% | Percentage floor prevents noise amplification |
MIN_ABSOLUTE_MAD_CV | 0.01 | Absolute floor for CV scoring |
MIN_PILLAR_SCORE | 10 | No pillar scores below 10 |
Why 3 MAD bands?
With 5–15 surgeons (typical ASC size), being 2 MADs from median just means you’re the best or worst — not necessarily an outlier. 3 MAD provides useful differentiation:| Distance | Score |
|---|---|
| At median | 50 |
| 1 MAD | ~33 or ~67 |
| 2 MAD | ~17 or ~83 |
| 3 MAD | 10 (floor) or 100 (cap) |
Graduated decay (Pillars 3 & 4)
Schedule Adherence and Availability use direct graduated scoring with no peer comparison:Volume weighting
Scoring is done within procedure-type cohorts (THA vs. THA, not THA vs. TKA), then volume-weighted across the surgeon’s case mix.Composite calculation
Grade thresholds
| Grade | Score | Description |
|---|---|---|
| A | ≥ 80 | Elite — top performer |
| B | ≥ 65 | Strong — above average |
| C | ≥ 50 | Developing — meeting expectations |
| D | < 50 | Needs improvement |
Data requirements
Per case
patient_in_at, patient_out_at, incision_at, prep_drape_complete_at, closing_at, start_time, scheduled_date, procedure_type_id, surgeon_id, or_room_id
Financial
profit — null means skip, 0 means break-even
Settings
start_time_milestone, grace_minutes, floor_minutes, min_procedure_cases, min_case_threshold
Data flow
case_completion_stats→ raw per-case data (41 columns)orbitScoreEngine.ts→ scoring calculation (client-side)surgeon_scorecards→ cached results (refreshed nightly via Edge Function +pg_cron)- Both web and iOS read from
surgeon_scorecards
Improvement plans
The engine generates per-surgeon improvement plans viagenerateImprovementPlan(), identifying weakest pillars, projecting composite improvement, and estimating annual time and dollar savings.
Implementation notes
Why MIN_MAD_PERCENT exists
Why MIN_MAD_PERCENT exists
When all surgeons perform identically (MAD approaches zero), the score formula would amplify tiny differences into huge score swings. The 5% floor prevents noise amplification while still allowing differentiation.
Why pillar scores are floored at 10
Why pillar scores are floored at 10
A floor of 10 prevents zero or negative pillar scores, which would make weighted composite calculations unintuitive. Even the worst performer gets at least 10 points per pillar.
Testing the scoring engine
Testing the scoring engine
The scoring engine is a pure function — given
case_completion_stats rows and settings, it produces deterministic scores. Write unit tests with known input data and verify expected pillar scores and composite output.