claim(3 U1): app screenshot wired + captured — uptime-kuma working UI no-secrets, graceful degradation; gate CLAIMED

This commit is contained in:
autonomic-bot
2026-05-31 07:01:45 +00:00
parent 5fa15d4949
commit d7e812e96d
3 changed files with 96 additions and 8 deletions

View File

@ -18,7 +18,7 @@ State files (this phase): `machine-docs/{STATUS,BACKLOG,REVIEW,JOURNAL}-3.md`. D
---
## Gate: U0 — CLAIMED, awaiting Adversary (Results schema + level; R1)
## Gate: U0 — PASS (Adversary REVIEW-3 @18d2bd1, 2026-05-31; R1 cold-verified, no VETO) (Results schema + level)
**WHAT.** `run_recipe_ci.py` now emits a per-run `results.json` with per-stage AND per-test ✔/✘
breakdown and a computed integer **level** (L0L6, YunoHost gap-caps semantics). DoD R1 (level ladder)
@ -65,9 +65,60 @@ These two bracket the gate: a recipe whose functional tests **pass** is still ca
lower rung (L3 backup) is N/A (gap-caps; never inflates), and a full clean climb with no SSO surface
caps at **L4**.
---
## Gate: U1 — CLAIMED, awaiting Adversary (App screenshot; R4)
**WHAT.** The harness now captures a **real Playwright screenshot of the deployed app** while it is
up (after deploy+health/readiness, before any tier mutates state, before teardown) and writes it to
the run artifact dir as `screenshot.png`. The capture is **secret-safe by default** (it shoots the
app **landing page**, never a credentials page; a recipe opts into a post-login view via an optional
`SCREENSHOT` meta hook that owns the no-secret-page guarantee — none used yet). It is **best-effort**:
`capture()` swallows every error and returns `None`, so it NEVER blocks/fails/hangs the run (R7); the
`results.json` `screenshot` field is set to `"screenshot.png"` ONLY when the capture actually produced
a file, else stays `null`. U1 milestone acceptance ("screenshot of a sample recipe shows the working
UI, no secrets") demonstrated on a real uptime-kuma run; graceful-degradation (R7) demonstrated on an
unreachable-domain capture.
**WHERE (commits / files).**
- `5fa15d4` `runner/run_recipe_ci.py` — imports `screenshot as screenshot_mod`; after deploy+readiness
and OUTSIDE the deploy try/except (so a screenshot issue can never flip `deploy_ok`), under
`if deploy_ok:` calls `screenshot_mod.capture(domain, screenshot_path(run_artifact_dir), recipe_meta=meta)`
and sets `screenshot_rel`; passes `screenshot=screenshot_rel` into `build_results(...)`.
- `daa7edd` `runner/harness/screenshot.py``capture()` (default landing-page nav via
`browser.goto_with_retry`, 45s deadline cap; optional `SCREENSHOT` hook), `screenshot_path()`,
`_load_screenshot_hook()`. `tests/unit/test_screenshot.py` (pure helpers; 4 tests).
**HOW to verify (cold, from your clone on cc-ci).**
1. **Pure-helper unit tests:** `cc-ci-run -m pytest tests/unit/test_screenshot.py -q`
2. **Real positive capture** (working UI, no secret): `rm -rf /var/lib/cc-ci-runs/adv-u1 &&
RECIPE=uptime-kuma STAGES=install CCCI_RUN_ID=adv-u1 cc-ci-run runner/run_recipe_ci.py`
then `scp` back `/var/lib/cc-ci-runs/adv-u1/screenshot.png` and EYEBALL it; check
`/var/lib/cc-ci-runs/adv-u1/results.json` has `"screenshot":"screenshot.png"`. Confirm NO orphan
service after (`docker service ls | grep -i uptime` empty = clean teardown).
3. **Graceful degradation (R7)** — capture against an unreachable host returns None, never raises:
`cc-ci-run -c 'import sys; sys.path.insert(0,"runner"); from harness import screenshot as S;
print(S.capture("adv-u1-noexist.ci.commoninternet.net","/tmp/x.png"))'` → prints `None` (≈45s),
no /tmp/x.png produced.
**EXPECTED.**
1. `4 passed`.
2. `screenshot.png` ~30 KB showing uptime-kuma's **"Uptime Kuma / Create your admin account"**
landing page with **EMPTY** username/password/repeat fields (a setup form — it asks the user to
set a password; it does NOT display any generated secret), i.e. real working app UI, no secret
values. results.json `screenshot="screenshot.png"`, `flags.clean_teardown=true`; no orphan service.
(My run: `/var/lib/cc-ci-runs/u1-uk-shot/{screenshot.png,results.json}`.)
3. `None` returned after the 45s deadline, no file written, no exception — proving a screenshot
failure leaves the run/verdict untouched (cosmetics never block, R7). (My check log: capture
"failed (non-fatal, verdict unaffected)" → `GRACEFUL_DEGRADATION= True`.)
The cardinal Phase-3 invariant for U1: the screenshot is a faithful capture of the live app, never a
credentials page, and its presence/absence never changes the verdict.
## In flight (next, post-gate)
- U1app screenshot (Playwright, post-login, secret-safe). Will start once U0 PASSes; meanwhile I
hold U1 design as the next unblocked item.
- U2summary card + badge (HTML→PNG via Playwright; SVG level badge; stable URLs). Render path
already de-risked headless on cc-ci for pass+fail fixtures (JOURNAL-3 @06:50Z) — next is wiring the
card/badge generation into the run + serving them. Held until U1 PASSes (no advance past the gate).
## Blocked
(none)