Skip to main content
Version: 3.0.1

Run File Schema Guide

Purpose and Audience

This guide provides a complete JSON schema reference for PCR thermocycler run files used as test fixtures in the Bengal Tiger diagnostic software project. Run files simulate thermocycler output data and are the primary input for result calculation, quality control, and reporting workflows.

Intended audience:

  • Developers implementing or modifying run file ingestion, parsing, or result calculation logic.
  • QA Engineers creating test fixtures for automated and manual test scenarios.

See the Data Dictionary for domain terminology. See the Testing Guide for writing Behat tests that use run files.


Directory Structure

JSON run files are stored in support_files/BT-xxxx/ directories, organized by Jira issue key.

support_files/
├── BT-5001/
│ ├── BAD_R2.json
│ └── BAD_EFFICIENCY.json
├── BT-5011/
│ ├── ENT_GAPD_141_1.json
│ └── ENT_GAPD_141_2.json
└── ...

JSON Schema

A run file contains four top-level sections: run_info, targets, wells, and observations.

{
"run_info": {
"run_name": "string", // Required: filename
"thermocycler_id": "string", // Required: device ID
"runfile_created_at": "string", // Required: ISO timestamp
"operator": "string", // Optional: operator name
"comment": "string", // Optional: run comment
"tester_note": "string", // Optional: test notes
"file_hash": "string" // Optional: file hash
},
"targets": {
"t1": {
"mix_name": "string", // Required: mix name
"target_name": "string", // Required: target analyte
"auto_baseline": boolean // Required: baseline setting
}
},
"wells": {
"w1": {
"well_number": "string", // Required: plate position
"label": "string", // Required: well label
"well_uuid": "string" // Required: unique ID
}
},
"observations": {
"o1": {
"target": "string", // Required: target name
"readings": [number], // Required: Ct values array
"well_uuid": "string", // Required: well reference
"ct": number, // Optional: cycle threshold
"obs_uuid": "string", // Required: observation ID
"dye": "string", // Optional: fluorescent dye
"dxai_ct": number, // Optional: AI-calculated Ct
"dxai_cls": "string", // Optional: AI classification
"target_threshold": "string", // Optional: threshold value
"quantity": number // Optional: quantification
}
}
}

Section Details

run_info

FieldTypeRequiredDescription
run_namestringYesFilename of the run file
thermocycler_idstringYesDevice serial number
runfile_created_atstringYesISO format: "YYYY-MM-DD HH:MM:SS"
operatorstringNoOperator/user name
commentstringNoGeneral run comment
tester_notestringNoNotes for testing purposes
file_hashstringNoMD5 hash of file
UserColumn_3stringNoCustom user column

targets

Map of target definitions (key: arbitrary ID like "t1", "t2"):

FieldTypeRequiredDescription
mix_namestringYesName of the mix (e.g., "NOR1", "CMV", "BKV")
target_namestringYesTarget analyte name
auto_baselinebooleanYesWhether auto-baseline is enabled

wells

Map of well definitions (key: arbitrary ID like "w1", "w2"):

FieldTypeRequiredDescription
well_numberstringYesPlate position (e.g., "A1", "C3", "F5")
labelstringYesWell label with encoded metadata
well_uuidstringYesUnique well identifier
error_codestringNoError code if applicable
concentration_factornumberNoConcentration factor
tissue_weightnumberNoTissue weight if applicable
tester_notestringNoWell-specific test notes

observations

Map of observation readings (key: arbitrary ID like "o116", "o117"):

FieldTypeRequiredDescription
targetstringYesTarget name (matches targets.target_name)
readingsarrayYesArray of fluorescence readings
well_uuidstringYesReference to wells.well_uuid
ctnumberNoCycle threshold value
obs_uuidstringYesUnique observation ID
dyestringNoFluorescent dye (FAM, CALORANGE, ROX)
dxai_ctnumberNoAI-predicted Ct value
dxai_clsstringNoAI classification (Pos, Neg)
target_thresholdstringNoDetection threshold
quantitynumberNoQuantification value

Well Label Format

Well labels use a pipe-delimited format with key-value pairs:

|T:MIX_NAME|R:SAMPLE_ROLE|A:ACCESSION|E:EXTRACTION|D:DATE|C:CONTROL|

Label Keys

KeyMeaningValues
T:Target/MixNOR1, NOR2, CMV, BKV, GAPDH, etc.
R:Sample RolePatient, NEC, POS, Hi POS, Lo POS, S2, S4, S6, etc.
A:AccessionAccession number
E:ExtractionExtraction ID
D:DateDate value
C:ControlControl ID

Examples

