mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 08:41:13 -07:00
laser.emission-witness required the hardware button latch clear in every sample the engine reported armed, and, after a first fix, in every sample up to the last nonzero emission count. Both windows were drawn from lagging signals: the engine's armed flag follows the controller's next report, and the emission counter latches once per second and reads nonzero about two seconds past the relock. Both reached into the tail where the job-end relock sets the button latch by design, and the rule refused three clean runs on image 20260902144848 (all four sides burned; the trail shows the latch clear from the press to the relock, emission through the fourth side, HV_ENABLE's dip in the dwell and its return). The rule now uses the window the hardware defines: from the first emission, in every sample whose readback word shows the laser latch unlocked, the button-latch bit of that same word must be clear. That spans the kernel-run gap of the dwell and ends at the relock, and no lagging flag can misplace it. dwell_gap() is a pure function; tests/test_laser_dwell.py holds the relocked tail, a set inside the gap, and a trail without emission. The recorded trail of the third run replays to a pass (47 unlocked samples, none set). The live runs keep a per-sample trail in the evidence (TRAIL_FIELDS: the readback word, the switches, the lock flag, the controller's state and messages), so a run's timeline can be read back without a rerun. A fourth run then errored on a name the refactor had removed and one later check still used; py_compile does not catch it and a live drill never executes on the host, so the CI job now fails on any undefined name in the harness (pyflakes). Catalog consequence: the laser implementation hashes move.
47 lines
2.1 KiB
Python
47 lines
2.1 KiB
Python
"""The dwell-gap verdict of laser.emission-witness over synthetic trails:
|
|
the button latch judged from the first emission through every sample
|
|
whose readback word shows the laser latch unlocked, both bits from that
|
|
word, so the relocked tail (where the relock itself sets the latch and
|
|
the emission counter still reads nonzero) is never judged."""
|
|
import unittest
|
|
|
|
from forgetest.suite import laser
|
|
|
|
|
|
def smp(emission, latch, locked=False, hv_enable=True, armed=True):
|
|
il = (laser.IL_BUTTON_LATCH if latch else 0) | (laser.IL_LASER_LATCH if locked else 0)
|
|
return {"emission": emission, "armed": armed, "il": il, "hv_enable": hv_enable}
|
|
|
|
|
|
class DwellGapTests(unittest.TestCase):
|
|
def test_the_relocked_tail_is_not_judged(self):
|
|
# the arm wait (unlocked, latch set until the press), fire, the
|
|
# gap, fire, then the relock sets the latch while the counter
|
|
# still reads nonzero
|
|
trail = [smp(0, 1), smp(0, 0), smp(10, 0), smp(20, 0, hv_enable=False),
|
|
smp(20, 0, hv_enable=False), smp(30, 0), smp(40, 0),
|
|
smp(40, 1, locked=True, armed=False), smp(40, 1, locked=True, armed=False),
|
|
smp(0, 1, locked=True, armed=False)]
|
|
g = laser.dwell_gap(trail)
|
|
self.assertEqual(g["button_latch_unlocked_max"], 0)
|
|
self.assertEqual(g["button_latch_unlocked_samples"], 5)
|
|
self.assertEqual(g["button_latch_set_at"], [])
|
|
self.assertTrue(g["hv_enable_dipped"])
|
|
self.assertTrue(g["hv_enable_back_lit"])
|
|
|
|
def test_latch_set_while_unlocked_is_the_defect(self):
|
|
trail = [smp(0, 0), smp(10, 0), smp(20, 1, hv_enable=False), smp(30, 0), smp(0, 1, locked=True)]
|
|
g = laser.dwell_gap(trail)
|
|
self.assertEqual(g["button_latch_unlocked_max"], 1)
|
|
self.assertEqual(g["button_latch_set_at"], [1])
|
|
|
|
def test_no_emission_judges_nothing(self):
|
|
g = laser.dwell_gap([smp(0, 0), smp(0, 1, locked=True)])
|
|
self.assertIsNone(g["button_latch_unlocked_max"])
|
|
self.assertEqual(g["button_latch_unlocked_samples"], 0)
|
|
self.assertFalse(g["hv_enable_dipped"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|