Campaign 10 closeout Part 1: Q1 lowers R-158's rank; Q2 clears ValidateDump and kills C2's gate

Q1 - what the customer sees when a backup refuses for lack of space. The failure
IS customer-visible: /backups renders "Adatmentés sikertelen" with a cross mark.
It is absent from the dashboard, the launcher, the app detail page, and - the one
worth fixing - from /backups/apps, the per-app page where you would naturally ask
whether a given app is backed up.

Point 5 measured across three runs: it retries, stays failed while constrained
(marker persists, unit mtime unchanged at 07:30:50), and clears on recovery with a
fresh unit at 07:37:38. /backups/apps reading "Utolsó: 3 perce" tracks the unit's
REAL mtime, not the failed run, so it is honest about the age of the last good
unit rather than claiming a fresh one. Explicitly NOT the R-156 family.

So R-158 is a NOTIFICATION GAP, not a silent-failure defect, and ranks BELOW
R-157 - whose mechanism B leaves a deployed app not running while deadapp reports
"0 currently down", silent on every channel.

Q2 - ValidateDump was right and no bad dumps are shipping. The live DB genuinely
had zero accounts (only _prisma_migrations 129, cc_proof 82, instance_settings 1).
An empty table proves nothing, so an account was SEEDED as the task required: the
warning then stopped entirely and the dump provably contained the rows (c10acct 1,
c10user 2; 102766 -> 103029 bytes).

But that kills C2's proposed ordering. A fresh appliance legitimately has zero
accounts, so gating on "accounts has rows" would block the backups of every new
customer until someone registers. The validator's fact is right; its inference
("may predate the customer's data") is wrong - there was no data to predate. The
chain is therefore longer: a sound predicate first (compare the dump against the
LIVE db, per-table counts, not an absolute expectation), then warn->gate, then the
tar-drop. Until then the DB volume tar stays load-bearing - not because dumps are
bad, but because nothing can yet prove one is good.

