Both from their own records, not re-run. calibre-web: the flash_error refusal is in its log; that walk predated the refusal-capture code. The report's own prose already said so while the table disagreed. calcom: the restore was accepted and the app read running; the classifier then saw "starting", a settling state it did not list beside running/unhealthy, and fell through to failed. The brief supposed a read-back artefact - that is wrong, and restore_state_seen says so. Restores correctly refused: 2 -> 3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
6.0 KiB
THE TWENTY-EIGHT — every app no update drill had ever touched, 2026-09-22
Evidence: the-28-2026-09-22/. What came before: DRILL-update-night-2026-09-21.md (21 edges,
19 apps) and PROBE-FIX-2026-09-22.md (the probe fix and the fifteen moves).
Not done, or changed from the brief
Read this first.
Claims in the brief that turned out wrong
-
There is no
requires:key in.felhom.yml. The brief said "A.felhom.ymlrequires:(HDD, x86) that 9202 cannot meet → recorded, not forced." No template has such a key. The constraints live underresources:asneeds_hddandpi_compatible. And none of them excluded anything: 9202 isx86_64with/mnt/felhom-drives/scratch_hddmounted, so every one of the 28 was installable on those grounds. Nothing was skipped for a resource reason. -
The brief's file-leg list is wrong, and it told me where to check. It grouped "immich, jellyfin, plex, emby, komga, calibre-web, gokapi, homebox, gramps-web" as file-leg apps. Read from
07-backup-architecture.md§6.2 — which the brief itself says to read rather than re-derive — only four of the 28 are class A (at least one readable file leg):calibre-web,immich,komga,paperless-ngx.jellyfin,plexandembyare class B precisely because their only bind is a:romedia mount, whichClassifyBindsexcludes;gokapi,homeboxandgramps-webkeep everything in named volumes. The table below uses07§6.2's classes, not the brief's. -
The brief's database list is incomplete. It named calcom, claper, outline, paperless-ngx, rallly and sparkyfitness for PostgreSQL and kimai for MariaDB.
immichalso carries PostgreSQL (a vectorchord variant) and redis, andwanderercarries meilisearch — two apps the brief put in the "file-leg" group actually run their own datastore. -
One of the 28 cannot be installed at all, by design.
plant-itislifecycle: "abandoned", and the product's lifecycle gate refused the deploy with 409 „Ez az alkalmazás jelenleg nem telepíthető." That is correct behaviour, measured live for the first time. It is the only lifecycle-gated template in the whole catalog of 53.
Claims in the brief that were verified true, by looking
- The drill repo's Actions are off (R-629). Read from the API:
has_actions: false, private: true. And measured rather than trusted: 47 CI jobs before the reset push, 47 after — the push produced no run and no mail. repoint_drill.pystill works after the catalog moved. 9202's cache now readsorigin …/app-catalog-drill.gitat1ad1f34.- 9202 has the capacity. 25.9 GB RAM (23.5 free), 28 GB free on
/, 842 GB on the scratch drive. The root disk is the binding constraint, so each app's images are removed by name after its verdict — neverprune(rule 3). Disk held at 1.9 GB used throughout.
Changed method, named
-
A restore that is REFUSED is recorded as
refused-with-a-sentence, not as a failed restore. The first version of the harness collapsed the two and mislabelledcalibre-web. The product had in fact done the right thing — see the finding below — and a harness that calls a correct refusal a failure would have buried it. -
The harness now waits for a restore to settle before removing. It did not at first, and that race produced R-633, a real defect. The race was left in the record for
gokapiand fenced out afterwards, so the remaining apps measure the product rather than the harness. -
Three bugs in tonight's own harness, each named with what it cost. A missing
import rein the restore step killed the restore half forwanderer,claper,sparkyfitnessandcalcom. A variable namedmshadowed the app's metadata and brokepaperless-ngx's teardown. An empty phase list crashed on[-1]when the Update was refused before any phase existed, which costralllyits whole walk. All four apps, and five more, were re-walked serially afterwards — and that re-walk is what corrected R-634 and producedghost's proof. An instrument that can drop results silently is not a measurement; these dropped them loudly and were re-run. -
Concurrency is part of the method and it changed two results. Three walks ran at once to fit 28 apps in one night.
POST /api/backup/runis box-wide, so a second caller gets409 „Mentés már folyamatban", and the Update refuses while a backup or restore is in flight (409 busy). Both refusals are the product being right and both are quoted below. But they costghostandralllytheir edge on the first pass, and they are implicated in two of R-634's three instances. The nine re-walks were serial for exactly this reason.
Corrected 2026-09-22 (evening), from the records rather than by re-running
-
calibre-web's restore wasrefused-with-a-sentence, notfailed. The refusal is in this app's ownlog.txtline 12 as aflash_erroron the redirect, and its state stayedrunningthroughout. It was recordedfailedbecause that walk ran before the refusal-capture code was added later the same night — the document's own section "A restore that is REFUSED" already said so while the table and the record disagreed with it. -
calcom's restore wasinconclusive, notfailed— and that was my harness, not the product. The restore was accepted (flash=flash.restore.started, noflash_error), the app readrunningat +45 s with no hold and no phase, and the classifier looked once more and sawstarting— a settling state it did not list besiderunning/unhealthy, so it fell through tofailed. The task brief supposed a different cause — "a read-back of data that was never seeded" — and that is wrong: the seed half is recorded separately and was alreadyno route. The record's ownrestore_state_seen: "starting"is the evidence.Totals move with it: restores correctly REFUSED go from 2 to 3.