The three blockers yesterday's English walk found are closed and each proven on
a live system. The verdict is deliberately 'nothing known now stands in their
way' rather than 'the walk passed': fixes are not a journey, and the hour has
not been re-walked by a stranger on a fresh install.
Also filed: the HP demo box answers on no route this session has (R-601), the
cookie-vs-session language instrument trap that would have had me fix R-598
twice (R-602), and the apostrophe that silently never matches a rendered page
(R-603).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Part 0 shipped as controller v0.258.0 (its own commit). Part A is the guide's
English twin. Part B is the walk: a box installed from scratch that day, used by an
English speaker following only the English guide and the screens.
VERDICT: not yet ready for an English-speaking tester, because the claim page — the
one screen between them and their box — is English chrome with Hungarian messages
(R-596, P1). Everything else held: the download page, the bilingual console, all
three customer mails, the bind page and its refusal, the dashboard's first language
from customer.language alone, both app pages, the whole catalog, the language switch
both ways — every one of them with zero Hungarian lines.
One intervention (I1 = R-494, filed 2026-09-14); the stop rule was not reached. The
walk exercised what 2026-09-14 could not: the graphical installer, the auto-reboot,
and the mailed link and self-bind page end to end — that walk's H1 is closed, because
this session had a mailbox.
R-214 CLOSED as a side effect and seen rather than reasoned about: the console's last
paint is now the bilingual "the box is linked" banner.
R-516 does NOT close, and the item-by-item note says why: more than half its twelve
items are about what a HUNGARIAN household reads, and an English walk cannot see
them. It now waits on a Hungarian walk with a second drive.
Rows opened: R-596 (P1, the claim page), R-597 (the setup code is three Hungarian
words), R-598 (the Backup page's protection warnings), R-599 (a drill's teardown is
blocked 30 minutes by report staleness and the 409 does not say so).
Golden 0.258.0 baked, published, vouched, with its record. The waiver was NOT retired
and the record says why in one line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Part C shipped the same day the pilot was read: fifty apps in three pushes, 1 031 of
1 032 strings. The English Apps list shows ZERO Hungarian app descriptions across all
53 apps — the only Hungarian left on it is the "Naprakész" badge (R-589) and the
language picker naming itself, which is correct.
The Hungarian Apps list is identical to the pre-slice capture once the per-session
CSRF token AND Docker's own "Up N hours" container string are normalised. Both
normalisations are stated in the evidence rather than applied quietly — the second
one moved because two hours of wall clock passed between captures, not because any
copy changed.
Fleet floor raised to 0.257.0 with the declared MinAgent 0.131.0, above the vouched
golden so the declaration carries it. demo-felhom went 0.255.0 -> 0.257.0 by itself
in under 12 seconds and THEN rendered the English tagline: the floor delivered the
feature, not a version string.
Rows: R-593 (papra describes a session-signing key as "the app's subdomain" — the one
string left untranslated) and R-594 (the catalog gate can convict a retrieval promise
but has no way to REGISTER a true one, which the shared vocabulary's design calls
for). R-560 closed. 281 -> 287 rows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
10-localisation.md §7 goes from [DESIGN, proposed] to [FACT], with the numbers it was
specced against corrected — and one correction chose an instrument rather than a
footnote. The catalog has 1 032 copy strings, not 835. 832 carry a Hungarian letter,
which was right. But the ASCII-ONLY Hungarian is ~120 strings, not three: „Aldomain"
appears 53 times and „A szerver domain neve" 53 times, and the three the plan named
(„Igen"/„Nem"/„Nincs") do not occur in this catalog at all. An accent-only gate passes
every one of them inside an English block — R-565's blind spot arriving again in a
different repo.
New §10.6 records what was proven live rather than reasoned about: the English pages
show English; the seven Hungarian pages are byte-identical before and after the push
apart from the per-session CSRF token; and a 0.255.0 box with the block synced onto it
renders identically and logs no warning, in a 93-line window that contains the sync's
own lines, so the absence is evidence and not a dead log.
Rows R-589 (the update badge is Hungarian on an English page), R-590 (the data-folder
card's backup promise, likewise, and it is a promise about the customer's files),
R-591 (Stack.Copy() deep-copies five Meta fields and not the new I18n map — safe
today, which is precisely why it is a row), R-592 (three defects inside the new
catalog gate, closed the same session, each found by its own decoy). R-560 updated:
Parts A and B done, Part C waiting on the operator's read of the pilot.
STATUS asks for that read, and for the floor to 0.257.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Live at iso.felhom.eu, sha256 dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571
db2fd8449a97e94829, and both download pages now name it.
Every gate criterion is recorded with its OBSERVED value in
documentation/tests/iso-release-1.29.0-2026-09-18/ — including two proof
installs from the published bytes, one per boot-menu entry, each with a first
boot AND one reboot: /etc/issue bilingual with zero hits for 8006, pvebanner
masked, package 1.29.0 installed, unit enabled and fired, pairing code present,
and the installed script byte-identical to repo HEAD. G11: the downloaded bytes
hash to the published checksum.
A defect was caught BETWEEN builds by looking at the screen rather than at the
config: the second menu entry read "Felhom telepítés (szöveges mód) / Install
Felhom (text mode)" — 58 characters — and the GRUB menu box cut it at "Instal".
The English half was unreadable on the boot screen. Shortened to "… / text" and
rebuilt; the published image is the rebuilt one. The Hungarian half is the part
that may not change, so the English half is the part that gave.
Teardown: VMs 323/324/325 destroyed, the two unclaimed appliance registrations
discarded (zero left in `registered`), guest 9201 untouched — 23 containers
before and after. The two stale *.rootpw.txt files were shredded from the
publish source directory before the upload ran from it (R-587, files gone; the
guard that would stop it recurring is still open).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Part B's live proof is a LOG LINE rather than an e-mail, and the evidence page
says why: severityNotifies drops `info`, so every safe converted producer mails
nobody, and every converted producer above `info` describes something bad that
is not true. Triggering one would mean a false record on a real box's timeline
or a state change the fences forbid. The log line was built in v0.256.1 for
exactly this, after finding there was nothing to look at on either side of the
wire.
15:07:28 controller_started [hu-only] — Controller elindult (0.256.1)
15:08:25 controller_started [+household(en)] — Controller elindult (0.256.1)
The Hungarian sentence is identical in both, and the hub stored the Hungarian
in every case including the English-household push.
R-558 CLOSED, R-555 closed with it. R-585 filed: six producers still send
Hungarian only because their sentence arrives already finished from another
package — `offbox_enlarge_blocked` matters most, since it has no hub entry so
its raw sentence IS the household's whole mail.
10-localisation.md gains 10.4; STATUS rewritten for the operator with the two
decisions left (raise the floor to 0.256.1; whether to rotate the demo
password after R-584).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
POST /configuration/global-floor with min_controller_version=0.255.0 and the
declared min_agent=0.131.0 (R-472); 303 flash=floor_set, read back from the
form, not from the POST.
The proof is the N100: it was never hand-deployed and its own Docker reports
felhom-controller:0.255.0 healthy within five minutes of the save. Three boxes
remain below — all BLOCKED or DOWN, which is a floor being held, not a floor
failing; each takes it on its next check-in.
R-580 filed: curl's %{redirect_url} rebuilds the request URL WITH the --netrc
credentials in it, so the hub password was printed into the session's own
output. Nothing written to a file, nothing committed. The build-deploy skill
now carries the rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
R-579 filed and closed the same day: five shell templates loaded style.css
with no cache-buster, so a browser holding the pre-0.254.0 file rendered the
new globe unstyled; and the globe sat outside the card.
- STATUS.md rewritten for the operator: what was seen, why, the third defect
found while fixing it (version disclosure on the guest share page, caught by
TestShareGuest_HeadersTilesNoAdminChrome), and the one decision left —
raise the fleet floor to 0.255.0, with what happens either way.
- 10-localisation.md §3: the shells' asset tag, and why the two guest pages get
an opaque tag rather than the version.
- Audit D: the parity diff (91 of 106 fixtures identical, every dashboard page
among them) and the live endpoint evidence from demo-hp guest 9201.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Agent versions checked FIRST (both live boxes 0.132.0, above the declared 0.131.0), because a
floor is held for a box whose agent is below the requirement. Positive observables at both ends,
and the box's is the one that counts: demo-felhom's own log reads "settle-gate: GO — at/above
floor 0.254.0", its image file and running container agree, and its four other containers stayed
up. The release's visible change is on that second box too: one globe on the sign-in page, zero
of the old text links.
What the table must not be read as: the two DOWN customers got nothing and will take 0.254.0
unattended when they next report, from 0.115.0 and 0.245.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
10-localisation.md §10.3: the saved notes follow the box language at write time, with the
one-night consequence stated rather than hidden; the globe, and the table of WHO reads which
page and where its globe posts — getting that wrong makes the button do nothing, which it did
on /recovery until the live probe found it. Decision 6 superseded a second time; decision 8
(a claim carries the visitor's language) recorded. Decision 5 of §11's anonymous-surface line:
changing what a VISITOR reads is within what an anonymous request may do; changing anything the
household owns is not, and POST /lang can do only the first.
R-578 — the deadlock, and why it is a row rather than a fixed bug: UpdateOffboxStatus holds the
settings write lock while running its callback, boxLang() wants the read lock, sync.RWMutex is
not reentrant. On a real box an off-site run would have hung FOREVER holding that lock. The
symptom was a test suite going from 8 minutes to a 25-minute timeout. Fixed and guarded, but the
guard covers one package and three helper names; the class needs a gate.
R-577 — a guest share visitor still has no way to pick a language, and the household's setting
is the wrong default for a stranger. Deliberately left, pinned by a test, and the operator's to
decide because it is a promise the share feature makes.
.claude/rules/live-probes.md, unconditional: never send a deploy request for an app that is not
installed, not even expecting a refusal — the endpoint accepts first and validates later. Two
sessions made that mistake in two days, the second WITH a prompt line forbidding it. A prompt is
read once; a rule file is loaded every session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The floor went 0.250.0 -> 0.253.0 with min_agent 0.131.0 declared, which is what carries a floor
above the vouched golden (R-472). Agent versions were checked FIRST, because the floor is held for
a box whose agent is below the requirement.
Positive observables at both ends, and the box's is the one that counts: the hub logs "managed
floor SERVED … from declared", and demo-felhom's own log reads "settle-gate: GO — at/above floor
0.253.0 (we are 0.253.0)" with the running image and four untouched containers to match. The
release then answered in both languages on that second box.
What the table must not be read as: the two DOWN customers got nothing and will take 0.253.0
unattended when they next report, from 0.115.0 and 0.245.0 — nobody has carried a box forward
from 0.115.0 in one step. Ordinary for a floor, and stated rather than left implied.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
All 179 Hungarian error messages carry a key; zero remain. 10-localisation.md gains §10.2: the
four properties util.MsgError had to have at once and the failure each one prevents, and the
plural rule as a BUNDLE rule rather than a per-call-site flag, with the answerable sentence and
what the other option would have cost.
Two instrument defects recorded rather than tidied away, because both shapes recur:
R-576 — the parity gate has a measured blind spot. The bulk converter dropped the continuation
of multi-line concatenations, damaging 7 producers, and the gate stayed GREEN: every surviving
fragment WAS a byte-equal base-commit literal, so its question ("is this text real?") was
answered yes while the CALL had lost half its sentence. Two behaviour tests caught it. The
general form: a structural gate over the TEXT cannot see a defect in the CALL.
And the script counting what was left was case-sensitive, so it said "0 remain" while five did —
R-565's shape inside the measurement. Every "no Hungarian left" claim in this slice is now made
case-insensitively and with both controls.
R-575 — the soft memory-overcommit warning has no error to carry a key and no language where it
is built, so it renders Hungarian on an English page. Named in the code, not hidden.
Live evidence includes a mistake I made and corrected: a probe of the deploy refusal INSTALLED
vaultwarden on demo-hp (the endpoint accepts before it validates), the same mistake the previous
session recorded. Removed through the product's own path with its data; verified gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Endpoint-level on demo-hp guest 9201, method stated. The flash key renders the sentence in
each language where 0.251.0 showed the raw key; a link an OLD controller minted still shows
its prose, in both languages; country names follow the language and the list re-sorts.
Hungarian parity measured on the page BODIES, not on a hash: ten pages on 0.252.0, the guest
rolled back to 0.251.0, the same ten again, then rolled forward. Six byte-identical; the other
four differ only in live state (a clock crossing a minute, an app unhealthy for a moment, and
the update-available line, which is true on the old version and false on the new). The first
pass had saved only hashes — a hash cannot show WHAT moved — so the before-state was
reproduced rather than asserted.
Box left on 0.252.0 with its saved language `hu`; every English probe used the ?lang= override,
which is not persisted. Provisioned nothing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
- audits/r553-2026-09-17/: live before/after on demo-hp (CSRF redacted), the 409 refusal, the hub
health block, red-proofs for all five sites, the site-5 fixture diff, gates.
- 10-localisation.md §9: the five decisions with what each reads now; the rule (a text signature may
remain only where the text is not ours); the one legacy exception and its end date.
- Register: R-553 and R-563 closed to CLOSED-ITEMS; R-569 (four API handlers match English words),
R-570 (the legacy stale-note fallback + the slice-2 fence), R-571 (classifier and alert placement
documented nowhere). R-557 carries the R-570 dependency. 263 -> 264 open.
- STATUS, including the live probe that installed an app and was removed the same minute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Design written after the spike ran (controller v0.247.0 live on demo-hp): mechanism,
flow, fallback, gates per language, catalog model, sliced plan with costs, operator
rulings 1-4 recorded, CC decisions 5-6, open decisions 1b and 7.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Decision 1 delivered: floor 0.246.0 served, the N100 on 0.246.0 within seconds;
Peti's box is DOWN on the hub and receives it when it reports.
Decision 2: the agent vouch was refused by R-120 until a newer golden existed.
On the operator's choice, golden 0.246.0 was baked (sha 05b7559d, amd64, all
markers, token leak 0 with control 1, registry 200 before teardown) and vouched
together with agent 0.132.0. golden_currency_gate: WAIVED -> OK. Bake evidence
filed where the gate and runbook read it: tests/golden-0.246.0-2026-09-17/.
Recorded, none reaching the registry: a first attempt on the arm64 template
(my version sort), a self-matching pkill, and an OOM-killed watcher whose
post-bake steps were done by hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
R-539 closed: five real controller kills on demo-hp 9201 with the production
24 h window raised controller_slow_crashloop, and exactly one operator mail
arrived (09:29:40Z). The fast brake never armed.
Register 215 -> 213 open (R-551, R-552 filed; R-539, R-546, R-549, R-550 closed).
unproven.py: 35 of 55 not walked, no number moved.
The report names the brief's wrong claims first and one recommendation not
followed: the controller floor was not raised - validated on one guest, a gap
of my own found during validation, and a floor above the golden reaches Peti's
box too. The operator's call.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
STATUS.md gets the morning note in the rules' order - decisions (none under the
unattended rule), what was exercised, what broke (nothing in the product; three
fixes worth making, all filed), rows (five opened, none closed), what could not
be tested, cleanup, and what needs the operator with the cost of doing nothing.
REPORT-chaos-night-2026-09-17.md follows template section 15 and names the
prompt's wrong claims first. It is a separate file because REPORT.md holds the
earlier session's write-up and this repo's rule forbids clobbering it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The tier-3 pause is the zero-knowledge escrow design and is untouched. What was
missing was the ASK, while the backup page promised the copy that had never run.
- VOLUNTEER-first-hour.md: a new step 6, right after the dashboard password and
before the first app - what the code is, where, write it on PAPER, and that
Felhom cannot get it back for them. Sections 6..12 renumbered to 7..13.
- day0-install.md A.2b: the operator step for a REBUILT box, which was missing.
Acknowledged delete -> the hub re-issues by itself; otherwise ONE press of
"Re-issue PBS credentials" (F-14 ruling 2026-07-13, hub/internal/web/pbsdr.go).
This is the correction to last night's "zero presses" note.
- 07-backup-architecture.md: 6.1 records tier-3's paused state as a DESIGN, and
2 records that the household is asked from first login.
- capability map: the first-hour row's last gap closed, with what it still does
not claim (no volunteer has walked the ask from the written guide).
- register: R-543 CLOSED with the live measurements; R-545 filed (nothing
un-configures an off-site target). R-511 was already closed yesterday.
- STATUS: the answered publish question removed (1.28.0 is live), readiness yes.
- evidence: red-proofs, the two-box live validation, teardown on three layers,
and both of my own mistakes in this session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The capability map's journey row now carries the half it could never finish: five
photos in, deleted the way a child would, the old route refusing and touching
nothing, the off-site restore returning them, and them opening — sha256 identical,
5 of 5, with a negative control.
Stated with it, because both are true: the bind needed ZERO operator presses (the
box registered itself and used the mail the hub sent itself), but the PBS cascade
needed ONE — the Re-issue press R-511 documents, which then succeeded because of
this morning's ep0 grant.
R-543 (P1) is the honest caveat: off-site ON by default is not off-site WORKING on
day one — a fresh box waits at „Kulcsletétre vár" until the household creates its
recovery code, and nothing asks them to, while the tier-1 row already promises that
copy. R-544 records a log line that says „escrow deleted" where the effect is
demotion to retained custody.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The automatic connect e-mail is proven with a real mailbox: the host record was
deleted at 12:22:59Z and the mail reached the customer at 12:23:00Z, one second
later, with selfbind_link_sent (host delete) on the timeline. The requirement was
two minutes. The hub refuses to delete an ONLINE host with no override, so the
record had to fall stale first — that wait is part of the proof.
Interventions: 0. Every P1 fix this drill set out to prove held on a fresh box.
The verdict is still no, for a new reason: a one-drive box with no off-site tier
keeps none of the household's own files in any backup, the page says otherwise,
and the restore that should save them makes it worse (R-537, R-538).
Teardown, three layers, stated. Customer tester-1 kept; RESET never used; nothing
on the off-site server written or removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
ISO 1.27.1 gated PASS and proven live: first-boot console Felhom-only,
pvebanner masked across a proven reboot. Hub v0.113.0 hand-over copy live
(R-497 closed). Full first hour walked again on customer tester-1 (three
disks + one disk): deploy, use, backup, removal, byte-identical restore,
power cut, typo all PASS. The tunnel gives a fresh box no routes: 12/12
503 from DooPlex (R-505); the record has no e-mail (R-508). Rows R-507,
R-508 filed; R-496/R-495 fixed/answered awaiting publish; day-0 A.1 no
longer claims the controller creates hostnames (R-506). NOT PUBLISHED.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Machine: VM 330 destroyed with its disks. Host: ISO and scratch removed,
~8.5 GiB returned on nvme-scratch. Hub: customer drill0242 DELETED via the
cascade (journal #17), pages 404; its ep0 WireGuard peer dropped at the next
full-list push. Evidence pulled before the destroy. R-501: the documented
CI-check recipe reads only the last jobs page, which is not in id order.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Golden 0.242.0 baked, round-trip verified and vouched (cadence rule, R-468).
Fresh box from the public ISO on demo-hp: landed on the vouched set, two apps
deployed and used, backup, remove, byte-identical restore, power cut and code
typo all PASS. Stopped for a volunteer by R-493 (no instructions) and R-494
(the setup mail's dashboard link has no DNS; intervention I1). R-493..R-500
filed. Capability map: first-hour row added (PARTIAL), journey row scoped.
Stopgap Hungarian volunteer guide written. Hub teardown layer pending.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Hub v0.112.0 serves a floor above the golden with a declared MinAgent;
controller v0.239.0 reached both demo boxes by that floor in 14 s and 15 s
and updates on any backup tier. 09 §3 decisions 7 and 8, §6/§6.1; 07 §6
line; capability map row; STATUS items 15/16 done and the cadence line
corrected; CONTEXT; register: R-470/R-472/R-475 compressed to CLOSED-ITEMS
(full text at 2f5d3af), R-477..R-480 opened, R-474 reproduced a third
time. OPEN-ITEMS 431689 -> 432156 bytes, CLOSED-ITEMS 118051 -> 120598.
Evidence: documentation/audits/rulings-r472-r475-2026-09-13/.
Controller v0.237.0-v0.238.1: the Update button is a guarded job — refusals, backup-first when the
proven Tier-2 copy is stale, safety dump, pin, pull (pin back on failure), health, HOLD on failure.
Proven live on demo-hp: A, B, E, F, H and the restore walk (audits/slice4-2026-09-13/).
Correction to this morning's pages: between golden bakes the hub HOLDS a floor above the vouched
golden, so a release does not reach the fleet by floor (R-472, operator decision). Corrected in the
runbook, STATUS, CONTEXT, R-468 and the gate docstring.
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Operator rulings 2026-09-13, both shipped the same day:
- MariaDB finishes its own conversion (catalog eec1228/bd32830/3525e35). Harness E3/E3b `proven`
with engine_state_after "already upgraded to 12.3.3-MariaDB [exit=1]", the skip line gone, C3
still `failed`; landed on demo-hp through the real 15-min cycle, nothing recreated, one deliberate
restart logged "MariaDB upgrade not required" with the app serving. Evidence:
documentation/audits/r459-close-2026-09-13/. The engine-major rule + gate keep every engine
inside its major until Slice 4 (R-448) — removal tracked as R-469.
- Goldens on a cadence, not per release. golden_currency_gate.py reads a dated waiver
(documentation/tests/golden-waiver.yml, <= 14 days, row-bound): valid + BEHIND -> loud advisory,
exit 0; expired -> red again naming the date; UNRECORDED (R-385) never covered; malformed -> 2,
never 0. Tests cases 5-15 incl. the R-421 decoy; red-proof old-vs-new on the real behind tree.
R-242's vouch half stays open. Cadence in RUNBOOK-manual-build.md §4.2 + the checklist.
- Golden 0.236.0 baked, round-tripped, vouched, floor raised 0.232.0 -> 0.236.0
(documentation/tests/golden-0.236.0-2026-09-13/) — the last per-release bake; the waiver was
issued AFTER it landed. No --no-verify anywhere in this session.
Rows: R-459 CLOSED, R-467 CLOSED, R-242 narrowed; R-468/R-469/R-470/R-471 opened. 09 §3 gains
decisions 5 and 6; STATUS items 11 and 12 closed; CONTEXT records the cadence ruling.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Outcome A, qualified. Not B and not C.
It does not degrade: 5 of 5 restarts of 12.3 on an 11.6 datadir, readback passed
every time, mariadb_upgrade_info unchanged, the entrypoint line never escalated
past [Note]. It also never heals - the engine answers 'Major version upgrade
detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!' on every start
and will forever.
The trade R-459 was expected to produce is not real. Converting properly SUCCEEDS
across the multi-major jump, takes 7 seconds, backs up the system database
unasked - and putting 11.6 back afterwards STILL starts and serves the data. So
the operator is being handed a cheap correction, not a choice between a correct
engine and a reversible one.
The exit-code polarity was measured rather than read: 0 means the upgrade IS
needed, 1 means it is not. Assuming either the flag name or the polarity would
have inverted the headline. And run without credentials the same command returns
a confident-looking FATAL ERROR that is an auth failure.
R-464: after converting and going back, the entrypoint prints 'MariaDB upgrade
not required' on a state the same engine calls an unsupported downgrade. The
obvious cheap instrument for R-459 would have been to grep for that line, and it
would have reported fine for the broken case.
R-463: the PostgreSQL analogue, deliberately NOT measured here. 11 templates, 8
on postgres:16-alpine, register grep for pg_upgrade returns zero. The two engines
fail in OPPOSITE directions - MariaDB skips quietly, Postgres refuses to start -
so that one cannot hide; it presents as eight apps down at once.
No template changed. Teardown all three layers, hub checked rather than asserted,
local-lvm 30.53 percent before and after.
R-449. Until today one app upgrade out of 53 had ever been measured, by hand, and
the whole update arc was designed against that single data point.
C3 first: the negative control, whose TO image exits immediately, came back
failed. That is what makes the greens mean anything, and it cost 556s because a
negative is only honest if it waits out the full settle window.
Seven edges, three apps. All five real catalog upgrades kept the customer's data.
The finding that changes an assumption the arc was carrying: whether an upgrade
can be UNDONE is a property of the individual APP, not of upgrades. Docmost
refuses - 'corrupted migrations: previously executed migration
20260213T085259-notifications is missing' - and privatebin does not. That
reproduces the Nextcloud result on a second app by a DIFFERENT mechanism, so the
struck word 'rollback' now rests on two measurements instead of one.
The finding nobody was looking for, R-459: our own bookstack template moves
MariaDB across a major and sets no MARIADB_* env at all, so the engine logs that
the datadir upgrade it requires is being skipped, and serves anyway. The cause is
assigned rather than guessed - the app half alone produces no upgrade line, both
edges that move the engine produce it - which is exactly what decomposing E3 into
E3a and E3b was for. It also explains why E3's abort looked like it worked: the
datadir was never converted. Whether that ever breaks is NOT established, and the
row says so.
Also opened: R-460 (bookstack's file half cannot be seeded headlessly), R-461
(target-selection.md names a venue that does not exist and fences a VM that is
gone), R-462 (the widening, costed with this run's real numbers - and the cost is
dominated by fixtures, which do not amortise).
Teardown all three layers, hub checked rather than asserted. local-lvm read 30.50
percent before and after. The capability map was deliberately NOT edited: this
measured apps, not the product.
09-update-architecture.md gains the fourth dated operator ruling (2026-09-06,
Option 1) and its section 5 is rewritten from a proposed shape into the shipped
one: the pin, the stored definition, the render table, the four writers, the
startup ordering, and the trap this slice set for slice 2 - the live compose file
is now the frozen one, so a badge comparing against it would answer Naprakesz on
exactly the apps that are behind.
02-controller-module-map.md said 'copy compose + .felhom.yml'. That stopped being
true today, so it is corrected, and the two sections describing the old seam now
carry a banner saying they describe v0.234.0 and below - kept because every box
under v0.235.0 still behaves that way and because they are the measured account
of why it changed.
R-447, R-441, R-438 and R-455 closed and compressed into CLOSED-ITEMS; R-458
opened for the .felhom.yml asymmetry, with what would settle it by measurement.
Live evidence: two real catalog pushes travelling the real 15-minute cycle, both
reverted, the tree byte-identical afterwards. The restart that used to take 18.3
seconds and pull a new image now takes 0.1 seconds and pulls nothing.
The operator looked at demo-felhom and found OpenGist - up 15 hours, running
exactly the catalog pin, showing no badge at all. 09-update-architecture.md had
recorded that as an accepted limitation the day before: 'the fleet view fills in
gradually'. On a quiet box gradually means never, and a feature that fills itself
in on an event nobody triggers is, on the quiet installations, not shipped. That
limitation row is now struck with the reason kept.
The living document gains slice 1b, the two admission rules of the backfill (it
never overwrites, and it refuses to seed a partial observation because the badge
reads a service-count mismatch as BEHIND), and the note that the same field having
two writers with two different admission rules is deliberate.
Live evidence added: all nine apps already had records by the time 0.234.0 was
ready, so the natural fleet state could no longer exercise the new code - said
plainly rather than papered over. The pre-0.233.0 shape was recreated on demo-hp
by stripping two records; the backfill re-seeded exactly those two with digests
matching independently-read ground truth and left the other seven alone.
The refusal half was deliberately NOT staged live: it needs a degraded app, and
manufacturing one risks the false-customer-email class that already cost 61 mails
(R-330). Unit-tested with a red-proof, and recorded as unproven-live.
R-457: a test that hardcodes a date and asserts an age derived from it is green
only on the day it is written. Mine was, and it went red overnight. Six other
files carry both a date literal and time.Now() - named as candidates, not accused.
I reported that the vaulted dashboard password no longer worked on either demo
box, and quoted the controller's own 'Failed login' as the discriminator. The
password was fine. ~/.config/credentials quotes its values with SINGLE quotes and
my sed stripped only double quotes, so the quote characters went out as part of
the password. The operator corrected it in one line; one retry returned 302.
The instrumentation lesson is the finding and R-453 now carries it: 'Failed login'
separates wrong-password from wrong-Host-header, and that is ALL it separates. It
cannot tell a wrong password from wrong password HANDLING, and I read it as if it
could. This is the second time this file's quoting has produced a confident wrong
verdict, so the fix is one shared extraction helper, not a resolution to be careful.
With the session recovered, the badge is validated on live pages: Naprakesz twice
on /stacks and on /apps/bookstack; NO badge at all on /apps/docmost (a deployed app
with no record - absent is UNKNOWN, not current); and 'Frissites elerheto - 52
napja' on both surfaces, the age being real arithmetic on bentopdf's catalog_since.
The behind state was staged by editing one compose tag, with no restart and no
up -d, and reverted byte-identically (sha256 equal, diff empty, container never
touched). Capability-map row upgraded to PROVEN-LIVE with the one unexercised
badge state named. STATUS item 9 now needs nothing from the operator.
R-438's document half. It records how an update works AS MEASURED, quotes the
RestartStack comment that proves the restart half was CHOSEN (a design decision
is not a defect), carries the three operator rulings of 2026-09-02, strikes the
word 'rollback' (once a migration has run the old image will not start), states
the target shape, and lists the seven slices with a status each.
R-438 and R-440 amended and BOTH STAY OPEN: the mechanism is documented, not
changed. Nothing closed, so CLOSED-ITEMS.md is untouched.
Eight new register rows, 194 -> 202: R-446 (Naprakesz can be false for the 23
floating pins), R-447..R-451 (one per remaining slice, with a rank and an owner),
R-452 (no gate enforces catalog_since - the runner fetches at --depth 1), and
R-453 (the vaulted dashboard password is stale on BOTH demo boxes, which is what
stopped the badge render from being validated live).
Live evidence for slices 1 and 2 in documentation/tests/. The record is PROVEN
LIVE through the boot reconciler on demo-hp - one entry per compose service,
digests matching ground truth read independently. The badge RENDER is not, and
the five attempts are listed rather than summarised.
THE GATE IS ANSWERED: YES. compose up -d upgrades an app whose compose file has
already moved, and the Restart button does it — 18.3s with a network pull when
the target image is absent, 0.5s when present, against a negative control that
did not even recreate the container. The boot reconciler does the same thing
unattended when an app fails to come back (bootrecon.go:269 -> StartStack).
AND ONE FEAR IS SMALLER THAN THE BRIEF CLAIMED: a plain power cut upgrades
nothing. Docker restores the old containers and the reconciler logs 'no
boot-orphaned apps (nothing to start)'.
AND ONE IS BIGGER: app data CANNOT be rolled back. Once a migration has run,
the old image refuses to start — Nextcloud: 'the version of the data (32.0.9.2)
is higher than the docker image version (31.0.14.1) and downgrading is not
supported'. 'Rollback' is the wrong word for this arc and is struck.
Phases 0-6 all run, on demo-hp (Tier 0, disposable). Phase 6 run on operator
confirmation. Peti's box was never contacted. No production code written in any
repo: felhom-controller is at 960d29b0612c before and after, tree clean, and
build/vet/test are green — run at the end to prove exactly that.
Register: R-438/439/440 updated with live evidence; R-441..R-445 opened
(restore-vs-sync conflict; remove_hdd_data inert with no paths.hdd_path, 128 MB
left behind; update reports success over a broken app; no fleet fstrim; hub
telemetry outlives the app). Capability map gains three measured rows. The
mechanism is written down in 02-controller-module-map.md as MEASURED BEHAVIOUR,
not [DESIGN] — the operator has not ruled. STATUS.md carries the one decision.
Five claims in the brief are named as wrong, including two of my own method.
R-434 CLOSED — and the row's own "blocked on R-433" verdict was wrong, which is the point.
The fix is a DELETION, not a replacement: withdraw the promise instead of swapping it for a
new one, and the sentence is true under every possible answer to the provider questions, so
it never needs a second rewrite. A replacement would have been blocked; a withdrawal is not.
was: "...still hold the older copy, so this is recoverable file-by-file; it is NOT
confirmed data loss. Check whether a deletion ran on the box before restoring."
now: "...still hold the older copy. The route back out of them is not yet established,
so treat this as neither confirmed data loss nor confirmed recovery. Get in touch
before restoring anything, and check whether a deletion ran on the box."
It must not swing the other way either: "your backups are gone" is still usually false.
Clause (a) — the box cannot WRITE into the snapshot area — stands and is re-confirmed.
Tests: offsite_r434_test.go, three, all driving the production path so they assert the
sentence an operator RECEIVES. ASCII-only fragments, positive and negative controls.
RED-PROOF: restoring the v0.111.0 sentence failed all three, on every fragment, with the
offending sentence printed. TestR431_FiresOnAMassDeletion asserted "NOT confirmed data
loss" and caught this fix correctly; its wording fragment is REMOVED rather than updated,
so the wording keeps ONE home.
R-435 written into the detector's own documentation, no threshold changed: it sees a mass
deletion, not one app being wiped (69 across 9 apps -> ~35 needed, one tag is ~9, and
forget --prune groups by host,tags). Says explicitly not to lower the numbers.
THE STOPPING LINE, in all three places — register, 07 section 8 head, STATUS.md.
Deferred set ENUMERATED, not described: 07 section 8 rows 4, 8, 9, 10, 11 (+11b), 12,
each tagged [BETA-DEFERRED]. A number in the brief was wrong and is corrected in place:
six rows are DEFERRED, ELEVEN carry a blank RTO (4,5,8,9,10,11,11b,12,13,14,15); the other
five are blank for reasons that are not deferred work, and row 15 is an open DEFECT (R-104)
that the stopping line does NOT cover. NO STATUS MOVED — nothing was proven today.
Two provider questions drafted, not sent, no API called (11-D stands):
documentation/runbooks/provider-questions-2026-09-01.md, linked from R-95 and R-433, and
tracked by a dated DUE-CHECKS row (2026-09-15) — the 2026-07-27 check that sat unconfirmed
for 36 days is the scar that block exists for.
R-95, R-433 BLOCKED-ON-PROVIDER. R-95's one-day demotion on a clause that did not hold is
recorded; the proposal to rank it back near the top is stated and NOT acted on. R-430 marked
LATENT with its trigger: it becomes live the moment delete is withdrawn, so it is a
precondition on the R-95 build, not a follow-up. The stale ranking paragraph ("armed",
"zero snapshots") is corrected in place, order unchanged.
Register 621 -> 688 lines; 181 rows throughout; open-state 170 -> 169.
No controller or agent change. No golden owed, no floor change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LB8FmJaGd2cyjvy6dbEjpM