Production testing catches defects, but the real value comes from closing the loop: diagnosing failures, repairing units, and retesting them. TofuPilot links every retest to the original serial number so you get a complete history of each unit's journey through your repair process.
The Repair Loop
A typical repair workflow follows this cycle: a unit fails a test, a technician diagnoses the root cause, performs a repair, and the unit goes back through testing. Without structured data, this history gets lost in spreadsheets or paper logs.
TofuPilot solves this by keying everything to the serial number. Every test run against the same DUT automatically appears in its unit history. You don't need special configuration. Just use the same serial number when retesting.
Structure Your Initial Test
Start with a standard OpenHTF test that measures your DUT and uploads results to TofuPilot.
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("output_voltage")
.in_range(minimum=4.8, maximum=5.2)
.with_units(units.VOLT),
htf.Measurement("current_draw")
.in_range(minimum=0.095, maximum=0.105)
.with_units(units.AMPERE),
)
def functional_check(test):
test.measurements.output_voltage = 5.05
test.measurements.current_draw = 0.1012
def main():
test = htf.Test(functional_check)
with TofuPilot(test):
test.execute(test_start=lambda: "SN-20260312-001")
if __name__ == "__main__":
main()When this test fails, the unit enters your repair queue.
Record Repair Actions as Metadata
After a technician diagnoses and repairs the unit, capture that context in the retest. Use phase-level metadata to record what was found and what was done.
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("repair_code"),
htf.Measurement("failure_category"),
htf.Measurement("repair_action"),
)
def record_repair_info(test):
# These values come from your repair technician's input
test.measurements.repair_code = "RC-042"
test.measurements.failure_category = "solder_bridge"
test.measurements.repair_action = "reworked_U3_solder_joints"
@htf.measures(
htf.Measurement("output_voltage")
.in_range(minimum=4.8, maximum=5.2)
.with_units(units.VOLT),
htf.Measurement("current_draw")
.in_range(minimum=0.095, maximum=0.105)
.with_units(units.AMPERE),
)
def functional_recheck(test):
test.measurements.output_voltage = 5.01
test.measurements.current_draw = 0.1003
def main():
test = htf.Test(record_repair_info, functional_recheck)
with TofuPilot(test):
# Same serial number links this retest to the original failure
test.execute(test_start=lambda: "SN-20260312-001")
if __name__ == "__main__":
main()The record_repair_info phase stores the diagnosis and repair action alongside the retest measurements. This data shows up in TofuPilot's run detail view, tied to the same unit.
Use Failure Categories Consistently
Define a standard set of failure categories and repair codes across your team. Consistent naming lets you filter and aggregate repair data later.
Common failure categories for PCBA testing:
| Category | Description |
|---|---|
solder_bridge | Unintended solder connection between pads |
cold_joint | Insufficient solder wetting |
missing_component | Component not placed during assembly |
wrong_value | Incorrect component value populated |
damaged_component | Component damaged during handling or reflow |
pcb_defect | Board-level issue (trace crack, via failure) |
Store these as string measurements so they're searchable in TofuPilot.
Track Multiple Repair Cycles
Some units need more than one repair attempt. Each retest creates a new run against the same serial number. TofuPilot's unit history view shows the full chain: initial fail, first repair attempt, second repair attempt, and final pass.
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("repair_code"),
htf.Measurement("failure_category"),
htf.Measurement("repair_action"),
htf.Measurement("repair_cycle"),
)
def record_repair_info(test):
test.measurements.repair_code = "RC-043"
test.measurements.failure_category = "cold_joint"
test.measurements.repair_action = "reflowed_C12_pads"
test.measurements.repair_cycle = 2
@htf.measures(
htf.Measurement("output_voltage")
.in_range(minimum=4.8, maximum=5.2)
.with_units(units.VOLT),
htf.Measurement("current_draw")
.in_range(minimum=0.095, maximum=0.105)
.with_units(units.AMPERE),
)
def functional_recheck(test):
test.measurements.output_voltage = 4.98
test.measurements.current_draw = 0.0998
def main():
test = htf.Test(record_repair_info, functional_recheck)
with TofuPilot(test):
test.execute(test_start=lambda: "SN-20260312-001")
if __name__ == "__main__":
main()Adding a repair_cycle measurement makes it easy to count how many attempts each unit needed.
Analyze Repair Trends in TofuPilot
Once your repair data flows into TofuPilot, the dashboard gives you what you need without writing analysis scripts.
Unit history shows every test run for a serial number in chronological order. You can see exactly when a unit failed, what was repaired, and whether the retest passed.
Failure Pareto charts rank your failure categories by frequency. If solder_bridge dominates, that's a signal to investigate your reflow profile or stencil design.
FPY trends reflect your repair effectiveness over time. A rising FPY after process changes confirms the fix is working. A unit that keeps failing the same test after multiple repairs may point to a deeper design issue.
Separate Test Procedures for Initial Test and Retest
For traceability, consider using distinct procedure names for initial tests and retests. TofuPilot groups runs by procedure, so this separation makes reporting cleaner.
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("repair_code"),
htf.Measurement("repair_action"),
)
def record_repair_info(test):
test.measurements.repair_code = "RC-042"
test.measurements.repair_action = "reworked_U3_solder_joints"
@htf.measures(
htf.Measurement("output_voltage")
.in_range(minimum=4.8, maximum=5.2)
.with_units(units.VOLT),
)
def functional_recheck(test):
test.measurements.output_voltage = 5.02
def main():
test = htf.Test(record_repair_info, functional_recheck)
with TofuPilot(test, procedure_name="PCBA Functional Test - Retest"):
test.execute(test_start=lambda: "SN-20260312-001")
if __name__ == "__main__":
main()This way you can compare FPY between initial tests and retests independently, while the unit history still ties everything together under one serial number.