Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de812bc027 | |||
| 3e8ebeb96c | |||
| cefdc731a4 | |||
| e56dcb8a4c |
@@ -21,6 +21,7 @@ fast, and wrong.
|
|||||||
|
|
||||||
This rule used to be duplicated verbatim in felhom-agent/CLAUDE.md with a note explaining that
|
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
|
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
|
path-scoped rules existed. Deliberate scoped copies now live in felhom.eu/.claude/rules/hub.md and
|
||||||
file is the scoped copy that loads exactly where health checks are written. (2026-08-06)
|
felhom-controller/.claude/rules/gates.md (hub.md's comment names them); none is the single source. This
|
||||||
|
file is the copy that loads exactly where agent health checks are written. (2026-08-06; corrected 2026-10-06)
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
unconditional: true
|
||||||
|
---
|
||||||
|
# Unprompted work — rules for any session without a task file
|
||||||
|
|
||||||
|
> Goal sessions, nightly sessions, "work the register" sessions. **A session that starts from
|
||||||
|
> `/goal` or a standing brief inherits these rules exactly as it inherits the gates.** They are the
|
||||||
|
> part of `PROMPT-TEMPLATE.md` that a task file used to carry and a goal does not. Same wording lives
|
||||||
|
> in `felhom.eu`, `felhom-controller`, `felhom-agent` and `app-catalog-felhom.eu` `.claude/rules/`, and in the workspace
|
||||||
|
> root's unversioned `.claude/rules/`; change all five or none.
|
||||||
|
|
||||||
|
## 1. What you may pick up on your own
|
||||||
|
|
||||||
|
- A register row **you or another CC session filed**, with owner CC, at P3 or a bounded P2, that
|
||||||
|
needs **no operator decision**, touches **no customer data by design**, and introduces **no
|
||||||
|
mechanism nobody has measured**. Smallest first.
|
||||||
|
- A defect you find while exercising the product, filed as a row **before** you fix it — **unless it is small**:
|
||||||
|
a small finding is fixed in the session and never filed (the size rule, `OPEN-ITEMS.md` „How a row is filed").
|
||||||
|
- Hygiene: register compression, stale citations, rows with no owner, documents that contradict
|
||||||
|
live source.
|
||||||
|
|
||||||
|
**Not yours, ever, without a task file or an operator word:** money; anything that changes risk to
|
||||||
|
customer data; anything that changes a promise the product makes to a customer; anything that
|
||||||
|
reverses a documented design decision (`documentation/architecture/` — a design decision is not a
|
||||||
|
defect, R-370); anything on DooPlex or ep0; baking or vouching a golden; promoting a
|
||||||
|
catalog version; a new external dependency.
|
||||||
|
|
||||||
|
## 2. When you may decide instead of ask (operator grant, 2026-09-14)
|
||||||
|
|
||||||
|
You may take a decision yourself when **all** of these hold: the architecture folder and the register
|
||||||
|
give a clear direction; your choice follows that direction; it is reversible without customer-data
|
||||||
|
risk; and you can write it in the `09-update-architecture.md` §3 shape — one answerable sentence, the
|
||||||
|
options, what each costs, why this one. **Then record it** as a dated decision in `CONTEXT.md` and
|
||||||
|
the owning architecture document, tagged *decided by CC unattended — operator may reverse*, and put
|
||||||
|
it **first** in the morning note. A decision you cannot write in that shape is one you do not take.
|
||||||
|
|
||||||
|
## 3. The discipline a task file used to carry
|
||||||
|
|
||||||
|
1. **Baselines first.** Read each repo's `main` hash and version from live source before touching it.
|
||||||
|
2. **Read the architecture document for the area, and name it** in the report, before any claim.
|
||||||
|
3. **Red-proof every correctness fix.** A test never seen failing has not been shown to test anything.
|
||||||
|
4. **Live-validate on a Tier-0 box** through the endpoints the UI invokes. `demo-hp` is `ssh hp`.
|
||||||
|
Throwaway apps only; the standing apps and `bentopdf` stay.
|
||||||
|
5. **Evidence off the machine at the end of each phase**, before any revert (R-320).
|
||||||
|
6. **One release per repo per session**, with a CHANGELOG entry (controller: with its `MinAgent`
|
||||||
|
line), REPORT overwritten, floor raised to deliver it. **No golden unless a drill or fresh install
|
||||||
|
needs one** (the waiver, R-468). **No `--no-verify`.**
|
||||||
|
7. **An enumerated gap becomes a row in the same session — or, if it is small, is fixed in it** (the size rule).
|
||||||
|
Prose is not a record.
|
||||||
|
8. **Hungarian text is searched with ASCII fragments**, with a positive and a negative control.
|
||||||
|
9. **Never leave a half-state.** If time runs out, revert to clean and say what was reverted.
|
||||||
|
10. **Teardown, three layers, stated** — machine, host, hub — or "provisioned nothing".
|
||||||
|
|
||||||
|
## 4. The morning note
|
||||||
|
|
||||||
|
One screen, plain language, in this order: **decisions you took** (§2) first; what you exercised;
|
||||||
|
what broke and whether you fixed it; rows opened and closed with the register size before and after;
|
||||||
|
what needs the operator, each with what happens if they do nothing. No file paths, no function
|
||||||
|
names, no row numbers as the subject of a sentence.
|
||||||
|
|
||||||
|
## 5. Instruction files
|
||||||
|
|
||||||
|
**Instruction files (`CLAUDE.md`, `.claude/rules/*`) are kept true by the session that finds them wrong**
|
||||||
|
(operator ruling 2026-10-06, `09` §3 decision 150). A session MAY, without asking: correct a stale fact (a command, a
|
||||||
|
count, a version, a path, a description of what a gate does), add a fact it proved, and remove a reference to something
|
||||||
|
that no longer exists. Each edit is named in the report (file, line, before, after, why). A session MAY NOT, without the
|
||||||
|
operator's word: loosen a safety rule, a fence, a „never", a protected machine, a secret rule, or a review step; or
|
||||||
|
remove a rule. When in doubt, it is a rule change, and it goes to the operator. If Claude Code's own permission check
|
||||||
|
asks before such an edit, wait for the operator's click; if it refuses, record that and file the exact line.
|
||||||
+16
-1
@@ -1,4 +1,19 @@
|
|||||||
## unreleased
|
## Unreleased (2026-10-06 evening) — the shared rule file (`09` §3 decision 152); no code change
|
||||||
|
|
||||||
|
- `.claude/rules/unprompted-work.md` added, byte-identical to the copies in felhom.eu, felhom-controller, app-catalog-felhom.eu and the workspace root (checked with `diff` against the controller's copy and one md5 across all five). Its copies line names five copies.
|
||||||
|
|
||||||
|
## Unreleased (2026-10-06 afternoon) — instruction files kept true (`09` §3 decision 150); no code change
|
||||||
|
|
||||||
|
- `CLAUDE.md` „Gates — ONE entry point": the runner runs every gate in its `GATES` table (five: three shared, `published`, `release-complete`); `--fast` skips `published` (network). It said two gates and „all of them".
|
||||||
|
- `CLAUDE.md`: the decoy gate and its audit are named with their `felhom.eu/` prefix (they do not exist in this repo).
|
||||||
|
- `.claude/rules/health-checks.md` (comment): the health-check rule's copies live in felhom.eu `hub.md` and the controller's `gates.md`; it named felhom.eu `CLAUDE.md` „Code quality rules", which holds no such rule.
|
||||||
|
|
||||||
|
## v0.149.0 — a weekly disk trim of each customer guest, the crash-boot fact for the controller, the phantom WARN names its runbook (R-444, R-856, R-99; operator rulings `09` §3 139, 143, 140) (2026-10-06)
|
||||||
|
|
||||||
|
Released by `scripts/release-agent.sh`: binary sha256 `6bcae9c2eb5d97e8285316583870059835793893299e291891a53a4ce505585f`
|
||||||
|
config bundle sha256 `e182c82dcf4a67faa3bcb74dbe4ffa7b06e0b27dc8451cb7574d6339ce91ad66` (tag `v0.149.0` = `f277e61`).
|
||||||
|
**The bundle carries the new sudoers rule for the trim (`FELHOM_FSTRIM`) — deliver it with the binary:** signed
|
||||||
|
`agent_update`, then signed `agent_config_update`.
|
||||||
|
|
||||||
- R-856 (`09` §3 decision 143): new local-API route `GET /host/crash-guard` — passes the host crash guard's last-boot record (present, last_boot_at, last_boot_unclean, tripped) from /var/lib/felhom-crash-guard/state.json to the controller, which waits ~15 min with app mails after a crash boot. Read-only, no Proxmox call, guest-token authed; a missing/unreadable/garbled file answers 200 present:false (never an error page). An older agent answers 404, which the controller reads as unknown (normal 90 s grace) — no controller MinAgent raise needed.
|
- R-856 (`09` §3 decision 143): new local-API route `GET /host/crash-guard` — passes the host crash guard's last-boot record (present, last_boot_at, last_boot_unclean, tripped) from /var/lib/felhom-crash-guard/state.json to the controller, which waits ~15 min with app mails after a crash boot. Read-only, no Proxmox call, guest-token authed; a missing/unreadable/garbled file answers 200 present:false (never an error page). An older agent answers 404, which the controller reads as unknown (normal 90 s grace) — no controller MinAgent raise needed.
|
||||||
- R-444 (`09` §3 decision 139): weekly guest disk trim. New sudoers alias FELHOM_FSTRIM with ONE exact rule `/usr/sbin/pct ^fstrim [0-9]+$` (rides the signed config bundle; decoys pinned by TestSudoersFstrimRuleIsExact) and capability guest-fstrim (non-critical). New internal/fstrim job: each owned RUNNING guest gets `pct fstrim <vmid>` once a week - due Wednesday from 10:00 host-local, starts only 10:00-20:59 (never the 01:00-06:59 night), holds the one-heavy-op gate so it never runs beside a backup or restore-test (busy -> deferred to the next hourly tick; a box that was off catches up at its next daytime hour); a failed trim WARNs and is retried at most 3 times that week; bytes parsed from `pct fstrim`'s "(N bytes) trimmed" lines; positive log `fstrim: guest N trimmed X GiB in Ys`; last result per guest persisted in <state_dir>/guest-disk-trim.json and reported as the new omitempty host-report stanza `guest_disk_trim`. Opt-out: agent.json "disk_trim": {"disable": true}.
|
- R-444 (`09` §3 decision 139): weekly guest disk trim. New sudoers alias FELHOM_FSTRIM with ONE exact rule `/usr/sbin/pct ^fstrim [0-9]+$` (rides the signed config bundle; decoys pinned by TestSudoersFstrimRuleIsExact) and capability guest-fstrim (non-critical). New internal/fstrim job: each owned RUNNING guest gets `pct fstrim <vmid>` once a week - due Wednesday from 10:00 host-local, starts only 10:00-20:59 (never the 01:00-06:59 night), holds the one-heavy-op gate so it never runs beside a backup or restore-test (busy -> deferred to the next hourly tick; a box that was off catches up at its next daytime hour); a failed trim WARNs and is retried at most 3 times that week; bytes parsed from `pct fstrim`'s "(N bytes) trimmed" lines; positive log `fstrim: guest N trimmed X GiB in Ys`; last result per guest persisted in <state_dir>/guest-disk-trim.json and reported as the new omitempty host-report stanza `guest_disk_trim`. Opt-out: agent.json "disk_trim": {"disable": true}.
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ This is in the core because breaching it is how this component stops being audit
|
|||||||
|
|
||||||
## Gates — ONE entry point
|
## Gates — ONE entry point
|
||||||
|
|
||||||
**Run `python3 scripts/agent_gates.py` from the repo root after ANY change here.** It runs this
|
**Run `python3 scripts/agent_gates.py` from the repo root after ANY change here.** It runs every
|
||||||
repo's gates — `reuse_refs_check` and `instructions_gate`, both the **shared** copies in
|
gate in its `GATES` table (that table is the list); the shared ones — `reuse_refs_check`,
|
||||||
`felhom.eu/scripts/`, never copied into this repo (a copy recreates the drift they detect; an absent
|
`instructions_gate`, `observations_gate` — are the copies in `felhom.eu/scripts/`, never copied into
|
||||||
sibling clone FAILS). `--fast` selects the gates touching no network and no container runtime; today
|
this repo (a copy recreates the drift they detect; an absent sibling clone FAILS). `--fast` selects the
|
||||||
that is all of them. **A missing gate is a FAILURE, never a skip.**
|
gates touching no network and no container runtime, and skips `published` (network), naming it. **A missing gate is a FAILURE, never a skip.**
|
||||||
|
|
||||||
**The pre-push hook** (`.githooks/pre-push`) runs it with `--fast` and refuses a failing push. It is
|
**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
|
**per-clone** — switch it on once with `git config core.hooksPath .githooks`, and a manual run WARNS
|
||||||
@@ -104,8 +104,8 @@ the mechanism are exempt.
|
|||||||
|
|
||||||
**A gate ships with a decoy test that has been seen to fail (R-421).** A decoy is the LABEL without
|
**A gate ships with a decoy test that has been seen to fail (R-421).** A decoy is the LABEL without
|
||||||
the FACT — a directory with the right name and no bake log, a note whose prose mentions the marker it
|
the FACT — a directory with the right name and no bake log, a note whose prose mentions the marker it
|
||||||
lacks. `scripts/decoy_coverage_gate.py` refuses a new gate that has neither a decoy nor a named
|
lacks. `felhom.eu/scripts/decoy_coverage_gate.py` (run by felhom.eu's `repo_gates.py`, for all four repos) refuses a new gate that has neither a decoy nor a named
|
||||||
exemption carrying its row. The four shapes, the 2026-09-01 sweep that fooled 16 of 29 gates, and the
|
exemption carrying its row. The four shapes, the 2026-09-01 sweep that fooled 16 of 29 gates, and the
|
||||||
decoys withdrawn as illegitimate: `documentation/audits/AUDIT-gate-decoys-2026-09-01.md` and
|
decoys withdrawn as illegitimate: `felhom.eu/documentation/audits/AUDIT-gate-decoys-2026-09-01.md` and
|
||||||
`felhom-controller/.claude/rules/gates.md`. **Scope is a fact too** — prefer `os.walk` over
|
`felhom-controller/.claude/rules/gates.md`. **Scope is a fact too** — prefer `os.walk` over
|
||||||
`os.listdir`, and a glob over a hand-maintained list.
|
`os.listdir`, and a glob over a hand-maintained list.
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
# REPORT — agent v0.148.0 (2026-10-06, the burn-down night)
|
# REPORT — the shared rule file (2026-10-06 evening)
|
||||||
|
|
||||||
Full session report: `felhom.eu/REPORT-burndown3-2026-10-06.md`. Baseline `208fac8` (v0.147.0).
|
Operator ruling 2026-10-06 14:24 (`09` §3 decision 152): the agent repo gets its copy of the shared rule file. Added
|
||||||
|
`.claude/rules/unprompted-work.md`, byte-identical to the other copies (`diff` against felhom-controller's copy: no
|
||||||
**Released:** v0.148.0 (tag = `861d32a`; binary sha256 `3e68a087…`, bundle `a6fa4f58…`, verified by download). R-349
|
output; md5 `c1e6c881…` across all five before the copies line changed, one md5 after). No code changed; no release.
|
||||||
(the host report carries `agent_sha256` — the hub's host pages read „matches vouched” for all three boxes) and R-25's
|
`agent_gates.py --fast`: reuse-refs, instructions, release-complete, observations OK. The session report is
|
||||||
agent half (the format answer carries the verified `fs_uuid`). **Delivered** by signed `agent_update` (all three on
|
`felhom.eu/REPORT.md`.
|
||||||
0.148.0 by 22:50Z) and `agent_config_update` (root files 0.148.0 by 22:58Z) to demo-hp, demo-felhom, Tester 1. Tester 2:
|
|
||||||
nothing sent (off).
|
|
||||||
|
|
||||||
**On main, unreleased:** R-426 decoys (new `scripts/test_gate_decoys.py`: published against a fake Gitea,
|
|
||||||
release-complete, the shared reuse-refs/instructions/observations).
|
|
||||||
|
|
||||||
**Said plainly:** `go test ./internal/osupdate` was red on DooPlex from 21:25 to 01:55 — `configs/test_felhom_config_bundle.py`
|
|
||||||
read the installer 1.32.0 KEPT names as installer-written files. The v0.148.0 binary was released inside that window;
|
|
||||||
its code is unaffected (a test-only sibling coupling; CI has no Go). Fixed in `b2b82ae`.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user