227 -> 115 effective lines, split into .claude/rules/{hub,website,manifests,docs}.md, and
repo_gates.py gains gate 6. Trim first, register second: a registered-but-failing gate refuses
every push through the pre-push hook, which is why this repo -- the one that OWNS the gate --
was the only one not running it.
Register discipline and the R-110 installer fence deliberately stayed in the core; both have
triggers no fixed glob covers, and scoping them would have rebuilt the failure class they exist
to prevent.
Scoping proven from the InstructionsLoaded hook log in two fresh sessions, not from frontmatter.
7.6 KiB
CLAUDE.md — felhom.eu
Stable orientation only — current state lives in
CONTEXT.mdand the tops ofhub/CHANGELOG.md/scripts/CHANGELOG.md/website/CHANGELOG.md, never here. Cross-repo conventions (the three-component model, artifact taxonomy, access, clean-tree gate, secrets): workspace-root/mnt/5_hdd/felhom.eu/git/CLAUDE.md, whose versioned copy isdocumentation/runbooks/workspace-CLAUDE.md. Path-scoped detail:.claude/rules/.
What this repo is
Four surfaces in one repo, plus the design home for the whole system:
hub/— felhom-hub, the operator backend (Go, k3s,hub.felhom.eu).website/— static HTML at felhom.eu, served by k3s nginx + git-sync.manifests/— k3s manifests for felhom-system, GitOps via one ArgoCD app.scripts/— the public installer (felhom-host-install.sh) and this repo's gates.documentation/— the authoritative design home for all of Felhom, not just this repo.skills/— versioned source of the Claude Code skills; install withpython3 scripts/install_skills.py(symlink — repo edits are live immediately).
Doing X → read Y
| Doing | Read |
|---|---|
| writing any new code | REUSE.md — helpers, seams, extension points, traps |
| needing current state / roadmap | CONTEXT.md |
| hub work (architecture, deploy, patterns) | loads itself: .claude/rules/hub.md |
| website or installer work | loads itself: .claude/rules/website.md |
| manifests / ArgoCD / secrets | loads itself: .claude/rules/manifests.md |
| writing or routing a document | loads itself: .claude/rules/docs.md |
| build, deploy, publish, verify a version | the felhom-build-deploy skill |
| writing or reviewing a test, fixing a bug | the felhom-testing skill |
| UI, tokens, badges, Hungarian copy | the felhom-ui-design skill |
| host addresses, break-glass, node facts | documentation/operations/nodes.md — never restate them |
| which box may I break | documentation/runbooks/target-selection.md |
| what version is live anywhere | ask the hub (/hosts, /configs) or the box — never a doc |
| the authoritative design | documentation/architecture/01..05-*.md |
Code quality
- If you need more input or troubleshooting output, ask first — don't guess.
- A
go test -runpattern that matches no test printsokand exits 0. A red-proof using-runmust first prove the filter matched something (-v, look for=== RUN). Generally: an instrument that can drop results silently is not a measurement.
The installer publishes by TAG, not by push (R-110)
This fence is in the core deliberately: its trigger is editing scripts/felhom-host-install.sh, and
no path-scoped rule covers that file. It governs the one artifact that runs as root on a virgin
box.
- Pushing
scripts/felhom-host-install.shtomainpublishes NOTHING.manifests/webpage.yamlruns two git-syncs: the website frommain, and/scripts/from the taginstaller-v<SCRIPT_VERSION>. - To publish: cut
installer-v<new SCRIPT_VERSION>, bump the--refinwebpage.yaml(both the sidecar and the init container), commit, sync. - To roll back: move the tag back and wait ~30 s. No ArgoCD sync, no deploy — that is the emergency lever; fix forward afterwards.
- Do NOT pin the website to the tag, and the URL never carries a ref —
felhom-bootstrap.shand the hub's day-0 command follow the tag with no edit. hostinstall_gates.pygate 6 fails if the manifest stops naming aninstaller-v…tag or if the website stops trackingmain.
Workflow — what is specific to this repo
- Never
git add -Ahere — parallel sessions share the clone and it sweeps foreign WIP. Stage explicit paths only,git pull --rebasebefore every push. REPORT.mdis overwritten, so two sessions in this repo clobber each other. The second session writesREPORT-<topic>.mdand never touches the sharedREPORT.md.CHANGELOG.mdhere is per-area:hub/,scripts/,website/.
Gates — ONE entry point
Run python3 scripts/repo_gates.py after ANY change in this repo. It runs every gate —
site_gates.py, hostinstall_gates.py, hub_confirm_gate.py, manifest_bearer_gate.py,
reuse_refs_check.py and instructions_gate.py — 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.
site_gates.py is a gate, not a runner — do not model new work on it;
app-catalog-felhom.eu/scripts/catalog_gates.py is the canonical runner (R-161).
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. Both facts are why CI is still owed (OPEN-ITEMS.md R-168).
End-of-session checklist
Registers first — a finding goes in documentation/backlog/OPEN-ITEMS.md first, never only in a
report, an audit or STATUS.md. Four items in this project were minted in a spike doc and lost
(R-153/154/155, R-156/157). This applies to every session that ships, breaks or decides
something, not only sessions that touch documentation/ — which is why it is here and not in
docs.md.
CHANGELOG.md+REPORT.mdin every repo touched (see the workspace root for the rule, and the parallel-session caveat above).REUSE.md, if a shared helper or pattern moved (same commit).OPEN-ITEMS.md— every finding, with a number.- Root
STATUS.md— at the end of every session in which something shipped, broke or was decided. It is a view ofOPEN-ITEMS.md; nothing may exist only there. One screen, written for the operator in plain language, and deliberately notCONTEXT.md. - The capability map (
documentation/architecture/00-capability-map.md), if a capability's status changed — with its new evidence citation. - Confirm your own last push's CI run went green, by run ID. CI emails on failure, which is a
PUSH signal; this is the PULL check that catches a lost, filtered or unread mail. Quote the run id
and its conclusion, e.g.
curl -s "https://gitea.dooplex.hu/api/v1/repos/admin/<repo>/actions/tasks?limit=3"→ match thehead_shato your commit. An unchecked green is an assumption, not an observation.