REPORT overwritten per the standing rule; every red-proof, the core.hooksPath probe's four measured outcomes, Scenario C's refusal-and-bypass, the hub deployment and the live Setup-tab read are recorded there, plus three observations and two deliberate deviations from the spec (a comment-only edit to felhom-host-install.sh, and __pycache__ in .gitignore). STATUS: the 'check that needs a person to remember it' line is rewritten — the checks now run themselves before every push, with both honest limits stated in plain words; and one entry records the thirteen-check cleanup and the deleted installer version number.
17 KiB
REPORT — Gate enforcement: fix the reds, one entry point per repo, pre-push hook (2026-08-02)
Overwritten per the standing rule. The prior contents (STATUS.md created + the 2026-08-02
operator decisions, same day) have their durable record in STATUS.md itself and in
documentation/backlog/OPEN-ITEMS.md; nothing was lost by this overwrite.
Outcome: complete. Nothing was left out. Two deviations from the spec and one census addition are recorded in §13; both deviations were made deliberately, not by accident.
1. Confirmed baselines — all four matched §1 on arrival
| Repo | main on arrival |
matched §1? | version on arrival | version now |
|---|---|---|---|---|
felhom.eu |
e994bf35d290 |
yes | hub v0.86.0 |
hub v0.87.0 |
felhom-controller |
4115e88f688b |
yes | v0.188.0 |
v0.188.0 (no bump — tooling only) |
felhom-agent |
4663df7ff3cc |
yes | v0.119.0 |
v0.119.0 (no bump — tooling only) |
app-catalog-felhom.eu |
fd7747d1293c |
yes | n/a | n/a |
All four trees were clean and HEAD == origin/main on arrival.
2. Files created / modified
felhom.eu
hub/internal/web/configs.go—hostInstallVersionconst,pageData.ScriptVersionfield and its assignment DELETED; a NOTE records why there is deliberately no constant here.hub/internal/web/templates/customer_unified.html— Setup Command copy rewritten, no version.hub/internal/web/render_test.go— tautological version assertion deleted (not replaced).scripts/hostinstall_gates.py— gate 1's third assertion inverted; docstring rewritten.scripts/felhom-host-install.sh— comment only;SCRIPT_VERSIONuntouched (deviation, §13).scripts/reuse_refs_check.py— rewritten resolution (exact/suffix/ambiguous/cross-repo/FAIL).scripts/test_reuse_refs_check.py— NEW, 13 tests.scripts/repo_gates.py— NEW, the entry point.scripts/test_repo_gates.py— NEW, 3 tests (seam)..githooks/pre-push— NEW.CLAUDE.md,CONTEXT.md(S-6, S-7),.gitignore,hub/CHANGELOG.md,scripts/CHANGELOG.md,manifests/hub.yaml,documentation/backlog/OPEN-ITEMS.md,documentation/backlog/ROADMAP.md,STATUS.md, this file.
felhom-controller — controller/scripts/docker_run_volume_path_gate.py (one ALLOWLIST entry),
controller/scripts/controller_gates.py (NEW), controller/scripts/test_controller_gates.py (NEW),
.githooks/pre-push (NEW), CLAUDE.md, controller/README.md, CHANGELOG.md, REPORT.md.
felhom-agent — scripts/agent_gates.py (NEW), scripts/test_agent_gates.py (NEW),
.githooks/pre-push (NEW), CLAUDE.md, CHANGELOG.md, REPORT.md.
app-catalog-felhom.eu — scripts/catalog_gates.py (--fast),
scripts/test_catalog_gates.py (NEW), .githooks/pre-push (NEW), CLAUDE.md, CHANGELOG.md,
REPORT.md.
3. Commits pushed to main
felhom-controller — Part 1.1 is its own commit, with nothing else in it, as specified.
| Hash | What |
|---|---|
c432f70 |
gate: allowlist appexport/estimate.go named-volume -v mount (R-29 leg a) — the reviewed diff, alone |
8cb3d7a |
gates: one entry point (controller/scripts/controller_gates.py) + pre-push hook |
7c32c74 |
docs: CHANGELOG + README for the gate entry point (no version bump) |
felhom.eu
| Hash | What |
|---|---|
d319ae5 |
hub: delete the host-install version label (R-94) + invert hostinstall gate 1 |
2137094 |
scripts: reuse_refs_check resolves package shorthand and sibling repos |
9bd1a54 |
gates: one entry point (scripts/repo_gates.py) + pre-push hook |
4707be7 |
docs: R-94 closed, R-29 leg (a) closed + leg (b) half, R-168 minted |
8d9b78c |
manifests: hub 0.86.0 → 0.87.0 |
| (final) | REPORT + STATUS |
felhom-agent — 054e85a (entry point + hook), 75245a4 (CHANGELOG).
app-catalog-felhom.eu — c3e4bb1 (--fast + hook), 340ff2a (CHANGELOG).
4. Per-test results and EVERY red-proof
| # | Red-proof | Method | Outcome |
|---|---|---|---|
| 1 | Part 1.1 — the docker--v gate |
renamed the new ALLOWLIST suffix to a non-existent file | gate exit 1, naming estimate.go:179; restored → exit 0 |
| 2 | Part 1.2 — the rewritten gate 1 | re-inserted const hostInstallVersion = "1.19.0" into configs.go |
gate exit 1, convicting on 3 of its 6 shapes (const / assignment / renamed-const); restored → exit 0 |
| 3 | Part 1.2 — the OLD render_test.go assertion tested nothing |
set the const to "9.9.9" while the served script was 1.22.0, ran TestTemplates_InstallGenerator -v |
--- PASS. It compared the constant to itself. (First attempt used -run TestCustomerUnified, which matches no test in that file and printed a vacuous ok — caught, corrected, re-run against the real test name.) |
| 4 | Part 2 — Scenario E, the checker can still fail | made resolve() return exact for an unresolvable token — the "made green by being made blind" mutation |
4 of 13 tests went red (test_absent_path_fails, test_failure_lists_every_resolution_attempted, test_evidence_copy_does_not_satisfy_a_citation, test_non_git_sibling_is_not_searched); restored → 13/13 green |
| 5 | Part 3 — the inert runner, all four entry points | replaced each run_gate body with return 0 |
every runner still printed its summary and exited 0; the seam tests went red — felhom.eu 1 failure, controller 1, agent 2, catalog 1. All restored green |
Integration scenarios
- A — all four entry points green in
--fast: exit 0 each. Summary lines quoted in §6. - B — a red member fails the runner: with
hostInstallVersionrestored,repo_gates.py --fastexited 1,hostinstall_gates' own finding text appeared on stdout, and the summary readhostinstall FAILED (exit 1)/CONVICTED: hostinstall. Reverted. The companion inert-runner proof is #5 above. - C — the hook refuses: §7.
- D — 13 findings resolve, none by blindness: all four roots exit 0.
appbackup/userdata.goresolves by suffix insidefelhom-controller;wgsync/reconciler.gois reported as cross-repo →felhom.eu/hub/internal/wgsync/reconciler.go, not as an exact match. - E — the kill condition: pinned by
test_reuse_refs_check.pyand red-proofed (#4 above). - F — runtime catalogue gates stay out of the hook:
catalog_gates.py --fastrancheck-image-pins.pyonly (image-pin gate OK — 53 templates, 0 unpinned images); the other two did not run and the skip was announced with its reason. Pinned bytest_catalog_gates.py.
5. Test count before / after
| Repo | Before | After | Delta |
|---|---|---|---|
felhom.eu — Go (hub/) |
568 test funcs, 17 packages ok |
568, 17 ok |
0 (one assertion deleted, no test funcs) |
felhom.eu — Python |
0 | 16 (13 test_reuse_refs_check + 3 test_repo_gates) |
+16 |
felhom-controller — Python |
0 | 4 | +4 |
felhom-agent — Python |
0 | 4 | +4 |
app-catalog-felhom.eu — Python |
existing test_check_volume_persistence.py |
+5 (test_catalog_gates) |
+5 |
All green. No failures.
6. The four entry points' summary lines, verbatim (--fast)
site OK (exit 0) template-id OK (exit 0)
hostinstall OK (exit 0) emoji OK (exit 0)
hub-confirm OK (exit 0) native-confirm OK (exit 0)
manifest-bearer OK (exit 0) offbox-rename OK (exit 0)
reuse-refs OK (exit 0) app-row-dedup OK (exit 0)
mojibake OK (exit 0)
all felhom.eu gates OK docker-v OK (exit 0)
reuse-refs OK (exit 0)
all controller gates OK
reuse-refs OK (exit 0) image-pins OK (exit 0)
all agent gates OK all catalog gates OK
Each run also prints its per-root reuse tally, which is the positive observable — e.g.
OK [felhom-controller]: 133 cited paths — exact 126, suffix 6, ambiguous 0, cross-repo 1, FAILED 0.
7. Part 4.0 probe — all four measured outcomes
Throwaway clone, git 2.47.3, relative core.hooksPath .githooks:
| Step | Expected | Measured |
|---|---|---|
1. hook + core.hooksPath .githooks + chmod +x |
— | set; git config --get core.hooksPath → .githooks |
2. git push from the repo root |
refused | REFUSED, rc=1 |
3. git push from a subdirectory (hub/) |
refused | REFUSED, rc=1 |
4. git push --no-verify |
succeeds | ACCEPTED, rc=0 |
Step 3 did not fail, so there is no STOP and no SPIKE-githooks-*.md was written. The form used
is therefore the relative core.hooksPath .githooks.
Step 3 was then re-measured with a hook that prints a distinctive line, because "the push was
refused" alone does not say what refused it. The hook printed PROBE-HOOK-RAN with cwd = the
repo root when git push was issued from the root, from hub/, and from a two-level-deeper
subdirectory. So git resolves a relative core.hooksPath against the repo root and runs the hook
there regardless of where the push is issued. The shipped hook does not rely on that — it resolves
the root explicitly with git rev-parse --show-toplevel.
Scenario C, in a throwaway clone of felhom.eu with origin re-pointed at a scratch bare repo
so nothing real could be pushed:
- unarmed clone — a manual
repo_gates.py --fastprintedWARNING: this clone is UNARMED — core.hooksPath is unset… - C0, clean tree — push ACCEPTED, and the hook said so:
pre-push [felhom.eu]: gates OK - push proceeding. - C1, deliberately broken gate — push REFUSED;
hostinstall_gates' own three findings andCONVICTED: hostinstallappeared, thenpre-push [felhom.eu]: PUSH REFUSED - gates exited 1. - C2,
git push --no-verifyon the same commit — ACCEPTED (9bd1a54..19e8cbb), and the broken commit was confirmed present in the scratch bare repo. The escape hatch works.
All five real pushes in this session ran their hook and announced it — see §3.
8. git config --get core.hooksPath in the four live workspace clones
felhom.eu core.hooksPath = .githooks
felhom-controller core.hooksPath = .githooks
felhom-agent core.hooksPath = .githooks
app-catalog-felhom.eu core.hooksPath = .githooks
9. Hub deployment
-
Version:
gitea.dooplex.hu/admin/felhom-hub:0.87.0, built and pushed from/mnt/5_hdd/felhom.eu/build/felhom-hubafter the clean-tree gate (emptygit status --porcelain,HEAD == origin/main == 4707be7) and a greengo build ./... && go vet ./... && go test ./.... -
ArgoCD:
manifests/hub.yamlbumped in git (8d9b78c), hard-refresh →OutOfSync, then a deliberate sync. Nokubectl set imageat any point. Final:sync=Synced health=Healthy, revision8d9b78c153ee…. -
Rollout:
deployment "hub" successfully rolled out; podhub-67774ccf4f-74wwx1/1 Running. -
Running image:
gitea.dooplex.hu/admin/felhom-hub:0.87.0. -
Pod log excerpt:
[INFO] Storage fill checker initialized: warn=90% crit=95%, 8 ok seeded, 0 already-breached left unseeded, 3 root-backed excluded [INFO] Offsite checker initialized: fill warn=90% crit=95%, stale after 48h0m0s, 3 ok-seeded [INFO] Listening on :8080 -
Live read of the Setup tab — method: endpoint-level (
curlover the hub ClusterIP with Basic auth; browser automation is not available on DooPlex).GET /customers/peti-felhom, 105 164 bytes. The Setup Command card renders:Day-0 host bootstrap. The command always fetches the current felhom-host-install.sh from felhom.eu — there is no version to pick here. Run it on a freshly-PVE-installed Proxmox host as root …
Zero occurrences of
1.19.0or1.22.0anywhere on the page. The page also servedstyle.css?v=0.87.0, independently confirming the new image is the one rendering.
10. OPEN-ITEMS.md rows opened / closed / re-ranked
- R-94 → CLOSED, all three legs. Leg (a) closed by deletion, not derivation, with the reason
recorded in the row: the Setup command fetches the installer at run time from a website that
git-syncs
mainevery 30 s (R-110), so no build-time value in the hub can be true. - R-29 → leg (a) CLOSED (
c432f70, its own reviewed diff); leg (b) HALF-SHIPPED. The census is written into the row: thirteen gates; every gate aCLAUDE.mdnames was green, and two of the four nobody names were red. Stays open for the automatic half. - R-161 → annotated:
--fast+ hook shipped; the automatic half now points at its successor row. - R-168 → NEW.
grep -oE "R-[0-9]+" OPEN-ITEMS.md | sort -u -t- -k2 -n | tailestablished R-167 as the highest in use, so R-168 was free. Gitea Actions runner for CI; owner CC; state BLOCKED on its own spike. It records what was measured on 2026-08-02 — Gitea 1.26.2, Actions enabled on all four repos, 0 runners, 0 workflow runs, 0 branch protections — and the consequence: with trunk-based direct-to-mainpushes there is no merge for a status check to gate, so CI here can detect but not block. Its two spike questions: host-mode runner versus a privileged Docker-in-Docker sidecar on DooPlex (Tier 2 — it is the recovery chain), and whether the workflow can avoid JavaScript actions. ROADMAP.md: R-94 collapsed to its one-liner, R-29 updated, R-168 added.architecture/00-capability-map.md— N/A, stated rather than silently skipped. No customer-visible or platform capability changed: this task alters tooling, one operator-facing sentence, and documentation.
11. Teardown
Provisioned nothing. No VM, no guest, no container and no host was created or modified. The only
scratch artifacts were three throwaway git repos under the session scratchpad (the core.hooksPath
probe, the Scenario C clone, and its bare origin) — disposable, and touching nothing real.
12. Was --no-verify used?
Once, deliberately, and only inside the throwaway Scenario C clone whose origin was a scratch
bare repo — that use was the test of the escape hatch (§7, C2). It was never used against a real
repository. All five real pushes ran their pre-push hook, and it passed each time.
13. Observations — noticed, recorded, NOT acted on
- The census held, with one thing worth stating plainly. Everything §4.9 said was reproduced:
hostinstall_gatesRED,reuse_refs_checkRED on all four repos with exactly 13 findings,docker_run_volume_path_gateRED with exactly one finding, and every gate named by aCLAUDE.mdgreen. Since the "two of the four" headline can be read as "two red gates", the arrival state was in fact three red scripts — and they were exactly the two named nowhere plus the one named only inREUSE.md:284. The correlation is unchanged and slightly stronger than the headline. - DEVIATION —
scripts/felhom-host-install.shwas edited, which §12 fenced. Comment only;SCRIPT_VERSION="1.22.0"is untouched and no behaviour changed. Its comment asserted thathostinstall_gates.py"asserts the two stay equal" — an invariant this task deleted. Leaving it would have shipped exactly the classCLAUDE.mdnames ("a comment asserting an invariant needs a test pinning it, or it is a wish"), so the fence — whose stated reason is thatSCRIPT_VERSIONis the single source and stays — was read as fencing the act, not the object. Recorded here so the decision is visible rather than absorbed silently. - DEVIATION —
.gitignoregained__pycache__/. Not in the spec. The new fixture tests import the checker by path, which writes bytecode intoscripts/, and an untracked directory there would have failed the clean-tree gate before every future build. - A
go test -runpattern that matches nothing printsokand exits 0. My first attempt at red-proof #3 used-run TestCustomerUnified, which matches no test inrender_test.go, andgo testreportedok … 0.062s. Caught and re-run against the real name. This is another instance of the "an absent line is not evidence" class already inCLAUDE.md, and it is worth its own line because it makes a red-proof itself hollow — the one place a false green costs most. hub/CHANGELOG.mdandscripts/CHANGELOG.mdstill contain historicalhostInstallVersionmentions. Correct and left alone: the rewritten gate matches code shapes, never prose, so the history is safe to keep and the gate stays green.- Not acted on, out of scope.
manifest_bearer_gate.pyprintsmanifests/felhom.secret.yaml:39 KNOWN-BACKLOG committed secret …on every run and still exits 0. It is a tracked backlog item (documentation/runbooks/secrets.md) and the gate deliberately does not fail on it — but it means every greenrepo_gates.pyrun now carries a line that reads like a finding, in front of a reader who did not previously see it. Left untouched, per "do not tighten or refactor a currently-green gate".