From c84960394225416a60f83b9ffaecdd60406308a5 Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Fri, 16 Oct 2026 23:49:30 -0400 Subject: [PATCH] bench README: the lifecycle harness row links the Test page plainly, as the bench page does The row said that what laser_lifecycle_test.py walks is on the site, Developers, "Testing". The Test page lists four of the fifteen sessions main() runs, by choice: the harness header is the full list. And the page is "Test" in the site's navigation, in its title and in the bench page's own link; the site has no page called "Testing". The row now says what the harness is and links the Test page by its title, as the bench page of the site does. Proof: README text only, no behavior change. The row names no session and makes no claim about the page's list; its link text is the page's title (mkdocs.yml: "Test: developers/testing.md"), and its URL does not change. 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 75345f1..85cded3 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 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. | +| `laser_lifecycle_test.py` | Host-side lifecycle harness of the operator-armed window (null-sink controller) ([Test](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 `. |