Ongoing reliability testing (ORT) pulls a sample of units from production and stress-tests them continuously, to catch reliability problems that end-of-line functional test cannot see. FQC tells you a unit works today. ORT tells you whether this month's units still work after 500 hours at 55 °C.
ORT vs the Other Reliability Tests
| Test | When | Sample | Purpose |
|---|---|---|---|
| HALT | Design phase | A few units | Find design margins by stressing to destruction |
| HASS | Production ramp | Every unit or a large sample | Screen out infant mortality with a proven stress profile |
| ORT | Ongoing production | Small sample, continuously | Detect reliability drift in the running process |
| Burn-in | Production | Every unit or a sample | Precipitate early-life failures before shipment |
The distinguishing feature of ORT is that it never ends. It runs for the life of the product, on units drawn from normal production, and it exists to answer one question: has anything about the product or the process changed in a way that will show up in the field in six months?
What ORT Catches That FQC Does Not
- Supplier substitutions. A capacitor vendor change passes functional test and fails at temperature 300 hours in.
- Process drift. Reflow profile drifts, solder joints get marginal, thermal cycling finds them.
- Wear-out mechanisms. Connector insertion cycles, relay contacts, fan bearings, battery cycling.
- Firmware regressions under duration. Memory leaks and counter rollovers that no 90-second EOL test will ever reach.
Designing an ORT Program
| Decision | Typical choice | Notes |
|---|---|---|
| Sample size | 2-10 units per week | Enough to detect a shift, small enough to afford |
| Sampling method | Random across shifts and lines | Systematic sampling hides line-specific problems |
| Duration | 500-1000 hours, continuous | Long enough to reach the wear-out region |
| Stress profile | Elevated temperature, thermal cycling, duty cycling | Representative of field use, accelerated |
| Measurement interval | Every 24 hours, plus at entry and exit | Frequent enough to see drift, not so frequent it dominates |
| Exit criteria | Zero failures, or a defined failure rate | Defined before the program starts, not after a failure |
Implementing ORT as a Procedure
ORT has a shape that suits a declarative procedure: the same measurements, repeated on a schedule, on a unit that already exists in the system.
procedure.yaml48 lines
name: ORT Checkpointversion: 1.0.0description: 24-hour ORT checkpoint measurement, per REL-220unit: serial_number: default_value: "PCBA000001" part_number: default_value: "PCBA-100"plugs: - name: chamber python: plugs.chamber:Chamber - name: dmm python: plugs.dmm:Multimetermain: - name: Chamber Conditions python: phases.chamber_conditions measurements: - name: Chamber Temperature unit: "°C" description: Soak temperature at time of measurement validators: - operator: ">=" expected_value: 54.0 - operator: "<=" expected_value: 56.0 - name: Functional Checkpoint python: phases.functional_checkpoint measurements: - name: Output Voltage unit: V description: Same limits as EOL functional test validators: - operator: ">=" expected_value: 4.75 - operator: "<=" expected_value: 5.25 - name: Quiescent Current unit: mA validators: - operator: "<=" expected_value: 45.0 - name: Still Booting validators: - operator: "==" expected_value: truedef chamber_conditions(measurements, chamber): """Record the actual soak conditions, not the setpoint.""" measurements.chamber_temperature = chamber.read_temperature()def functional_checkpoint(measurements, dmm): """Re-run the EOL measurements on a unit that has been soaking.""" measurements.output_voltage = dmm.measure_dc_voltage() measurements.quiescent_current = dmm.measure_dc_current_ma() measurements.still_booting = Trueclass Chamber: def read_temperature(self) -> float: return 55.1 # Replace with a real chamber queryclass Multimeter: def measure_dc_voltage(self) -> float: return 5.02 # Replace with a real instrument read def measure_dc_current_ma(self) -> float: return 41.3 # Replace with a real instrument readTwo deliberate choices in that procedure:
The functional limits are identical to the end-of-line test. ORT is only meaningful as a comparison against the unit's own EOL result. Same measurement names, same limits, so the drift is visible as a trend on one chart rather than two unrelated numbers.
The chamber conditions are measured, not assumed. A checkpoint taken while the chamber was recovering from a door opening is not comparable to one taken at soak. Recording the actual temperature lets you discard or annotate those points instead of chasing a phantom drift.
Recording the Elapsed Hours
The independent variable in ORT is time under stress, so it has to be on the run.
def record_elapsed(run): """Hours since this unit entered the ORT chamber.""" run.metadata["ort_hours"] = 168 run.metadata["ort_lot"] = "2026-W38"With ort_hours on every run and the same serial number across checkpoints, the unit's history is the reliability curve: open the serial and the measurements are in order from hour 0 to hour 1000.
Running It on a Schedule
An ORT checkpoint has no operator. Resolve the identify prompt from the procedure and pre-bake any UI values so a cron job or scheduler can drive it:
tofupilot run ./procedure.yaml --upload --json --ui-values ui.json --ui-timeout 300Reading the Results
| Signal | What it means | Action |
|---|---|---|
| A single unit fails at hour 700 | Possibly normal wear-out, possibly a real defect | Failure analysis on that unit; check whether its lot is over-represented |
| Every unit from one week drifts | Process or supplier change in that window | Pull the build records for that lot |
| Quiescent current climbs across all units | A wear-out mechanism reaching the population | Compare against the design margin; this is a product problem |
| Chamber temperature out of band | The stress was not applied | Discard the checkpoint, fix the chamber |
| No failures for a year | Either genuinely robust, or the stress is too gentle | Revisit the profile; ORT that never fails may not be testing anything |
That last row is the one most programs get wrong. An ORT program with zero findings over a long period is not automatically good news; it may mean the stress profile is below the level that provokes any real mechanism.
If You Already Run OpenHTF
Existing OpenHTF suites run natively on the TofuPilot CLI. The same checkpoint with decorators:
ort_checkpoint.py22 lines
import openhtf as htffrom openhtf.util import unitsfrom tofupilot.openhtf import upload@htf.measures( htf.Measurement("output_voltage_V") .in_range(4.75, 5.25) .with_units(units.VOLT), htf.Measurement("quiescent_current_mA") .in_range(maximum=45.0) .with_units(units.MILLIAMPERE),)def phase_checkpoint(test): test.measurements.output_voltage_V = 5.02 test.measurements.quiescent_current_mA = 41.3 test.metadata["ort_hours"] = 168test = htf.Test(phase_checkpoint, part_number="PCBA-100")test.add_output_callbacks(upload())test.execute(test_start=lambda: input("Scan ORT unit serial: "))Install with pip install "tofupilot[openhtf]", or skip the callback and run it with tofupilot run.
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Different limits than the EOL test | Drift is invisible because the numbers are not comparable | Reuse the EOL measurement names and limits |
| Sampling only from one line or shift | Line-specific problems never enter the sample | Randomise across lines and shifts |
| Not recording elapsed hours | No reliability curve, just scattered pass/fail | Put ort_hours on every run |
| Assuming the chamber setpoint | Checkpoints taken during recovery pollute the trend | Measure the actual conditions |
| Stopping ORT when yield looks good | The program exists precisely to catch what yield cannot | Keep it running for the product life |
| Discarding failed ORT units without analysis | The most informative unit you have gets thrown away | Failure analysis on every ORT failure |
