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
| Field | Type | Required | Description |
|---|---|---|---|
run_name | string | Yes | Filename of the run file |
thermocycler_id | string | Yes | Device serial number |
runfile_created_at | string | Yes | ISO format: "YYYY-MM-DD HH:MM:SS" |
operator | string | No | Operator/user name |
comment | string | No | General run comment |
tester_note | string | No | Notes for testing purposes |
file_hash | string | No | MD5 hash of file |
UserColumn_3 | string | No | Custom user column |
targets
Map of target definitions (key: arbitrary ID like "t1", "t2"):
| Field | Type | Required | Description |
|---|---|---|---|
mix_name | string | Yes | Name of the mix (e.g., "NOR1", "CMV", "BKV") |
target_name | string | Yes | Target analyte name |
auto_baseline | boolean | Yes | Whether auto-baseline is enabled |
wells
Map of well definitions (key: arbitrary ID like "w1", "w2"):
| Field | Type | Required | Description |
|---|---|---|---|
well_number | string | Yes | Plate position (e.g., "A1", "C3", "F5") |
label | string | Yes | Well label with encoded metadata |
well_uuid | string | Yes | Unique well identifier |
error_code | string | No | Error code if applicable |
concentration_factor | number | No | Concentration factor |
tissue_weight | number | No | Tissue weight if applicable |
tester_note | string | No | Well-specific test notes |
observations
Map of observation readings (key: arbitrary ID like "o116", "o117"):
| Field | Type | Required | Description |
|---|---|---|---|
target | string | Yes | Target name (matches targets.target_name) |
readings | array | Yes | Array of fluorescence readings |
well_uuid | string | Yes | Reference to wells.well_uuid |
ct | number | No | Cycle threshold value |
obs_uuid | string | Yes | Unique observation ID |
dye | string | No | Fluorescent dye (FAM, CALORANGE, ROX) |
dxai_ct | number | No | AI-predicted Ct value |
dxai_cls | string | No | AI classification (Pos, Neg) |
target_threshold | string | No | Detection threshold |
quantity | number | No | Quantification 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
| Key | Meaning | Values |
|---|---|---|
T: | Target/Mix | NOR1, NOR2, CMV, BKV, GAPDH, etc. |
R: | Sample Role | Patient, NEC, POS, Hi POS, Lo POS, S2, S4, S6, etc. |
A: | Accession | Accession number |
E: | Extraction | Extraction ID |
D: | Date | Date value |
C: | Control | Control 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
| Role | Description |
|---|---|
Patient | Patient sample |
NEC | Negative Extraction Control |
POS | Positive Control |
Hi POS | High Positive Control |
Lo POS | Low Positive Control |
S2, S4, S6 | Standard controls (numbered) |
CC1, CC2 | Calibration controls |
NC | Negative Control |
PEC | Positive 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 123NORO_123_2.json- Second run for same accessionBAD_R2.json- Test for bad R2 correlationSYSTEMIC_INHIBITION_1.json- Inhibition test case 1Westgard_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
- Use meaningful file names - Include assay type and scenario
- Include tester_note - Document what the file is testing
- Match well_uuid to observation well_uuid - Ensure proper linking
- Use realistic readings - Base on actual PCR curve patterns
- Set appropriate Ct values - Match classification expectations
- Use correct sample roles - Follow the role conventions
- Organize by Jira issue - Keep files in BT-xxxx directories