A cleaner films the room or snaps photos. Unit Ready samples the frames, asks Gemini one strict question per room against the host's own reference photos, and lets plain code decide PASS, FAIL or REVIEW. Every verdict points at the frame it came from.
Short-term rental hosts set a standard for each room: bed made, towels out, bin empty, toilet paper stocked. Confirming that standard before check-in means someone walks the unit or trusts the cleaner's word. Unit Ready turns a phone walkthrough into a checklist verdict with evidence, and routes everything it is not sure about to a person instead of guessing.
It is a working prototype, not a product: one command line, one JSON checklist, two HTML pages, and an evaluation harness so a change to the prompt, model or thresholds can be measured rather than eyeballed.
Sample the video at 1 fps or read the photo folders. Drop blurry frames (Laplacian variance). Collapse near-duplicates (perceptual hash). The model only sees sharp, distinct frames. On the demo unit: 11 frames in, 5 kept.
One Gemini 2.5 Flash call per room: reference photos, kept frames, checklist items. The answer must fit a fixed JSON schema (present / missing / unclear, confidence, evidence frame, one-line note). Schema failure is retried once with the error, then falls back to "unclear" so the unit goes to review rather than passing.
Unclear or low confidence → REVIEW. A confident "missing" on a hard-stop item (bed, towels, toilet paper) → FAIL. A confident "missing" elsewhere → FIX, cleaner goes back. The model advises; the rules decide, and the rules are in a file the host can read.
One self-contained HTML page per unit: the verdict, a review queue worst-first, every item with its confidence and the frame the model pointed at, and the frames that were dropped and why.
24 labelled frames covering every item in both states, including deliberate hard cases. Reports catch rate and false-alarm rate per item, abstains, latency and cost. Misses and false alarms are separated because they cost different people different things.
A stitched bedroom walkthrough (three staged frames with a slow pan and motion-blurred swings between them) plus two bathroom photos. The review queue it produced:
toilet_paper: Only an empty cardboard roll is left on the toilet paper holder.bin_empty: The trash can next to the nightstand is overflowing with garbage.| Item | Checks | Catch rate | False-alarm rate | Abstain |
|---|---|---|---|---|
bed_made | 16 | 100% | 0% | 0 |
towels_present | 24 | 100% | 0% | 0 |
bin_empty | 24 | 100% | 12% | 7 |
surfaces_clear | 24 | 100% | 19% | 0 |
toilet_paper | 8 | 100% | 0% | 0 |
Where it was wrong on this run:
bin_empty in b06.jpg (unmade, no towels): model said missing at 0.90. "The trash can by the window has a liner and appears to contain discarded items."surfaces_clear in b06.jpg (unmade, no towels): model said missing at 0.95. "A used mug and item are left on the bedside table and carpet has visible spots."surfaces_clear in b07.jpg (unmade, towels on bed, bin overflowing): model said missing at 0.99. "Trash and bottles are spilled across the floor and items sit on the desk."surfaces_clear in b12.jpg (hard case: duvet pulled up but lumpy, pillows strewn): model said missing at 0.90. "A pillow is discarded on the floor and items are left on the nightstand."Open the full evaluation with every frame →
uv sync # Python 3.12 + deps
echo GOOGLE_API_KEY=... > .env # Gemini key, free tier works
uv run unit-ready frames units/demo-unit # what the model would see, no model call
uv run unit-ready check units/demo-unit # -> out/demo-unit/report.html
uv run unit-ready eval eval # -> out/eval/eval.html
uv run unit-ready render out/demo-unit # rebuild HTML from saved JSON
A unit is a folder: standard/<room>/ with 3–5 reference photos, then walkthrough.<room>.mp4 and/or photos/<room>/. Rooms, items, hard stops and thresholds all live in checklist.json; add an item there and nothing else changes.