Commit Graph
49 Commits
Author SHA1 Message Date
ScottW514 cd00b0d16f Warm the loop by the mixed bulk; record the flow check to the heater's ceiling
flow_warm_validate.py warms in rounds (the heater at 50 percent for three
minutes, off, 45 s of circulation, the two sensors' mean read) and judges
the target on that mixed bulk, not on the upstream sensor beside the
heater, which reached any target within two minutes while the bulk had
barely moved. Run with a 28 C target the loop plateaus at 27.2 C in a
20 C room; six checks at 26.2 to 27.2 C read every verdict right, the flow
band under 11.9 C and the no-flow band over 18.0 C against the 14.4 C
limit. CAMPAIGN-LOG records the run; BRINGUP item 1 keeps only the
button-latch check, the flow check's bands holding from 19 to 27 C.

No catalog consequence: a bench tool and documentation.
2026-08-29 19:37:26 -04:00
ScottW514 9bfdba44c8 Record the calibrate tool's accepted run on image 20260829220329
CAMPAIGN-LOG: aa-offset-calibrate with its 3 s at 8 Hz sampling reads
16.2 counts with a spread of 4.5 over six edges, the value already
applied; the compensation's whole path stands on the bench. BRINGUP item
21 keeps only a second machine's value and the lit-tube toggling.

No catalog consequence: documentation.
2026-08-29 18:16:07 -04:00
ScottW514 e32c86fb10 Pin forgectrl 42cdb71; add the coolant offset check; record the compensation on the bench
The pin carries the calibrate tool's sampling fix (3 s at 8 Hz a side).
aa_offset_check.py proves the correction on a machine: M8 brings the fans
to the run profile, dark, while the raw coolant counts, /status and the
engine's readings are averaged before, during and after; it turns the
flow check off for its session and restores it. On the bench page as
aa-offset-check. CAMPAIGN-LOG records the tool's first run, the applied
value (16 counts) and the proof: the readings held within 0.1 C under
the run profile where they dropped 1.0 C uncorrected. BRINGUP item 21
names what remains.

No catalog consequence: a bench tool and records.
2026-08-29 18:02:46 -04:00
ScottW514 8831d9b20e Pin forgectrl f9b4893: the air-assist ground shift off the coolant readings
The pin carries the compensation (cool_aa_offset_counts, the
aa-offset-calibrate diagnostic, the panel's Apply); verified with
bitbake -c fetch. The catalog gains cooling.aa-offset-calibrate, which
runs the tool and checks its recommendation and spread.

scripts/bench/offset_probe.py is the differential probe that found the
source: one actuator switched at a time with both sensors at 25 Hz
(survey), the air-assist duty ladder, the gantry jogging under the fan,
and an armed dark dwell. CAMPAIGN-LOG records the four probes and the
result; BRINGUP item 21 names the setting and the calibrate as what
remains on this machine, and item 23 opens the initial commissioning
procedure.

No catalog consequence beyond the new case: a bench tool and records.
2026-08-29 17:36:02 -04:00
ScottW514 61b68c387c Add cooling.flow-under-load: the flow check with the tube lit through its window
A live catalog case: two 30 x 4 mm fills at full power on the press, the
window held open until the engine's flow verdict lands in the forgectrl
log, then M2 and the head brought back. PASS needs a verified line with
the laser's share on it (at least 0.3 C: the window and the fire
overlapped) and the judged rise at least 1 C under cool_flow_rise; an arm
refused by a gate names the gate. Passed on image 20260829190323
(heater rise 11.9 C judged, laser 0.6 off 12.5).

CAMPAIGN-LOG records the run and the day's two air-assist gate holds (a
bench connection, reseated); BRINGUP item 21 names the case.

Catalog: the case covers forgectrl src/cool.* and its neighbors; a new
test, so the manifest's coverage widens rather than moves.
2026-08-29 16:39:03 -04:00
ScottW514 5c282ce167 Add the senderchg and overrun bench drills; close BRINGUP item 20
Two live-fire drills in live_fire_drills.py: senderchg drops the sender
mid-line with the tube lit, reconnects and expects the next laser-on to
prompt again; overrun writes a 93-line fill at once into a running job
and expects the report, the alarm, the disarm and a fresh prompt after
$X. Both read hv_current and the thermopile for the instant emission
ends and open their "nothing lit" window 2.5 s after the event, since
laser_on_sampled is a one-second window count. Both passed on image
20260829190323; CAMPAIGN-LOG records the runs.

