The lid lamp now has a resting policy in forgectrl (lid_lamp_idle, default 236, asserted at start and at every spawn), so the baseline expects it there instead of preserving whatever level a boot left, and forgectrl.settings-bounds proves it: resting at the setting, 256 / -1 / 'bright' refused, a new level applied to the lamp at once, the cleared key back to the default. Clearing a key goes through the query-string form (an empty JSON value reads as no setting). motion.liveness-probe adds the regression the bench needed: with every axis masked (cnc/motor_lock=15, as a bench tool may leave it) forgectrl is restarted and its fresh probe must read MOTION OK on the first try - the probe unmasks the axes itself - and the controller comes up with the mask cleared. Bench 2026-08-16: MOTION OK at p2p 2047/1341 against the 800 threshold, no ladder. The baseline's settle no longer counts 'probe verified, spawn pending' as settled (the post pass ran between the probe's own writes and the controller's init writes and mis-flagged motor_lock/step_freq): it waits for the controller to be running, with a bounded allowance for a respawn backoff.
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
docs/ACCEPTANCE.md.
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>:8080 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_MARKER |
/run/forgetest.active |
takeover marker |
FORGECTRL_URL, FORGECTRL_TOKEN_FILE |
http://127.0.0.1:8080, /data/forgefirm/panel.token |
forgectrl client |
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, 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.