Our PracticeTeamPricingConsultingInvestorsGet Started
← All Field Notes
July 30, 2026 Β· by Raviotlocal-firstdebuggingverificationjarvishome-automation

It Said OK. Nothing Happened.

We spent a day taking our own house back from the cloud, and every real bug wore the same disguise: a success code with no effect behind it. Notes on verifying physical computing, and why "the API returned 200" is not evidence that a light turned on.

It started with a 500 error on an endpoint nobody was looking at, and ended with the house doing what it was told. Somewhere in between, we learned the same lesson six different ways.

The disguise

Every genuine bug that day looked identical from the software side: the call succeeded, and nothing in the physical world changed.

A Bluetooth light accepted a GATT write and returned success. The bulb did not move. A speaker accepted a play command and returned HTTP 200. The room stayed silent. A microphone returned a valid WAV file, correct sample rate, plausible waveform. It contained nothing.

If you are writing to a database, a 200 is close enough to truth that you can move on. If you are writing to a room, it is barely a rumour. The API boundary is not the end of the system. There is a radio, a firmware, a power supply and a physical object past it, and any of them can accept your packet and do nothing at all.

The most useful habit we adopted: ask a human what they see before writing "working" in the log. Not because humans are precise, but because they are downstream of everything.

The bug is rarely where the story says it is

We lost real time to a confident diagnosis.

Voice announcements were not playing. The obvious culprit was network segmentation: smart-home devices on one VLAN, the server on another, and cross-VLAN routing is exactly the sort of thing that breaks audio. We traced switch ports. We read port profiles. We planned a re-cabling.

The actual cause was one missing line in the server's own firewall. The speakers were reaching out to fetch the audio and the host was silently dropping them. The proof had been sitting in a log the whole time: a single blocked packet from a speaker to the audio port.

Two things went wrong in our heads. We let a plausible mechanism outrank an observed one. And we tested reachability from a machine that turned out to be dual-homed, so it quietly answered from the wrong network and produced a "still broken" result that was pure measurement error. A test that is not testing what you think it is will lie to you with total confidence.

The watchdog calibrated to failure

The best find of the day was a microphone that had been broken for an unknown length of time while a monitoring service watched it and reported OK.

It was returning audio at roughly 0.8 percent of full scale. Technically a signal; practically silence. Two gain stages, each set to a reasonable-looking value, had multiplied into a near-mute. Speech was present in principle and unintelligible in fact.

The watchdog logged its measurements faithfully and called them healthy, because its threshold was effectively "is this number greater than zero?" At some point someone calibrated the alarm to the noise floor, and from then on the noise floor was the definition of working.

Worth naming as a general failure: a monitor tuned to the level it observes will eventually certify whatever it finds. If your health check cannot distinguish "quiet room" from "dead device", it is not a health check, it is a heartbeat with opinions.

The fix was not a better threshold. It was a control test. We fed the transcription service a known-good sentence from our own text-to-speech and got it back verbatim. That split the system in half instantly: recognition was fine, so the problem was upstream, in the air.

When the wall is real, buy the ladder

Part of the day went to taking local control of a smart bulb from a semi-abandoned ecosystem. We got impressively far. We could see it, connect to it, and speak its protocol correctly enough that it answered our authentication attempts with a proper rejection rather than gibberish.

Then we hit the actual wall. Its credentials are minted per-account inside a vendor cloud and there is no local path to them. Not hard. Not undocumented. Structurally absent.

The right call was to stop. A replacement bulb with an open local protocol cost about the same as twenty more minutes of our time, and it does more: it acknowledges every command and reports its true state, which the thing we were fighting never did. Engineering effort spent defeating a business model is usually better spent routing around it.

This is also what local-first actually costs. Not zero. It costs a shopping trip and a willingness to choose hardware on protocol rather than on brand.

Guardrails you can forget are not guardrails

We found a device on the network belonging to someone else in the household and tagged it "do not touch".

A tag is a note to your future self, and your future self is a stranger with different priorities. So we made the rule mechanical: the control layer now raises an error before any adapter is called for anything marked protected. It cannot be forgotten because it is not remembered.

The same principle caught us in a softer way. We had a rule that the system must never disturb a room where someone might be sleeping. Then we asked it to flash a light we could not locate, to help find it. Every named room was respected. The unlabeled device, the one whose location was genuinely unknown, got flashed anyway.

The rule needed rewriting. It is not "do not actuate in a sleeping room". It is "unknown location is not permission". Uncertainty should trigger the constraint, not exempt you from it.

What we ended up with

By the end the house could be driven from one interface: lights across three different protocols, a kettle, speakers, a television. It speaks in its own voice rather than a vendor's. It can hear a room and transcribe it, with every use logged and no audio retained, because a capability like that should leave receipts.

And a piece we did not plan. Because the same system now controls both the lights and a camera, it can change the lighting and re-observe the scene. A colour that is ambiguous under white light separates cleanly under blue. That is not a smart-home feature, it is a measurement technique, and it only became possible when the lights stopped being someone else's API.

The thread running through all of it is unglamorous. Prefer observation to inference. Distrust success codes that cross into the physical world. Test the test. And when a system tells you everything is fine, ask what "fine" was measured against.

Sometimes the answer is: the noise floor.