Files
felhom-controller/.claude/rules/gates.md
T
admin 66d80efb9f
gates / gates (push) Successful in 19s
docs: R-168 is CLOSED — the "CI is still owed" sentence was stale (R-229 part 2)
Corrected in all four instruction files across all four repos. Found while confirming this
session own push by run ID, which is precisely the check that catches it.

In felhom-agent/CLAUDE.md the sentence contradicted the same file release section, which
already said R-168 mails the failure -- a contradiction inside one instruction file, the exact
class the R-229 work exists to find.

REPORT.md deliberately NOT overwritten in the sibling repos: a one-line docs correction must not
destroy the record of their last real implementation.
2026-08-06 11:02:55 +02:00

55 lines
3.1 KiB
Markdown

---
paths: ["controller/**/*.go", "controller/**/*.html", "controller/**/*.css", "controller/scripts/**"]
---
# Gates and logging — felhom-controller
## The ONE entry point
**Run `python3 controller/scripts/controller_gates.py` (from `controller/`) after ANY change in this
repo.** It runs all seven local gates — `template_id_gate`, `emoji_gate`, `native_confirm_gate`,
`offbox_rename_gate`, `app_row_dedup_gate`, `mojibake_gate`, `docker_run_volume_path_gate` — plus
`reuse_refs_check` and `instructions_gate` on the repo root, streaming each gate's own output and
exiting non-zero if any fails.
- `--fast` selects 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` and `instructions_gate.py` live in `felhom.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 `--fast` and refuses a failing push. It is
per-clone — switch it on once with `git config core.hooksPath .githooks`, and a manual run WARNS
when this clone is unarmed. `git push --no-verify` bypasses 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).
<!--
WHY A RUNNER AND NOT SEVEN INVOCATIONS (2026-08-02, R-29) — rationale, not a directive.
A census of all thirteen gates across the four repos found that every check a CLAUDE.md named was
passing, and two of the four nobody is told to run were failing. This repo's CLAUDE.md used to name
two of the seven; the other five were reachable only through a line in REUSE.md, and
docker_run_volume_path_gate.py was RED. The single-entry-point shape is the only one that
demonstrably gets run. app-catalog-felhom.eu/scripts/catalog_gates.py is the canonical version of
the runner (R-161); repo_gates.py copies it. site_gates.py is a *gate*, not a runner — do not model
new work on it.
-->
## 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.
<!--
Measured, R-117 spike §6.3 (felhom.eu/documentation/audits/SPIKE-r117-bind-liveness-2026-07-30.md):
a probe stayed in D state 3m50s after kill -9; a buffered write with no fsync blocked too (O_CREAT
needs journal access); and statfs/getdents returned HEALTHY on a namespace that EIOs every byte —
fast, and wrong.
-->