A test station is documented when anyone on the team can rebuild it on a new PC and change a limit without calling the person who built it. That's the whole test, and most stations fail it. This guide gives the hand-over checklist, a README template, and the reason a text-based station layout does most of the work for you.
The Goal
Two questions decide whether a station is handed over. Can a colleague rebuild it on the spare PC from what's in the repo? Can they change one limit, release it, and prove afterwards which units ran the old version and which ran the new?
Everything else is nice to have. A wiring photo helps. A 40-page validation report helps less than the two answers above, because nobody opens it at 2 a.m. when the fixture is down.
Bus factor is the score across the plant. This guide is how you raise it for one station, before or after the person who built it leaves.
The Hand-Over Checklist
Work down the table. "Where it lives" is one place, not "the share drive and also Marc's laptop".
| Item | Where it lives | Done when |
|---|---|---|
| Sequence of phases and their order | procedure.yaml, under main: | A colleague reads the list and can say what the station does without opening a .py file |
| Limits and units per measurement | procedure.yaml, under validators: | Every limit has a comment naming its source (spec section, datasheet table, or the engineer and the date) |
| Instrument list, VISA addresses, firmware | plugs/*.py defaults, listed again in README.md | *IDN? from each plug matches the README line |
| Wiring | docs/wiring.pdf and a bench photo in docs/, referenced from the README | The spare PC can be cabled from the picture alone |
| Fixture | Drawing number, revision, pogo pin map in docs/ | The fixture on the bench carries the same revision as the README |
| Station PC setup | README.md, "How to run" | A fresh Windows install reaches a green run from the README, no phone call |
| Release process | README.md, "How to release", plus Git tags | The tag on the station PC matches version: in procedure.yaml |
| Results history | TofuPilot, per unit and per station | Anyone can open a serial number and see every run with the procedure version that produced it |
| Calibration dates | README.md, one table | Every next-due date is in the future |
| Known failure modes | README.md, one table | The top three failures from the TofuPilot Pareto are listed with their usual cause |
Station README Template
Copy this into the station repo. Fill it in one sitting; a half-finished README ages badly.
README.md58 lines
# FCT Station 3, PCBA-100Owner: test engineering (the team, not a person). Last reviewed: 2026-09-15.## WiringSee `docs/wiring.pdf`, revision C. Bench photo in `docs/bench.jpg`.| From | To | Cable ||---|---|---|| DMM HI / LO | Fixture J3 pins 1, 2 | 1 m banana, red / black || PSU CH1 | Fixture J1 | 18 AWG, 0.5 m || Station PC | DUT console | USB to UART, COM3 |## Instruments| Instrument | VISA address | Firmware | Calibration due ||---|---|---|---|| Keysight 34465A DMM | TCPIP::192.168.1.100::INSTR | A.03.03 | 2027-02-01 || Keysight E36313A PSU | TCPIP::192.168.1.101::INSTR | 1.0.6 | 2027-02-01 || DUT console | ASRL3::INSTR (COM3, 115200 8N1) | n/a | n/a |## FixtureDrawing FIX-0042 rev B. Pogo pin map in `docs/FIX-0042-pinmap.pdf`.Spare pogo pins: drawer 2, part P-0910.## LimitsAll limits live in `procedure.yaml`. Each one carries a comment with itssource. Don't change a limit without changing the comment.| Measurement | Limit | Source ||---|---|---|| rail_3v3 | 3.2 to 3.4 V | PCBA-100 spec rev 4, section 5.1 || idle_current | <= 45 mA | MCU datasheet table 12, plus 20 % margin |## How to run curl -fsSL https://www.tofupilot.app/install | sh git clone <repo url> && cd fct-station-3 tofupilot run ./procedure.yaml --uploadRuns land in TofuPilot under the procedure "PCBA-100 FCT", one page per unit.## How to release1. Branch, change, open a pull request. One reviewer from test engineering.2. Bump `version:` in `procedure.yaml`.3. Merge, tag `vX.Y.Z`, pull the tag on the station PC.4. Run three golden units. Compare their runs in TofuPilot with the previous tag.## Known failure modes| Symptom | Usual cause | Fix ||---|---|---|| rail_3v3 reads 0.00 V | Pogo pin 7 worn | Replace, part P-0910 || DMM timeout on the first unit of the day | DMM in standby | Power cycle the DMM |The README is short on purpose. Four other things carry the rest.
Why the Framework Layout Does Most of This
A TofuPilot Framework station is a folder: procedure.yaml, phases/*.py, plugs/*.py. All text, all under Git. Each part carries exactly one kind of information, so the README only has to cover what none of them can.
| Carrier | What it holds | What that replaces |
|---|---|---|
procedure.yaml | The sequence, the unit fields, every measurement with its unit and limits | The test spec spreadsheet and the "which step runs first" question |
plugs/*.py | VISA addresses, SCPI strings, timeouts, close() | The instrument list scribbled on the fixture |
| Git | Who changed which limit, when, reviewed by whom; git blame on the line | The change log tab nobody updates |
| TofuPilot | Every run per unit and per station, with the procedure version, FPY, Cpk, Pareto | The TDMS folder on the station's local disk |
What's left for the README is physical: wiring, fixture, calibration, the failure modes an operator sees. That's why the template above fits on one screen.
procedure.yaml as Living Documentation
Treat the procedure as the spec. A reviewer who can't read Python can still read this file, and a limit change is a one-line diff with a comment.
procedure.yaml39 lines
# The procedure is the spec. Every limit names its source in a comment.name: PCBA-100 FCTversion: 2.3.0unit: serial_number: default_value: "SN-000001" part_number: default_value: "PCBA-100"plugs: - name: dmm python: plugs.dmm:Multimeter # 34465A at 192.168.1.100 - name: psu python: plugs.psu:PowerSupply # E36313A at 192.168.1.101main: - key: power_rails name: Power Rails python: phases.power_rails measurements: - name: rail_3v3 unit: V validators: - operator: ">=" expected_value: 3.2 # PCBA-100 spec rev 4, section 5.1 - operator: "<=" expected_value: 3.4 # same - key: idle_current name: Idle Current python: phases.idle_current depends_on: [power_rails] measurements: - name: idle_current unit: mA validators: - operator: "<=" expected_value: 45 # MCU datasheet table 12, plus 20 % marginWhen quality asks "why 45 mA", the answer is on the line. When they ask "since when", git log -p procedure.yaml answers. When they ask "which units shipped with 50 mA", the run pages in TofuPilot carry the procedure version, so the answer is a filter, not an archaeology project.
What a LabVIEW Project Makes Hard Here
None of this is impossible in LabVIEW. It's more work, for three concrete reasons.
VIs are binary. A diff needs NI's compare tool and a licensed seat, so reviews happen by opening both versions side by side, and git blame on a limit isn't available. Most teams skip the review.
Limits tend to live inside VIs, or in an Excel file next to the sequence. Either way the spec and the code drift, and the comment naming the datasheet section has nowhere natural to go.
Results tend to land in TDMS or CSV on the station's local disk. Per-unit history means finding the right file on the right PC. When the PC is replaced, the history usually isn't.
A LabVIEW station can still meet the checklist. Someone has to write the README by hand, keep the limits table in sync by hand, and copy the results somewhere shared by hand. The Python layout makes those three automatic, which is the difference between a hand-over that happens and one that's planned.
A Five-Minute Review Before Every Release
Run this before tagging. Five minutes, every time, by the reviewer and not the author.
- Open the pull request diff. Every changed limit has a changed comment on the same line.
version:inprocedure.yamlwent up.- The README instrument table still matches
*IDN?from each plug. Run them. - Three golden units passed on the branch, and their runs in TofuPilot sit within the previous version's histograms.
- The tag name matches
version:. Pull it on the station PC, not a copy of the folder.
If step 4 fails, the change is real and needs a conversation with quality before it ships. That's the review doing its job.
Start With One Station
Pick the station with the highest volume, or the one only one person can open. Rebuild it in Python with the TofuPilot Framework, then run it side by side with the LabVIEW version on the same units for two weeks. Write the README on day one and keep it true; the onboarding guide uses that README as the new engineer's first task.
# Install the CLI, run a procedure locally, then run it with upload.curl -fsSL https://www.tofupilot.app/install | shtofupilot run ./procedure.yamltofupilot run ./procedure.yaml --uploadThe LabVIEW migration guide covers the rebuild step by step, and the Framework page covers the layout. The Lab tier is free, and tofupilot run works with no account.
