bench: fd-scan tool; BRINGUP session record - dry drills, homing witness, panic drill

This commit is contained in:
ScottW514
2026-08-15 08:52:51 -04:00
parent 31ac4bbcbd
commit e1207316ec
2 changed files with 75 additions and 25 deletions
+42 -25
View File
@@ -285,9 +285,31 @@ found and fixed: `auth.c` read `X-ForgeFIRM-Token`/`Host`/`Origin`/
`Sec-Fetch-Site` case-sensitively (a title-casing client was refused);
now `u_map_get_case`. Bench tooling for the session is committed
(`scripts/bench/platform_drills.py`, `live_fire_drills.py` `ircut` /
`expstop` / `ctrlstart`). Session rules, now standing: one live-laser run
per turn with the operator's confirmation before the next; only
observations, never inferences, in live-fire reporting.
`expstop` / `ctrlstart`, `fdscan.sh`). Session rules, now standing: one
live-laser run per turn with the operator's confirmation before the next;
only observations, never inferences, in live-fire reporting.
**Dry drills the same session, all PASS on the board:** decay/microstep
readback per axis (every value reads back, out-of-range `3` refused
`EINVAL`); dead-man trip readback (closing the flock'd fd mid-run →
`closed while locked and driver is running! Emergency stop`, `pic`/`head`/
`thermal: making safe`; heater and TEC off, measure laser, UV LED and Z
driver off, pump/exhaust/intake/air-assist **unchanged**); three
`rmmod`/`modprobe` cycles with a thread reading state/position/faults/
hall_sensor throughout (6618 reads served, 14162 refused while unloaded,
no oops/BUG/WARNING); the LED sequence ran (bright/dark/pulse/restore);
the module's probe lines read `EPIT clock 66000000 Hz` and `SDMA channel
26 reserved for pulse playback (script at halfword 7680)` with no bank
warnings; forgectrl's helper children (`curl` during `/update/check`, the
snapshot path) never hold a pulse-device descriptor — only the controller
does; a `$H` gfcloud homing session completed in 56 s with 7 accelerometer
motion windows above the 500-count threshold at the ~100 Hz sampler
(anchor written, `H:1`); a kernel panic (`sysrq c`) mid-move stopped
motion instantly (operator-witnessed) and the board rebooted on `panic=10`
into a healthy state (liveness MOTION OK, controller running, latch
commanded locked). Observed once, cause not established: after the three
module reloads the first liveness probe read NO MOTION (p2p 343/241); the
ladder's rail-off/re-probe recovered it (p2p 3466/2163) — a module reload
resets the analog configuration, and the ladder exists for this.
**Phase 11 (licensing, legal, and documentation hygiene — the last
phase) is code-complete and host-verified, 2026-08-15.** Licensing:
@@ -2264,34 +2286,29 @@ accordingly ("Automatic — AP country, else World").
trip before calling the gate proven, and measure an actual flame
signature (a tea light in the closed bed, machine idle) so the 15
is anchored on both sides.
- **Kernel platform-hygiene batch (item 9), on the flashed image:**
panic mid-motion with motors locked and the laser latched (motion
stops, safety lines read safe); decay mode set/readback per axis;
LED behavior and a clean module unload; the two new probe lines in
dmesg with no bank warnings; a dead-man trip with the head
registers read back (measure laser off, UV LED off, lens motor
de-energized). Plus the Phase 8 trio: load/unload under
`CONFIG_DEBUG_MUTEXES` (needs a debug kernel build), a forced
`-EPROBE_DEFER` unwind, and a concurrent `cat` of a state attr
during `rmmod`.
- **Dead-man collateral:** a kernel dead-man trip leaves the pump and
airflow running (heat sources only go off); kill forgectrl during
an update download (no pinned device, no EBUSY respawn storm).
~~Re-run the armed kill drill on the *expected*-stop path~~ —
**DONE 2026-08-15**: it failed first (5 s of continued fire), the
defect is fixed on both sides, and the re-run passed (see the
session record above).
- ~~**Kernel platform-hygiene batch (item 9), on the flashed
image**~~ — **DONE 2026-08-15** (panic mid-motion, decay/microstep
readback, LED sequence + clean unload, probe lines, dead-man head
readback, concurrent `cat` during `rmmod` — session record above).
Still needing a debug kernel build: load/unload under
`CONFIG_DEBUG_MUTEXES` and a forced `-EPROBE_DEFER` unwind.
- ~~**Dead-man collateral**~~ — **DONE 2026-08-15**: the trip leaves
pump and airflow running (readback drill); helper children never
hold the pulse device (fd-scan during `/update/check` + snapshot);
the armed kill on the *expected*-stop path failed first (5 s of
continued fire), the defect is fixed on both sides, and the re-run
passed. The literal "kill forgectrl mid-download" variant needs a
published `.fw` to download and was covered by the fd-scan instead.
- **Physical-evidence negatives:** force a head I²C error and confirm
the witnesses report an error, not a plausible value; confirm a
failed head capture leaves the measure laser off.
- **Cloud mode:** the C-1 cancel-with-a-rejected-settings-action
drill (cancel must stop the cut); malformed-frame and DNS-blip
injections against a live session; the oversize/bad-header job
rejected before the ring loads (tracked in `CLOUD.md`); re-verify
the homing accelerometer motion-window counts against the
characterized thresholds at the next live homing (the witness now
samples at ~100 Hz); confirm a real print header's fan duties
round-trip to the engine (item 8).
rejected before the ring loads (tracked in `CLOUD.md`); ~~re-verify
the homing accelerometer motion-window counts~~ **DONE 2026-08-15**
(7 windows ≥ 500 at the ~100 Hz sampler on a real `$H`); confirm a
real print header's fan duties round-trip to the engine (item 8).
- **Opportunistic:** `STATE_FAULT` recovery via `enable` without a
module reload the next time a DRV8825 fault line actually trips.
- **Config-dependent, deliberately not gated:** an armed GRBL job
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
# Runs ON the board. While forgectrl spawns helper children (the update
# check's curl, the snapshot's media-ctl/v4l2-ctl), scan every child for a
# descriptor on the pulse device. Expected: none - only the controller
# inherits /dev/glowforge (F_SETFD cleared for that one spawn); every other
# child gets the O_CLOEXEC default. Usage: fdscan.sh <panel-token>
TOK="$1"
P=$(pidof forgectrl)
[ -n "$P" ] || { echo "forgectrl not running"; exit 1; }
CTRL=$(pidof grblHAL_glowforge)
echo "forgectrl pid $P, controller pid ${CTRL:-none}"
# Kick the helpers off in the background.
( curl -s -X POST -H "X-ForgeFIRM-Token: $TOK" http://127.0.0.1:8080/update/check >/tmp/upd.out 2>&1 ) &
( curl -s -o /tmp/snap.jpg http://127.0.0.1:8080/cam/snapshot ) &
hits=0; seen=0; names=""
i=0
while [ $i -lt 60 ]; do # ~6 s of scanning at 10 Hz
for c in $(pgrep -P "$P"); do
seen=$((seen+1))
n=$(cat /proc/$c/comm 2>/dev/null)
names="$names $n"
if ls -l /proc/$c/fd 2>/dev/null | grep -q glowforge; then
if [ "$c" != "$CTRL" ]; then hits=$((hits+1)); echo "HIT: child $c ($n) holds the pulse device"; ls -l /proc/$c/fd | grep glowforge; fi
fi
done
i=$((i+1)); usleep 100000 2>/dev/null || sleep 0.1
done
wait
echo "children observed: $(echo $names | tr ' ' '\n' | sort | uniq -c | tr '\n' ';')"
echo "controller holds pulse fd: $(ls -l /proc/$CTRL/fd 2>/dev/null | grep -c glowforge)"
echo "update/check reply: $(cat /tmp/upd.out | head -c 200)"
echo "snapshot bytes: $(wc -c < /tmp/snap.jpg 2>/dev/null)"
echo "non-controller children holding the pulse device: $hits ($([ $hits -eq 0 ] && echo PASS || echo FAIL))"