Table of Contents
- What running it taught us
- Class 1 — the UI claiming more than the data supports
- Class 2 — silent failure that looks exactly like success
- Class 3 — asserting a security property without checking the assertion
- Class 4 — a convenience write clobbering an explicit state
- Class 5 — the cross-platform move
- Class 6 — test hygiene that costs real disk
- Related
What running it taught us
Every bug below was found by running the thing on a real machine and looking at what it actually produced, not by a test failing. They are grouped by class, because the classes recur and the individual bugs do not.
The reason this page exists: in a product whose entire value is that its claims are checkable, a bug that makes a claim wrong is worse than a crash. A crash is obvious. A screen that confidently says the wrong thing is not.
Class 1 — the UI claiming more than the data supports
- "CAPTURING" beside three zeroes. The status word was derived from configuration, not from whether anything had been captured.
- "142 records" beside "0 stored", in the same window.
signal_stateholds one row per signal per machine, not per session, so a new session inherited the previous one's counters. Somebody who sees two numbers disagree stops believing either. - "NOT BUILT" beside a live record count. The gate reused one state for "not implemented" and "held", so the console called a merely-paused signal unbuilt.
- A running collector reported as not running.
tasklist's default table truncates the image-name column at 25 characters, sohere-collector-input-metrics.execame back ashere-collector-input-metrand a substring check said no. Three of this product's binaries are long enough to hit it — and that answer is what the console prints directly under the Start button. Telling somebody capture failed while it is working is the same class of lie as the reverse.
The lesson: derive status from the data, and put the derivation somewhere testable. The console's now lives in one module with its own tests, rather than inline in a component.
Class 2 — silent failure that looks exactly like success
- Frames too big for the pipe. The collector sent full-resolution RGBA; a 2560x1440 window is about 20 MB against an 8 MB limit, so the send failed, the connection dropped, and screenshots recorded nothing while the log said "granted — capturing". Fixed by fitting in the collector before sending, capped by area as well as width: a 1280x1280 window is still 8.7 MB encoded.
- Scope rules that never matched. Collectors report full paths, rules are written as file names, and the comparison was a plain case-insensitive equality. On a real machine the seeded allow-list allowed nothing, and the gate said "not on the allow-list" about apps that were on it. Every test used a bare exe name, which is how it survived.
- The uploader retrying a 404 twelve times, logging nothing, because it only logged successful passes. A wrong ingest URL looked identical to an empty queue.
- The uploader silently dead on macOS. Its own copy of
data_dir()fell back to".", so it read an empty store beside the binary, concluded capture had stopped, and exited cleanly twenty times until the supervisor's restart ceiling left it down. - A hung indicator keeping content flowing forever. The visibility check asked what the indicator last said, not whether it had said it recently.
The lesson: log the failure path, not the success path. And any check of the form "did something report itself healthy" needs a freshness bound.
Class 3 — asserting a security property without checking the assertion
- A firewall rule that was never created. PowerShell's
New-NetFirewallRulefailed with "Access is denied", exited 0 because-ErrorAction Stopwas missing, and the supervisor printed "blocked (rule created)" when no rule existed. The one claim that does not depend on collector code being correct was, for a while, a print statement. - A verification script reporting zero tests and "everything passed". The test-count regex matched raw bytes and vitest colourises its summary, so the count came back empty and an empty count was not treated as a failure.
The lesson: a script that asserts a security property must fail loudly, and a count of zero is a failure, not a number.
Class 4 — a convenience write clobbering an explicit state
- A deletion receipt claiming a record was gone while a copy sat on the server.
mark_uploadedset the deletion toconfirmed, overwriting a refusal the caller had just recorded. Fixed by narrowing the update to rows stillpending. - An empty setting read back as a value.
set_settinghas no delete, so a "cleared" setting returnsSome("")rather thanNone. Treating that as a value made the uploader re-claim an empty pairing code every tick, get a 400, and overwrite a successful pairing status with a failure a second later.
The lesson: state machines need their transitions guarded in the WHERE clause, not in the
caller's head.
Class 5 — the cross-platform move
- A banner that only existed on one platform.
build_bannerlived in the Windows body, so the Mac pill built its own from a health flag — and the load-bearing case,Zoom — not captured, appeared on one platform and not the other. Absence made observable is the one thing a contributor cannot check for themselves, and it was missing because a function was in a file that platform does not compile. - A capture card drawn on top of the pill. The pill sits a margin above the Dock, so "under the pill" is off the bottom of the screen and the clamp put the card over the one window whose visibility the gate verifies. The bitmap looked perfect.
- Imports left behind by a module move, in files the moving session could not compile. Both faults landed in exactly the two files its handoff had named as unverifiable.
The lesson: what a surface says and how a window is placed belong in different files, and the sayings should be shared. And when a session edits a platform it cannot build, its own list of untested files is the next session's search order — it will be right.
Class 6 — test hygiene that costs real disk
Nothing that opened a store on disk ever deleted it. 641 directories and 392 MB had collected in the temp folder across runs. Both fixtures now clean up on drop — and keep the directory, with its path printed, when the test failed, because then it is the evidence.
Related
Environment gotchas has the platform quirks that cost time without being bugs in this code.
here-telemetry
How it works
Working on it
Taking it further
In the repo: README.md for the
invariants, HANDOFF.md for
current state.