After a home the driver's X/Y envelope reaches to the far edges the bed check measured (envelope_x_mm, envelope_y_mm, held by gfhome_clamp_envelope_mm to 50 mm up to the travel plus 30 mm), and to $130 by $131 only on a machine that was never measured. The test aimed its refusals at $130 + 5 and $131 + 5, which holds only on an unmeasured machine. On the bench reference, measured at X 508 and Y 288.925, image 20260928153805's campaign sent G90 G1 X500 F600, the controller rightly ran it, and the failed check ended the test before its soft reset, so the head drove the whole 476.6 mm while the restores were refused with 409 for 40 s; the jog check after it would have run a 500 mm jog the same way. The test now takes the far edges as the driver does and sends each move and the jog 5 mm past them, and it stops a move that was not refused (the soft reset, now ahead of the checks) and a jog that was not refused (the jog cancel) before it fails. The judgment is unchanged: ALARM:2 with no kernel motion, error 15 for the jog, a move inside the bed runs. Proof: pyflakes clean. On the bench reference, image 20260928153805 with this suite bind-mounted, the test passes: the far edges 508 and 288.925, G90 G1 X513, Y293.9 and X-1 each refused with ALARM:2 and 0.0 mm of kernel motion, the jog refused with error 15, the move inside the bed run, the head handed back where it was found, the baseline clean. Acceptance: the change is motion.soft-limits itself; its own function and description carry it, so no other test's fingerprint moves.
forgetest - the ForgeFIRM release acceptance tool
The daemon behind http://<machine>:8090/ on the dev image: runs the
acceptance catalog against the machine, keeps the append-only result log,
decides which results still apply to the image that is running, exports
the release artifact scripts/release.sh gates on, and serves the bench
diagnostics page. The contract - catalog, campaigns, fingerprints,
inheritance, the gate, the coverage rule - is
the Acceptance page of the documentation site.
Run the host tests
cd forgetest
python3 -m unittest discover -s tests -v
Run the daemon on a workstation (against a mock or a manifest file)
FORGETEST_DATA=/tmp/ft FORGETEST_MANIFEST=../tree-manifest.json \
FORGECTRL_URL=http://<machine> python3 -m forgetest --port 8090
scripts/manifest-from-tree.py produces tree-manifest.json from the recipe
pins; the coverage lint is python3 -m forgetest.coverage --manifest ....
Environment
| Variable | Default | Purpose |
|---|---|---|
FORGETEST_DATA |
/data/forgetest |
results.jsonl, bench.jsonl, token, export/ |
FORGETEST_MANIFEST |
/etc/forgefirm-manifest.json |
the image manifest |
FORGETEST_PORT, FORGETEST_HOST |
8090, 0.0.0.0 | listener |
FORGETEST_BENCH_DIR |
/usr/share/forgetest/bench |
the installed bench scripts |
FORGETEST_BENCH_DATA |
<FORGETEST_DATA>/bench |
passed to bench tools: where they keep their data files (with GF_HOST=127.0.0.1 and the panel token in GF_TOKEN) |
FORGETEST_MARKER |
/run/forgetest.active |
takeover marker |
FORGECTRL_URL, FORGECTRL_TOKEN_FILE |
http://127.0.0.1, /data/forgefirm/panel.token |
forgectrl client (HTTP; the token authorizes writes from the board) |
FORGECTRL_TLS_URL |
https://127.0.0.1 |
forgectrl over HTTPS (self-signed, unverified), for the login test |
GF_SYSFS_ROOT |
/sys/glowforge/ |
kernel module sysfs |
GRBL_HOST, GRBL_PORT |
127.0.0.1, 23 | Grbl TCP |
Adding a test
Register it in the subsystem module under forgetest/suite/ with
@test(...): id subsystem.name, kind, hardware, mode (the controller
mode the test needs; the runner switches to it first), covers,
requires, always, steps. The body gets a Context (log, check, fail,
prompt, confirm, instruct, sleep, evidence, forgectrl, sysfs,
grbl, takeover). Return normally for PASS, raise runner.Failed for
FAIL. Then run the unit tests and the coverage lint.