BRINGUP item 20 (the arm skipped on a stale spindle state) closes: fix on
the image, host and bench proven, covers in place. Its text moves to the
superseded notes; items 21 to 23 are now 20 to 22.

No catalog consequence: bench drills and documentation.
2026-08-29 16:05:59 -04:00
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 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 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 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 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 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 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 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 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 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 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 60db95634b docs: the first campaign with the operator's part cut down
CAMPAIGN-LOG: the 2026-08-22 entry for dev image 20260822204234 - the
three harness findings (the jog witness, the fading button LED, the
duplicated app cancel), the implementation-hash change they exposed,
and campaign c-20260822220701-a1c0: 43 of 43 from nothing, the 16
attended tests in 19 minutes, with what each new witness read on the
machine. BRINGUP: bench-validated, the latest campaign, the hash rule.
2026-08-22 18:48:09 -04:00
ScottW514 58d1ac4746 The envelope's close-out image: 18 of 18, and what the new lines said
Dev image 20260822182931 (forgectrl b27398a, python3-gfhardware e65cfc2):
every unattended test PASS. The record keeps what the instrumentation
said on the machine: the status temps with the SoC die, the run-end
range line with all three sensors, and the cloud client's first counted
gap line on a live hunt header (30 declared ignored, 49 undecided), so
the next decision on the undecided families starts from a number. Also
the stale-layer build that was stopped and rebuilt on the way.
2026-08-22 14:48:47 -04:00
ScottW514 6458f6aec0 The ceiling at its off end stands with the default critical line
forgectrl pin 368fd0c: the critical-line cross-check binds only while the
ceiling is a gate. cooling.critical-tier pins that a POST setting the
ceiling to its off end is accepted with the default line (the unit fake
mirrors the exemption); CAMPAIGN-LOG records the gate-off failure on dev
image 20260822165832 that found it, and the critical-tier pass.
2026-08-22 13:43:36 -04:00
ScottW514 9fae47cf14 The critical tier proven on a rising loop; the board temperatures watched
forgectrl pin 76115fd: the chassis LM75 and the supply sensor ride /status
as temps (degrees and a raw count), the engine ranges them over every run
session into one run-end line, and a critical fault that clears with its
session yields the reason to the standing hold.

Bench: critical_tier_drill.py (a bench tool now, registered as
critical-tier) sets the ceiling, the resume gate and the critical line a
few tenths above the live upstream reading and lets the engine's own
flow-check heater warm the loop through them inside one M8 session;
temp_calibrate.py gains supply-watch, supply-point and supply-fit for the
supply sensor against a thermometer on its heatsink, the fit shown beside
UAPI.md's unverified guess.

Catalog: cooling.gate-off checks the /status temps fields and the run-end
board-temperature line (the unit fake mirrors both, one new failure
case); cooling.critical-tier checks the reason after a faulted session.

Docs: CAMPAIGN-LOG entries for cooling.critical-tier on dev image
20260822154257 and the warm-loop drill (OVERTEMP at 10 s, CRITICAL at
14 s, the fault ending with the session); BRINGUP item 19, the facts bank
(board temperatures at idle), COOLING section 9, the bench README.
2026-08-22 12:56:40 -04:00
ScottW514 f51140e528 The coolant critical tier, with its catalog test
forgectrl pin a1875a8: cool_temp_critical_c (default 38 C) is the fail
tier above the coolant ceiling's pause: at or over it in a run session the
verdict is CRITICAL (fire blocked, hold, no resume this job), the fault
ends with the session, the settings API keeps it above the ceiling, and
its top turns the gate off.

Catalog: cooling.critical-tier (auto, 45 tests), driven through the
settings API like cooling.gate-off: a critical line at the ceiling is
refused (and undone should it ever be accepted); with the ceiling, the
resume gate and the critical line all under the coolant's temperature a
session reads CRITICAL rather than OVERTEMP with no resume and the reason
naming the tier and the coolant; after the session the ceiling alone
holds (OVERTEMP); with the critical line at its top the gate is off
(gates_off, the run-start log line) and the ceiling alone pauses;
restored, OK with nothing off. Four unit cases against a scripted engine.
_after_session takes the condition to wait for.

