Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
4.8 KiB
paths
| paths | ||||
|---|---|---|---|---|
|
Gates and logging — felhom-controller
The ONE entry point
Run python3 scripts/controller_gates.py from controller/ (or python3 controller/scripts/controller_gates.py from the repo root) after ANY change in this repo. It runs
every gate in the runner's GATES table, the shared reuse_refs_check, instructions_gate and
observations_gate among them, streaming each gate's own output and exiting non-zero if any fails;
one, golden-notice, is ADVISORY and never refuses. The GATES table is the list; this sentence is a
pointer to it, not a second copy — it drifted twice (it said "seven" while nine were registered, then
named ten local gates while fourteen were).
--fastselects the gates that touch no network and no container runtime; today that is all of them.- A missing gate script is a FAILURE, never a skip.
- The shared
reuse_refs_check.py,instructions_gate.pyandobservations_gate.pylive infelhom.eu/scripts/and are never copied here — a copy would recreate the drift they detect; an absent sibling clone FAILS. - The pre-push hook (
.githooks/pre-push) runs it with--fastand refuses a failing push. It is per-clone — switch it on once withgit config core.hooksPath .githooks, and a manual run WARNS when this clone is unarmed.git push --no-verifybypasses it deliberately; say so in the session report when you use it — CI re-runs the same entry point on every push and emails the operator on failure, so a bypass is noticed even though it is not blocked (R-168, CLOSED 2026-08-02).
Logging
New leveled lines use internal/logx — DEBUG always reaches the debug ring; stdout respects
logging.level. English, keys-never-values, durations on outcomes. Full rules:
felhom.eu/documentation/runbooks/logging-conventions.md.
Health checks issue no block I/O
A probe that touches a wedged device enters uninterruptible sleep, survives SIGKILL, and cannot be
recovered until the device returns or the host reboots — so systemctl restart hangs too. A timeout
protects the caller's control flow and nothing else: the blocked thread remains. Liveness is decided
from /proc and kernel state, never by reading or writing the filesystem.
A gate ships with a decoy test that has been seen to fail (R-421)
A decoy is the LABEL without the FACT — a directory with the right name and no bake log, a
handler case that exists only in a comment, a note whose prose mentions the marker it lacks. Write
one for every new gate, run it, and watch it convict. felhom.eu/scripts/decoy_coverage_gate.py (run by felhom.eu's repo_gates.py, for all four repos) refuses a gate
registered without one, or without a named exemption carrying its row.
Earned by five instances, every one found by accident: R-410, R-400, R-378, R-419, R-94. The 2026-09-01 sweep read all 29 gate scripts and fooled 16 of them. The four shapes to test against:
- name-for-fact — it matches a path or directory NAME while the fact lives inside the file.
- substring-for-field — it matches a token anywhere in a body instead of in the field carrying it.
- declaration-for-reachability — it checks a thing is declared, not that it RESOLVES.
- constant-for-measurement — it compares a value against itself.
Scope is a fact too. Eight of the sixteen were os.listdir (one level) where os.walk was meant:
green and correct today, blind the moment anyone adds a subdirectory. Prefer os.walk, and prefer a
glob over a hand-maintained list of files.
A decoy nobody would write proves nothing — say the gate is sound and move on. Five of mine were
withdrawn as illegitimate and are named in felhom.eu/documentation/audits/AUDIT-gate-decoys-2026-09-01.md.