# Handoff: hermes reviewer findings — all 3 fixed and deployed ## Session Metadata - Created: 2026-10-24 11:07:15 - Project: /home/claude/bin/lgtv - Branch: master - Session duration: ~20 min ### Recent Commits (for context) - cc0e023 fix: catch helper timeouts in run(); report probed state when a command fails - 2f96013 docs: handoff — pairing fix verified on/off/on - 9d6f2d2 docs: handoff — pairing prompt / IP drift / zombie bridge - 34adf28 fix: IP-independent pairing key fallback; run commands off the paho thread - f7422c9 fix: survive command exceptions; TV DHCP-reserved so pairing key stays valid ## Handoff Chain - **Continues from**: [2026-10-23-003809-pairing-fix-verified.md](./2026-10-23-003809-pairing-fix-verified.md) - Previous title: pairing-prompt / zombie-bridge fix — verified on→off→on - **Supersedes**: None. The previous handoff's open questions (the key fallback across an IP change is still unverified; the Omada reservation didn't stick) still stand and are restated below. ## Current State Summary Seth asked to "claim the 3 open bugs and fix them". They are the hermes codex reviewer's findings for lgtv, not Gitea issues. All three are fixed in `cc0e023` (pushed) and deployed to `/usr/local/bin/lgtv_mqtt.py`. `lgtv-mqtt.service` restarted at 11:05:43, reconnected cleanly and reported state ON. The hermes store now shows lgtv with 3 fixed and 0 open. Nothing is in progress. ## Codebase Understanding ### Architecture Overview Unchanged from the previous handoff: Google Home → HA → Mosquitto → `lgtv_mqtt.py` → `lgtv.sh`. New this session: every call to `lgtv.sh` goes through `run()`, which now turns `subprocess.TimeoutExpired` into `("timeout", 124)`. No caller can get an exception from it any more. ### Critical Files | File | Purpose | Relevance | |------|---------|-----------| | `lgtv_mqtt.py` | MQTT bridge | `run()` timeout guard; `handle_command` checks the ok result | | `tests/test_bridge.py` | Replays the reviewer's 3 repros without a broker or TV | Run: `sudo -u seth python3 tests/test_bridge.py` → `ok` | | `~/bin/hermes/state/reviewer/findings/lgtv/codex_bugs/` | Hermes findings store for this repo | Where "open bugs" live; Seth's daily Telegram digest counts these | | `~/bin/hermes/bin/hermes-findings` | Findings lifecycle CLI | `list/stats/fix/wontfix/apply-trailers --repo lgtv` | ### Key Patterns Discovered - **"Open bugs" = hermes findings, not Gitea issues.** The lgtv Gitea repo has never had an issue. Findings are `.md` files whose status lives in an optional frontmatter block (no block = open). - **Closing findings:** put `Fixes-finding: ` trailers in the commit message, then pipe it in: `git log -1 --format=%B | ~/bin/hermes/bin/hermes-findings apply-trailers --repo lgtv --sha `. lgtv has no post-commit hook that does this automatically (vdj does). - There is no "claimed" status. The statuses are open/fixed/wontfix/duplicate/invalid. - The reviewer can lag the code. Its 10-22 23:03 pass predates `34adf28`, so one finding was already half fixed. Check against HEAD before you fix. ## Work Completed ### Tasks Finished - [x] Located the findings (after first wrongly checking Gitea issues and reporting none) - [x] Timeout: guard in `run()`. It covers the poll loop and `on_connect`, which `34adf28` had left exposed. - [x] False success state: when a command fails, publish a fresh `get_tv_state()` probe instead of the requested state - [x] Malformed payload: `decode(errors="replace")`. `safe_handle` from `34adf28` already kept the paho thread alive. - [x] Test fails on the old code and passes on the new - [x] Deployed, restarted, journal healthy; findings marked fixed via trailers ### Files Modified | File | Changes | Rationale | |------|---------|-----------| | `lgtv_mqtt.py` | `run()` try/except; `handle_command` restructured | One guard where every caller goes through | | `tests/test_bridge.py` | new | The repo's single regression check | ### Decisions Made | Decision | Options Considered | Rationale | |----------|-------------------|-----------| | Catch the timeout in `run()` | a guard in each caller | Smallest diff; fixes every path at once | | Publish the probed state when a command fails | keep the last state; publish nothing | Reports reality; the probe is cheap | ## Pending Work ### Immediate Next Steps 1. Nothing required. 2. Known limit: `get_tv_state()` returns OFF when the status probe itself fails, so on a flaky network a failed OFF can still show OFF. Fix it only if it causes trouble. 3. Carried over: check whether the TV accepts the fallback key after a real IP change, and find out why the Omada reservation for `.20` didn't stick. ### Blockers/Open Questions - [ ] Fallback key across an IP change is still unverified (from the previous handoff) - [ ] Omada reservation not applied (from the previous handoff) ### Deferred Items - A post-commit hook in lgtv that runs `apply-trailers` automatically. It wasn't needed for 3 findings. ## Context for Resuming Agent ### Important Context - When Seth says "open bugs" for a repo, look in `~/bin/hermes/state/reviewer/findings//codex_bugs/` first. - Hermes CLAUDE.md: its agents are read-only. Marking finding status through `hermes-findings` is the designed workflow. ### Assumptions Made - Reporting the probed state after a failed ON is better than the old optimistic ON. WoL rarely fails at the script level. ### Potential Gotchas - Run the test as `seth` (`sudo -u seth`), because paho is installed for that user. - The journal needs sudo: `sudo journalctl -u lgtv-mqtt.service`. - Re-run `install.sh` (or `sudo install -m 0755 lgtv_mqtt.py /usr/local/bin/`) after editing the bridge. ## Environment State ### Tools/Services Used - `~/bin/hermes/bin/hermes-findings`, `journalctl -u lgtv-mqtt.service` ### Active Processes - `lgtv-mqtt.service` on steel141, restarted 2026-10-24 11:05:43 with `cc0e023`. ### Environment Variables - `MQTT_PASSWORD` (from `/etc/lgtv/lgtv.env`; name only) ## Related Resources - `~/bin/hermes/src/hermes_glue/findings_status.py` (status and trailer format) - Backups: `lgtv/.backup/lgtv_mqtt.py.*` (repo copy and deployed copy before the change)