New shared scripts/instructions_gate.py, registered in controller_gates.py and agent_gates.py, never copied into a sibling repo (the reuse_refs_check.py precedent). 20 fixture tests, all asserting the effect: exit code AND that the message names the file and the reason. It is a consistency gate, not a budget gate, and the failure message says so. A /context reading measured the instruction files at 15k tokens against 869k free in a 1M window -- space is not the constraint, and a future reader must not re-derive the wrong reason. The 200-line ceiling is adherence guidance; a file nobody can hold in their head is where contradictions hide, and five were found here. Checks run against effective text (HTML comments stripped, because they are stripped before injection): the line ceiling; every .claude/rules/*.md declares paths: or an explicit unconditional: true; no component version literal; no TEMPORARY block carrying a past date; and the workspace-root CLAUDE.md is byte-identical to its versioned copy -- the live file sits outside any git repo, so that copy is its only version-controlled record. Two traps recorded so they are not reintroduced: a bare \d+\.\d+\.\d+ matches the first three octets of every IPv4 (the gate excludes dotted quads, or it fails on 192.168.0.180 in the agent's own file); and unconditional: true is NOT a Claude Code feature but this project's own marker. Workspace-root CLAUDE.md 208 -> 182 lines (142 effective), copy kept identical. The nine-instance invariant table moved into the felhom-testing skill, which triggers when writing or reviewing a test; all three directive bullets stayed in the core. felhom.eu/CLAUDE.md got surgical corrections only and is knowingly still over the ceiling at 227 effective lines -- closing it needs the restructure R-229 defers, said plainly rather than quietly absorbed. CONTEXT.md gains standing ruling S-35. OPEN-ITEMS.md gains R-229. Docs only -- no Go, no version bump, nothing built or deployed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJc8sAGRWmavP3rMtdpkr2
This commit is contained in:
@@ -16,10 +16,8 @@ This repo contains:
|
||||
- **Architecture docs** (`documentation/`) — the **authoritative design home for the whole Felhom
|
||||
system**: `architecture/01..05-*.md`, `proxmox-platform.md`, `tests/phase*-findings.md`,
|
||||
runbooks, audits. Read these before designing.
|
||||
- **Skills** (`skills/`) — the versioned source of the Claude Code skills
|
||||
(`felhom-build-deploy`, `felhom-ui-design`, `felhom-testing`, `felhom-app-catalog`);
|
||||
install/update with `python3 scripts/install_skills.py` (symlink into `~/.claude/skills/` on
|
||||
POSIX, junction on Windows — either way repo edits are live immediately).
|
||||
- **Skills** (`skills/`) — the versioned source of the Claude Code skills; install/update with
|
||||
`python3 scripts/install_skills.py` (symlink — repo edits are live immediately).
|
||||
|
||||
See `README.md` for full architecture/DNS/email/SEO docs. See `TASK.md` for the current task (if any).
|
||||
See `REUSE.md` before writing new code.
|
||||
@@ -46,32 +44,40 @@ UI. Package map, helpers, seams, extension points: **`REUSE.md`** (e.g. new even
|
||||
|
||||
## Code quality rules
|
||||
|
||||
- Always double-check generated code for bugs, logic issues, syntax errors.
|
||||
- Handle edge cases without overcomplicating.
|
||||
- Add debug capabilities (logging, verbose output).
|
||||
- If you need more input or troubleshooting output, **ask first — don't guess**.
|
||||
- Testing doctrine (non-hollow tests, red-proofs, seams): use the `felhom-testing` skill.
|
||||
- **Seam-wiring rule — and it covers TEMPLATE GATES (fourth inert seam, hub v0.70.1):** a feature
|
||||
is not shipped until its entry point is reachable. For UI, any conditional affordance
|
||||
(`{{if .Flag}}` around a button/form/script) ships with a render test per branch of the gate —
|
||||
handler tests that POST directly prove nothing about reachability. The v0.70.0 ghost-delete was
|
||||
fully implemented server-side and fully dead UI because the button sat inside the wrong gate.
|
||||
- **A `go test -run` pattern that matches no test prints `ok` and exits 0.** Found 2026-08-02 while
|
||||
red-proofing: `-run TestCustomerUnified` matched nothing in the target file and reported
|
||||
`ok … 0.062s`, which was read as a passing red-proof. **A red-proof that uses `-run` must first
|
||||
prove the filter matched something** (`-v` and look for `=== RUN`). This is the "an absent line is
|
||||
not evidence" rule aimed at the one place a false green costs most — the proof itself. The same
|
||||
class bit twice that day: a `| tail -5` inside a census query silently dropped rows and looked
|
||||
exactly like a real finding. **An instrument that can drop results silently is not a measurement.**
|
||||
- **Seam-wiring rule — it covers TEMPLATE GATES:** a feature is not shipped until its entry point is
|
||||
reachable. For UI, any conditional affordance (`{{if .Flag}}` around a button/form/script) ships
|
||||
with a render test **per branch of the gate** — handler tests that POST directly prove nothing
|
||||
about reachability.
|
||||
- **A `go test -run` pattern that matches no test prints `ok` and exits 0.** A red-proof that uses
|
||||
`-run` must first prove the filter matched something (`-v`, look for `=== RUN`). Generally: **an
|
||||
instrument that can drop results silently is not a measurement.**
|
||||
- **A health check issues 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 the kernel's own state, never by reading
|
||||
or writing the filesystem. Measured, R-117 spike §6.3
|
||||
(`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 `EIO`s every byte — fast, and wrong.
|
||||
or writing the filesystem.
|
||||
- UI/design work (tokens, gates, copy rules): use the `felhom-ui-design` skill.
|
||||
|
||||
<!--
|
||||
CODE-QUALITY RULE CITATIONS — history, not directives.
|
||||
|
||||
Seam-wiring / template gates: the fourth inert seam, hub v0.70.1. The v0.70.0 ghost-delete was fully
|
||||
implemented server-side and fully dead UI because the button sat inside the wrong gate.
|
||||
|
||||
go test -run: found 2026-08-02 while red-proofing — `-run TestCustomerUnified` matched nothing in the
|
||||
target file and reported `ok ... 0.062s`, which was read as a passing red-proof. This is the "an
|
||||
absent line is not evidence" rule aimed at the one place a false green costs most: the proof itself.
|
||||
The same class bit twice that day — a `| tail -5` inside a census query silently dropped rows and
|
||||
looked exactly like a real finding.
|
||||
|
||||
Health check / block I/O: measured, R-117 spike §6.3
|
||||
(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.
|
||||
-->
|
||||
|
||||
- **Logging**: levels/English/no-secrets rules per `documentation/runbooks/logging-conventions.md`
|
||||
(DEBUG = flow detail, INFO = state change + duration; logs are operator-tier English; keys never
|
||||
values — the hub's bundle secret-gate blocks violating pulls fail-closed).
|
||||
@@ -104,11 +110,13 @@ pushes; **you (Claude Code) implement**. A file being open in the editor is NOT
|
||||
> **Never write secrets** into any committed file — reference them as "stored out-of-band".
|
||||
|
||||
- Update `REUSE.md` if you added/changed/deprecated a shared helper or pattern (same commit).
|
||||
- **Never `git add -A` in this repo** — parallel sessions share the clone and it sweeps foreign
|
||||
WIP (the v0.47.0 `146d165` incident: a red-proof-mutated guard got swept to `main`). Stage
|
||||
explicit paths only, `git pull --rebase` before every push, and do not run two writing
|
||||
- **Never `git add -A` in this repo** — parallel sessions share the clone and it sweeps foreign WIP.
|
||||
Stage explicit paths only, `git pull --rebase` before every push, and do not run two writing
|
||||
sessions on one clone (use `git worktree` if truly needed).
|
||||
|
||||
<!-- The sweep incident: v0.47.0, commit 146d165 — a red-proof-mutated guard got swept to main. -->
|
||||
|
||||
|
||||
## End-of-session checklist
|
||||
|
||||
- **`CHANGELOG.md` + `REPORT.md`** per the rule above, in every repo touched.
|
||||
@@ -128,13 +136,13 @@ pushes; **you (Claude Code) implement**. A file being open in the editor is NOT
|
||||
`curl -s "https://gitea.dooplex.hu/api/v1/repos/admin/<repo>/actions/tasks?limit=3"` → match the
|
||||
`head_sha` to your commit. An unchecked green is an assumption, not an observation.
|
||||
|
||||
## Tech stack (Hub)
|
||||
## Hub stack — the two constraints
|
||||
|
||||
- **Language:** Go (stdlib `net/http` + `html/template`, no frameworks). **DB:** SQLite via
|
||||
`modernc.org/sqlite` (pure Go). **Auth:** bcrypt + Bearer tokens + session cookies + CSRF.
|
||||
- **Deploy:** Docker on k3s (`felhom-system` ns). **Storage:** Longhorn PVC at `/data/` (SQLite DB).
|
||||
- **Config:** YAML via ConfigMap at `/etc/felhom-hub/hub.yaml`. Secrets via out-of-band
|
||||
`secretKeyRef` (never inline stringData — REUSE.md §3).
|
||||
The dependency list is `hub/go.mod`'s business and the deploy shape is `manifests/`. Only the two
|
||||
rules that the code cannot tell you belong here:
|
||||
|
||||
- **No web frameworks.** Go stdlib `net/http` + `html/template`, and it stays that way.
|
||||
- **Secrets via out-of-band `secretKeyRef` — never inline `stringData`** (REUSE.md §3).
|
||||
|
||||
## Environment & access
|
||||
|
||||
@@ -142,23 +150,22 @@ Claude Code runs **on DooPlex (192.168.0.180, Debian 13, user `kisfenyo`)** —
|
||||
Repos in `/mnt/5_hdd/felhom.eu/git/`, build dirs in `/mnt/5_hdd/felhom.eu/build/`. `kubectl` and the
|
||||
image build/push are local commands; felhom-pve is one SSH hop.
|
||||
|
||||
| Host | Access | Role | Blast radius |
|
||||
|------|--------|------|--------------|
|
||||
| **DooPlex (this host)** | local — `/mnt/5_hdd/felhom.eu/{git,build}/` | Build + push images, `sudo kubectl` | **Tier 2 — precious.** It *is* the recovery chain (hub, Gitea, registry, PBS, k3s+Longhorn). **Never a drill target** |
|
||||
| Demo Proxmox host (N100) | `ssh felhom-pve` — via Tailscale `100.70.170.35` (location-independent); `felhom-pve-lan` = LAN `192.168.0.162` fallback | pveum/pct + live Proxmox validation | **Tier 0 — disposable** |
|
||||
| Demo Proxmox host (HP t740) | `ssh demo-hp` — via Tailscale `100.76.96.79`; `demo-hp-lan` = LAN `192.168.0.87` (ProxyJump `felhom-pve`). **No baked SSH key** — G1 break-glass password vaulted in the hub | **The designated drill + build VM host** (operator ruling 2026-07-25) | **Tier 0 — disposable. Reach here first** |
|
||||
**Host addresses, routes, node names, break-glass and what is provisioned on each:**
|
||||
**`documentation/operations/nodes.md`** — the single home. Do not restate them here; re-check an
|
||||
address rather than trusting one written down. Tailscale topology, the accept-dns rule, the
|
||||
accept-routes spike result and rollback: `documentation/operations/tailscale.md`.
|
||||
|
||||
**Which box do I break?** → **`documentation/runbooks/target-selection.md`** — the tiers, and per
|
||||
machine what is freely permitted / needs care / forbidden, each with its reason. Read it before picking
|
||||
a machine for a drill, a destructive test or a throwaway VM.
|
||||
a machine for a drill, a destructive test or a throwaway VM. **DooPlex is Tier 2 — precious**: it *is*
|
||||
the recovery chain (hub, Gitea, registry, PBS, k3s+Longhorn), and never a drill target. Both demo
|
||||
Proxmox hosts are Tier 0 — disposable; drill and build VMs belong on the t740.
|
||||
|
||||
The `felhom-pve` transport is Tailscale (the N100 is travel-portable) — topology, the accept-dns
|
||||
rule, the accept-routes spike result, rollback, and the vacation-day checklist live in
|
||||
`documentation/operations/tailscale.md`.
|
||||
|
||||
> **Legacy: Windows workstation.** Until 2026-07-19 CC ran on Windows 11 with repos in `E:\git\`,
|
||||
> and every remote command needed `SSH=/c/Windows/System32/OpenSSH/ssh.exe` (Git Bash's ssh fails
|
||||
> silently). Retained in case that environment is revived.
|
||||
<!--
|
||||
LEGACY: WINDOWS WORKSTATION — until 2026-07-19 CC ran on Windows 11 with repos in E:\git\, and every
|
||||
remote command needed SSH=/c/Windows/System32/OpenSSH/ssh.exe (Git Bash's ssh fails silently). The
|
||||
workspace-root CLAUDE.md carries the full version. Retained in case that environment is revived.
|
||||
-->
|
||||
|
||||
## Build & deploy — Hub (GitOps via ArgoCD)
|
||||
|
||||
@@ -194,12 +201,16 @@ and runs every gate — `site_gates.py`, `hostinstall_gates.py`, `hub_confirm_ga
|
||||
and exiting non-zero if any fails. `--fast` selects only 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.
|
||||
|
||||
**Why a runner and not five invocations** (2026-08-02, R-29): a census of all thirteen gates across
|
||||
the four repos found that every check a `CLAUDE.md` names was passing, and two of the four nobody
|
||||
is told to run were failing — one since 14 July. The single-entry-point shape is the only one that
|
||||
demonstrably gets run here; `app-catalog-felhom.eu/scripts/catalog_gates.py` is the canonical
|
||||
version of it (R-161) and `repo_gates.py` copies it. `site_gates.py` is a *gate*, not a runner —
|
||||
do not model new work on it.
|
||||
`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).
|
||||
|
||||
<!--
|
||||
WHY A RUNNER AND NOT FIVE INVOCATIONS (2026-08-02, R-29): a census of all thirteen gates across the
|
||||
four repos found that every check a CLAUDE.md names was passing, and two of the four nobody is told
|
||||
to run were failing — one since 14 July. The single-entry-point shape is the only one that
|
||||
demonstrably gets run here.
|
||||
-->
|
||||
|
||||
|
||||
**The pre-push hook.** `.githooks/pre-push` runs `repo_gates.py --fast` and refuses the push if it
|
||||
fails. It is **per-clone** and switched on once with `git config core.hooksPath .githooks` — a
|
||||
|
||||
Reference in New Issue
Block a user