Files
admin 054e85a2bf gates: one entry point (scripts/agent_gates.py) + pre-push hook
A census of all thirteen gate scripts across the four felhom repos on 2026-08-02 found that
every check a CLAUDE.md names was passing and two of the four nobody is told to run were
failing. This repo was the extreme case: nothing ran against it at all, and its REUSE.md — 90
cited paths — was checked by no one.

agent_gates.py exists at ONE gate on purpose, so the agent is not the one repo with nowhere to
put a check and so the pre-push hook has the same entry point in all four repos. It grows when
the agent grows a second gate. The shared reuse checker stays in felhom.eu/scripts/ and is
invoked across the workspace — never copied here; an absent sibling clone FAILS the gate and
prints the path tried, which test_agent_gates.py pins by running the entry point from a lone
directory with no sibling.

.githooks/pre-push runs it with --fast and refuses the push. Per-clone and --no-verify-able,
both stated in the hook itself; a manual run WARNS when the clone is unarmed.

Tooling only: no Go change, no build, no deploy, no version bump.
2026-08-02 15:22:58 +02:00

68 lines
2.8 KiB
Python

# -*- coding: utf-8 -*-
"""Seam test for scripts/agent_gates.py.
Run from the repo root: python3 scripts/test_agent_gates.py
WHY THIS EXISTS. An entry point is a seam by definition: a runner that LISTS a gate but never
executes it is inert and fully green, and this project has shipped an inert seam four times. So
the assertion is on the member gate's OWN distinctive stdout — never on the runner's summary
line, which the runner can print without ever calling anything — plus the exit code, which is a
runner's actual effect.
The second test is the one that matters here: this repo's only gate lives in a SIBLING clone, so
"the sibling is missing" must be a FAILURE and not a quiet skip.
"""
import os
import shutil
import subprocess
import sys
import tempfile
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ENTRY = os.path.join(ROOT, "scripts", "agent_gates.py")
class AgentGatesTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
p = subprocess.run([sys.executable, ENTRY, "--fast"], cwd=ROOT,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
cls.rc = p.returncode
cls.out = p.stdout.decode("utf-8", "replace")
def test_exit_code_is_zero(self):
self.assertEqual(self.rc, 0, self.out)
def test_member_gate_actually_ran(self):
self.assertIn("cited paths — exact", self.out,
"the reuse-refs gate is listed but its own output never appeared — an inert "
"runner prints the summary without calling anything:\n%s" % self.out)
def test_missing_shared_checker_is_a_failure_not_a_skip(self):
"""Fail-closed. Copy the entry point into a lone directory with no felhom.eu sibling and
confirm it CONVICTS rather than reporting green with nothing run."""
tmp = tempfile.mkdtemp(prefix="agent-gates-")
try:
lone = os.path.join(tmp, "felhom-agent", "scripts")
os.makedirs(lone)
shutil.copy(ENTRY, os.path.join(lone, "agent_gates.py"))
p = subprocess.run([sys.executable, os.path.join(lone, "agent_gates.py"), "--fast"],
cwd=os.path.dirname(lone),
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out = p.stdout.decode("utf-8", "replace")
self.assertNotEqual(p.returncode, 0, out)
self.assertIn("is MISSING — tried", out)
finally:
shutil.rmtree(tmp, ignore_errors=True)
def test_unknown_argument_is_rejected(self):
p = subprocess.run([sys.executable, ENTRY, "--nope"], cwd=ROOT,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
self.assertEqual(p.returncode, 2, p.stdout.decode("utf-8", "replace"))
if __name__ == "__main__":
unittest.main(verbosity=2)