Docs: COOLING (verdict table, section 5, the settings table, the quick
reference), BRINGUP item 19 and the catalog count, the CAMPAIGN-LOG
paragraph for the fan-fault session rule on dev image 20260822145201.
2026-08-22 11:41:18 -04:00
ScottW514 c1591e4f69 The fan floors proven on a pinned image; a fan fault ends with its session
The dated record of dev image 20260822135848: the campaign of every
non-operator, non-live test at 18 of 18 PASS with the measured floors and
the operating-point rule (cooling.fan-gate-trips and the hunt leg of
cloud.mode-switch as recorded), and the unplugged-exhaust-fan drill:
AIRFLOW at the grace plus three ticks with the exhaust dead, the other
fans held, the reason relayed on the Grbl port, the replugged fan ok
inside the next session's grace.

The drill showed the fault riding into idle, where the hold canceled
jogs and would have refused the cloud print that re-proves the fan.
forgectrl pin d51dbdb: the fault ends with its run session, and the next
session judges every fan afresh. cooling.fan-gate-trips checks the
verdict is OK with no hold once the tripped session is over (the unit
fake mirrors it); its covers, and the cooling tests' shared covers, gain
src/coolfmt.* (the tree manifest carries the new files at the bumped pin,
and the lint was right to ask). cloud.mode-switch samples the hunt's gate
rows twice a second: a hunt's run phase is a few seconds long.

Docs: COOLING 3a, BRINGUP item 19, CAMPAIGN-LOG.
2026-08-22 10:46:41 -04:00
ScottW514 adcd1adb9a Fan floors from the measurement; a hunt is measured, not judged
forgectrl pin 47e4256: the airflow floors set from the bench measurement
(exhaust 6400, intake 2290, air assist 6000 rpm, purge current 300, grace
15 s) and the operating-point rule: a fan is judged while the laser is
armed, when a job's profile may raise it but never lower it below the run
duty, or whenever it is commanded at the run duty; the service's hunts,
sent with the extraction fans off, are measured and published unjudged.

Catalog: cloud.mode-switch now waits out the connect-time hunt sampling
/cool/status and requires no AIRFLOW, the exhaust row unjudged and the
exhaust actually off; its covers gain forgectrl src/cool.* and
src/airflow.*. cooling.fan-gate-trips uses an 8 s test grace (the
intakes take 7 s to 90 percent and tripped under the old 2 s at the new
floor) and its off leg waits for the row state as well as gates_off.
fan_floor_measure.py names a reply that is not JSON and calls the purge
readings idle and run (the pump is always on).

