forgetest: the offline jobs removed after each test; the record of the first offline campaign

Every offline test now removes the jobs it wrote under /tmp/forgetest
on its way out (the bench rule: nothing left behind in the session that
put it there). CAMPAIGN-LOG gets the 2026-08-22 entry for dev image
20260822232347: the offline service dry-checked, then campaign
c-20260822233344-08de, 13 run and 43 of 43 with the four offline tests in
5.5 minutes, and what the machine said under it. BRINGUP: the offline
service is done and bench-validated.
This commit is contained in:
ScottW514
2026-08-22 19:59:36 -04:00
parent 628f2f7a7f
commit 0cb9044e1d
3 changed files with 57 additions and 5 deletions
+5 -5
View File
@@ -1155,11 +1155,11 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
(the operator channel, the merged mode-switch, the sensor witnesses, (the operator channel, the merged mode-switch, the sensor witnesses,
the steps pane, the journal, per-test implementation hashing) is done the steps pane, the journal, per-test implementation hashing) is done
and bench-validated (CAMPAIGN-LOG 2026-08-22). The offline cloud and bench-validated (CAMPAIGN-LOG 2026-08-22). The offline cloud
service for the machine-behavior tests is code-complete (gfutilities service for the machine-behavior tests (gfutilities `OfflineService`,
`OfflineService`, `gfcloud --offline`, `forgetest/puls.py`, four tests `gfcloud --offline`, `forgetest/puls.py`, four tests re-ported) is done
re-ported) with host replays; **its bench run on the next dev image is and bench-validated on dev image `20260822232347` (43 of 43, the four
owed.** A bench actuator for the lid, interlock and button is planned, offline tests in 5.5 minutes, nothing on the bed). A bench actuator for
not started. Catalog the lid, interlock and button is planned, not started. Catalog
gaps left from the tool's own plan: `cooling.confirm-escalate` and gaps left from the tool's own plan: `cooling.confirm-escalate` and
`cooling.fire-gate-blocks-arm` are not ported (both need the pump switched `cooling.fire-gate-blocks-arm` are not ported (both need the pump switched
by hand mid-run, so they are bench-tab material first), and whether by hand mid-run, so they are bench-tab material first), and whether
+39
View File
@@ -3503,6 +3503,45 @@ campaign asked for was performed by the operator and recorded so in
the evidence; a bench actuator, when there is one, takes the same the evidence; a bench actuator, when there is one, takes the same
calls. calls.
## 2026-08-22: the machine's print behavior without the service
Dev image `20260822232347` (forgefirm 628f2f7; python3-gfutilities 768730e
and python3-gfhardware a3ca36f pinned by meta-openglow a52e68c and the
forgefirm-app pin), the first image with the offline service: gfcloud
restarted under the `/run/gfcloud-offline` marker comes up with no
account and no network, takes the service's action messages on
`/run/gfcloud-offline.sock`, and hands the machine's events back. Four
cloud tests run on it with a job synthesized on the board
(`forgetest/puls.py`: a factory print's header over a square the laser is
never commanded on): the lid and interlock aborts, the button-wait
cancel, a paused print ended by the lid, and a print longer than the ring
ended the way the app ends one. Nothing on the bed; the arm press is the
operator's only hand on a print.
Before the operator's run the plumbing was dry-checked from a shell:
stop, marker, start, the `OFFLINE service` line and the socket, a
`settings` action answered `settings:completed`, then a restart without
the marker and the web session `ready` again. One lesson from the dry
script, not the harness: the marker has to stay until the offline line
is logged, because the supervisor reports the client running seconds
before Python has finished importing and read it.
Campaign `c-20260822233344-08de`: 30 of 43 inherited across the pin bump
(the catalog's covers put every `cloud.*` test on the moved components,
and the core always runs), **13 run, 13 PASS, 43 of 43, release
authorized**; 11 minutes of test time, the four offline tests 5.5 of it.
No release cut. What the machine said under the offline service: the
lid edge to the stop 10 ms; the button-wait cancel with no run started;
the paused print cancelled by the lid, parked to the job start, latch
locked, button dark; the long job (33.4 MiB of ticks in an 87 kB gzip)
live-fed with the kernel's program total climbing 33.29 to 34.60 MB while
the report divided by the job's 35.0 MB, no underrun, the backtrack held
at 164 214 steps, the pause and resume taken, and the cancel's tail the
same as the lid's. Every print's `print:running`,
`print:return_to_home:succeeded` and `print:cancelled` came back over
the socket. The real service was still proven on the same image by
`cloud.mode-switch` and the one real print, `cloud.pause-resume`.
## Superseded status notes ## Superseded status notes
### Shared machine services — remaining polish, as listed 2026-08-13 ### Shared machine services — remaining polish, as listed 2026-08-13
+13
View File
@@ -720,6 +720,15 @@ def offline_events(off):
return [e.get("event") for e in off.events if e.get("event")] return [e.get("event") for e in off.events if e.get("event")]
def offline_cleanup(ctx):
"""The jobs the offline tests wrote are gone when the test is (tmpfs
would lose them at reboot; the bench rule is sooner)."""
import shutil
if os.path.isdir(JOB_DIR):
shutil.rmtree(JOB_DIR, ignore_errors=True)
ctx.log("offline jobs removed from %s", JOB_DIR)
def offline_job(ctx, name, **kw): def offline_job(ctx, name, **kw):
"""Write a synthesized, laser-never-commanded job for the offline """Write a synthesized, laser-never-commanded job for the offline
service and return its path (under JOB_DIR, tmpfs).""" service and return its path (under JOB_DIR, tmpfs)."""
@@ -858,6 +867,7 @@ def lid_interlock_abort(ctx):
lid_interlock_abort_body(ctx, ev, off, job, offset) lid_interlock_abort_body(ctx, ev, off, job, offset)
finally: finally:
off.__exit__(None, None, None) off.__exit__(None, None, None)
offline_cleanup(ctx)
def lid_interlock_abort_body(ctx, ev, off, job, offset): def lid_interlock_abort_body(ctx, ev, off, job, offset):
@@ -956,6 +966,7 @@ def lid_during_button_wait(ctx):
lid_during_button_wait_body(ctx, ev, off, job, offset) lid_during_button_wait_body(ctx, ev, off, job, offset)
finally: finally:
off.__exit__(None, None, None) off.__exit__(None, None, None)
offline_cleanup(ctx)
def lid_during_button_wait_body(ctx, ev, off, job, offset): def lid_during_button_wait_body(ctx, ev, off, job, offset):
@@ -1135,6 +1146,7 @@ def oversize_stream(ctx):
oversize_stream_body(ctx, ev, off, job, offset, before) oversize_stream_body(ctx, ev, off, job, offset, before)
finally: finally:
off.__exit__(None, None, None) off.__exit__(None, None, None)
offline_cleanup(ctx)
def oversize_stream_body(ctx, ev, off, job, offset, before): def oversize_stream_body(ctx, ev, off, job, offset, before):
@@ -1252,6 +1264,7 @@ def paused_lid_cancel(ctx):
paused_lid_cancel_body(ctx, ev, off, job, offset) paused_lid_cancel_body(ctx, ev, off, job, offset)
finally: finally:
off.__exit__(None, None, None) off.__exit__(None, None, None)
offline_cleanup(ctx)
def paused_lid_cancel_body(ctx, ev, off, job, offset): def paused_lid_cancel_body(ctx, ev, off, job, offset):