IRI Filler Pro — Acceptance Test Plan v1.1 (prototype sign-off)
Purpose: objective checklist for accepting the working prototype (payment milestone gate).
Reference documents: docs/DRIVE_REQUIREMENT.md · docs/HAL_SPEC.md v0.6 · docs/CHAMBER_SELECTION.md · docs/API_CONTRACT.md · firmware repo.
Rule: every test has a MEASURABLE pass criterion. "Works for us" is not a criterion.
Changes in v1.1 (2026-08-10) - A4 is now the governing acceptance test and is measurable. In v1.0 it read "no observable difference", which is an opinion, not a criterion — a milestone could have been invoiced on it without proving anything about injection pressure. - New section F: the three safety-critical mechanical features. None of them can be solved in firmware, and each needs its own line here before the milestone it belongs to. - New section G: the firmware gate. The manufacturer's HAL must pass the supplied automated test suite, which runs on a PC in about a second. - New billing-integrity tests D11–D13 covering behaviour that is easy to get wrong and expensive to get wrong. - The drive principle is the manufacturer's choice (
DRIVE_REQUIREMENT.md). Tests A2, A3 and A6 apply to the motor-cocked spring path; on a different drive they are replaced by the equivalent ready-time and fault-handling tests, agreed in writing beforehand.
A. Drive & mechanics — equivalence with the reference unit
The physical manual IRI device is the reference standard. YAVU supplies a reference unit. All comparisons are run side by side, on the same day, with the same dosing chamber and the same product, by the same operator, on the same target.
| # | Test | Pass criterion |
|---|---|---|
| A4 | Injection performance vs. the reference unit. 10 shots each, alternating, into a standardised gel or skin surrogate. Measure penetration depth. | Mean penetration depth within ±10 % of the reference unit, and no single shot outside ±20 %. Photographic documentation of every shot. |
| A4b | Delivered volume vs. commanded volume. Shot weight on a 0.1 mg balance, 5 shots each at 1, 5 and 10 units. | Within ±10 % of the reference unit at every setting, and within ±10 % of the commanded volume (units × 6 µl). |
| A1 | Hand-cocking torque measured (torque screwdriver, 10×) | documented; motor sized ≥ 2× margin |
| A2 | Motorized re-cock FAST mode (20×) | every cycle ≤ 1.5 s, cocked switch confirms |
| A3 | Motorized re-cock QUIET mode (20×) | every cycle ≤ 2.8 s |
| A5 | Cycle test | ≥ 500 cycles without fault, re-cock time drift < 15 % |
| A6 | Cocking timeout / stall | motor stops ≤ 6 s, FAULT state, no overheating |
| A7 | Dose servo accuracy (units 1..10, 3× each) | correct mechanical stop position every time |
| A8 | Mechanical hard stop — attempt to exceed the maximum dose by every means available: dose sleeve forced past its stop, servo commanded beyond range, firmware commanded to 11+ units | physically impossible to exceed 60 µl (dispense measurement, 10 attempts). The servo must not be able to push past the mechanical stop — slip clutch or torque limit required. |
| A8b | Dose steps. Set each of the 10 unit steps and dispense onto a 0.1 mg balance, 3× each | every step reproducible; monotonically increasing; step 10 = 60 µl within tolerance |
| A9 | ~~Ampoule bore of the 0.3 ml chamber measured~~ — resolved, see measured data (plunger Ø 3.60 mm = 10.18 mm², nozzle 0.18 mm). Replaced by: nozzle bore of the 5 ml refill chamber measured with pin gauges and documented | value recorded in the test report; the per-chamber delivery time is set from it via setChamberCalibration() |
B. Acoustics (dB(A) @ 30 cm, calibrated meter, quiet room)
| # | Test | Pass criterion |
|---|---|---|
| B1 | Re-cock QUIET mode | ≤ 45 dB(A) |
| B2 | Re-cock FAST mode | measured + documented (target: as low as achievable) |
| B3 | Shot click with PU buffer vs old rubber (A/B) | PU variant quieter or equal; subjectively duller |
| B4 | A/B vs manual device (shot + cocking) | electric device NOT louder overall |
C. Trigger & safety logic
| # | Test | Pass criterion |
|---|---|---|
| C1 | Trigger latency (button → release) | ≤ 150 ms perceived-instant |
| C2 | Guards: fire attempt while UNCOCKED / LOCKED / during re-cock | no release pulse, correct display state |
| C3 | Rate limit | min. shot interval enforced (config 800 ms) |
| C4 | Grip-pressure false trigger test (top button) | no accidental release in 50 handling cycles |
| C5 | Trigger contract: pressed() returns the LEVEL while held (HAL_SPEC §2.4), not an auto-clearing latch |
held button reads true across ≥ 10 consecutive polls; device fires exactly once per press |
D. Firmware / IoT (against LIVE register, test licence)
| # | Test | Pass criterion |
|---|---|---|
| D1 | Provisioning: token stored in NVS, survives power cycle | device authenticates after reboot |
| D2 | Heartbeat roundtrip | settings applied (heartbeat interval, max_level clamp) |
| D3 | Shot metering online | volume_ul correct (units × 6 µl), balance debited correctly |
| D4 | Offline queue: 50 shots offline → reconnect | all delivered once, no duplicates (server last_seq), queue pruned |
| D5 | Replay protection | re-sending same batch = 0 accepted duplicates |
| D6 | Grace behaviour | device keeps treating offline ≤ 72 h; display shows remaining grace |
| D7 | Offline unlock code | valid code unlocks 7-day window; wrong code rejected; event reported on reconnect |
| D8 | action=lock from server | device enters LOCKED, no release possible, recovers on unlock |
| D9 | OTA | update applied ONLY with correct sha256; corrupted image rejected, device stays operational |
| D10 | Display screens | match docs/ui_screens.html states 1–6 (wording "Applikation", YAVU design) |
| D11 | Lock survives a power cycle. Lock the device from the server, then disconnect the network AND remove power. Restart. | device is still LOCKED and cannot fire. A lock response must not refresh the offline grace. |
| D12 | No billing without a trusted clock. Boot with no network time available and attempt to fire. | device refuses to fire; no shot record with timestamp 0 is ever created |
| D12b | Per-studio dose cap. Set settings.max_level to 4 in the register. |
device refuses any dose above 4 units, pulls a standing higher dose down immediately, keeps the cap after a power cycle while offline, and a max_level above 10 does NOT raise the 60 µl ceiling. |
| D13 | Shot log full / storage failure. Fill the offline log to capacity, and separately simulate an NVS write failure. | full log: device refuses to fire, stays serviceable, no fault. Write failure: the shot is NOT reported as stored. |
E. Power
| # | Test | Pass criterion |
|---|---|---|
| E1 | Shots per charge | ≥ 300 applications (full day) incl. display |
| E2 | Dock charging | full charge ≤ 3 h, correct display, no operation blocked while docked/charging per spec |
F. Safety-critical mechanics (NEW — cannot be solved in firmware)
These three are requirements, not options. See docs/HAL_SPEC.md §4b.
| # | Test | Pass criterion |
|---|---|---|
| F1 | Non-backdrivable holding element. Cock the device, then remove power completely. Wait 10 minutes, handle and transport the device normally. | the spring stays safely cocked with no power applied; no backdrive, no creep, no release. On the next power-up a controlled de-cock runs BEFORE the device becomes ready. |
| F2 | Controlled de-cock. Trigger "end treatment", and separately place the cocked device in the dock. | motorized, never free-running; release is interlocked while it runs; device ends in the home position. Repeat 20×. |
| F3 | Mechanical dry-fire block. Attempt to fire with no dosing chamber fitted, and after the fifth 60 µl shot from one chamber. | firing is mechanically prevented in both cases |
| F4 | Chamber presence + manual selection. (Replaces the electrical chamber keying test — withdrawn 10.08.2026, see docs/HAL_SPEC.md §4b.3 and docs/CHAMBER_SELECTION.md.) Power the device up with a chamber fitted. Answer the chamber question, fire, then select the other chamber and fire again. Separately: remove the chamber and attempt to fire. |
after power-up the device asks which chamber is fitted and does not fire until answered; the delivery time changes with the selection while the force does not; a selection that does not match the fitted chamber produces W-201 within three shots; firing with no chamber fitted is blocked. |
G. Firmware gate (run before every delivery)
The manufacturer's HAL implementation must pass the supplied automated suite. It needs a C++ compiler only — no ESP32, no toolchain, no hardware, about one second:
| # | Test | Pass criterion |
|---|---|---|
| G1 | ./tools/host_test.sh on the delivered branch |
all checks pass (204 at the time of writing) |
| G2 | ./tools/syntax_check.sh on the delivered branch |
passes, no new warnings |
| G3 | pio run for the target board |
builds clean |
| G4 | Every pull request follows CONTRIBUTING.md |
branch-based, main untouched, PR template completed |
Sign-off
All A–G passed → prototype ACCEPTED → milestone payment released. Any failed item → documented, fixed, re-tested (only the failed section is re-run).
A4 and A4b are not waivable. They are the only tests that prove the electric device does what the manual device does, and the entire product claim rests on them.
Source: firmware repo docs/ACCEPTANCE_TEST_PLAN.md · confidential · NDA