docs: remove expired and contradictory blocks from CLAUDE.md (R-229)
gates / gates (push) Successful in 8s
gates / gates (push) Successful in 8s
Surgical corrections only; the file is deliberately NOT restructured (deferred). Deleted the expired TEMPORARY block. It read "felhom-pve is at a remote site (until ~2026-08-02) ... Delete this block on return" and was still being read as current fact on 2026-08-06, four days past its own deadline, while felhom-controller/CLAUDE.md asserted the opposite. The location-independence fact worth keeping (localapi binds 169.254.253.1:8443 on vmbr9 since the R-50 island migration) moved to an HTML comment. Every component version literal is gone from effective text, including the --version reading and the go.mod Go directive. Versions change several times a day; ask the hub's /hosts + /configs or the box. The drill-VM claim and the host addresses now point at documentation/operations/nodes.md, which already stated both correctly. This file's drill-VM claim was the correct one -- confirmed by qm list on demo-hp. The R-115/R-188/R-186 release narratives moved to an HTML comment and to the felhom-build-deploy skill; the directives stayed (never hand-roll the build; the build -> tag -> publish -> push order; reproducible -trimpath -buildvcs=false). The health-check block-I/O rule became .claude/rules/health-checks.md, scoped to the five packages where health checks are written. It had been duplicated from felhom.eu/CLAUDE.md with a note explaining that that file does not load in an agent-only session -- correct reasoning, made obsolete by path-scoped rules. agent_gates.py registers the shared instructions gate. Docs only -- no Go, no version bump, nothing built or deployed. Ledger: felhom.eu/documentation/audits/LEDGER-instruction-trim-2026-08-06.md Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJc8sAGRWmavP3rMtdpkr2
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
paths: ["internal/capability/**", "internal/storage/**", "internal/localapi/**", "internal/hub/**", "internal/guesthook/**"]
|
||||||
|
---
|
||||||
|
|
||||||
|
# A health check issues no block I/O
|
||||||
|
|
||||||
|
No `statfs`, no `getdents`, no read, write or `fsync` — **not even behind a timeout**.
|
||||||
|
|
||||||
|
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 (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.
|
||||||
|
|
||||||
|
This rule used to be duplicated verbatim in felhom-agent/CLAUDE.md with a note explaining that
|
||||||
|
felhom.eu/CLAUDE.md "does not load in an agent-only session". That reasoning was correct before
|
||||||
|
path-scoped rules existed. The single source is now felhom.eu/CLAUDE.md "Code quality rules"; this
|
||||||
|
file is the scoped copy that loads exactly where health checks are written. (2026-08-06)
|
||||||
|
-->
|
||||||
@@ -1,3 +1,32 @@
|
|||||||
|
## docs — expired and contradictory blocks removed from CLAUDE.md (2026-08-06, R-229) — no version bump
|
||||||
|
|
||||||
|
**Documentation and gate registration only. No Go changed, nothing built, nothing deployed.**
|
||||||
|
|
||||||
|
Surgical corrections; the file was deliberately **not** restructured (that is deferred, R-229).
|
||||||
|
|
||||||
|
- **Deleted the expired TEMPORARY block.** It read *"felhom-pve is at a remote site (until
|
||||||
|
~2026-08-02) … Delete this block on return"* and was still being read as current fact on
|
||||||
|
**2026-08-06**, four days past its own deadline — while `felhom-controller/CLAUDE.md` asserted the
|
||||||
|
opposite. The location-independence fact worth keeping (`localapi` binds `169.254.253.1:8443` on
|
||||||
|
`vmbr9` since the R-50 island migration) moved to an HTML comment.
|
||||||
|
- **Every component version literal is gone** from effective text, including
|
||||||
|
`felhom-agent --version → 0.115.0` and the `go.mod` Go directive. Versions change several times a
|
||||||
|
day; ask the hub's `/hosts` + `/configs` or the box.
|
||||||
|
- The drill-VM claim and the host addresses now point at `documentation/operations/nodes.md`, which
|
||||||
|
already stated both correctly. **This file's drill-VM claim was the correct one** — confirmed by
|
||||||
|
`qm list` on demo-hp.
|
||||||
|
- The R-115/R-188/R-186 release **narratives** moved to an HTML comment and to the
|
||||||
|
`felhom-build-deploy` skill; the **directives** stayed (never hand-roll the build; the
|
||||||
|
build → tag → publish → push order; reproducible `-trimpath -buildvcs=false`).
|
||||||
|
- The health-check block-I/O rule became `.claude/rules/health-checks.md`, scoped to the five
|
||||||
|
packages where health checks are written. It had been duplicated from `felhom.eu/CLAUDE.md` *with a
|
||||||
|
note explaining that that file does not load in an agent-only session* — correct reasoning, made
|
||||||
|
obsolete by path-scoped rules.
|
||||||
|
|
||||||
|
`agent_gates.py` now registers **`instructions`** (shared, `felhom.eu/scripts/`, never copied).
|
||||||
|
|
||||||
|
Full accounting: `felhom.eu/documentation/audits/LEDGER-instruction-trim-2026-08-06.md`.
|
||||||
|
|
||||||
## v0.126.0 — a fetch failure is not a wrong recovery code (2026-08-06, R-224)
|
## v0.126.0 — a fetch failure is not a wrong recovery code (2026-08-06, R-224)
|
||||||
|
|
||||||
**A hub the agent could not reach was reported to the customer as a bad recovery code.** Measured live
|
**A hub the agent could not reach was reported to the customer as a bad recovery code.** Measured live
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ internal/storage/ storage observer + durable ids + role/claim classifiers + S
|
|||||||
## Build / run
|
## Build / run
|
||||||
|
|
||||||
- Module `gitea.dooplex.hu/admin/felhom-agent`; binary `felhom-agent` (`cmd/felhom-agent/`).
|
- Module `gitea.dooplex.hu/admin/felhom-agent`; binary `felhom-agent` (`cmd/felhom-agent/`).
|
||||||
- **Pure Go stdlib + `golang.org/x/crypto` only** — no web frameworks. `go.mod` directive go 1.25.0;
|
- **Pure Go stdlib + `golang.org/x/crypto` only** — no web frameworks. The Go version is `go.mod`'s
|
||||||
DooPlex (192.168.0.180, where CC runs) has the Go toolchain and is on the same LAN as the demo
|
business, not this file's. DooPlex (where CC runs) has the toolchain — build and run live tests
|
||||||
host — build and run live tests locally.
|
locally.
|
||||||
- Version via `-ldflags "-X main.version=<v>"`; `--version` flag. Bump on meaningful changes + CHANGELOG entry.
|
- Version via `-ldflags "-X main.version=<v>"`; `--version` flag. Bump on meaningful changes + CHANGELOG entry.
|
||||||
- **Full build/deploy/publish runbook: use the `felhom-build-deploy` skill.** Summary:
|
- **Full build/deploy/publish runbook: use the `felhom-build-deploy` skill.** Summary:
|
||||||
|
|
||||||
@@ -63,41 +63,39 @@ internal/storage/ storage observer + durable ids + role/claim classifiers + S
|
|||||||
> stays (it is a no-op when you work in this tree, and load-bearing if anything was pushed from
|
> stays (it is a no-op when you work in this tree, and load-bearing if anything was pushed from
|
||||||
> elsewhere).
|
> elsewhere).
|
||||||
|
|
||||||
> **RELEASING IS ONE COMMAND, AND IT PUBLISHES (R-115).** There used to be a raw `go build` line
|
> **RELEASING IS ONE COMMAND, AND IT PUBLISHES (R-115).** Never hand-roll the build. The release
|
||||||
> here and a *separate* "Publish" row, so publishing was a step someone had to remember — and it was
|
> script also creates the `v<version>` git TAG that `felhom-host-install.sh` fetches that version's
|
||||||
> **forgotten three times in five days**, the last leaving agent v0.120.0 deployed on both demo hosts
|
> sixteen config files from (R-183), and verifies by an **independent download** rather than trusting
|
||||||
> and undownloadable, where a documented-path reinstall would have silently downgraded them while
|
> the publish step's own output. The order is **build → tag LOCALLY → publish → push tag**, and each
|
||||||
> reporting success. Do not hand-roll the build: the script also creates the `v<version>` git TAG
|
> step protects something (R-188, R-186). A released binary is independently verifiable: the build
|
||||||
> that `felhom-host-install.sh` fetches this version's sixteen config files from (R-183), and it
|
> uses `-trimpath -buildvcs=false`, so the same source produces the same bytes.
|
||||||
> verifies by an **independent download** rather than trusting the publish step's own output.
|
|
||||||
> `scripts/publish-agent.sh` still exists and is still correct — the release script CALLS it rather
|
|
||||||
> than reimplementing it.
|
|
||||||
>
|
>
|
||||||
> **THE ORDER IS build → tag LOCALLY → publish → push tag, and each step protects something (R-188,
|
> The ordering rationale, the verification recipe and the `publish-agent.sh` flag-parity trap are in
|
||||||
> R-186).** The tag is created before the publish so the build and the tag describe the same commit;
|
> the **`felhom-build-deploy`** skill.
|
||||||
> it is *pushed* after, because the push is what wakes CI (`on: [push]`) and a tag visible before its
|
|
||||||
> package makes the published-versions gate correctly fail a correct release — it did, on roughly
|
<!--
|
||||||
> every second release, and R-168 sends that failure to you by mail. The invariant the old order
|
RELEASE RATIONALE — history, not directives. Full procedure lives in the felhom-build-deploy skill.
|
||||||
> protected is asserted directly instead: the gate now also refuses a **published version with no
|
|
||||||
> tag**. If the push fails after a successful publish the script says so loudly and prints the
|
R-115: there used to be a raw `go build` line here and a *separate* "Publish" row, so publishing was
|
||||||
> one-line recovery; if the *publish* fails it removes the local-only tag so a retry is clean.
|
a step someone had to remember — and it was forgotten three times in five days, the last leaving an
|
||||||
>
|
agent version deployed on both demo hosts and undownloadable, where a documented-path reinstall
|
||||||
> **A RELEASED BINARY IS INDEPENDENTLY VERIFIABLE (R-186).** The build uses `-trimpath
|
would have silently downgraded them while reporting success. scripts/publish-agent.sh still exists
|
||||||
> -buildvcs=false` so the same source produces the same bytes whether or not the tag exists yet —
|
and is still correct — the release script CALLS it rather than reimplementing it.
|
||||||
> before this, a rebuild could not reproduce the sha you were vouching. To check any published
|
|
||||||
> version yourself:
|
R-188/R-186 ordering: the tag is created before the publish so the build and the tag describe the
|
||||||
>
|
same commit; it is *pushed* after, because the push is what wakes CI (on: [push]) and a tag visible
|
||||||
> ```bash
|
before its package makes the published-versions gate correctly fail a correct release — it did, on
|
||||||
> V=0.122.0
|
roughly every second release, and R-168 sends that failure by mail. The invariant the old order
|
||||||
> git checkout "v$V" && go build -trimpath -buildvcs=false -ldflags "-X main.version=$V" \
|
protected is asserted directly instead: the gate now also refuses a published version with no tag.
|
||||||
> -o /tmp/felhom-agent-check ./cmd/felhom-agent
|
If the push fails after a successful publish the script says so loudly and prints the one-line
|
||||||
> sha256sum /tmp/felhom-agent-check
|
recovery; if the publish fails it removes the local-only tag so a retry is clean.
|
||||||
> curl -fsSL "https://gitea.dooplex.hu/api/packages/admin/generic/felhom-agent/$V/felhom-agent" | sha256sum
|
|
||||||
> ```
|
R-186 reproducibility: before -trimpath -buildvcs=false, a rebuild could not reproduce the sha you
|
||||||
>
|
were vouching. publish-agent.sh's fallback build uses the SAME flags — it used to force
|
||||||
> The two hashes must match. `publish-agent.sh`'s fallback build uses the **same** flags — it used to
|
CGO_ENABLED=0 and produce a 74 KB-smaller binary for the same version; if either build line ever
|
||||||
> force `CGO_ENABLED=0` and produce a 74 KB-smaller binary for the same version; if either build line
|
changes, change both, or one version name means two binaries again.
|
||||||
> ever changes, change both or one version name means two binaries again.
|
-->
|
||||||
|
|
||||||
|
|
||||||
| Step | Where | One-liner |
|
| Step | Where | One-liner |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -124,39 +122,32 @@ internal/storage/ storage observer + durable ids + role/claim classifiers + S
|
|||||||
|
|
||||||
## Demo host (for live tests)
|
## Demo host (for live tests)
|
||||||
|
|
||||||
Node **`demo-felhom`**, API `https://192.168.0.162:8006`. SSH alias `felhom-pve` (root@pam) —
|
Two demo nodes: **`demo-felhom`** (N100, `ssh felhom-pve`) and **`demo-hp`** (HP t740, `ssh demo-hp`,
|
||||||
available to CC as plain `ssh felhom-pve`. A **second demo node `demo-hp`** (HP t740, node name
|
no baked key — break-glass via the hub). **Addresses, routes, node names, break-glass recipe and what
|
||||||
`felhom-host`, `ssh demo-hp` — no baked key; break-glass root via hub `host_recovery/demo-hp-bb76ea` +
|
is provisioned on each: `felhom.eu/documentation/operations/nodes.md`** — the single home; do not
|
||||||
`sshpass`) is the **designated drill+build VM host** per the 2026-07-25 operator ruling, and that ruling
|
restate them here, and re-check an address rather than trusting one written down.
|
||||||
is **realized** — it hosts drill VM `300` (`drill-r50`), so **start there**, not on DooPlex. (The
|
|
||||||
historical golden-bake `drill.qcow2` still lives on DooPlex and is a bake fixture, not a drill target.)
|
|
||||||
**Which box is safe to break, and what may be done to each:
|
**Which box is safe to break, and what may be done to each:
|
||||||
`felhom.eu/documentation/runbooks/target-selection.md`** — read it before any destructive test. Both
|
`felhom.eu/documentation/runbooks/target-selection.md`** — read it before any destructive test.
|
||||||
nodes + the break-glass recipe: `felhom.eu/documentation/operations/nodes.md`. The agent pins the served leaf cert — verify the
|
Drill and build VMs belong on `demo-hp`, not DooPlex (operator ruling 2026-07-25).
|
||||||
fingerprint still matches before a live run. Selftest modes (run locally on DooPlex, pointed at the
|
|
||||||
demo API): `--selftest[=read|task|hub|storage|backup|restore-test|pbs-verify]`; no flag = the daemon.
|
|
||||||
|
|
||||||
> **TEMPORARY — felhom-pve is at a remote site (until ~2026-08-02).** The home-LAN literal
|
The agent pins the served leaf cert — verify the fingerprint still matches before a live run.
|
||||||
> `192.168.0.162` is NOT reachable from DooPlex for the duration. Access via Tailscale:
|
Selftest modes (run locally on DooPlex, pointed at the demo API):
|
||||||
> felhom-pve = 100.70.170.35; the `Host felhom-pve` entry in `~/.ssh/config` on DooPlex already
|
`--selftest[=read|task|hub|storage|backup|restore-test|pbs-verify]`; no flag = the daemon.
|
||||||
> points there (the direct-LAN path stays available as `Host felhom-pve-lan`). Delete this block on
|
|
||||||
> return. All documented `ssh felhom-pve` / `pct exec` workflows are unchanged. Path is **direct**
|
<!--
|
||||||
> (not DERP), ~37 ms rtt per hop. At the remote site the host is on **DHCP**; re-check its address
|
`localapi` does not bind a LAN literal: since the R-50 island migration (2026-07-25) it binds
|
||||||
> rather than trusting one written here (`ip -br addr show vmbr0` — it read `192.168.0.162/24` on
|
169.254.253.1:8443 on vmbr9, which is location-independent by design, and proxmox.endpoint is
|
||||||
> 2026-07-30, and `felhom-pve-lan` from DooPlex is still `No route to host`). Details + findings:
|
https://127.0.0.1:8006. That is why the N100 being travel-portable needs no config edit. The
|
||||||
> `felhom.eu/documentation/audits/AUDIT-vacation-remote-ops-2026-07-20.md`
|
2026-07-20..08-02 vacation window and its findings are recorded in
|
||||||
>
|
felhom.eu/documentation/audits/AUDIT-vacation-remote-ops-2026-07-20.md.
|
||||||
> **The "agent does not run at the remote site" warning this block used to carry is RETRACTED
|
|
||||||
> (2026-07-30) — it was true before R-50 and is false now.** `localapi` no longer binds a LAN literal:
|
LEGACY: WINDOWS WORKSTATION — until 2026-07-19 CC ran on Windows 11; pct commands over SSH needed
|
||||||
> since the R-50 island migration (2026-07-25) it binds `169.254.253.1:8443` on `vmbr9`, which is
|
export MSYS_NO_PATHCONV=1, and every remote command used SSH=/c/Windows/System32/OpenSSH/ssh.exe.
|
||||||
> location-independent by design, and `proxmox.endpoint` is `https://127.0.0.1:8006`. Verified live:
|
Agent deploy was a two-hop copy via the Windows box (cygpath -w for the local scp path; CRLF hazard
|
||||||
> `systemctl is-active felhom-agent` → `active`, `felhom-agent --version` → 0.115.0, and the per-guest
|
on config files). The workspace-root CLAUDE.md carries the full version.
|
||||||
> local API answered `GET /disks` over the island. No config edit and no Viktor GO are outstanding.
|
-->
|
||||||
|
|
||||||
> **Legacy: Windows workstation.** Until 2026-07-19 CC ran on Windows 11; `pct` commands over SSH
|
|
||||||
> needed `export MSYS_NO_PATHCONV=1`, and every remote command used
|
|
||||||
> `SSH=/c/Windows/System32/OpenSSH/ssh.exe`. Agent deploy was a two-hop copy via the Windows box
|
|
||||||
> (`cygpath -w` for the local scp path; CRLF hazard on config files).
|
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
@@ -175,10 +166,8 @@ All shippable work commits **directly to `main`**; `main` equals what is deploye
|
|||||||
|
|
||||||
- Code quality: verify generated code for bugs/edge cases; add debug logging; **ask rather than
|
- Code quality: verify generated code for bugs/edge cases; add debug logging; **ask rather than
|
||||||
guess** when you'd otherwise invent input/output.
|
guess** when you'd otherwise invent input/output.
|
||||||
- **A health check issues no block I/O** — no `statfs`, no `getdents`, no read, write or `fsync`, **not
|
- **A health check issues no block I/O** — full rule, measurement and scope:
|
||||||
even behind a timeout**. Liveness is decided from `/proc` and kernel state. The full rule + the
|
`.claude/rules/health-checks.md`, which loads when you touch the packages that write them.
|
||||||
measurement lives in `felhom.eu/CLAUDE.md` "Code quality rules"; it is repeated here because health
|
|
||||||
checks are written in THIS repo and that file does not load in an agent-only session. R-117 spike §6.3.
|
|
||||||
- Update `REUSE.md` if you added/changed/deprecated a shared helper or pattern (same commit).
|
- Update `REUSE.md` if you added/changed/deprecated a shared helper or pattern (same commit).
|
||||||
- **Run `python3 scripts/agent_gates.py` from the repo root after ANY change in this repo.** It is
|
- **Run `python3 scripts/agent_gates.py` from the repo root after ANY change in this repo.** It is
|
||||||
the ONE entry point for this repo's gates. Today it runs one — `reuse_refs_check` over this
|
the ONE entry point for this repo's gates. Today it runs one — `reuse_refs_check` over this
|
||||||
|
|||||||
@@ -41,10 +41,13 @@ import sys
|
|||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
SHARED_REUSE = os.path.join(os.path.dirname(ROOT), "felhom.eu", "scripts", "reuse_refs_check.py")
|
SHARED_REUSE = os.path.join(os.path.dirname(ROOT), "felhom.eu", "scripts", "reuse_refs_check.py")
|
||||||
|
SHARED_INSTRUCTIONS = os.path.join(
|
||||||
|
os.path.dirname(ROOT), "felhom.eu", "scripts", "instructions_gate.py")
|
||||||
|
|
||||||
# (label, absolute script path, args, fast)
|
# (label, absolute script path, args, fast)
|
||||||
GATES = [
|
GATES = [
|
||||||
("reuse-refs", SHARED_REUSE, [ROOT], True),
|
("reuse-refs", SHARED_REUSE, [ROOT], True),
|
||||||
|
("instructions", SHARED_INSTRUCTIONS, [ROOT], True),
|
||||||
("published", os.path.join(ROOT, "scripts", "check-published-versions.py"), [], False),
|
("published", os.path.join(ROOT, "scripts", "check-published-versions.py"), [], False),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user