Eagle light lab · read before the next session
Written 2026-09-15 at Ray's instruction, after a fourteen-hour lab that ended by overturning its own conclusion. Its job is to stop the next session re-deriving all of this from scratch — and to stop it inheriting the parts that turned out to be wrong.
“I don't want to leave here believing we have this locked down and then run in circles again at the next eagle light lab. Some days your ability to adapt on the fly and improvise is a trap.”
He is right, and this lab is the proof. Every section below is tagged by how well it is known. Improvisation is not the failure mode — improvisation that gets written down as fact is. Do not promote a tag without new measurement.
# 1. IS EAGLE RUNNING THE COMMITTED BASELINE? Everything else depends on this answer.
sha256sum /root/eagle-gui-mk3/mk3_ui.py
git --git-dir=gty6-eagle-live.git cat-file -p db676c6:eagle-gui-mk3/mk3_ui.py | sha256sum
# 2. Did the power cycle revert the debugfs value we set on a dead theory?
cat /sys/kernel/debug/bluetooth/hci0/supervision_timeout # expect stock 42
# 3. Is the wired-LED service up? The MK III LED pane proxies to it and dies without it.
systemctl is-active led-control && curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/
Command 1 is the whole fork in the road. If the hashes match, the scanning story is dead and the cause is somewhere nobody has looked. If they differ, the diff is the bug report. Either way, do not rewrite MK III before running it.
btmgmt add-device -a 2 into the
controller's accept-list, then busctl for reads/writes. Never Device1.Connect().
Locked by Ray 2026-08-18. Short Whip Driver: 135 s and 12+ consecutive
failures → 0.2 s. Unchanged and not in question.allied work. Ray's own word, and the
retrieval is in gty6-ble-lab.git.
allied imports the six unchanged and routes. Per-device parallel dispatch,
ThreadPoolExecutor(12).db676c6, one grep each: bleak, BleakScanner,
BleakClient, DETECTORS, .detect(),
Device1.Connect, scan — all zero.
It calls A.register_all() at :689 and
A.start_state_poller() at :691, and imports allied read-only.DO-NOT-REGRESS.md never mentions MK III. It bans a
pattern — software scan-then-connect. We attached that ban to a
component and nobody checked the component.gty6-eagle-live.git → eagle-gui-mk3/mk3_ui.py, 36,959 B.
Claimed after searching one repo out of 223. A wrong
absence-claim travels further than a wrong presence-claim, because nobody re-runs a search that
found nothing.mk3_ui.py is
2026-09-12; the thrashing is 09-15. Nobody has diffed them.
Same trap that hit eagle-uptime-probe.sh the same evening: the file
said CONTROL_HOST=10.0.5.12, the running process carried
10.0.5.1.led-control proxy path on :8000, which has never been
examined at all.supervision_timeout. Set to 400 on a theory that went nowhere;
stock is 42. debugfs is runtime state, so a power cycle should have reverted it.
“Should” is not measured. Eagle went dark before it could be read
back.send_many()
calls ensure() per command, which can block per device.
Strongly supported by reading the code and by the vendor-app contrast. Not yet
demonstrated against hardware.State may only be used to fail fast. Never to wait.
allied, device down "DOWN — skipped, not waiting 45s (kernel still pursuing it)" FAIL FAST ensure(), device down blocks up to 45 s trying to MAKE the state true WAIT
Same mechanism, opposite purpose. This is why “remove all state” was the wrong instruction — it would have deleted allied's poller, the one thing Ray identified as making it work. It is also the vendor apps in one sentence: the app never blocks, it fails and you tap again.
Ordered. Each states its own precondition. None of them are started, and none should be started before command 1 above.
| # | Change | Where | Precondition |
|---|---|---|---|
| 1 | Delete the Connected guard | eagle_auto.py :107-110 | none — safe cold |
| 2 | Take ensure() off the send path | eagle_auto.py :103 | test on 3 whips |
| 3 | Propagate 1+2 to the other five | copy-forward only | 2 proven first |
| 4 | MK III: nothing | — | the hash diff first |
eagle_auto.py:107-110The guard exists because “a write to a dropped link reports OK” — true of
the old _write. :120-129 already fixed exactly that by
parsing output instead of the exit code. The guard now protects nothing and only refuses to
send where the app would have sent. A guard that outlived its bug.
ensure() off the send pathThis is the actual subtraction. Acquisition belongs at establish or in the background, not inside a keypress. A cold device should return “down, try again” in milliseconds — the kernel keeps pursuing it via the accept-list either way.
The baseline already meets the spec Ray issued. register_all() at
startup, the poller, allied imported read-only, no scanning. There is nothing in it to strip.
Rewriting it would destroy a locked baseline and fix nothing.
Three things its own doc marks as must not be tidied out, worth knowing before anyone
touches it: register_all() at startup; full state on every SSE reconnect, never
a delta (browsers reconnect silently, so a delta-based screen returns looking healthy and
showing a stale value); and LED calls as POST, 1-based channel, rgb in the query
string — a JSON body returns 200 and silently applies red.
Four failures, one shape. All four were caught by a second person or a negative control. None was caught by the author re-reading their own work.
DO-NOT-REGRESS.md bans software scan-then-connect. It never names MK III. We supplied
the component ourselves and then cited the document as the source.gty6-ble-lab.git” —
carries its own scope and would have been harmless.