diff --git a/CLAUDE.md b/CLAUDE.md index d8095753..ce6686e9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -134,10 +134,21 @@ something, not only sessions that touch `documentation/` — which is why it is number moved**. It exists because "which claims are unproven?" was answerable only by a person reading a page: a session asked for "the nine grey claims" could not determine which nine and rightly refused to guess (R-326). *Nine was real and answered a different question — it is the - count of claims the 2026-08-09 pass DOWNGRADED. Not-walked is 32 of 55.* A status that moves + count of claims the 2026-08-09 pass DOWNGRADED. Not-walked is 35 of 55 as of 2026-09-01 -- the figure read 32 here for weeks while the tool said 35, so re-read the tool rather than this line.* A status that moves without anyone noticing is how the picture stops being true. - **Confirm your own last push's CI run went green, by run ID.** CI emails on failure, which is a PUSH signal; this is the PULL check that catches a lost, filtered or unread mail. Quote the run id - and its conclusion, e.g. - `curl -s "https://gitea.dooplex.hu/api/v1/repos/admin//actions/tasks?limit=3"` → match the - `head_sha` to your commit. An unchecked green is an assumption, not an observation. + and its conclusion. **Use the `jobs` endpoint and match on `head_sha`, never on an id** (R-417, + measured 2026-09-01): `actions/tasks` returns `"conclusion": null` for every run, so a session + following the old recipe here quotes a conclusion it never read; its `id` is also offset from the + `jobs` id for the same run (479 vs 478), and `actions/runs/` takes a JOB id, so `runs/294` + cheerfully returns an unrelated job from three weeks earlier. The list is oldest-first — page to + the end. + + ```bash + T=$(curl -s -u "$U:$P" ".../actions/jobs?limit=1" | python3 -c 'import json,sys;print(json.load(sys.stdin)["total_count"])') + curl -s -u "$U:$P" ".../actions/jobs?limit=50&page=$(( T/50 + 1 ))" # then grep your own head_sha + ``` + + An unchecked green is an assumption, not an observation — and so is a green read off a field the + API never populates. diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 4a71dd55..874e7c67 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -593,6 +593,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server` | **R-412** | **A recovery unit lost DURING an off-site run — after its own dump leg, before its push — is shipped hollow and the run reports success.** **CORRECTED 2026-09-01 04:22, and the first wording of this row OVERSTATED it.** As first filed it claimed the hollow unit sat in the store for a whole cycle because "the volume-dump leg runs on the backup schedule, not on capture". **That is wrong, and measuring it overnight is what showed it:** the off-site run has its OWN pre-push dump leg — *"Stopping calibre-web for safe volume dump"*, *"Volume dump: calibre-web/calibre-web_calibre_web_config -> 877.5 KB"* — so a unit that is hollow when a run starts is **REPAIRED before it is pushed**. Proven twice: `opengist` (2026-08-31 21:0x) and `calibre-web` (2026-09-01 04:15) both went in hollow and came out complete, and the snapshot pulled back from the store (`6fee3b5a`) holds the volume tar and all 17 userdata files. **WHAT REMAINS REAL, and it is narrower:** the one hollow snapshot that DID reach the store (`35ba9fe7`, opengist) was created when the unit was destroyed **inside** a run that had already completed opengist's dump leg — so the push shipped what the capture had just rebuilt empty, and logged *"backed up opengist (… 0 mandatory path(s))"*, **a success line over a backup holding none of the app's data**. That race is real, it was observed, and the success wording is wrong either way. **The R-403 mirror guard holds throughout** — proven live: *"unit leg SKIPPED … The copy was PRESERVED rather than replaced with an empty one"*, secondary byte-identical. | **LEG 1 CLOSED 2026-09-01 (controller v0.232.0) — LEG 2 STILL OPEN, LOW** | R-403, R-87, R-413 | **LEG 1 IS DONE:** a per-app push whose unit carried no database dump and no volume tar now logs at WARN and says what it did not carry, using the existing `unitIsHollow` predicate. Wording only — no guard, and the capture is untouched (08 §8.2). Pinned by `TestR412a_EmptyPushDoesNotReadAsAPlainSuccess`, which asserts the hollow line carries the words, the SOUND line does not, and neither is at INFO; red-proofed by restoring the single unconditional line. **LEG 2 IS STILL OPEN and is the remaining work on this row:** whether the push should RE-READ the unit it is about to send, or whether the race window is small enough to accept. Two separable things. (1) The success line: a per-app push that carried no dumps and no tars should not read as a plain success — that is a wording fix in the run's own reporting, not a new guard. (2) The race: decide whether the push should re-read the unit it is about to send, or whether the window is small enough to accept. **Do NOT guard the capture** (08 §8.2). Evidence: `audits/DRILL-soak-2026-08-31/phase2-guard-interactions/` and `phase5-mutated-cycle/09-what-reached-the-store.txt`. | CC | | **R-413** | **R-87's proof caught a naturally-produced hollow snapshot, end to end, unattended — the validation yesterday's session could only do with a declared hand-built fixture.** 2026-08-31 soak, demo-hp. After R-412's chain left `opengist`'s newest off-site snapshot hollow, the nightly proof rotated to it and returned **`verdict:"fail"`, `reason:"volumes_expected_none_captured"`, missing `opengist_data`**, logged *"READABLE AND EMPTY — the store is not damaged; the backup does not contain this app's data"*, and pushed **one** `offsite_proof_empty` at severity `error`. The four apps ahead of it in the rotation all passed, so the discrimination is real and not a constant fail. **This is recorded as a row rather than only as a report line because it upgrades a claim:** the capability map's R-87 row cites a CONSTRUCTED failing case; it can now cite a natural one. | **CLOSED 2026-08-31 — the claim it upgrades is recorded** | R-87, R-412 | Nothing to build. When the capability map is next touched, cite this instead of the constructed case. | CC | | **R-416** | **`closed_register_gate.py` still has no within-register duplicate-id rule.** R-406 closed by renumbering the only collision (R-133 → R-415), so the register is clean today and nothing stops the next one. The rule was deliberately NOT added in the same commit: with its only real subject removed, the red-proof would have had to be a planted fixture rather than the live defect, and this project's own standard is that a guard ships with a proof against something real. **Now that the register is clean it can be added safely** — a fresh duplicate would be the first thing it ever sees. | **OPEN — LOW** | R-406, R-405 | Add a third rule to `closed_register_gate.py`: no `R-` id may appear twice within either register. Ship it with a planted red-proof, and note that suffixed ids (R-88a/R-88b, R-209/R-209a) are distinct and must NOT be convicted. | CC | +| **R-417** | **A drill night that forbids baking a golden makes `golden_currency_gate.py` red, and the only way to push the drill's own evidence is `--no-verify` — which is exactly the signal CI e-mails about.** MEASURED 2026-09-01: five consecutive felhom.eu CI runs went red (jobs 469, 470, 471, 473, 476 — shas ab8b8847, cee8f70e, f8f9ffdf, 22e1c95e, f41a1a0a), every one on step 3 `Run the gate entry point`, every one MINE, and the sixth (63eff21a) went green the moment the golden-0.232.0 evidence was committed. Cause CONFIRMED by isolation, not inferred: the only functional diff between the last red and the green is that evidence directory, and moving it aside in the real clone reproduces exit=1 / restoring it gives exit=0. THE GATE WAS RIGHT EVERY TIME — 0.231.0 and 0.232.0 were released with no golden carrying them, so a machine installed that night would have received 0.230.0. The gap is that the RUNBOOK's own instruction (*'no golden bake, no vouch, no floor change tonight'*) makes red unavoidable, and the gate's stated remedy for that case is a WAIVER recorded here — which I did not write, so I bypassed instead, five times. Fix is one of: teach the gate to read a dated waiver row, or have the drill runbook require the waiver be filed BEFORE the first push. **Until then a red CI run on a drill night cannot be told apart from a real one, and that is the whole value of the signal.** | OPEN |