55 claims verified. Twelve moved, all downwards: walked 32 -> 20, built 5 -> 17. Register ceiling R-284 -> R-290. THE RULE DID NOT FIRE THE WAY IT WAS EXPECTED TO. Not one downgrade came from code moving under an old proof. All twelve came from step 1 of the same rule -- the cited evidence does not exist. Measured: of the 28 capability-map rows behind the page's claims, 8 carry a tests/ or audits/ path and 20 carry prose only. The green dots were drawn from rows that cite an argument, not a walk (R-290). The map, not the dataset, is what needs fixing -- it still says PROVEN-LIVE for all twelve. And once it ran backwards: fault.operator-email looked contradicted by R-182, but live source shows the backup_run_failures digest allowlisted, operator-only and templated, with recovery_unit_capture_failed now record-only. The claim is right and the REGISTER ROW is stale (R-289). The session went looking for stale proofs and found a stale defect. R-281 WITHDRAWN -- wrong in both directions, settled by the operator's mailbox. The tripwire DID fire (escrow_blob_served 10:19:41Z = 12:19 CEST) and false error-severity alarms fired too, for deliberate attended work (R-285). The measurement's cause is ESTABLISHED: the P7 query copied hub.db without hub.db-wal, and the signature is exact -- it reported "2 events all day, newest 00:30:07", and the rows at or before 00:30:07 number exactly 2. Timezone and wrong-key were tested and refuted. The control had been drawn from the same stale snapshot as the measurement, which is why it agreed (R-286). Part 4: NO WORKFLOW CHANGED, deliberately. The gate is not ref-sensitive -- it enumerates from the Gitea tags API, and both previous tag pushes passed. The red is TRUE: run 267 saw v0.120.0 downloadable, run 284 on the same commit saw 404. Who deleted the package is NOT established and is not guessed (R-287). The page is now generated from where-felhom-stands.yaml by scripts/render_stands.py: static, zero script tags, every moved status carrying a visible "changed, was X" chip. The React bundle -- whose content was gzip+base64 inside a JS module map -- is kept as a dated snapshot. scripts/check_stands.py gates the data and convicted 51 problems in my own first draft before the staged positive control ever ran.
12 KiB
REPORT — making the picture true (2026-08-09, unattended)
Read-only against all live infrastructure. Both demo machines were powered off and in transit; no
box was probed, woken or waited on. Claims that only a running box could settle are marked
needs-hardware, which is a verdict, not a gap.
1. The verdict table
55 claims. Statuses moved on 12 of them — all downwards. walked 32 → 20, built 5 → 17;
partial 14 and missing 4 unchanged. Full per-claim detail with sources is in
documentation/architecture/where-felhom-stands.yaml.
| claim | was | now | why |
|---|---|---|---|
install.installer-by-tag |
walked | built | gate 6 asserts the manifest names an installer tag; no walk of a rollback on file |
use.lifecycle |
walked | built | no walk document cited |
drives.enrol |
walked | built | the 08-09 walk exercised RE-attach (which failed, R-280); first enrolment of a NEW drive has no walk |
drives.migrate |
walked | built | no walk document cited |
backup.tier1 |
walked | built | no walk document cited |
backup.whole-machine |
walked | built | no walk document cited |
backup.restore-proof |
walked | built | no walk cited, and the last recorded restore-test on demo-hp FAILED (2026-08-05) |
fault.selfheal |
walked | built | no walk document cited |
fault.operator-email |
walked | built | source-verified as correct, but no run observed delivering |
fail.drive-filling |
walked | built | no walk document cited |
fail.lost-recovery-code |
walked | built | by-design refusal; no walk document cited |
fail.hub-down |
walked | built | no walk document cited |
Upgraded: 1. install.byo — the page said "the first real one has not happened". A real
--mode byo install completed on demo-hp on 2026-08-09 (Day-0 provision SUCCESS, 3 m 49 s). Still
not a customer's own hardware, so not walked, but the sentence was false.
needs-hardware: 4 — use.lan-fallback, backup.restore-proof, fail.disk-failing,
fail.internet-down. Each needs an observation on a running box; each says which.
Confirmed: 38. Seven of those were re-confirmed against live source or the live hub tonight rather than against paperwork: the tripwire, the off-site repository, the claim path, the catalogue, the tunnel, the reset code and the operator-email digest.
2. Every downgrade, with the coupling that broke
The rule is "a proof is about the code that existed when it ran". It did not fire the way the task expected. Not one downgrade came from code moving under an old proof. All twelve came from step 1 of the same rule — the cited evidence does not exist.
Measured: of the 28 capability-map rows behind the page's claims, 8 carry a tests/ or audits/
path in their evidence column and 20 carry prose only. The green dots were being drawn from rows
that cite an argument, not a walk. Filed as R-290.
And the decay ran the other way once. fault.operator-email — "one mail per run, every failing
app named" — I first took to be contradicted by R-182 (open, "tells the operator about ONE app and
silently swallows every other"). Reading live source: the digest backup_run_failures is allowlisted
(hub/internal/api/handler.go:1837), operator-only (notify/dispatcher.go:423) and templated
(notify/templates.go:48); recovery_unit_capture_failed is record-only (dispatcher.go:376); a
cooldown drop now logs a suppressed row (dispatcher.go:314-330). The claim is right and the
register row is stale — filed as R-289. The session went looking for stale proofs and found a
stale defect.
3. The positive control
1 BASELINE real dataset -> OK, exit 0
2 PLANT scratch copy: use.dlna missing -> walked -> CONVICTED, exit 1
"use.dlna: status 'walked' but NO evidence document cited"
3 REMOVE scratch copy deleted; committed dataset never touched
4 RE-RUN real dataset -> OK, exit 0
Plant → convicted → removed → clean. The gate also convicted 51 problems in my own first draft of
the dataset (bad anchors, register ids that are not in OPEN-ITEMS.md, an evidence path that does not
exist) before any of this — which is the more convincing demonstration, because it was not staged.
4. The two known disagreements — both settled, and neither document was wrong
"A customer restores their own data with no help." The map says MISSING (as evidence); the 2026-08-07 walk records a customer route completed with no shell. Not a contradiction. The map's row is "A customer (not the operator) performs a restore via UI alone" — it is about who. The walk proves the route. No non-operator has ever done it, which is what the page's own neighbouring claim already says.
The reinstall story. The map's PROVEN-LIVE (2026-08-04 night drill) row is scoped in its own text
to "a controller-data-volume rebuild — NOT a total host loss". The 2026-08-09 rehearsal was a
whole-host uninstall and reinstall. The map has no row for that case at all — a gap, not a
disagreement.
Would anything here have caught either one? No — and it could not have, because neither was false. Both are collisions of vocabulary: "customer" meaning the route or a person, "rebuild" meaning the guest or the host. No gate detects an ambiguity that makes two true sentences look contradictory. They surfaced only when someone tried to state them side by side. That is the argument for the dataset — one id, one scope, one status — and against prose rows.
5. The data file
documentation/architecture/where-felhom-stands.yaml, 55 entries. Every entry cites at least one
source and the gate proves it (check_stands.py rule 1). YAML rather than JSON because statuses move
one line at a time and a YAML diff shows which claim moved; a JSON re-dump reflows.
Rules honoured: it is a view (every entry cites map / register / evidence); no status was raised in it — the one upgrade is recorded against evidence and the map is named as the thing that must change; and it is regenerated, not hand-edited for the page.
6. The page
where-felhom-stands.html— generated, 54 KB, zero<script>tags, same palette (#0b1220/#121b2c/#34d399#60a5fa#fbbf24#64748b), 1600 px, A3 landscape print rules.- Every moved status carries a visible
changed 2026-08-09, was walkedchip plus a why it moved line — 12 of them, no diffing required. Where Felhom Stands.html→where-felhom-stands-2026-08-09-snapshot.html(git mv, so the space is out of every shell path), with a line indocumentation/README.mdcalling it a dated snapshot that is not maintained.- What the old bundle actually was, since it aimed the fix: not merely minified — the content sat gzip+base64 inside a JS module map, and the three blobs decompress to the bundler and React, with the document itself in a JSON-escaped string on line 393. It rendered and nothing else.
7. The corrected silence rows
R-281 is WITHDRAWN. It was wrong in both directions, and the operator's mailbox is what settled it.
- The tripwire DID fire:
escrow_blob_servedat 10:19:41 UTC = 12:19 CEST, eight minutes before the verified restore. - False alarms fired too:
host_down09:28 UTC andnode_down09:30 UTC, both error severity, bothsent, for deliberate attended work — eight operator mails in all. → R-285, the opposite gap from the one filed.
The measurement's cause IS established. The P7 query copied /data/hub.db without
hub.db-wal; the hub runs SQLite in WAL mode, so everything after the last checkpoint was invisible.
Signature, exact: P7 reported "2 events all day, newest db_dump_completed 00:30:07", and the
number of rows on 08-09 at or before 00:30:07 is exactly 2.
The two obvious alternatives were tested and refuted, not waved away: a timezone offset — all nine mailbox stamps equal the hub's UTC + 2 h exactly, so the window was right; and a wrong key or wrong store — the same table and key return the correct rows now. A live re-run cannot reproduce the fault because the WAL has since been checkpointed, and that is stated rather than dressed up as a reproduction.
The lesson, filed as R-286: the control was drawn from the same stale snapshot as the
measurement, so it agreed. A control must come from a different channel. The independent channel —
the mailbox — was available the whole time. This is also a trap operations/nodes.md already
documents, and which I had avoided correctly earlier in the same session.
8. Register
Ceiling moved R-284 → R-290. Opened: R-285 (planned reinstall pages the operator), R-286 (same-channel control), R-287 (the CI red is true), R-288 (the capability map is unreadable), R-289 (R-182's row is stale), R-290 (map rows cite no evidence). Withdrawn: R-281.
9. Part 4 — and I did not change the workflow, on purpose
Which gate: published / scripts/check-published-versions.py.
The premise is wrong in every particular. It is not ref-sensitive. The gate enumerates releases
from the Gitea tags API (main(), /api/v1/repos/admin/felhom-agent/tags?limit=200), so the
checked-out ref is irrelevant — and the two previous tag pushes passed (run 190 v0.126.0, run 216
v0.127.0).
What is true: run 267 (main, 28ba8593b8, 08-08 14:29 UTC) printed ok v0.120.0: binary downloadable. Run 284 (the same commit, on the tag, 08-09 09:30 UTC) printed
FAIL v0.120.0 — HTTP 404. A published release became uninstallable between the two. The registry now
holds exactly the ten newest versions; 0.128.0 was published 14:47 UTC, eighteen minutes after run
267.
Who removed 0.120.0 is NOT established, and I will not guess: package_cleanup_rule is empty
(queried in Postgres), app.ini sets no limit, publish-agent.sh:77 only pre-deletes the version it
is publishing, the Gitea pod has 53 days uptime and 0 restarts, and no DELETE on the packages API
appears in 48 h of router logs. The internal [cron.cleanup_packages] @midnight job falls in the
window and would leave no router line — a leading candidate, not a conclusion.
So nothing was silenced and no workflow file was changed. The red is a true positive — a tagged
version that cannot be installed is the exact R-115 defect the gate exists to catch, and muting it
would hide the next one. What it therefore still does not check: nothing. Nothing was disabled.
The honest fixes — bound the gate to versions at or above the vouched min_agent floor (0.127.0 today;
nothing can install 0.120.0), or retire tags whose packages go — are release decisions, and §8 forbids
fixing findings here. Filed as R-287.
Also measured, and it is good news: the failure alarm did send —
RESEND-ACCEPTED id=fa1a7a83-714f-4357-b0ca-d3c4bb7ae73f.
10. Observations — noticed, not acted on
fail.app-crashandfault.operator-emailare the same unobserved thing seen from two sides: the digest is wired and correct in source, and no one has watched it arrive.- The verification depth is recorded per claim (
depth: source-read | register+map | needs-hardware). 23 of 55 got a source or evidence read; the rest were checked against the register and map only. That is on the face of the data rather than implied by a green tick. - The capability map is the thing that actually needs fixing. The dataset now disagrees with it for twelve rows, and the dataset is only a view — the map still says PROVEN-LIVE for all twelve (R-290).
documentation/audits/holds 131 files andtests/37; the evidence exists in quantity. The gap is that the map does not point at it.- Out of scope and left alone, as instructed: every finding above, the capability-map restructure (R-288), and the two guards owed from yesterday.