# REPORT — the guards, and one thing I could not establish (2026-08-09 evening) No machine touched. Both demo boxes stayed offline and in transit; nothing here needed them. **Parts 1, 2 and 3 are done. Part 4 and Part 5 are NOT done** — see §6 and §8. Part 5 was marked droppable and was dropped; **Part 4 was not marked droppable and I ran out of session before it**, which is a shortfall rather than a decision. --- ## 1. Part 1.1 — the deleter: **NOT ESTABLISHED** **This prompt's account is not corroborated by any source I can reach**, and the prompt itself said not to take its word for it. | claim in the prompt | what I found | |---|---| | "the register records … pruned to the newest ten … 33 deletions, all HTTP 204" | **No such row exists.** The only prune-adjacent row is **R-210**, which is `WAITING-ON-OPERATOR`, says in terms *"Nothing was deleted; this is a list, not an action"*, and concerns **local Docker images on DooPlex**, not the Gitea registry | | "newest ten per package" | **Not visible in the current state.** `felhom-agent` generic holds 10, but `felhom-controller` and `felhom-hub` container packages hold **19 each** | Everything else I could reach is silent, and silence here is not evidence of anything: `package_cleanup_rule` is **empty**; `package_version` has **no soft-delete column**, so a deletion leaves no row; Gitea's `action` feed carries **no package operation at all** across 2026-08-08 → 2026-08-09 and nothing whatever on the evening of 08-08; the Gitea pod has **53 days uptime, 0 restarts**. **And I withdrew one of my own claims.** R-287 said *"no DELETE on the packages API appears in 48 h of Gitea router logs"*. Re-checked: `kubectl logs --since=72h` returns nothing older than **2026-08-09 16:35** and contains **zero** `api/packages` lines even for requests I made myself. **The log never covered the window, so its silence was never evidence.** Withdrawn in the register. **What remains established, unchanged:** `v0.120.0` downloadable at 2026-08-08 14:29 UTC (run 267 printed `ok v0.120.0`), 404 by 2026-08-09 09:30 UTC (run 284), and `0.128.0` published at 14:47 UTC — eighteen minutes after the good run. **It may be unestablishable from this side: Gitea keeps no package-deletion trail.** Per §8 I halted that part's attribution and carried on; the rest is independent. ## 2. The number, and the two places that read it `felhom-agent/scripts/retention-policy.json` → `generic_versions_kept: 10`. Read by **`scripts/check-published-versions.py`** (bounds its assertion) and referenced by the prune procedure. The file states, in its own header, that ten is an **observed state and not a located ruling**, and that the principled bound is the hub's vouched `min_agent` floor — nothing can install below it — which needs network the gate does not have. **What CI no longer covers:** *a released version older than the retention window is no longer asserted downloadable.* Its **git tag and config tree are still asserted**; only the binary's presence is dropped. The check **prints the dropped versions every run**: ``` 11 released version(s); retention policy keeps the newest 10 NOT ASSERTED (older than the retention window …): 0.120.0 ^ these versions still have git TAGS … what is no longer asserted is the BINARY's presence. ``` Three controls: green at 10 naming what it dropped (**exit 0**); widened to 11 the evicted version re-enters and convicts (**exit 1**, `FAIL v0.120.0`); the policy file removed → **exit 2 INCONCLUSIVE**, naming the path it tried — never silently unbounded. ## 3. `felhom-agent` green ``` reuse-refs OK · instructions OK · published OK · release-complete OK · all agent gates OK ``` **At `main`: green**, pushed as `53d047a`, and the pre-push hook ran the same entry point. **At a tag: not re-proved tonight, and I will not claim it.** The available evidence is that the gate is ref-independent — it enumerates from the Gitea tags API, and runs **190** (`v0.126.0`) and **216** (`v0.127.0`) were tag pushes that passed. Minting a tag purely to prove it would have published a release, which this session forbids. ## 4. Red-proofs | # | mutation | asserted applied | outcome | |---|---|---|---| | 1 | **tag check removed** | `grep -c` → 1 | **scenario A FAILED, reporting `flash = "artifacts_set"`** — Friday's exact defect returned: the manifest saved with no tag | | 2 | package check removed | `grep -c` → 1 | scenario B FAILED, `flash = "artifacts_set"` | | 3 | inconclusive branch mapped onto success | `grep -c` → 2 | scenario E FAILED | All reverted; `grep -c MUTATION` → **0**; all seven tests green again. **Red-proof 3, stated precisely rather than flatteringly:** with the inconclusive branch mapped onto success the save did **not** complete — it fell through to `artifact_sha_invalid`. So the mutation proves the guard is load-bearing for *the message the operator sees*, not for the save itself. That is the honest reading, and it is exactly the defect scenario E exists to prevent: the operator being told "missing or invalid" when the truth is "I could not reach the registry". ## 5. The five scenarios, as the operator sees them | | outcome | the message | |---|---|---| | **A** tag missing | REFUSED | *"Refused: that version has no usable git tag. The installer fetches an agent's config files from `raw/tag/v/configs/`, so a version published without its tag makes every fresh install and reinstall fail at step 5 of 8 — as root, on a virgin machine. … Fix it by pushing the tag: `git tag -a v && git push origin v`"* | | **B** package pruned | REFUSED | *"Refused: that version's artifact is not downloadable. The version is tagged but its package is not in the registry, so a box would 404 fetching the binary itself. … Publish it — `bash scripts/release-agent.sh `"* | | **C** no checksum | REFUSED | the pre-existing *"Couldn't set the checksum …"* — see the note below | | **D** both good | **SAVES**, `artifacts_set`, byte-identical behaviour | — | | **E** registry unreachable | REFUSED | *"Refused: could not verify — this does not mean anything is missing. … It refuses rather than saving with a warning, because a warning beside a success reads as a success. … There is deliberately no override: the registry is on your own server, so if it is unreachable the vouch can wait."* | **Scenario C changed shape because my first draft modelled nothing real,** and that is worth recording. With a Gitea client configured, `resolveArtifactSHA` fetches the sha **authoritatively and ignores what was submitted** — so "submit an empty sha" cannot produce an empty stored sha. The genuine shape is Gitea answering with no `sha256`, and the **existing** refusal already owns it. The test now pins the guarantee (*the manifest is unchanged*) rather than a mechanism I had invented. **No override was built, and none is wanted.** §8's halt condition did not trigger. ## 6. Part 4 — NOT DONE The twelve downgraded claims were not re-examined and no receipts were searched for. **The honest-grey count is therefore still the twelve from last night, unverified in either direction**, and `where-felhom-stands.*` is untouched. This is the session's shortfall: Parts 1–3 took the budget, and splitting Part 4 in half would have produced exactly the kind of half-checked green the whole exercise exists to prevent. **Whether the "code moved under the proof" rule can fire at all** is therefore still open from last night, where it fired **zero** times out of twelve — every downgrade came from missing evidence, not from decayed evidence. My reading remains that it *can* fire but will stay rare until rows cite evidence at all, which is R-290. ## 7. Hub deployed **v0.102.0**, live and verified: deploy image `gitea.dooplex.hu/admin/felhom-hub:0.102.0`, rollout complete, and the page footer reads `0.102.0`. Manifest commit **`36bcd12`**; code commit `b55fc17`. The image was verified **served by the registry before** the manifest was bumped, not after. ArgoCD's `felhom` app has `automated.enabled: false`, so the sync was triggered explicitly — **no `kubectl set image` at any point.** ## 8. Part 5 — dropped, as marked Not started. It was explicitly droppable and it is the only part that touches customer-facing wording, which is where a rushed edit does most harm. ## 9. Register **Ceiling R-290 → R-292.** Opened **R-291** (the narrowing, with its reason, so it can be widened deliberately) and **R-292** (`artifact_sha_invalid` conflates three facts — found by scenario E failing, worked around by ordering, message untouched). Closed: **R-273's owed-guards tail**, both guards built. Corrected: **R-287**, twice. ## 10. Observations — noticed, not acted on - **R-292 is the interesting one.** A test I wrote to check a new guard failed for a reason that had nothing to do with the guard, and that reason was a real pre-existing defect. The five scenarios earned their keep before the feature shipped. - **The golden gets no tag probe.** It is fetched by version and has no config tree, so a tag probe would assert something the installer never does. Deliberate, and stated in the code. - **The gate is skipped entirely when no Gitea client is configured**, or a hub without registry credentials could never vouch anything. Pinned by its own test, and worth knowing: the guard is only as present as the client is. - `check-release-complete` runs in `--fast`, so a missing tag is now caught by the **pre-push hook**, earlier than CI.