{
 "about": {
  "method": "sample-normalisation 1.0.0: dye = V/k; s = m/c (or C*V/c); withheld when s > V - V/k; transfers below 2 µL flagged",
  "oracle": "Python 3.13.16 fractions (standard library); written from the method in this script's docstring",
  "recordedOn": "2026-10-17"
 },
 "cases": [
  {
   "name": "panel-defaults-mass",
   "about": "The panel's five example lysates, 20 µg in 20 µL with 4x dye: two withheld, one below 2 µL",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "Sample_1",
      "concentrationMgPerMl": 2.45
     },
     {
      "sampleId": "Sample_2",
      "concentrationMgPerMl": 4.8
     },
     {
      "sampleId": "Sample_3",
      "concentrationMgPerMl": 1.15
     },
     {
      "sampleId": "Sample_4",
      "concentrationMgPerMl": 0.65
     },
     {
      "sampleId": "Sample_5",
      "concentrationMgPerMl": 45.0
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "Sample_1",
      "status": "planned",
      "sampleUl": 8.16326530612245,
      "diluentUl": 6.836734693877552,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": []
     },
     {
      "sampleId": "Sample_2",
      "status": "planned",
      "sampleUl": 4.166666666666667,
      "diluentUl": 10.833333333333334,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": []
     },
     {
      "sampleId": "Sample_3",
      "status": "too-dilute",
      "requiredSampleUl": 17.39130434782609,
      "availableUl": 15.0,
      "deliverableLimit": 17.25
     },
     {
      "sampleId": "Sample_4",
      "status": "too-dilute",
      "requiredSampleUl": 30.769230769230766,
      "availableUl": 15.0,
      "deliverableLimit": 9.75
     },
     {
      "sampleId": "Sample_5",
      "status": "planned",
      "sampleUl": 0.4444444444444444,
      "diluentUl": 14.555555555555555,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": [
       "sample"
      ]
     }
    ],
    "totals": {
     "sampleUl": 12.77437641723356,
     "diluentUl": 32.22562358276644,
     "dyeUl": 15.0
    }
   }
  },
  {
   "name": "exact-fit",
   "about": "s equals the volume left after dye: planned with zero diluent",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 15,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "fit",
      "concentrationMgPerMl": 1.0
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "fit",
      "status": "planned",
      "sampleUl": 15.0,
      "diluentUl": 0.0,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 15.0,
      "finalConcMgPerMl": 0.75,
      "belowMinimum": []
     }
    ],
    "totals": {
     "sampleUl": 15.0,
     "diluentUl": 0.0,
     "dyeUl": 5.0
    }
   }
  },
  {
   "name": "just-too-dilute",
   "about": "s exceeds the room by a fraction: withheld",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 15,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "over",
      "concentrationMgPerMl": 0.9999
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "over",
      "status": "too-dilute",
      "requiredSampleUl": 15.001500150015001,
      "availableUl": 15.0,
      "deliverableLimit": 14.9985
     }
    ],
    "totals": {
     "sampleUl": 0.0,
     "diluentUl": 0.0,
     "dyeUl": 0.0
    }
   }
  },
  {
   "name": "minimum-boundary",
   "about": "Sample exactly 2 µL is not flagged; 1.999 µL is",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "two",
      "concentrationMgPerMl": 10.0
     },
     {
      "sampleId": "under",
      "concentrationMgPerMl": 10.005
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "two",
      "status": "planned",
      "sampleUl": 2.0,
      "diluentUl": 13.0,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": []
     },
     {
      "sampleId": "under",
      "status": "planned",
      "sampleUl": 1.9990004997501247,
      "diluentUl": 13.000999500249875,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": [
       "sample"
      ]
     }
    ],
    "totals": {
     "sampleUl": 3.9990004997501245,
     "diluentUl": 26.000999500249875,
     "dyeUl": 10.0
    }
   }
  },
  {
   "name": "small-diluent",
   "about": "A positive diluent below 2 µL is flagged",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "dil",
      "concentrationMgPerMl": 1.5
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "dil",
      "status": "planned",
      "sampleUl": 13.333333333333334,
      "diluentUl": 1.6666666666666667,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": [
       "diluent"
      ]
     }
    ],
    "totals": {
     "sampleUl": 13.333333333333334,
     "diluentUl": 1.6666666666666667,
     "dyeUl": 5.0
    }
   }
  },
  {
   "name": "six-x-dye",
   "about": "6x dye in 30 µL",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 30,
     "dyeFactor": 6
    },
    "samples": [
     {
      "sampleId": "a",
      "concentrationMgPerMl": 2.0
     },
     {
      "sampleId": "b",
      "concentrationMgPerMl": 3.3
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 25.0,
    "rows": [
     {
      "sampleId": "a",
      "status": "planned",
      "sampleUl": 10.0,
      "diluentUl": 15.0,
      "dyeUl": 5.0,
      "totalUl": 30.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 0.6666666666666666,
      "belowMinimum": []
     },
     {
      "sampleId": "b",
      "status": "planned",
      "sampleUl": 6.0606060606060606,
      "diluentUl": 18.939393939393938,
      "dyeUl": 5.0,
      "totalUl": 30.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 0.6666666666666666,
      "belowMinimum": []
     }
    ],
    "totals": {
     "sampleUl": 16.060606060606062,
     "diluentUl": 33.93939393939394,
     "dyeUl": 10.0
    }
   }
  },
  {
   "name": "no-dye",
   "about": "No dye: the whole load is sample and diluent",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 10,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 15,
     "dyeFactor": 0
    },
    "samples": [
     {
      "sampleId": "a",
      "concentrationMgPerMl": 2.0
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 0.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "a",
      "status": "planned",
      "sampleUl": 5.0,
      "diluentUl": 10.0,
      "dyeUl": 0.0,
      "totalUl": 15.0,
      "massUg": 10.0,
      "finalConcMgPerMl": 0.6666666666666666,
      "belowMinimum": []
     }
    ],
    "totals": {
     "sampleUl": 5.0,
     "diluentUl": 10.0,
     "dyeUl": 0.0
    }
   }
  },
  {
   "name": "concentration-mode",
   "about": "Target 1 mg/mL in the loaded 20 µL with 4x dye",
   "input": {
    "config": {
     "mode": "concentration",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "a",
      "concentrationMgPerMl": 2.0
     },
     {
      "sampleId": "b",
      "concentrationMgPerMl": 1.2
     },
     {
      "sampleId": "c",
      "concentrationMgPerMl": 7.0
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "a",
      "status": "planned",
      "sampleUl": 10.0,
      "diluentUl": 5.0,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": []
     },
     {
      "sampleId": "b",
      "status": "too-dilute",
      "requiredSampleUl": 16.666666666666668,
      "availableUl": 15.0,
      "deliverableLimit": 0.8999999999999999
     },
     {
      "sampleId": "c",
      "status": "planned",
      "sampleUl": 2.857142857142857,
      "diluentUl": 12.142857142857142,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": []
     }
    ],
    "totals": {
     "sampleUl": 12.857142857142858,
     "diluentUl": 17.142857142857142,
     "dyeUl": 10.0
    }
   }
  },
  {
   "name": "invalid-rows",
   "about": "Zero and negative concentrations are invalid rows; the valid one is planned",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "zero",
      "concentrationMgPerMl": 0
     },
     {
      "sampleId": "neg",
      "concentrationMgPerMl": -1
     },
     {
      "sampleId": "ok",
      "concentrationMgPerMl": 4.0
     }
    ]
   },
   "expected": {
    "ok": true,
    "dyeUl": 5.0,
    "availableUl": 15.0,
    "rows": [
     {
      "sampleId": "zero",
      "status": "invalid"
     },
     {
      "sampleId": "neg",
      "status": "invalid"
     },
     {
      "sampleId": "ok",
      "status": "planned",
      "sampleUl": 5.0,
      "diluentUl": 10.0,
      "dyeUl": 5.0,
      "totalUl": 20.0,
      "massUg": 20.0,
      "finalConcMgPerMl": 1.0,
      "belowMinimum": []
     }
    ],
    "totals": {
     "sampleUl": 5.0,
     "diluentUl": 10.0,
     "dyeUl": 5.0
    }
   }
  },
  {
   "name": "refuse-load",
   "about": "Zero load volume refuses the batch",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 0,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "a",
      "concentrationMgPerMl": 2.0
     }
    ]
   },
   "expected": {
    "ok": false,
    "problem": "load-volume"
   }
  },
  {
   "name": "refuse-target",
   "about": "Zero target mass refuses the batch",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 0,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 4
    },
    "samples": [
     {
      "sampleId": "a",
      "concentrationMgPerMl": 2.0
     }
    ]
   },
   "expected": {
    "ok": false,
    "problem": "target"
   }
  },
  {
   "name": "refuse-dye",
   "about": "A 1x dye stock would fill the well: refused",
   "input": {
    "config": {
     "mode": "mass",
     "targetMassUg": 20,
     "targetConcMgPerMl": 1,
     "loadVolumeUl": 20,
     "dyeFactor": 1
    },
    "samples": [
     {
      "sampleId": "a",
      "concentrationMgPerMl": 2.0
     }
    ]
   },
   "expected": {
    "ok": false,
    "problem": "dye-factor"
   }
  }
 ]
}
