From d57421c71d5e0af465175fa0763ee4a56711ef3b Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Fri, 16 Oct 2026 14:54:18 -0400 Subject: [PATCH] bench README: the lifecycle harness row points at the Testing page The row for laser_lifecycle_test.py in scripts/bench/README.md credited the harness with M5/M3 persistence inside a job, the M2 close, and the arm refusal under a blocking cooling verdict. The harness runs no session for any of the three: its header gives them to laser_arm_test.c case I, laser_stream_test session K, and laser_arm_test.c cases C, A2 and A3. The row also restated a list that the site keeps (Developers, "Test"), and a README is an index card. The row now says what the harness is and points at that page, as the bench page of the site does. Proof: README text only, no behavior change. The three claims are gone from the file, and each of the four sessions the Test page lists is one main() runs. Acceptance: none. **/*.md is outside every fingerprint (NON_BEHAVIORAL in manifest.py). --- scripts/bench/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bench/README.md b/scripts/bench/README.md index 5ffe552..75345f1 100644 --- a/scripts/bench/README.md +++ b/scripts/bench/README.md @@ -31,7 +31,7 @@ page's takeover does that; from a host, stop them first. | `pwm_stream_test.py` | LASER_PWM stream-path scope test (runs on the board, controller and forgectrl stopped): streams power bytes only (no step bytes, no FIRE bits, `motor_lock=15`, latch locked) through `/dev/glowforge` so the scope verifies the real power path, including the run-start duty reset and the consecutive-power-byte drop; position counters compared before/after. Exit 0 = counters unmoved, idle at the end, no FIRE/emission read back. | | `gate_a_kernel_drills.py` | Kernel laser-safety drills (run on the board with forgectrl stopped so the pulse device is free): `K1` controlled-stop deceleration floor, `K2` resume waypoint honors the locked latch, `K3` a mid-ramp latch unlock never re-arms the FIRE drive. Software witnesses (`cnc/state`, `laser_enable`, `laser_on`, `laser_on_sampled`, interlock bit 3) plus the PSU-connector LASER_ON scope point; K3 refuses to run if HV reports good. | | `laser_stream_test.py` | Host-side laser pulse-stream emission harness: runs the native null-sink controller with `GFSINK_DUMP`, drives small laser jobs over TCP, and checks the dumped bytes against the kernel feeder contract (leading power byte, no back-to-back power bytes, FIRE only inside cutting moves, every stream ends FIRE-clear, no FIRE on a stepless gap, no FIRE leak across cycle churn, the rapids after an M5 executed at idle ship dark, and the next job in the same process fires at the level the previous one ended at). Runs in the grblHAL repo's CI. | -| `laser_lifecycle_test.py` | Host-side operator-armed-window lifecycle harness (null-sink controller): arm once per job with M5/M3 persistence, the M2 close, sender-change re-consent, the disarm grace counting down in Hold, and arm refusal under a blocking cooling verdict. Runs in the grblHAL repo's CI. | +| `laser_lifecycle_test.py` | Host-side lifecycle harness of the operator-armed window (null-sink controller); what it walks is on the site, Developers, "Testing" ([Testing](https://docs.forgefirm.org/developers/testing/)). Runs in the grblHAL repo's CI. | | `z_envelope_test.py` | Host-side Z envelope harness (null-sink controller): the Z soft limit belongs to the driver, not to `$20`, so the driver re-applies `sys.work_envelope`, `sys.homed` and `sys.soft_limits` for Z from the settings-changed chain. Checks that an unreferenced Z is collapsed to where the lens stands and blocks a move each way, that X and Y stay free, and that neither a `$20` write (the core clears the soft-limit mask in the setter) nor a `$132` write (which un-homes the axis as well) frees Z. Runs in the grblHAL repo's CI. | | `ctlport_test.py` | Host-side controller port and line-multiplexer harness (null-sink controller): a scripted sender that counts every `ok` and `error` runs beside the port's one client on `grbl.ctl`. A port jog's status goes to the port and never to the sender, a port error does not reach the sender's parser, a sender line cancels a port jog and gets its own `ok` (a line queued right behind the port's included), plus the refusals, the single client, a CR LF sender, a soft reset, and the client as the dead-man. `python3 ctlport_test.py `. | | `cool_report_test.py` | Host-side cooling report harness (null-sink controller, a stand-in for forgectrl's listener on `FORGECTRL_PORT`): every `POST /cool/state` carries the secret the supervisor hands the controller at its spawn (`GF_REPORT_SECRET`) as `X-ForgeFIRM-Report`; with no secret the header is absent; a value that is not 32 hex digits, a CR LF with a header behind it included, never reaches the wire; and the homing runner the controller starts is handed the secret (it reports in the controller's place during a gfcloud `$H`), and none when the controller has none or a malformed one. `python3 cool_report_test.py `. |