From e3d23b80999bf706ea56584509cac81ab2348d37 Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Thu, 20 Aug 2026 09:23:10 -0400 Subject: [PATCH] Record the download guards and the feed watchdog BRINGUP's cloud item said a print is capped by the ring and listed the two gaps that closed when it stopped being. It now says what bounds a print (memory, through pulse_reject_threshold_bytes) and what catches a feed that wedges (progress against room, not ring depth), and it names the two new cases that cannot be induced from the bench: a body past the memory guard, because the service has no such job to send, and a wedged feed, because a healthy machine will not stall on request. forgectrl.settings-bounds gains a probe at the far end of the new byte range, so the validator behind those keys is exercised rather than assumed. --- docs/BRINGUP.md | 26 +++++++++++++++++--------- forgetest/forgetest/suite/forgectrl.py | 7 +++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/BRINGUP.md b/docs/BRINGUP.md index 29383df..acbf2c3 100644 --- a/docs/BRINGUP.md +++ b/docs/BRINGUP.md @@ -972,15 +972,23 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`. for them under `/data` on a machine booted into the factory slot before deciding whether to reimplement the mechanism. Finally the deferred emulator homing-image smoke, now that the emulator can be pointed at live snapshots. -7. **Cloud mode.** The remaining gaps are tracked in - `python3-gfhardware/forgefirm-app/docs/CLOUD.md` "Outstanding items": - streaming-during-run (would lift the ring-size cap on job length), - oversize-job rejection against a real too-big job, packaged-path boot with - `controller_mode = cloud`, the three unobserved actions, the lid-flash LED, - and taking the pause constants from the pulse header (`CCbp`/`CCbt`) once a - capture confirms them. Not inducible from the bench: the - cancel-with-a-rejected-`settings`-action case and a malformed frame (needs a - MITM). The pulse header's unenforced safety envelope is item 19: it is +7. **Cloud mode.** A print is no longer capped by the ring: the client holds + the compressed body, fills the ring before the button, and tops it up as it + plays, with the body bounded by `pulse_reject_threshold_bytes` because + memory is what that costs. A feed that wedges is caught by progress rather + than by ring depth (a healthy feeder keeps the ring brim-full, so depth + only falls an hour after the feed died): thirty seconds of no progress with + room in the ring stops the job cleanly and retraces, and it resumes if the + feed moves again. The remaining gaps are tracked in + `python3-gfhardware/forgefirm-app/docs/CLOUD.md` "Outstanding items": a + live job longer than the ring (built and covered, never yet run from the + service), the memory guards against a real ceiling rather than a reasoned + one, packaged-path boot with `controller_mode = cloud`, the three unobserved + actions, the lid-flash LED, and taking the pause constants from the pulse + header (`CCbp`/`CCbt`) once a capture confirms them. Not inducible from the bench: the + cancel-with-a-rejected-`settings`-action case, a malformed frame (needs a + MITM), a body past the memory guard (the service has no such job to send), + and a wedged feed (a healthy machine will not stall on request). The pulse header's unenforced safety envelope is item 19: it is listed separately because the enforcement lands in the cooling engine and has to hold in GRBL mode too, not only under the cloud client. 8. **Shared machine services — remaining polish.** None of it blocking: diff --git a/forgetest/forgetest/suite/forgectrl.py b/forgetest/forgetest/suite/forgectrl.py index 329c579..3b1348b 100644 --- a/forgetest/forgetest/suite/forgectrl.py +++ b/forgetest/forgetest/suite/forgectrl.py @@ -129,6 +129,13 @@ def settings_bounds(ctx): ctx.log("POST /settings laser_disarm_s=99999 -> %s", st) ctx.check(st == 400, "out-of-range value -> %s, expected 400", st) + # The cloud download guard is bytes, so its range is far wider than the + # other numeric keys: check the far end is still a wall. + st, body = fc.post("/settings", data={"pulse_reject_threshold_bytes": "2000000000"}) + ev["pulse_bytes_out_of_range"] = st + ctx.log("POST /settings pulse_reject_threshold_bytes=2000000000 -> %s", st) + ctx.check(st == 400, "out-of-range byte limit -> %s, expected 400", st) + st, body = fc.post("/settings", data={"no_such_key_forgetest": "1"}) ev["unknown_key"] = st ctx.log("POST /settings no_such_key_forgetest=1 -> %s", st)