Files
forgefirm/forgetest
ScottW514 ee89b31c07 exthost.hold-pause-tier: a required hold from the grant to the verdict and out
The reference package takes the hold capability (its service ends at once
when the test leaves a file in its data directory: a package that cannot
speak for itself). The install is refused without the operator's grant; a
granted hold is advisory until `forgeext hold <id> required` names the
package under required-holds. With extensions on, the required hold stands
in the host's words until the service has run healthy, then the host keeps
its file fresh and clear and GET /cool/status reads OK. The test then makes
the service end at every start: verdict EXT, fire_ok false, hold true, the
reason naming the package, and never clear in 24 looks over the crash loop.
Safe mode ends it within four seconds and leaving safe mode brings it
back; marked advisory it is dropped; marked required again and the host
suspended, the reason becomes that the host is not answering, and resumed
it is the package's again; ext_enabled=0 ends it. The verdict is read at
idle: nothing moves and nothing fires. Everything is put back as
exthost.service puts it back, the required holds are empty, and the
verdict is OK at the end.

Proven. The unit suite: 451 tests, 0 undefined names. On the bench
reference, image 20260921121235 with the cross-built forgectrl and
extension host mounted over the image's: exthost.hold-pause-tier PASS
(held 1.0 s after the service was killed; safe mode released it in 0.8 s,
extensions off in 0.8 s); against the image's own forgectrl, whose engine
knows no holds, it FAILS with the verdict OK, as it should.

Acceptance. The test is new: auto, takeover. It covers forgeext's
src/holdkeep.*, src/run.*, src/install.*, src/state.*, src/main.c, and
forgectrl's src/holds.* and src/cool.*, and requires exthost.service.
2026-09-21 09:05:02 -04:00
..
2026-09-18 12:14:22 -04:00
2026-09-18 12:14:22 -04:00

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.