← Writing
October 25, 2026·3 min read

The Loop That Only Ran Once

Debloating a Google TV over ADB, a batch script that exited cleanly after doing a tenth of the work, and why I verify state instead of trusting exit codes.

This week I used Claude Code to strip the ads, telemetry and demo-mode junk off the master bedroom TV, a Sony Bravia running Google TV. The plan was sensible: connect over ADB, audit every installed package without changing anything, sort them into tiers by risk, and disable (not uninstall) the safe ones. Every change gets logged so a single restore script can undo it. The audit went cleanly. The apply step is where it got interesting.

Getting connected took longer than expected. The TV had debugging switched on, but port 5555 stayed closed and adb devices listed nothing. The fix wasn't in the TV's settings. Disconnecting and reconnecting from the Mac made the TV show its "Allow debugging" prompt, and once I approved it the connection held. Nothing in the error output hinted that an approval was what was missing.

Then I approved Tier 1, about thirty packages: home-screen promos, the Samba ACR tracker UI, Sony's usage-data services, hotel and demo mode, plus Parsec and YouTube Kids at my request. The script ran and printed no errors. When the assistant checked the TV's actual package state, only the first package in each batch had been disabled. Three out of roughly thirty.

The cause was a classic. The script looped over a list of package names, and inside the loop it called adb shell. adb shell reads from standard input, so on the first iteration it swallowed the rest of the list. The loop saw an empty remainder and ended cleanly. No error, no warning, and about a tenth of the work done. The fix was one redirect, </dev/null, on the adb call so it couldn't eat the loop's input.

What I want to point out is how it was caught. Nothing failed. The only reason the problem surfaced is that the assistant queried the device for what was really disabled instead of relying on what the script had said it did. After the rerun it checked again: 30 packages disabled, Home still resolving to the stock launcher, Cast port still open. Then a full reboot, and a third check that all 33 disabled packages (my 30 plus three the TV shipped with disabled) survived the restart. Free memory went from 819 MB to 1,023 MB.

A smaller version of the same discipline showed up later. I wanted to try a third-party media app on the TV. Before installing anything, the assistant read the TV's properties and flagged that it was a 32-bit ARM CPU, so the APK would need 32-bit libraries. We sideloaded it to test. Then I realized the app needed a container running on my NAS to be worth anything, and without that it was just a different skin on the Plex app already installed. We uninstalled it. Checking compatibility first made backing out a quick job.

The lesson I keep relearning with automation, AI-driven or not, is that "the script finished" and "the system is in the state I wanted" are different claims, and only the second one matters. Success output is something your code wrote about itself. If the work is cheap to verify, like a one-line package query, build that check into the routine and run it every time. The bug above would have sat there looking fine until I wondered why my TV still showed ads.