Commit Graph
377 Commits
Author SHA1 Message Date
ScottW514 89731e654b Record the bench proof on image 20260829190323 and the warm-loop flow check
CAMPAIGN-LOG: the three flowload t1 runs on the new image (judged rise
11.3, 11.1 and 11.8 C with the tube lit through most of each window, the
laser's share on the line), and Test 3, the flow check from a warm loop
(23.6 to 24.9 C baselines, 0 of 6 wrong, the 14.4 C limit 2.25 C above the
flow band and 3.67 C below the no-flow band).

flow_warm_validate.py takes the warm target and the warm-up budget as
arguments (defaults 28 C, 20 min), and the bench page passes them; the
warm-up still judges on the upstream sensor beside the heater, which is
why this run's baselines stopped near 25 C (BRINGUP item 1).

BRINGUP: item 1's open half is the loop above 25 C; item 22 holds what
remains of the flow check under load.

No catalog consequence: a bench tool's arguments and documentation; no
runtime behavior of the release image changes.
2026-08-29 15:55:21 -04:00
ScottW514 d577629c80 Pin grblHAL-glowforge a7dcdca and forgectrl 2f18b16: the arm reads the window, the flow check reads means
grblHAL-glowforge a7dcdca: the arm at the first laser-on is decided by the
window alone, and an RX overrun drops the overrunning line whole and stops
the job. forgectrl 2f18b16: the flow check's baseline and end are means,
and the tube's share of the rise is taken off before the limit. Both
verified with bitbake -c fetch.
2026-08-29 15:01:49 -04:00
ScottW514 9d9f235219 Add the sender-change and RX-overrun harness scenarios and the day's records
laser_lifecycle_test.py gains sender-change-mid-job (a laser-on against a
window closed while the spindle was on must prompt again) and rx-overrun
(a job written past the RX ring is reported, stopped in alarm with the
window closed, and a clean job arms after it). The flowload drill's
verdict parser accepts the engine's laser-share suffix.

BRINGUP: item 20 holds only the owed work; item 21 opens the mid-job
sender-change discussion with the Grbl expectation; item 22 is the flow
check under a lit tube; item 23 is the power-good line's meaning.
CAMPAIGN-LOG records the driver fix and the flow-check reading, both
host-proven.

No catalog consequence: harness scenarios and documentation; no runtime
behavior of the release image changes in this commit.
2026-08-29 14:58:21 -04:00
ScottW514 b5de7cda9d Add the flowload drill and record the flow check under laser load
The flowload drill in scripts/bench/live_fire_drills.py runs the two tests
of the flow-check plan: t1 fires two CW fills on the press with the check
at its defaults, t2 fires one fill of a chosen length with the check off,
and fit reads rise against dose over the t2 records with the ADC offset
steps masked. The sampler adds the heater output, /cool/status is polled
at 1 Hz with the fan gates, and every controller reply is kept. The job
is fed against the RX buffer's free count, M5 is acknowledged before a
run, a run is refused while the window is armed, and M2 is acknowledged
and the window's close is waited for.

BRINGUP gets item 20: the arm at the first laser-on is skipped while the
driver's spindle-state record reads on, the record is not cleared on
disarm, and the serial layer drops bytes on a full RX ring, so a job with
a lost M5 lets the next job run unarmed. CAMPAIGN-LOG records the Test 1
and Test 2 runs and their numbers.

No catalog consequence: a bench drill on the dev image and documentation;
no runtime behavior of the release image changes.
2026-08-29 13:54:41 -04:00
ScottW514 f84b9df040 Point the dev image recipe comment at the acceptance page on the site
Comment only. The edit changes the content hash of meta-forgefirm, which
the acceptance manifest counts as a platform change, so the next image
needs a full campaign. Accepted: a full campaign is planned.
2026-08-26 10:27:02 -04:00
ScottW514 533ae7e46d Move the build, release, and acceptance docs to the documentation site
The documentation site, docs.forgefirm.org, is the one home for the
developer documentation. BUILD.md, kas/README.md, and docs/ACCEPTANCE.md
are on the site under Developers, and they are deleted here. Every
reference points at the site: README.md, BRINGUP.md, UPDATE-SYSTEM.md,
the kas configuration comments, release.sh, the CI workflow comments, and
the releases, forgetest, and bench READMEs.

BRINGUP.md: "Next work" item 16, step timing under CPU contention, is
closed; the video work resolved it. The items after it are renumbered.
CAMPAIGN-LOG.md records the closure.

Documentation and comments only. No layer content changes, so the
manifest identity of the next image does not change. No catalog
consequence.
2026-08-26 10:00:42 -04:00
ScottW514 d17c348e91 Add the dpatch depth-witness drill
live_fire_drills.py gains dpatch: two rows of small serpentine-filled
patches on scrap. Row A is CW at feeds that give relative doses from 1.0
to 0.25. Row B is density 100, 80, 60, 45, and 30 percent at F600. The
operator matches each row-B patch to the row-A patch of equal depth. That
reads the light fraction of a density off the material, next to the
prediction of the thermopile. The drill samples sysfs at 25 Hz, as pcurve
does, and writes a JSON record to the bench data directory.

Bench tool only, on the dev image; no catalog consequence.
2026-08-26 10:00:42 -04:00
ScottW514 ac8c4318a7 Pin grblHAL-glowforge 015b23d: set_state pushes the whole laser state
The rapids after an M5 ship dark, and a job whose M3 runs at the level
the previous job ended at fires its first cut. Fetch-verified.

Catalog consequence: laser.m5-rapid-dark and the laser live tests cover
the driver; the pin lives in the pin file, so only those tests re-run.
2026-08-25 17:43:26 -04:00
ScottW514 fff0980079 Laser: the rapids after an M5 ship dark and the next job fires; the performance-curve drill
Stream harness rules 16 and 17 with their sessions: an M5 executed with
the planner drained and the kernel run over must darken the rapids that
follow it (m5-idle), and a job whose M3 runs at the level the previous
job ended at must still fire its first cut (next-job). The second rule
is the core's contract: set_state records the rpm and the per-segment
update is skipped while it is unchanged, so the driver's set_state is
the only thing that can light that move. Both sessions run under both
dose models; the bench build that went dark on its second job fails
next-job with one fire span.

Bench drills: pcurve (a per-level ladder of 100 mm lines read from the
HV current and the head thermopile at 25 Hz, with the instrument checks
and the JSON record) and m5dark (one line, M5, two rapids, judged on the
current trace and laser_on_sampled until the armed window closes).

Catalog: laser.m5-rapid-dark, a live test of the M5 case (46 tests; the
counts in BRINGUP follow). CAMPAIGN-LOG carries the day's record: the
two curve ladders, the defect pair, the root cause, the host and bench
proof.
2026-08-25 17:41:45 -04:00
ScottW514 0416986e52 Update README 2026-08-25 06:52:06 -04:00
ScottW514 9b4ff7e0f8 docs: the SoC under a full core, measured; the heatsink question closes
CAMPAIGN-LOG gains the 2026-08-24 drill: five minutes of openssl speed on
the one core over the live camera stream, 30 C chassis, plateau 70.8 C
with no throttle and every cooling device at state 0. BRINGUP's facts bank
drops its open question (whether ForgeFIRM's load wants the heatsink the
factory never fitted) for the measured fact: 14 C of headroom to the
passive trip under full load, a hot-chassis case above roughly 44 C.

Docs only, outside every layer; no acceptance consequence.
2026-08-24 20:13:35 -04:00
ScottW514 f90c7c01a2 docs: the release authorized on dev 20260824230512; the kernel trim, the video offload and two working files close into the log
The attended nine passed after the unattended 36, so campaign
c-20260824231028-b7ca closed 45 of 45 from nothing and authorizes the
image. BRINGUP now describes the present: the 45-test catalog with the
bench actuator, the latest campaign, the board-only kernel's shape in the
facts bank (the performance governor, the SDMA clock ownership,
pstore/ramoops, MemTotal), the GPU stream path as the proven default.
Items 20 and 21 close; item 12 keeps only its leftovers and item 13 the
first release; item 16 records the hog case passing and the stream-live
drill still owed; a new item 20 carries the image trims the kernel review
left on the table.

CAMPAIGN-LOG gains the dated record of the attended run and, under the
superseded notes, the closed items as they stood plus the acceptance
burden plan and the kernel configuration review verbatim; both tree-root
working files are deleted.

No catalog consequence: documentation only.
2026-08-24 19:53:38 -04:00
ScottW514 4a7d11d48d Pin forgectrl to 78efd16: the loopback peer check in src/peer.c with its unit test 2026-08-24 19:02:52 -04:00
ScottW514 243320f65d ulfius: client_address carries the whole peer sockaddr; forgectrl.auth asserts the loopback report is accepted
The dual-stack listener reports every peer as a sockaddr_in6; ulfius
2.7.15 copied sixteen bytes of it, so forgectrl's loopback-only cooling
channel refused the controller's every report (403 loopback only) and
the engine never saw a run or an armed window. The recipe carries the
patch: a sockaddr_storage allocation and a copy of the family's length,
in the dispatcher and in ulfius_copy_request.

forgetest: forgectrl.auth asserts POST /cool/state from loopback -> 200
beside the LAN 403, and covers src/peer.*. BRINGUP item 21 and the
campaign log record how the campaign on dev 20260824215906 found it.
2026-08-24 19:02:52 -04:00
ScottW514 4d3d173445 forgetest: image.health asserts the SDMA clock enable count; the gated engine recorded
The check reads /sys/kernel/debug/clk/sdma/clk_enable_count and fails
below 1, naming the fault directly instead of the free-space mismatch it
surfaced as after the 150 s settle. BRINGUP item 21 describes the fault
and the fix; CAMPAIGN-LOG carries the record of how it was found.

Covers: image.health already maps kernel-module-glowforge/** and
linux-fslc/**.
2026-08-24 17:51:18 -04:00
ScottW514 dc43ccf3bf docs: IPv6 proven end to end (CAMPAIGN-LOG, BRINGUP item 21)
With the access point's RA and DHCPv6 off, the board took the firewall's
lease; every service answered on the global address from another VLAN
and the board reached the IPv6 WAN gateway.

Docs only; no catalog consequence.
2026-08-24 16:42:19 -04:00
ScottW514 76906f3aef docs: the second DHCPv6 responder (CAMPAIGN-LOG, BRINGUP item 21)
The missing GUA was not the firewall: its DHCPv6 server advertises an
address, but an access point on the bench VLAN still runs RA and DHCPv6
in server mode, its Advertise with nothing to give arrives first, and
busybox udhcpc6 keeps the first Advertise it sees. Dev 20260824201945
carries patch 0015 and the last stray dmesg line is gone.

Docs only; no catalog consequence.
2026-08-24 16:38:17 -04:00
ScottW514 57b428ebd4 docs: the second kernel round on the bench (CAMPAIGN-LOG, BRINGUP item 21)
The 2026-08-24 second-round record on dev 20260824200726: the dmesg
lines gone, the UP kernel on the performance governor, the two-file
WL18xx firmware set, every port answering over IPv6 on the ULA, the
export, and the two leftovers (no GUA from the DHCPv6 server, the NVS
loader line that patch 0015 removes). Item 21 says what remains.

Docs only; no catalog consequence.
2026-08-24 16:19:06 -04:00
ScottW514 7ca1c0f936 images: the release trim is a variable the dev image blanks
Removal specs accumulate and apply after every append, so a dev-recipe
IMAGE_INSTALL:remove could not give nano back. The release recipe now
removes ${FORGEFIRM_RELEASE_TRIM} (default nano) and the dev recipe sets
it empty; the spec expands when it is applied.

No catalog consequence: image composition only.
2026-08-24 16:06:37 -04:00
ScottW514 db2baa03bb dev image: keep nano by re-setting the removal list
IMAGE_INSTALL:remove applies after every append, so the release recipe's
removal of nano also stripped it from the dev image that appended it back.
The dev recipe now re-sets the removal list to gfui-client alone.

No catalog consequence: dev image composition only.
2026-08-24 16:04:09 -04:00
ScottW514 4e79ee5f92 Pin forgectrl to cf8cf1e and grblhal-glowforge to 58b8379: dual-stack listeners, the pstore export 2026-08-24 15:40:19 -04:00
ScottW514 326c3e758b IPv6 on, forgetest dual-stack, the release rootfs sheds 22 MB
The distro keeps its ipv6 feature (busybox networking and ifupdown
inet6, openssh, ntp and rsyslog follow); forgetest binds "::" by
default, one dual-stack socket that still serves IPv4. The release
image drops nano (with it file and the libmagic database, 8.7 MB; the
dev image keeps nano), the udev hardware database (7.7 MB of USB and
PCI identities for buses the board lacks, via BAD_RECOMMENDATIONS) and
urllib3's pyOpenSSL/cryptography recommendation (6 MB; nothing imports
them, TLS goes through the standard library). BRINGUP item 21 records
the round and the bench checks it owes; item 16 gets its re-measure
plan on the UP kernel with the performance governor.

Coverage: image.* covers the rootfs composition and the distro conf
(platform); forgetest's own suite covers the server.
2026-08-24 15:40:19 -04:00
ScottW514 dd6f459d7b docs: kas README is build procedure; status moves to BRINGUP and the log
kas/README.md keeps the build, lockfile, pin and release procedure and a
short build-time facts list; its push/release checklist and Scarthgap
migration backlog are archived verbatim in CAMPAIGN-LOG ("Superseded
status notes"). The real-time argument (PREEMPT, why PREEMPT_RT is not
selectable or needed) joins the BRINGUP hardware facts bank next to the
ring measurements it rests on; the camera pointers go to the 0011-0013
patch headers; the image recipe's gfui-client comment drops its README
pointer.

Docs and a recipe comment only; no catalog consequence.
2026-08-24 14:58:09 -04:00
ScottW514 1e059bb90c forgetest: the camera tests cover the IPU stride-fix crop
forgectrl's src/ipu_copy.{c,h} sit on the GPU render path that feeds
both the JPEG grab and the H.264 encoder, so every camera.* test already
exercises them; the coverage lint reported both files uncovered. Added
to _CAM_COVERS. Lint on the tree manifest: 0 uncovered paths, 45 tests.
2026-08-24 14:53:08 -04:00
ScottW514 cb9cd53bda docs: the kernel built for one board (BRINGUP item 21, CAMPAIGN-LOG)
BRINGUP gains Next-work item 21, the kernel trim and what it still owes
(a GRBL job and the acceptance campaign); CAMPAIGN-LOG records the
2026-08-24 review, the trim, and the bench drill on dev 20260824164619
through the forced-panic read-back from pstore.

Docs only; no catalog consequence.
2026-08-24 14:17:48 -04:00
ScottW514 00ded74189 forgetest: the fixture spaces its presses; an unattended refusal is an error; the baseline resets out of a hold
The first campaign with the bench actuator wired failed
motion.button-hold-resume on the tool, not the machine: the second press
was asked while the first 200 ms pulse was still on, the fixture answered
409, the runner handed the step to an operator who was not in the room,
and the post pass could not jog a controller left in Hold.

- fixture.py: a press waits for the last pulse to end (the fixture's
  pulse_ms) plus a 300 ms release, so the controller sees the edge; a
  409 for a pulse in progress is waited out against button_pulsing and
  retried once.
- runner.py: in an unattended run a fixture refusal ends the test at
  once as ERROR naming the refusal; the operator fallback stays for
  attended runs.
- baseline.py: a controller in Hold or Door gets a soft reset before the
  return jog, position kept.
- tests: the fake fixture refuses a press while one is in progress and
  reports button_pulsing; FakeGrbl records ^X and can land a reset in a
  chosen state; five new tests.
- docs: ACCEPTANCE.md fixture rules, fixture/README.md tool's side.

No catalog consequence: tool-side change, no covers map moves.
Bench: campaign c-20260824174545-0bdc 25/25 with every action by the
fixture; the hold reset proven by a dry drill.
2026-08-24 14:02:59 -04:00
ScottW514 e99888753f forgetest's page moves onto Bootstrap with forgectrl's theme; forgectrl pinned at the panel overhaul
The acceptance page is assembled by page.py from forgetest/forgetest/ui/
(index.html, page.css, help.js, app.js) plus theme.css and the vendored
Bootstrap files, which are byte for byte the ones forgectrl's panel
carries, so the two pages look like one product and share the light and
dark themes (same localStorage key). A plain file is read in a checkout;
on the dev image the recipe installs ui/ gzipped and page.py reads the
.gz sibling, inflating once at first request: the rootfs is raw ext4, so
bytes in the package are bytes on the image. The explanatory prose
(campaign rules, the queues, the campaign actions, the prerequisites
switch, the bench intro) is a "?" popover with a link into the
documentation site; operator steps, prompts, notices and the live-laser
acknowledgment stay in the page, and confirmLive() stays a blocking
dialog. The page's own rules hold: rows, prompt buttons and tool entries
are built once and updated in place, and the popovers sit on static
markup only, so no rebuild orphans one. On a phone the Run pane goes to
the top for the duration of a run.

scripts/check-ui-vendor.py compares the shared files against forgectrl
at its pinned revision (or a local checkout with --forgectrl); it runs
in forgetest-ci.yml, so the copies cannot drift.

Tests: test_page.py (the gzipped install assembles to the same bytes as
a checkout, one self-contained response, the token placeholder once, a
missing marker refused); test_server asserts the served page's
invariants; test_responsiveness keeps its rules with needles pointed at
the new files, its ASCII rule applied to our own sources (Bootstrap's
CSS carries an em dash of its own), and its self-contained rule testing
asset tags rather than the presence of https:// (the documentation links
are meant to be there). forgectrl.panel-serves gains two needles for the
panel's theme attribute and save bar. Proof: the unit suite, and the
page in Chrome against a fake catalog (both themes, popovers, the bench
tab, a full operator run with its prompt, abort).

forgectrl pinned at 9d1f6f2 (the panel on Bootstrap, one save bar, help
popovers, themes, the gzipped page); PV unchanged. The pin moves only
forgectrl's fingerprint. The forgetest changes are the harness's own and
have no catalog consequence.
2026-08-24 12:13:53 -04:00
ScottW514 bb186247d1 BRINGUP: the video offload's bench validation is complete; the campaign rides the release flow, not item 20 2026-08-24 10:48:48 -04:00
ScottW514 9887606cfc forgectrl pinned at the browser-proven H.264 view; jog coexistence in the record
The pin moves to forgectrl d97cb35: zero-based fragment timestamps and
the panel's clamped live-edge seek, the two faults MSE playback in a
real browser surfaced. CAMPAIGN-LOG carries the session record,
including the coexistence drill (a jog at commanded feed under both
live streams, underruns unmoved, planner buffer full). BRINGUP item 20
is down to one line: the full acceptance campaign on an image carrying
this pin.
2026-08-24 10:29:01 -04:00
ScottW514 62992fbd7d forgectrl pinned at the two-frame pipeline: ~14 fps on the GPU path
The pin moves to forgectrl deee6a1: the render overlaps the previous
frame' copies and encodes behind an EGL fence, measured 13.8 fps
single-viewer at ~14 percent CPU and 9.8 fps with both stream types
served at once, luma bit-clean throughout. CAMPAIGN-LOG carries the
dated record; BRINGUP item 20 is down to MSE playback, the
coexistence drill, and the campaign.
2026-08-24 09:59:32 -04:00
ScottW514 24dd2b49f6 forgectrl pinned at the 64 ms render; frame skip proven; the record
The pin moves to forgectrl 2d59d78: the chroma passes point-sample
instead of box-averaging, taking the GPU render from 140 ms to 64 and
the stream to ~9 fps at ~7 percent CPU, with luma measured bit-clean
against the CPU path (which also retired the bottom-row artifact).
The CSI hardware frame skip is live-proven with the GPU path and is
the recommended low-CPU configuration. CAMPAIGN-LOG carries the dated
second-session record; BRINGUP item 20 now lists the render-encode
overlap as the path to 15 fps, then MSE playback, the CPU measure,
coexistence, and the campaign.
2026-08-24 09:34:51 -04:00
ScottW514 904dfb4acb forgectrl pinned at the bench-proven GPU path; the session's record
The pin moves to forgectrl 6614833: the five hardware corrections from
the GPU demosaic's first bench session (surfaceless EGL config, ARGB
render targets, GR88 raw import, the ipu_copy stride-fix crop between
the GPU and the CODA, the chroma mirror width). CAMPAIGN-LOG carries
the dated record of how each was found; BRINGUP item 20 now separates
what that session proved (the full GPU -> IPU -> VPU path serving
correct frames, H.264 as valid fragmented MP4 on hardware, texture
limits, slot fit) from what remains (the 140 ms render, the bottom
row, MSE playback, the CPU measurement, frame skip, coexistence, the
campaign).
2026-08-24 09:11:50 -04:00
ScottW514 a47c0c7f6d Video offload: Mesa etnaviv in the image, forgectrl pinned, the H.264 stream in the catalog
The stream conversion now runs on the GC880 when the image carries the
GL stack, and /cam/h264 serves the same picture as fragmented MP4 in a
fraction of MJPEG's bytes (forgectrl 6573abd). This side supplies what
that needs and holds it to account:

- Distro: opengl stays a feature, with mesa trimmed to exactly the
  path used (gallium etnaviv, GLES/EGL/GBM, no GLX, no display
  platforms). Image: libegl-mesa, libgles2-mesa, libgbm,
  mesa-megadriver. forgectrl dlopens them, so an image without them
  still streams on NEON. Platform change: the next campaign is full.
- forgetest: camera.h264-stream exercises the endpoint end to end
  (codec header, init segment, media fragments, /cam/status encoder
  state) and accepts a clean 503 on a machine without the stack;
  the camera covers name the new forgectrl files.
- VIDEO.md 5.6 now describes both streams and why H.264 earns its
  place (the WiFi transmit path measures about 7 percent of the core
  per MB/s sent); BRINGUP records the bench-validation checklist as
  Next work item 20, including the release-size watch: mesa must fit
  the 200 MiB slot gate.
2026-08-24 08:10:33 -04:00
ScottW514 a2b391b8f6 forgectrl pinned at the status-page sys block; panel-serves reads it
The pin picks up /status carrying CPU and memory utilization (sys:
cpu_pct over the interval since the previous read, mem_pct from
MemTotal against MemAvailable) with the panel rows under the board
temperatures. forgectrl.panel-serves now re-reads /status after a
beat and asserts both percents are numbers in range, so the catalog
exercises the new telemetry on the bench.
2026-08-24 07:07:49 -04:00
ScottW514 f54d086625 forgetest: the fixture's resolver on a many-homed host; a missing box said once
The mDNS lookup sends its query from every local IPv4 interface (a
host whose default route is a VPN or a VM adapter never reached the
bench network), binds the mDNS port and joins the group as two
separate steps (a group join that fails left the socket half bound),
and a failed send is no answer rather than an error. The runner says
"running without it" once per reason, not on every 30 s probe.
2026-08-23 14:53:20 -04:00
ScottW514 7d982070c0 BRINGUP: the interlock loop is J8 (J6 is the speaker); the 3.3 V rail carries the fixture's coils 2026-08-23 14:35:36 -04:00
ScottW514 b54b94e8ae forgetest: the bench actuator plugs into the action seam
fixture.py: the bench's /data/forgetest/fixture.json (hostname, key,
optional ip, the channels wired, arm_press), a resolver for
<hostname>.local asked of the network directly (the image has no mDNS
resolver), and the client. The runner probes it before every run and
at most every 30 s otherwise; ctx.act asks it for a channel it covers
and still waits for the machine's own reading, falling back to the
operator's notice when the box fails. A test declares with hands=(...)
what it asks of a person beyond its typed actions; an operator test
with none, whose actions the fixture covers, is routed into the
unattended queue, its Ready gates pass, and a prompt it raises anyway
is a FAIL naming the undeclared step. Live tests never move; their arm
press stays a person's unless the bench opted in, in which case the
fixture presses when the button lights. Whatever the box still holds
after a run is released before the baseline's post pass and recorded.
The page shows what the fixture covers. Contract in ACCEPTANCE.md; the
wiring facts, with the interlock connector left to the bench to settle
(SAFETY.md and the sister map differ), in BRINGUP.

Catalog unchanged in its definitions; the cloud and laser suites'
shared code moved, so their implementation hashes move with it.
2026-08-23 14:15:53 -04:00
ScottW514 8ee4ee3946 forgefixture: the bench actuator's firmware
An ESP32-S3 DevKitC-1 on the bench network drives three relays at the
machine's connectors so the acceptance tool can open the lid loop, pull
the interlock loop and press the button with nobody in the room. Two
contacts are normally closed in loops the safety chain already reads,
the third is normally open across the button input and only ever
pulsed (20 to 500 ms, the end armed before the line rises); every line
is low at boot and after any reset, the task watchdog panics and
reboots, and the button channel needs a jumper. HTTP on port 80 under
a key in X-Fixture-Key; the hostname announced over DHCP and mDNS.

ESP-IDF v5.5 native, the mDNS component pinned in dependencies.lock;
fixture.env (git-ignored) is the one input: the wifi, the key, the
hostname. fixture.sh builds with idf.py or in the espressif/idf
container, flashes with esptool from pip. The decisions that need no
hardware live in policy.c with a gcc host test; CI runs it and builds
the firmware in the pinned container.
2026-08-23 14:15:53 -04:00
ScottW514 335c6dea9d forgetest: non-behavioral paths outside every fingerprint; hollow covers fail the lint
The coverage lint already allowed docs, CI, unit tests and licenses to
go uncovered; the same list now keeps them out of every fingerprint,
so a README edit in any component re-requires nothing. The list moves
to the manifest module as NON_BEHAVIORAL, the one place both uses read
it. And a coverage entry that selects no file of its component (a glob
without the recipe's subdirectory, a component the manifest lacks, a
glob naming docs only) fails the lint: such an entry covers nothing and
the test's fingerprint ignores the file it meant. The contract says
both. Every test whose maps reached a doc or a test file gets a new
fingerprint once.
2026-08-23 12:54:55 -04:00
ScottW514 53e4fa20e9 forgetest: the cloud tests' coverage maps follow the split
The six job tests covered all three cloud components whole, so a
one-line change anywhere re-required one real print and five attended
tests. The maps now say what each test proves: the protocol test the
web session, the emulator and its fixtures; the offline tests the run
loop, the hardware it drives, the offline dispatch and the pulse path;
cloud.mode-switch the homing path; every one of them the client's
common ground. The one real print keeps the coarse maps as the
integration and the lint's floor. Two entries of the protocol test
named app files without the recipe's subdirectory and selected nothing;
the paths are spelled out now.
2026-08-23 12:54:55 -04:00
ScottW514 e5fa4445c0 BRINGUP: the service protocol proven by the emulator; CAMPAIGN-LOG 2026-08-23
Dev image 20260823161333, campaign c-20260823161923-0dd7, 44 of 44,
release authorized. The cloud split of the acceptance plan is complete:
the protocol by the emulator with only the app to drive, the machine's
print behavior by the offline service, the two together by one real
print, and the service's hunt paid only where it is the subject.
2026-08-23 12:38:41 -04:00
ScottW514 8379aa5451 Pin forgefirm-app to 537d0db: the emulator reports idle to the cooling engine 2026-08-23 12:10:21 -04:00
ScottW514 ab0a515a85 forgetest: the supervisor's levers get a timeout above the daemon's own waits
POST /mode, /controller/start and /controller/stop answer only when the
switch is done: the old controller gone, the new one started after any
pending liveness probe, and its first job-state report in (15 s without
one). The client's 10 s timeout read a slow but honest switch as a dead
daemon and errored cloud.service-protocol on the bench; those three
paths now get 120 s. No catalog consequence: the tests and their covers
are unchanged, the client only waits longer.
2026-08-23 12:10:20 -04:00
ScottW514 908e0c76b8 Pin forgefirm-app to 351a623: gfcloud --no-hunt, the markers read first thing 2026-08-23 11:27:36 -04:00
ScottW514 969bac6013 forgetest: the service's hunt paid only where it is the subject
A cloud client the tool starts for anything but homing comes up under
the /run/gfcloud-nohunt marker: the real client back after the
emulator, a mode the runner switches to or hands back, a controller it
restarts. The service keeps the head position it has. cloud.mode-switch
and cloud.service-protocol keep their hunts, and so does the one real
print: enter_cloud reuses a running session only when that client has
hunted the machine itself (session_hunted: never the emulator's, never
a no-hunt start), otherwise it restarts the client with the hunt, since
a print placed on a head position the service only believes can run the
gantry into a rail. The markers are one start, taken down by the client
that read them first thing; the tool's own removal stays for a start
that never happened. Catalog unchanged; the cloud tests' shared code
moved, so their implementation hashes move with it.
2026-08-23 11:27:36 -04:00
ScottW514 5c7727b9bf Pin forgefirm-app to b7e8035: gfcloud --emulate keeps the service's connect-time hunt 2026-08-23 10:36:36 -04:00
ScottW514 4c9dcca7e6 BRINGUP: the emulator test built into dev image 20260823002125, its full campaign owed 2026-08-22 20:37:41 -04:00
ScottW514 1c8197faa3 forgetest: cloud.service-protocol, the service answered by the emulator in this machine's identity
The service-protocol half of the cloud catalog on its own test: the cloud
client restarted as gfutilities' emulator under the /run/gfcloud-emulate
marker signs in, passes the firmware check, opens the WebSocket, answers
the connect-time hunt and the image requests with the dev image's canned
frames, and runs a print from the app through the real download path to
':completed' - nothing moves, nothing arms, and only the app has to be
driven, by a person or an agent through the prompt API. The real client
is restarted afterward and its hunt waited out. session_live now knows
the emulator's session is not the machine's, so enter_cloud restarts it
rather than reusing it; restart_client is the one restart the offline
and emulator entries share.

The dev image adds python3-gfutilities-emulator (the fixtures, packaged
on their own in meta-openglow); forgefirm-app moves to 12ad3b1 (gfcloud
--emulate). Catalog: 44 tests, 27 auto / 9 operator / 8 live; the new
test covers the gfutilities service layer and examples/, which step 4
will take off the other cloud tests. Replays over the prompt script;
contract and BRINGUP updated. A layer change (the dev image recipe):
everything re-requires on the next image.
2026-08-22 20:19:46 -04:00
ScottW514 0cb9044e1d 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.
2026-08-22 19:59:36 -04:00
ScottW514 628f2f7a7f Pin forgefirm-app to a3ca36f: gfcloud --offline
The same python3-gfhardware commit meta-openglow pins; the cloud client
learns the offline service (actions on /run/gfcloud-offline.sock, no
web session) for the acceptance tests.
2026-08-22 19:20:38 -04:00