Docs: COOLING 3a and the settings table, BRINGUP item 19 and a facts-bank
entry with the measured fan speeds, the bench README, and a CAMPAIGN-LOG
entry for the measurement, the two status-document finds, the hunt find
and the hot-deployed bench runs of both tests.
2026-08-22 09:56:53 -04:00
ScottW514 5249c1ecf8 CAMPAIGN-LOG: the job's limits pass through on a live session
cloud.pause-resume passed on dev image 20260821220926 with the service's
hunt windows (10 to 50 C) ignored as looser and the print's window (33 C,
5 C floor, 116 rpm air-assist floor) matched. The test now quotes the
print's job-limits line rather than the session's first (a hunt's), and
keeps the engine line that carries the header beside the last one.
forgectrl pin moves to e0b41b3 (the "not stricter" notice once per value).
2026-08-21 18:39:43 -04:00
ScottW514 8ad3f79e26 CAMPAIGN-LOG: cooling.gate-off passes on the bench
First bench run of the gate-settings pattern: PASS on dev image
20260821210903 after the harness fix in f274eb1; the entry records the
failed first attempt and why it was the test's fault. BRINGUP item 19
carries the bench result.
2026-08-21 17:48:10 -04:00
ScottW514 f4b70a0826 BRINGUP: the first pin-file campaign is done; pin the cloud client
The full acceptance campaign on dev image 20260821181036, the first built on
the <recipe>-pin.inc layout, satisfied 42 of 42 and its export authorizes a
release, so the status header, the acceptance section and item 12 describe
that rather than a campaign still owed; the catalog count is 42, not 35.
CAMPAIGN-LOG records the completion. Item 7 stops listing the cloud work the
campaign closed (the oversize live print, the progress bar watched from the
app, packaged-path cloud boot, the lid flash), records the operator's
declines (SPKI pinning, emulator parity, the ten-event pause machine) and the
reasoned memory guards, and item 19 no longer lists MCsn and PDfm as
unchecked: the client refuses both, so seventeen mandatory tags are dropped,
not nineteen.

forgefirm-app pin moves to b82d379 (python3-gfhardware): CLOUD.md and three
present-tense comments, no behavior change. No catalog consequence beyond the
pin bump itself, which the manifest scopes to the component.
2026-08-21 15:34:25 -04:00
ScottW514 9c22240480 Hold the job start across a pause in the lifecycle harness
laser.pause-resume-lid-cancel failed on the bench with the head 14.925 mm
along the first leg after the lid cancel: the driver took every resume
from a feed hold as a new job start (the core restarts a held cycle
through Idle), so the cancel returned the head to the pause point and
said "returned to the job start" about it. The driver fix is in
grblHAL-glowforge; this is its proof and the record.

The null-sink lifecycle harness gains rule 9: a job paused and resumed by
the button, then cancelled by the lid, returns to where the job began
(reproduces the bench failure to the millimeter on the old driver,
X=13.088 instead of 0); and a job abandoned in a hold and soft-reset is
over, so the next job from that spot returns to that spot. The campaign
log carries the dated account.

No catalog consequence: the acceptance test measured exactly this and
stays as it is; the harness is the CI side of the same contract.
2026-08-21 13:00:56 -04:00
ScottW514 54d238d67d Record the campaign behind a print longer than the ring
The work ran from 2026-08-18 to 2026-08-20 and is finished, so the plan it ran
from is retired the way plans are retired here: what is durable moved into the
documents that describe the present, and the dated record of how the result was
obtained lands here.

The entry keeps the parts that live nowhere else. That the campaign started
from a wrong belief and was corrected by the factory itself, which stages a job
into its ring in free-checked segments and then appends to it for as long as
the job lasts. That the proof was already on the machine, a 107 MB job played
through a 32 MiB ring in this board's own factory logs. The two defects the
building surfaced, a dry ring reporting success and a pause that could not
retrace on a streamed job. And what it cost to be sure: a decoder 41 times too
slow to keep a ring fed, which only showed itself against a real 53 MB job.
2026-08-20 16:20:03 -04:00
ScottW514 0ef2047625 Record what the factory does about progress
The campaign log gains the F1 entry: the question was which of three carriers
the factory uses for the progress bar, and the answer is that two of them
collapse into one. Progress rides an outbound WSS type:"progress" frame that is
itself the periodic settings report, every 30 s. The write-up lives in CLOUD.md;
the log records how it was gotten and what else the capture proved, including a
factory progress total that grew 256 KiB per interval because the factory
live-appends to its own ring.

BRINGUP's cloud item now lists F2 among the open work with the carrier settled,
so what is owed is emitting the frame against the feeder's job total rather
than the kernel byte counter.

Documentation only, no behavior change, so no acceptance-catalog consequence.
2026-08-20 13:28:43 -04:00
ScottW514 4a6bd5a179 docs: record what the factory does about fans, temperature and coolant
Next-work item 19 carried three open questions about the factory's
operating envelope. All three are answered now, so the item states the
policy instead of the guess.

A fan tach alert during a cut pauses the print, taking the same transition
a user pause takes, and two of the factory's three tach monitors cannot
fire at all: they treat a zero limit as unconfigured and the limits arrive
zero anyway. So a stalled extraction fan is caught by the temperature it
causes, not by its tachometer. Every temperature alert pauses as well, and
a critical fails the machine outright, which is a different state and not a
pause. The scale question behind the header's ceilings is answered by the
coolant family, which the factory carries twice, once in raw ADC counts
where the tag named min is the hot limit, and once in millidegrees. And the
factory does not verify coolant flow at all, so the cooling engine's flow
check is ahead of the factory rather than behind it.

The crash, tilt and beam-detect bullets gain the same grading: an alert
threshold that pauses and a separate abort threshold that aborts.

Also drops references to paths that resolve outside this repo, in both
documents and in two bench scripts, naming the artifact instead. In
CAMPAIGN-LOG that is a wording substitution only; no date, claim or
measurement changes.

Documentation only, so no acceptance catalog consequence.
2026-08-19 17:23:02 -04:00
ScottW514 0c2dd37b2f Cover both dose models explicitly now that density is the default
grblHAL-glowforge 03d70e0, pinned here, makes density the shipped model
and $35 the density floor at 10. Every analog session in the stream
harness now selects its model rather than inheriting it: without that the
flip would have quietly turned them into density runs and taken the
analog fallback's coverage with them. laser.power-floor carries the new
floor and its PWMSAR minimum, and its description no longer describes
$35 as a duty floor.

All ten stream sessions pass on the new defaults, with the analog duties
shifting exactly as the floor predicts - min_value 12, gradient 0.115 -
along with both C harnesses and the lifecycle harness.

Recorded in BRINGUP and the campaign log, including what the defaults
rest on and what they do not: the seven ladders ran at F300 and F100 at
constant power, so production feeds, M4 into corners and the raster path
remain unproven on hardware.
2026-08-17 22:03:56 -04:00
ScottW514 1e855ab212 The density floor closes the low end: a commanded 1 percent now marks
$35 = 10 under the density model is a density floor, not a duty floor. It
maps S onto 9.4-100 percent density, so a commanded 1 percent lands at
10.2 percent, just above the marking floor the earlier ladders measured.
A ladder reweighted to the bottom of the user scale - 1, 2, 5, 10, 20,
40, 70, 100 percent of S - marked on all eight rungs, with eight current
segments over a 42.0 s window against exactly 8 x 5.25, and segment means
climbing 136 to 968.

That meets the goal the ladders started from: a user's 1 percent is a
real visible mark rather than silence, and 100 percent is full power. It
took all three pieces - density so every level is real pulses, the
minimum pulse so they stay strikeable, the floor so the user's range sits
on the band that works.

dladder no longer tells the operator to re-run at other base periods to
choose one. The period cancels out of the low end, and what a failing
rung now indicates is a floor set too low.
2026-08-17 21:58:05 -04:00
ScottW514 d7c23cce19 A longer minimum pulse is worse: the gap is what decides striking
min_ticks 6 broke 5 percent striking - seven current segments where 3 gave
eight, on a 36.5 s fire window against 41.4 s for eight rungs. Below the
minimum the model emits min ticks every min/on periods, so the interval
between pulses is min_ticks x tick / density and the base period cancels,
which is also why periods 10, 20 and 40 gave identical results earlier.
At 5 percent that is 2.26 ms at min 3, which struck, against 4.51 ms at 6,
which did not: doubling the minimum doubles the gap as well as the pulse,
and the discharge is re-struck each pulse.

min 3 sits at the factory's own operating point - its 6.5 percent engrave
jobs place 100 us pulses 1.54 ms apart against 1.64 ms for min 3 at that
density - and 6 is outside anything the factory does. The bench is back
at 3.

Measured band for this tube: strikes from ~5 percent density, marks from
~10 percent at F300. That closes the pulse-structure route to a usable
1 percent, since the interval grows as 1/density and 1 percent implies an
11 ms gap. The low end is a scaling problem, and $35 is the control.
2026-08-17 21:52:15 -04:00
ScottW514 ff454ed537 Retract the pulse-length conclusion; show the minimum in the drill table
The fifth ladder, the first with a minimum pulse, moved the floor down a
full rung: only 5 percent failed to mark, and 5 percent now strikes. The
trace carries eight current segments where the run before it had seven,
with fire beginning at 6.2 s exactly at rung 1 and the usual flat
saturated final segment anchoring the count from the other end.

That retracts what the previous entry concluded. Pulse length is not
irrelevant: 10 percent moved from no mark at F100, with three times the
dose per millimeter, to a mark at F300 at the same density, the only
change being its pulses growing from 36-71 us stubs to 106 us. The
matched-pairs argument was sound but drawn entirely from comparisons at
or above 20 percent density, where every pulse length in play was already
long enough - it generalized from the one regime where pulse length does
not bite. Above ~100 us dose governs; below it pulse length does; below
~36 us the supply does not strike. The factory's 100 us quantum sits on
that boundary.

dladder now reads laser_pulse_min_ticks and prints what is actually
emitted. Without that its table reports the pulse density alone would
give, which is wrong wherever the minimum applies - at min 6 the bottom
four rungs all emit 213 us and vary their rate instead, and the operator
reads that table to interpret the material.
2026-08-17 21:47:40 -04:00
ScottW514 a3e83c4fd5 Cover the minimum pulse width; record what the density ladders measured
Rule 15 in the stream harness holds both halves of the minimum
(grblHAL-glowforge f7e8c17, pinned here): no emitted burst falls below
laser_pulse_min_ticks, excepting one clipped by fire going off mid-burst,
and the levels too faint to fill a window still render their exact
average density. Checked against a run at minimum 1 so it cannot pass
vacuously - level 2 goes from 444 bursts of one tick to 147 of three at
the same density, and levels already above the minimum are unchanged.

Four bench ladders settle the base period at 20. The same six rungs
marked in all of them, and the matched pairs across periods separate the
variables: at identical pulse length, halving the density killed the
mark; at identical density, varying the pulse 3x changed nothing. Feed
does not move it either - 10 percent at F100 carries 44 percent more
energy per mm than 20 percent at F300, which marks, and still left
nothing. The low-end marking limit is average power, not dose per length
and not pulse length.

The F100 trace separates two failures that look alike on the material:
seven current segments for eight rungs, anchored by a flat saturated
final segment that can only be full density, put 5 percent at no
discharge at all and 10 percent at a full 15 seconds of current with no
mark. Only the first is ours, and the minimum pulse is the answer to it.

Also recorded: the factory's Precision Power 1 runs a 19.53 percent FIRE
duty cycle at full PWM duty, and its 1-100 scale maps onto density
18.9-79.5 percent, so its 1 percent is the bottom of the useful band
rather than 1 percent of the range. Under the density model $35 and $36
are that same control - a density floor and ceiling - which makes the
user-facing scale a settings choice rather than new code.
2026-08-17 21:37:41 -04:00
ScottW514 c2c627d9b6 Prove the density dose model host-side; close the idle-gap level loss
Four new sessions in the stream harness cover the model (grblHAL-glowforge
2bca017, pinned here). Density renders the commanded level exactly -
levels 2, 3, 7, 15, 25 and 38 came back as 0.0158, 0.0237, 0.0551,
0.1182, 0.1969 and 0.2993 against level/127 of 0.01575, 0.02362, 0.05512,
0.11811, 0.19685 and 0.29921 - S1000 renders 1.0000 and still ends dark,
every power byte carries full duty, and a level change inside a run costs
no stream byte where analog ships one per level.

Rule 13 is the one worth having: the same job run under both models
produces an identical motion grid tick for tick, and all 20051 density
FIRE ticks fall inside the 169776 the analog run fired. The model masks
the core's fire state and never sources one, measured rather than argued.

Rule 14 covers the idle-gap fix: a standalone S between moves, from a
sender slow enough to drain the planner, now fires each move at its own
level (28338 ticks each at duties 30, 52 and 84). Before the fix duty 30
held all 85014 and the other two levels never appeared. That closes
"Next work" item 18, which this work opened earlier today.

The harness now derives its expectations from the board's floor and
chains two launches over one settings file, because the core precomputes
the S to duty mapping once when the spindle is enabled: $35 written at
runtime persists and reports immediately but only enters force at the
next controller start. That is recorded in BRINGUP beside the existing
defaults note, and laser.power-floor's failure message now says so.

Acceptance: the density path shipping off by default is inert until
laser_power_model is set, and the laser tests' covers already name
grblhal-glowforge src/**; the model's own acceptance test waits on the
bench drill that picks the base period.
2026-08-17 20:24:43 -04:00
ScottW514 cb41a6030c Commission the laser duty floor; record how the factory sets power
The pthresh ladder on scrap puts the tube's two thresholds far apart: the
discharge strikes between 2 and 3 percent duty, but nothing lases usefully
below 16 percent (PWMSAR 20), and the rungs between show only a spot at
each line start. $35 ships at 16 (grblhal-glowforge 9466f76, pinned here).

The drill said current lift-off and first mark share a rung; this run
falsifies that, so its docstring and read-the-material text now name both
thresholds and warn that a start-of-line spot is below the threshold, not
at it.

A start-of-line spot is also what a full-power leak at a kernel run start
would look like, so laser_stream_test gains a ladder session (rule 10):
every FIRE tick must ride a commanded duty, and the fire ticks must divide
evenly across rungs. Both hold exactly - six commanded duties, no others,
and 28296 fire ticks on every rung - so the spots are the tube, not the
stream. The harness now derives its expectations from the floor, which
moves the M4 session's S500 plateau from 63 to 73.

laser.power-floor is a new auto acceptance test, the suite's only
non-firing one: a machine must actually carry the commissioned floor,
since stored settings beat freshly baked defaults.

Three cloud cuts of one square at Precision Power 1, 100 and Full Power
show what the analog path is competing with: the power byte is pinned at
127 in all three, dose is FIRE-bit density on a fixed 7-tick period with
the on-count dithered between adjacent integers, and the power setting
never reaches the machine at all. Facts bank and item 17 carry the
numbers; CAMPAIGN-LOG carries both sessions.
2026-08-17 19:45:19 -04:00
ScottW514 76d43686d0 Record the step-timing campaign
The dated record for the jerky-at-2000-mm/min thread: what was wrong,
what changed, and what the bench measured.

Keeps the two results that matter. The A/B that isolated the cause -
two acceptance runs 90 s apart on one image, camera the only variable,
PASS with a nice-5 CPU hog and FAIL with the camera streaming - and the
LB-GF-OG-FM job re-run on the fix with the video live, clean at
min margin 3.1 ms of 10.

Also records that max behind is not the instrument: it reads 0.0 ms
through a passing run whose real margin fell to 4.9 ms, because the
producer never falls behind its own wakeup epoch. Only the measured
min margin shows the condition.
2026-08-17 18:20:40 -04:00
ScottW514 05d68ba0a8 docs: BRINGUP describes the present, CAMPAIGN-LOG carries the dated record
BRINGUP had grown to 3,122 lines in which the same subject was answered
differently depending on where the reader stopped: GATE A "stays open, no
live-fire" in the phase text and closed in the campaign record, the catalog
at 24 tests in one section and 35 in another, several "bench validation
pending" headings over bodies that recorded the pass.

Split by kind rather than by age. BRINGUP (907 lines) is the present state
only - status, bench runbook, laser, lid/interlock/button policy, homing,
forgectrl, diagnostics, logging, release acceptance, the measured facts
bank, and a Next work list of the 15 items that are actually open.
CAMPAIGN-LOG (2,657 lines) takes the dated blocks verbatim, in
chronological order, and is append-only: a correction is a later entry, not
an edit. Its two reading rules are stated up front, since moved text keeps
its original "above"/"below" and its pre-split item numbers.

Facts corrected against the tree while rewriting: the liveness probe gates
at p2p 800, not 500, with the real wedge, noise and jolt figures; the panel
has seven tabs including Logs and is built from src/ui/, not ui.c; the
devserver replaced tools/mock.py; /status reports real head presence; the
estop_halts_motion opt-in is gone; core PR #999 is merged, leaving only the
step_us_min commit fork-only; the update system stands at Phase 5, the
uSDHC pads and the 2026-08-08 kernel batch have shipped, and 8 MP camera
capture is tracked here for the first time.

SAFETY and UPDATE-SYSTEM point their drill records at the log. ACCEPTANCE
records that inheritance is local: there is no import of a published
artifact, so a second bench starts from a full campaign - one of the four
open items the retired tool plan held, the rest of which are now in
BRINGUP's acceptance item.

Documentation only, no behavior change and no catalog consequence: docs/
is outside every layer and .md is excluded from the layer content hash.
2026-08-17 11:19:52 -04:00