The gate had no `select`, so it linted with whatever the installed ruff
version defaults to. `dev` pins only `ruff>=0.6` and CI installs that
extra fresh on every run, which made the rule set a function of when pip
last resolved rather than of this code. Two developers on one commit
could get different answers, and so could CI and a laptop.
This surfaced when T-47's converged setup reinstalled ruff and pulled
0.16.3: `make lint` failed on UP017 and BLE001 in main.py, a file the
commit before it had not touched. Under the previous install the same
code passed. Nothing about the code changed — only the linter's idea of
what to look at, which had grown to 413 rules with nobody choosing them.
Naming the families fixes that; pinning the version would only have
frozen the symptom and moved the surprise to whoever unpinned it. 217
rules now, selected on purpose, and a future ruff release becomes a
decision instead of a broken push.
ASYNC is included deliberately — this is a websocket gateway, and it is
the family whose findings would be real bugs rather than style. BLE is
deliberately excluded: main.py catches bare Exception when a device
disappears mid-send, which is correct there, and selecting BLE would
mean a noqa on every such site to say so.
UP017 is fixed rather than suppressed (datetime.timezone.utc -> UTC,
identical semantics, and requires-python is already >=3.11); isort then
reordered the import, which is the whole of the main.py diff.
Verified the selection is load-bearing rather than decorative: a probe
file with a mutable default argument fails the explicit set (B006, exit
1) and passes ruff's minimal default set (exit 0), so the rules named
here are doing work the fallback would not. Probe deleted; lint,
typecheck and the 9-test suite all green after.
Co-Authored-By: Claude <noreply@anthropic.com>
Tatlock turns take 10-25s, which is a long silence after a request. Speak
a canned "Let me check on that for you, sir" immediately (synthesized once
and cached), then re-assert the thinking state so the device keeps its
effort-face spinner up until the real reply arrives. On the device, guard
the playback-done handler so the filler audio finishing doesn't drop the
spinner back to idle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKPbR6DY2JygHbyLjxm7Uu
Recognize simple device commands in the transcript and act on them without
a Tatlock round-trip. commands.match() maps volume up/down, mute/unmute,
"set volume to N", and "goes to eleven"/max to a "command" message sent
straight to the device; the utterance never reaches the LLM. Matching is
deliberately precise so real requests ("set an alarm for a quarter to
eleven") are not hijacked. Adds the "command" message to the device
protocol in docs/architecture.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKPbR6DY2JygHbyLjxm7Uu
Stops the RuntimeError spam ('Cannot call receive once a disconnect
message has been received') every time the device's watchdog reboots.
Redeployed to the tatlock-ui stack.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- gateway: GET /devices registry (per-IP connect count, timestamps,
device/fw from new 'hello' protocol event); graceful utterance
errors -> error event; uvicorn ws-ping-timeout 120s
- firmware: hello on WS connect; static 192.168.86.53 fallback after
15s without DHCP; scan-on-boot removed (kept for diagnostics);
ping_interval 8s; fonts regenerated --no-compress (compressed
glyphs render blank with LV_USE_FONT_COMPRESSED off); 8MB factory
partition; montserrat_28; LVGL lock timeout semantics fixed
- docs: sauron endpoint plan, power ladder, versioning policy
Known issue: 'Outside' AP association succeeds but no L2 traffic
flows (no DHCP, no ARP) — worked once at 22:05 then never again;
ToJ side verified clean. Investigation ongoing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DeskLock gives the Tatlock butler a face and voice on a Waveshare
ESP32-P4-WIFI6-Touch-LCD-3.4C round display in the living room.
- firmware/: ESP-IDF project targeting esp32p4 with the Waveshare XC BSP
- gateway/: FastAPI voice bridge (faster-whisper STT, Tatlock chat, Piper TTS)
- docs/architecture.md: component design and device<->gateway WS protocol
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>