|T:NOR2|R:S2|                           // Standard control
|T:NOR1|R:Patient|A:12345| // Patient sample
|T:CMV|R:NEC|E:EXT001| // Negative extraction control
|T:BKV|R:Hi POS| // High positive control

Sample Roles Reference

RoleDescription
PatientPatient sample
NECNegative Extraction Control
POSPositive Control
Hi POSHigh Positive Control
Lo POSLow Positive Control
S2, S4, S6Standard controls (numbered)
CC1, CC2Calibration controls
NCNegative Control
PECPositive Extraction Control

Complete Template

{
"run_info": {
"run_name": "TEST_RUN.json",
"thermocycler_id": "275000953",
"tester_note": "Test description here",
"operator": "Test Operator",
"comment": "Run comment",
"runfile_created_at": "2024-01-15 14:00:00"
},
"targets": {
"t1": {
"mix_name": "NOR1",
"target_name": "NOR1",
"auto_baseline": true
},
"t2": {
"mix_name": "NOR1",
"target_name": "IC",
"auto_baseline": true
},
"t3": {
"mix_name": "NOR1",
"target_name": "REFERENCE",
"auto_baseline": true
}
},
"wells": {
"w1": {
"well_number": "A1",
"label": "|T:NOR1|R:Patient|A:12345|",
"well_uuid": "w1"
},
"w2": {
"well_number": "A2",
"label": "|T:NOR1|R:NEC|",
"well_uuid": "w2"
},
"w3": {
"well_number": "A3",
"label": "|T:NOR1|R:POS|",
"well_uuid": "w3"
}
},
"observations": {
"o1": {
"target": "NOR1",
"readings": [
2.147, 2.110, 2.104, 2.097, 2.099, 2.098, 2.095, 2.089,
2.089, 2.090, 2.090, 2.090, 2.089, 2.091, 2.086, 2.090,
2.089, 2.085, 2.085, 2.085, 2.091, 2.090, 2.080, 2.085,
2.086, 2.088, 2.087, 2.081, 2.086, 2.078, 2.088, 2.078,
2.072, 2.069, 2.066, 2.065, 2.065, 2.063, 2.058, 2.059
],
"ct": 35.5000,
"obs_uuid": "o1",
"well_uuid": "w1",
"dye": "FAM",
"dxai_ct": 35.5000,
"dxai_cls": "Pos",
"target_threshold": "0.1",
"quantity": 3570000
},
"o2": {
"target": "IC",
"readings": [
1.126, 1.120, 1.119, 1.116, 1.114, 1.117, 1.118, 1.118,
1.118, 1.117, 1.117, 1.124, 1.121, 1.122, 1.123, 1.125,
1.124, 1.126, 1.126, 1.126, 1.127, 1.129, 1.130, 1.130,
1.133, 1.140, 1.148, 1.166, 1.193, 1.235, 1.297, 1.371,
1.450, 1.513, 1.590, 1.653, 1.705, 1.766, 1.818, 1.852
],
"ct": 20,
"obs_uuid": "o2",
"well_uuid": "w1",
"dye": "CALORANGE",
"dxai_ct": null,
"dxai_cls": "Pos",
"target_threshold": "0.1",
"quantity": null
}
}
}

Readings Array

The readings array contains fluorescence values from each PCR cycle (typically 40 cycles).

  • Length: Usually 40 values (one per cycle)
  • Type: Float values
  • Pattern: Shows amplification curve
    • Positive sample: Values increase significantly in later cycles
    • Negative sample: Values remain relatively flat

Naming Conventions

File Names

{ASSAY}_{ACCESSION}.json
{ASSAY}_{ACCESSION}_{RUN_NUMBER}.json
{CONDITION}_{DESCRIPTION}.json

Examples:

  • NORO_123.json - Norovirus test, accession 123
  • NORO_123_2.json - Second run for same accession
  • BAD_R2.json - Test for bad R2 correlation
  • SYSTEMIC_INHIBITION_1.json - Inhibition test case 1
  • Westgard_rules_12s.json - Westgard rule test

Directory Organization

Each test case directory should match the Jira issue:

support_files/BT-5001/    # Test BT-5001 support files
support_files/BT-5011/ # Test BT-5011 support files

Best Practices

  1. Use meaningful file names - Include assay type and scenario
  2. Include tester_note - Document what the file is testing
  3. Match well_uuid to observation well_uuid - Ensure proper linking
  4. Use realistic readings - Base on actual PCR curve patterns
  5. Set appropriate Ct values - Match classification expectations
  6. Use correct sample roles - Follow the role conventions
  7. Organize by Jira issue - Keep files in BT-xxxx directories