Files
cc-ci/machine-docs/BACKLOG-3.md

81 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 3 — Beautiful YunoHost-style results — BACKLOG
Single source of truth: `/srv/cc-ci/cc-ci-plan/plan-phase3-results-ux.md`.
Milestones U0U5 (plan §5); each ends with an Adversary gate. DoD items R1R8 (plan §2).
## Build backlog
### U0 — Results schema + level (R1)
- [x] U0.1 — Pure `level()` function (harness/level.py): L0L6 gap-caps semantics; 15 unit tests
(incl L4-pass + L2-cap); Adversary fuzz-clean 729/729 (REVIEW-3 @df54693).
- [x] U0.2 — Per-tier pytest emits JUnit XML (parsed by harness/results.py) → results.json per-stage
AND per-test ✔/✘ breakdown.
- [x] U0.3 — `run_recipe_ci.py` writes `results.json` per run (level, cap_reason, rungs, stages,
flags) to the run-scoped artifact dir; assembly wrapped so it NEVER changes the verdict (R7).
- [x] U0.4 — Artifact hosting path decided + recorded in DECISIONS (`${CCCI_RUNS_DIR:-/var/lib/cc-ci-runs}/
<run_id>/`; dashboard serves `/runs/<id>/` in U2/U4 via host bind-mount).
- GATE U0: **PASS** (Adversary REVIEW-3 @18d2bd1, 2026-05-31) — R1 cold-verified, no inflation, no VETO.
### U1 — App screenshot (R4)
- [x] U1.1 — Harness captures a real Playwright screenshot of the deployed app while it is up
(default landing page = secret-safe; recipes opt into a post-login view via a SCREENSHOT meta
hook, never shoot a credentials page). Wired into run_recipe_ci.py post-healthy, pre-teardown.
- [x] U1.2 — Screenshot saved to run artifact dir (`screenshot.png`); results.json `screenshot` field
set ONLY when capture succeeds; degrades gracefully (capture() swallows all errors → None →
field null → run/verdict unaffected, R7).
- GATE U1: **PASS** (Adversary REVIEW-3 @74a6993, 2026-05-31) — R4 cold-verified (real screenshot of
working UI, no secrets, R7-safe wiring, graceful degradation), no VETO.
### U2 — Summary card + badge (R3, R6)
- [x] U2.1 — HTML results-card (recipe+version, level badge, per-stage/per-test ✔/✘ table, embedded
app screenshot) → PNG via Playwright; wired into run_recipe_ci.py, R7-best-effort.
- [x] U2.2 — Per-run SVG level badge (`badge.svg`) generated per run (shields-style, colour by level).
- [x] U2.3 — Card + badge + screenshot + results.json served at stable URLs
`/runs/<id>/{summary.png,badge.svg,screenshot.png,results.json}` (allow-list + traversal-guarded;
runs dir bind-mounted RO into the dashboard swarm service). LIVE over HTTPS, verified.
- GATE U2: **PASS** (Adversary REVIEW-3 @324d84d, 2026-05-31) — card+badge render correct for pass &
fail, served traversal-guarded, never-greener, leak-clean, R7-safe, no VETO. (R3/R6 stay partial
until embedded in PR comment (U3) + dashboard (U4) + per-recipe badge (U5).)
- Adversary polish items to fold in (low-sev, not gates): (a) dashboard `/runs/` HEAD→501 (no do_HEAD)
→ add do_HEAD (also enables a cheap bridge existence-check for U3 fallback); (b) per-recipe
latest-level badge endpoint → U5.
### U3 — YunoHost-style PR comment (R2)
- [x] U3.1 — Bridge posts a placeholder comment on run start (⏳ + live-logs link). `start_comment_body`,
reuses the marked comment if present (re-`!testme` refreshes to placeholder).
- [x] U3.2 — On completion, update the SAME comment to 🌻 + level/status badge + summary card image,
both linking to the run/dashboard. Re-`!testme` refreshes it. Fallback to text on render failure
(`result_comment_body` + `artifact_available` HEAD check). Deployed (bridge img 6377f9571f3b).
- [ ] U3.3 — Fold Drone repo activation into the drone reconcile so a DB reset self-heals: `POST
/api/repos/recipe-maintainers/cc-ci` (idempotent) BEFORE the timeout PATCH in drone.nix. Found
during the U3 live demo — the Hetzner-migration DB reset left the repo inactive (bridge `drone
trigger failed 404`); I reactivated by hand to run the demo. Not a U3 DoD item (cosmetics/comment
shape is); robustness hardening — fold in at U5 or flag to operator.
- GATE U3: live on a scratch PR — comment shows badge + card + screenshot, updates on re-run, no secrets.
### U4 — Dashboard polish (R5)
- [ ] U4.1 — Overview grid like `ci-apps.yunohost.org`: per-recipe level badge, latest pass/fail,
last-tested version, app screenshot/thumbnail, link to history.
- [ ] U4.2 — Regenerated on build completion; reads results.json artifacts.
- GATE U4: matches reality across several runs; mirrors the underlying results.json.
### U5 — Badges + docs + hardening (R6, R7, R8)
- [ ] U5.1 — Embeddable per-recipe latest-level badge documented for README embedding.
- [ ] U5.2 — `docs/` explains the level ladder, card/screenshot/badge generation, how to embed a badge.
- [ ] U5.3 — Hardening: render failure degrades to text (R7); secret-scan over published
images/screenshots/comments finds nothing; killing the renderer doesn't affect the verdict.
- GATE U5: Adversary leak-scan clean; graceful degradation proven; flip STATUS-3 to `## DONE`.
## Adversary findings
(Adversary owns this section — Builder does not edit.)
- [x] **A3-1 [adversary] — `/runs/<id>/<file>` returned 501 to HEAD requests** (low severity, polish).
**CLOSED @2026-05-31T09:34Z — re-tested live, fixed.** The dashboard `BaseHTTP` handler implemented
only `do_GET`, so `HEAD /runs/u1-uk-shot/summary.png` → `HTTP 501 Unsupported method`. The Builder
added a `do_HEAD` in `9a47aa2`, now deployed live. Re-verify (cold, from VM):
`curl -sSI https://ci.commoninternet.net/runs/u1-uk-shot/summary.png` → **HTTP/2 200**,
`content-type: image/png`, `content-length: 69313`, and **0-byte body** (`curl -X HEAD | wc -c` = 0
— correct HEAD semantics, headers only). badge.svg HEAD → 200 image/svg+xml. GET still 200/69313.
**Guards still hold under HEAD:** `HEAD …/evil.sh` → 404, `HEAD …/runs/nonexist-xyz/results.json`
→ 404 (whitelist + run-id guard not bypassed by method). Resolved; no regression.