# Plan — `/recipe-report` skill + report.ci.commoninternet.net **Status:** IMPLEMENTED 2026-06-02 — serving live at https://report.ci.commoninternet.net; skill `recipe-report` + helper `cc-ci-plan/recipe-report.py` + launcher `cc-ci-plan/launch-report.py` (REPORT_MODEL default opus); upgrade-all launches the report as its closing step. **Created:** 2026-06-02. **Goal:** after the weekly `/upgrade-all`, generate a public HTML **"Recipe Report"** that reviews how the upgrade went and the state of every recipe + open PR — served at **report.ci.commoninternet.net**, one page per week, with a home-page index of all reports. ## Why it's cheap to build The serving model already exists (dashboard): a Nix-built image → Swarm service → traefik `Host(...)` + wildcard TLS, bind-mounting a host dir read-only. We mirror it for reports. - **DNS:** `report.ci.commoninternet.net` already resolves (wildcard `*.ci.commoninternet.net` → the host) — **no operator DNS step.** - **TLS:** the same wildcard cert (`/var/lib/ci-certs/live/`) that serves ci./drone./backups. covers `report.ci.*` — **no cert step** (confirm at deploy). - **Data:** `/upgrade-all` already writes `/srv/cc-ci/.cc-ci-logs/upgrades/upgrade-all-.md`; open PRs + CI verdicts come from the Gitea/Drone APIs (creds in `.testenv`). ## A. Serving infra — new `nix/modules/reports.nix` (cc-ci product repo) A tiny static-file server mirroring `dashboard.nix`: - Image: `nginx:alpine` (or reuse the dashboard's Python `http.server`), content-hash tag, deployed by a `deploy-reports.service` reconcile oneshot (same pattern as dashboard/bridge). - Bind-mount **`/var/lib/cc-ci-reports`** (read-only) as the web root. - traefik labels: `Host(`report.ci.commoninternet.net`)`, entrypoint `web-secure`, `tls=true`. - Web root layout: `index.html` + `week-.html` (one per report). **Public, unauthenticated.** - *(Lighter alternative: add a second traefik router `Host(report.ci…)` → the existing `ccci-dashboard` service and serve `/var/lib/cc-ci-reports` from `dashboard.py`. Recommend the separate server for clean decoupling.)* ## B. The skill — `.claude/skills/recipe-report/` (orchestrator repo) A SKILL.md (LLM-driven review + HTML generation) + a helper script for the deterministic survey/scaffold. - **Runs as its OWN agent session — model independent of the upgrader.** The report must use a model configured **separately** from the upgrader, so it canNOT be a step inside the sonnet upgrader session. A new `launch-report.py` (mirroring `launch-upgrader.py`) starts a `cc-ci-report` session on **`REPORT_MODEL` (default `opus`)** / `REPORT_BACKEND` (default `claude`) — independent of the upgrader's `sonnet`. **Initial config: upgrader = sonnet, report = opus.** It's launched right after the weekly `/upgrade-all` completes (the weekly job launches the report agent as its final step / a follow-on). It reads the dated summary + PR/CI state from disk + the APIs, so it needs none of the upgrader's in-context state. - **Inputs:** the dated `upgrade-all-.md` summary; open PRs per recipe (Gitea API); each recipe's current vs latest version + CI verdict (Drone); the **previous** week's report (to note "new since last week"). Runs from the orchestrator; writes the HTML to `cc-ci:/var/lib/cc-ci-reports` over ssh. - **Review/judgment (the skill's job):** summarize the run (green / stale-test / failed / skipped counts, notable regressions), then **classify** every item into: - **Needs attention** — PRs ready to merge (GREEN), and errors/failures to investigate. - **Routine** — minor bumps, stale-test (needs `--with-tests`), up-to-date / skipped-by-design. ## C. HTML output (per-week page + index) Each `week-.html`: - **Title:** "The Recipe Report" · **Subtitle:** "Week of ". - **① Needs attention** (top, prominent): important PRs to merge + errors to look into — short prose per item with the PR/build link. - **② Routine** (below): the less-important items (minor upgrades, stale-test, skipped) — compact. - **③ Comprehensive table** (bottom): every recipe → `current → target` · upgrade status · CI verdict · open PR (link) · notes. - **CI results: LINK only, no embedded images.** For each run, show its level/result as a number / short info string (e.g. `level 8 ✓` or `RED · restore`) and hyperlink to the Drone build + dashboard summary — do **not** embed the summary-card images. - **Footer:** generated timestamp, link to the live dashboard (ci.commoninternet.net), link back to the index. `index.html` (home): "The Recipe Report" + a reverse-chronological list of all weekly reports (date + one-line headline), each linking to its page. The skill **regenerates the index** each run. Styling: self-contained, inline CSS, clean + mobile-friendly, no JS (fully static). ## D. Public-safety (unauthenticated page) - Include only public-safe data: recipe names, version bumps, PR titles + links, CI verdicts, concise error summaries. **No secrets, tokens, internal IPs, or raw logs** (summarize — never dump logs that could contain generated app secrets; the build already redacts, but the report must not re-introduce). - PR links point to the private `git.autonomic.zone` mirror — viewers need access to follow them; the report's prose must stand on its own. ## E. Definition of Done 1. `reports.nix` deployed; `https://report.ci.commoninternet.net/` serves the index over TLS. 2. `/recipe-report` skill generates `week-.html` (3 sections + full table) and regenerates `index.html`. 3. Wired to run automatically after the weekly `/upgrade-all`; produces a report end-to-end on the next run. 4. Page is public-safe (no secrets); renders cleanly on desktop + mobile. 5. NOT merged anything; the report only *reports* (read-only on PRs/CI). ## F. Decisions - **Report model is separately configurable** — `REPORT_MODEL` (default **`opus`**), independent of the upgrader (`sonnet`). The report runs as its own `cc-ci-report` agent (per §B). ✓ (operator-set) - **CI results: link only**, with the level/result shown as a number/info string — no embedded images. ✓ (operator-set) - Static server: `nginx:alpine` new stack (recommended) vs extend the dashboard. *(open)* - Retention: keep all weekly reports indefinitely (tiny static HTML) — assumed yes. *(open)*