No new R-n; register grepped. Nothing fixed. Part 2 (teardown) follows.
This commit is contained in:
2026-08-02 09:39:27 +02:00
parent 0afadbdeff
commit 7efb7a53d3
@@ -0,0 +1,135 @@
# Campaign 10 — closeout: two last measurements, then teardown (2026-08-02)
**Companion doc** (my call, per the task) to `CAMPAIGN-10-two-storage-soak-2026-07-31.md` and
`SPIKE-recovery-unit-space-2026-08-02.md`, rather than appending — those two are already long and this
session answers two specific questions and then dismantles the rig.
**Part 1 headline:** Q1 **lowers** R-158's rank — the failure *is* customer-visible, on one page, and it
persists and clears correctly. Q2 **clears** `ValidateDump` of shipping bad dumps, and simultaneously
**kills C2's proposed gate** on its current predicate.
---
## Q1 — when a backup refuses for lack of space, what does the customer see?
Method: ballast `/mnt/sys_drive` to 699 MB free (homebox's unit needs 2.3 GB), run the real backup
through `POST /api/backup/run`, and read every customer-facing surface through the real UI endpoints.
### The five points
| # | Surface | Result |
|---|---|---|
| 1 | **`/backups` overview** | **SHOWS IT**`✗ Adatmentés sikertelen` |
| 2 | **`/backups/apps`** (per-app) | **shows nothing** — Homebox tier-1 reads `Utolsó: 3 perce` |
| 3 | `/stacks/homebox` (app detail) | **shows nothing** |
| 4 | `/` dashboard · `/launcher` | **show nothing** — no banner |
| 5 | `GET /api/backup/status` | `db_dump.success: **false**` |
| — | hub event / email | **none** (R-158) |
Exact copy, as rendered on `/backups`:
✗ Adatmentés sikertelen
### The point-5 question — retry, stay failed, or quietly go green?
**It retries, stays failed, and does not lie.** Measured across three runs:
| run | condition | `status.success` | `/backups` shows failure | unit mtime |
|---|---|---|---|---|
| 1 | constrained | false | **true** | 07:30:50 |
| 2 | still constrained | false | **true (persists)** | 07:30:50 (unchanged) |
| 3 | space freed | **true** | **false (cleared)** | **07:37:38 (fresh)** |
**`/backups/apps` is honest, not deceptive.** Its `Utolsó: 3 perce` tracks the *unit's real mtime*
(07:30:50), not the failed run's timestamp — so it correctly reports the age of the last good unit. It
simply never says the latest attempt failed. **This is NOT the R-156 family**: no surface claims a
fresh backup over a stale unit.
### R-158's rank — LOWERED, with the reason
> **R-158 is a notification gap, not a silent-failure defect. It ranks BELOW R-157.**
Because the customer *can* find out by looking, and the indicator behaves correctly — it persists while
failing and clears on recovery. What is missing is that **nothing pushes it**: no hub event, no email,
no dashboard banner. And the discovery path is poor in a specific way worth fixing: **the per-app
backup page — the natural place to ask "is homebox backed up?" — shows no failure at all**, while the
answer lives one page away on the overview.
R-157 outranks it because R-157's mechanism B leaves a `deployed: true` app **not running**, with the
deadapp check reporting `0 currently down` — silent on *every* channel, including the one the customer
would look at.
---
## Q2 — was `ValidateDump`'s warning correct?
The warning, seen twice:
[WARN] [backup] ValidateDump: …/rallly-postgres.sql is structurally valid (32 tables) but its
accounts table has NO rows — the dump may predate the customer's data
### It was factually correct, and the dump was NOT deficient
The live database genuinely had no accounts. Every non-empty table, from `pg_stat_user_tables`:
_prisma_migrations | 129
cc_proof | 82 <-- the campaign's own canary
instance_settings | 1
accounts | 0
**An empty table proves nothing either way**, so — as the task required — an account was **seeded** and
the backup re-run:
INSERT users (id='c10user', …) -> 1 row
INSERT accounts (id='c10acct', …) -> 1 row
Result:
[INFO] [backup] DB dump: rallly-postgres → rallly-postgres.sql (100.6 KB, 396ms, 32 tables)
(no ValidateDump line at all — the warning STOPPED)
grep -c c10acct <dump> = 1 grep -c c10user <dump> = 2
dump size 102 766 B -> 103 029 B
**Verdict: the validator is sound and no bad dumps are shipping.** It correctly detected an empty
`accounts` table, and the moment real data existed the dump captured it and the warning cleared.
*(Seeded data disclosed: one `users` row and one `accounts` row, both `c10*`-prefixed, on a scratch
customer that this session then deletes.)*
### What it does to C2's ordering — the proposed gate is DEAD on this predicate
C2 proposed dropping the DB volume tar (halving DB-app units, and closing the D5/R-127(b) initdb-skip
password trap) **once `ValidateDump` is promoted from a warning to a gate**. That ordering does not
survive Q2:
> **A fresh appliance legitimately has zero accounts.** Gating on "the `accounts` table has rows" would
> **block the backups of every new customer** until someone registers a user — refusing good backups
> for the entire period a box is most fragile.
The validator's *fact* is right and its *inference* ("may predate the customer's data") is wrong: there
was no data to predate. So the dependency chain is longer than C2 assumed:
1. a **sound gate predicate** first — a comparison against the **live** DB (per-table row counts in the
dump vs `pg_stat_user_tables`) rather than an absolute expectation about one table;
2. **then** the warn→gate promotion;
3. **only then** the tar-drop.
**Until (1) exists, the DB volume tar stays load-bearing** — not because dumps are bad, but because
nothing can yet *prove* a given dump is good.
---
## Findings
**No new `R-n`.** Register grepped (`documentation/backlog/*.md`) for `ValidateDump`, `accounts table`,
`Adatmentés sikertelen`, and the per-app backup page: no existing rows, and neither Q1 nor Q2 produced
a defect that is not already covered.
- **Q1 does not mint an R** — it *re-ranks* R-158 (filed in the spike) and adds the specific
improvement worth making: surface the per-app failure on `/backups/apps`, where a customer looks for
a specific app.
- **Q2 does not mint an R** — it *removes* a proposed change (the gate) and lengthens C2's dependency
chain. Recording that a design is wrong is the spike rule working.
Nothing fixed, per the fences.