diff --git a/tools/loop-lint.py b/tools/loop-lint.py index 126a6ca..6f7e6dd 100644 --- a/tools/loop-lint.py +++ b/tools/loop-lint.py @@ -587,7 +587,8 @@ def self_test(): fh.write("all: coverage newthing\ncoverage:\n\techo\n") with open(os.path.join(tmp, "gates.toml"), "w") as fh: fh.write('not_control_gates = []\n\n[[gate]]\nid = "G"\n' - 'name = "n"\ntarget = "coverage"\nchecks = "c"\n' + 'name = "n"\ntarget = "coverage"\ncadence = "all"\n' + 'checks = "c"\n' 'added = "2026-01-01"\nreview_by = "2026-02-01"\n' 'retire_if = "r"\n') f = check_gate_registry(tmp) @@ -595,12 +596,41 @@ def self_test(): len(f) == 1 and "newthing" in f[0].detail, f"{len(f)} finding(s)") with open(os.path.join(tmp, "gates.toml"), "w") as fh: fh.write('not_control_gates = ["newthing", "coverage"]\n\n[[gate]]\nid = "G"\n' - 'name = "n"\ntarget = "ghost"\nchecks = "c"\n' + 'name = "n"\ntarget = "ghost"\ncadence = "manual"\n' + 'checks = "c"\n' 'added = "2026-01-01"\nreview_by = "2026-02-01"\n' 'retire_if = "r"\n') f = check_gate_registry(tmp) check("gate registry detects an entry naming a missing target", len(f) == 1 and "ghost" in f[0].detail, f"{len(f)} finding(s)") + + # CB-REV-0002 #7 generalised: a gate that does not run is not a + # gate. Both directions, because a rule that cannot say NO is + # decoration and one that cannot say YES fires on everything. + with open(os.path.join(tmp, "Makefile"), "w") as fh: + fh.write("all: coverage\ncoverage:\n\techo\npanels:\n\techo\n") + with open(os.path.join(tmp, "gates.toml"), "w") as fh: + fh.write('not_control_gates = []\n\n[[gate]]\nid = "G"\n' + 'name = "n"\ntarget = "coverage"\ncadence = "all"\n' + 'checks = "c"\n\n[[gate]]\nid = "P"\nname = "p"\n' + 'target = "panels"\ncadence = "all"\nchecks = "c"\n') + f = check_gate_registry(tmp) + check("gates: a cadence=all gate absent from `make all` is caught", + len(f) == 1 and "does not run it" in f[0].detail, + "the panels gate passed lint while running nowhere") + with open(os.path.join(tmp, "gates.toml"), "w") as fh: + fh.write('not_control_gates = []\n\n[[gate]]\nid = "G"\n' + 'name = "n"\ntarget = "coverage"\nchecks = "c"\n') + check("gates: a gate with no declared cadence is caught", + any("declares no cadence" in x.detail for x in check_gate_registry(tmp))) + with open(os.path.join(tmp, "gates.toml"), "w") as fh: + fh.write('not_control_gates = []\n\n[[gate]]\nid = "G"\n' + 'name = "n"\ntarget = "panels"\ncadence = "manual"\n' + 'checks = "c"\n\n[[gate]]\nid = "C"\nname = "c"\n' + 'target = "coverage"\ncadence = "all"\nchecks = "c"\n') + check("gates: a gate may be manual, and saying so clears it", + not check_gate_registry(tmp), + "without this every manual gate would fire") os.unlink(os.path.join(tmp, "gates.toml")) os.unlink(os.path.join(tmp, "Makefile"))