skills/completion-report/SKILL.md
--- name: completion-report description: Render a browsable HTML completion report at the tail of any autonomous coding run (Ralph, planner-worker, agentic-e2e-flow) or on demand for any git range. Shows per-story / per-PR cards with CI status, file-change stats, unified diffs, and a per-file rollback command. Inspired by ruizrica/agent-pi's completion-report extension. Fires automatically before Pushover when invoked from Ralph or planner-worker (the ping links to the report). Manual trigger vi
npx skillsauth add RonanCodes/ronan-skills skills/completion-reportInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
End-of-run artifact that answers four questions in one browser tab:
It's a single self-contained HTML file. No backend, no build step. Open it with open and read it in the browser. The HTML is also valid for emailing or sharing.
The shape is lifted from ruizrica/agent-pi's completion-report extension (see [ai-research:agent-pi]). The novelty for our flow is making it the tail call of every autonomous coding run, so the Pushover ping carries the report path as a deep link.
Automatic (the load-bearing case):
/ro:ralph run against a real backlog (NOT --plan-only, NOT one-shot --mode single exploration). Fires BEFORE /ro:pushover so the ping message can include the report path./ro:planner-worker run against a real backlog (NOT --plan-only). Same Pushover-precedes ordering./agentic-e2e-flow run that reached the ralph-or-planner-worker phase.The autonomous skills call this skill explicitly. Don't invoke it from inside an autonomous-loop body — only at the tail.
Manual:
/ro:completion-report.Do NOT fire:
git status / gh pr list — those are cheap and don't need rendering).Per repo, gitignored:
<repo-root>/.completion-reports/<YYYY-MM-DDTHH-MM>-<slug>.html
On first invocation in a repo, append .completion-reports/ to .gitignore (idempotent — check first). The slug comes from --title (kebab-cased) or, in autonomous mode, the PRD name (--prd <name>).
After writing, print the absolute path. If --open (default unless the parent skill passes --no-open), launch with open (macOS). The user wants open, not afplay-style direct playback, per memory feedback_open_audio_files.md — same principle applies here: GUI surface, not raw process.
The skill needs a commit range and a PR set. Pick whichever the caller supplied; otherwise auto-detect.
Priority order:
--prs n1,n2,n3 — explicit PR set. Resolves range from each PR's baseRefOid..headRefOid. Best when the caller (Ralph / planner-worker) already knows which PRs it shipped.--range <a>..<b> — explicit git range. Use as-is; no PR cards (only commit cards).--since <duration> (e.g. 4h, 1d) — git log --since=<duration> --pretty=%H. No PR cards unless gh pr list --state merged --search "closed:>=<computed-date>" returns matches.--prd <name> — read .ralph/<name>.progress.txt, extract every PR: #<n> line, treat as --prs.git merge-base HEAD origin/main..HEAD, PR set = gh pr list --author @me --state merged --search "closed:>=$(date -u -v-1d +%Y-%m-%d)" if gh available, otherwise empty.If the resolved range is empty (zero commits), exit early with a one-line summary: no commits in range, nothing to report. Do NOT write an empty HTML file.
┌────────────────────────────────────────────────────────────────┐
│ <title> │
│ <repo> @ <branch> · started <ts> · finished <ts> · <duration> │
│ <N> PRs · <M> commits · <added>+/<removed>- lines · <files> │
│ │
│ Risk panel — flags only show if triggered: │
│ ⚠ schema migration touched (drizzle/, migrations/) │
│ ⚠ env or secret changes (.env*, wrangler.toml secrets) │
│ ⚠ deletions > 100 lines in one PR │
│ ⚠ lockfile-only PRs │
└────────────────────────────────────────────────────────────────┘
┌─ PR #61 · ✅ merged · ✅ CI green · planner ────────────────────┐
│ Title: "feat(onboarding): add email-verification step" │
│ Author: claude (Ralph) · Squash: 8240af6 │
│ Files: 6 changed · +142 / -8 │
│ Summary: <if available from PRD/progress.txt> │
│ │
│ Files (click to expand diff): │
│ ▸ src/auth/verify.ts +88 / -0 [revert this file]│
│ ▸ src/auth/verify.test.ts +42 / -0 [revert this file]│
│ ▸ db/schema.ts +6 / -2 [revert this file]│
│ ... │
│ │
│ Rollback this PR: git revert -m 1 8240af6 │
└─────────────────────────────────────────────────────────────────┘
(repeat per PR / per orphan commit)
Each [revert this file] button is a one-liner clipboard copy:
git checkout <sha-before-pr>~1 -- <file>
The HTML uses <details>/<summary> for collapsible diffs, no JS framework and no external assets. Diff colouring is pure CSS: diff-to-html.sh tags each line with a class (d-add, d-del, d-hunk, d-meta, d-ctx) and the inline <style> colours them. No highlight.js, no CDN, works fully offline.
| Source | Use |
|---|---|
| git log <range> | commit list, commit messages, timestamps |
| git diff <range> per file | unified diff bodies |
| git diff --stat <range> | per-file change stats |
| gh pr view <n> --json title,number,state,mergeable,statusCheckRollup,author,baseRefOid,headRefOid,mergedAt,body | PR card data |
| gh pr diff <n> | optional fallback when range diff is messy |
| .ralph/<prd>.progress.txt | story timestamps, duration, learnings (when --prd supplied) |
| .ralph/<prd>.session.md | session-level duration |
Surface a top-of-page warning when ANY of these match a PR or commit in range. The risks are deterministic — no LLM judgement, just file-path and stat checks:
**/migrations/**, **/drizzle/**.sql, **/prisma/migrations/**, or commit message starting with 🗄️/migration: per the repo's emoji map..env*, wrangler.toml with a [[secrets]] block change, **/secrets.toml, or commit message containing secret/token/key:.package-lock.json, pnpm-lock.yaml, bun.lockb, yarn.lock, or Cargo.lock.✨ feat: and no path matches **/*.test.* / **/*.spec.* / tests/**.These are warnings, not blockers — the user reads the report after the run, the gate is the merger / reviewer.
/ro:pushoverThe autonomous-skill callers (Ralph, planner-worker, agentic-e2e-flow) drive this ordering:
/ro:completion-report with --prd <name> (or --prs <list>) and --no-open (we don't want to pop a browser on a remote / SSH session)./ro:pushover with a --url file://<absolute-path> so the ping deep-links to the report.Pushover message anatomy stays the same (state + concrete metric + next step) — the URL is supplementary, not a replacement for the message text.
If the report isn't useful (zero commits, range empty), the caller skips both the report and the URL but STILL sends the Pushover ping with state.
# After a heavy hand-rolled session, no flags — defaults to "since I branched from origin/main"
/ro:completion-report
# Specific PR set (e.g. cherry-picked, or PRs you reviewed today)
/ro:completion-report --prs 61,62,64 --title "auth slice — 3 PRs"
# A named PRD, after Ralph
/ro:completion-report --prd phase-2-onboarding-2026-05-06
# Custom git range, custom title
/ro:completion-report --range origin/main..HEAD --title "feature-x review"
# Don't open the browser (useful in scripts and over SSH)
/ro:completion-report --no-open
The scripts ship under scripts/ — invoke generate.sh, don't re-implement inline. They are pure bash (no Node), portable to macOS bash 3.2, and depend only on git (plus gh/jq for PR cards, both optional).
# from the repo you want a report for
bash <skill-dir>/scripts/generate.sh --range origin/main..HEAD --title "..." --no-open
generate.sh:
--range --since --prs --prd --title --out --open/--no-open --no-ping).no commits in range, nothing to report when the range is empty..completion-reports/ is gitignored (idempotent; skipped when --out is given).<<'EOF' heredocs, then appends every dynamic section through html_escape / diff-to-html.sh. No template engine.--url).open unless --no-open.Three scripts under scripts/:
scripts/generate.sh — main entrypoint, orchestrates the steps above.scripts/risk-checks.sh — runs each risk-panel trigger against the resolved range, prints triggered ones as key:value lines.scripts/diff-to-html.sh — wraps a unified diff (on stdin) into a class-tagged <pre> block for CSS colouring.Diff bodies and PR titles routinely contain HTML-special (< > & " ') and shell-special ($, backtick, \, $(...)) characters. A naive cat <<EOF heredoc that interpolates diff text will both mangle the HTML and let the shell re-parse the content. The scripts never do that:
<<'EOF' (single-quoted delimiter, zero expansion).html_escape (& first, then < > " ') before it touches the document.diff-to-html.sh, which escapes in a single awk pass and is fed by a pipe — the shell never re-parses the text.This is verified by a smoke test that plants a file containing <script>, & "quotes" 'apostrophes', `whoami`, and $HOME, generates a report, and confirms the HTML-special chars are entity-escaped while the shell-special chars survive verbatim.
feedback_artifact_format_mix.md) but defer until a real "I want to paste this into a PR description" moment surfaces. HTML wins for diffs + collapsible sections; markdown wins for inline pasting. If both end up needed, expose a --format html|md flag and reuse the same risk + range resolution.git log --graph and adding Mermaid drags in a renderer dependency. The HTML report is a list of changes, not a diagram of them.assets/highlight.min.js and load it locally — do NOT load from a CDN (bandwidth and offline-on-plane reasons).ruizrica/agent-pi's completion-report extension. See ai-research:agent-pi and the article ai-research:agent-pi-medium-ruiz./ro:ralph, /ro:planner-worker, /ro:pushover, /ro:close-session.testing
--- name: linear-pipeline description: The Fable orchestrator for a single dispatched Linear ticket. Holds almost no context itself; it receives `--issue <ID> --detached`, decides the stage sequence, and fans out a sub-agent per stage, passing forward only each stage's artifact (never re-derived, never inlined into its own context). Step zero, before any planning or stage routing, is a boundary triage against `canon/security-boundary.md` (#199): a match tags Ronan Connolly and stops the run, no
development
--- name: in-your-face description: Capture a chat-only answer into a durable artifact (markdown + HTML, PDF when cheap) and launch it automatically so the user cannot miss it. Use when user says "in your face", "don't let me lose this", "save that answer", "make that durable", or right after answering a substantive side question (a recipe, comparison, how-to, or generated prompt) that would otherwise die with the context. category: workflow argument-hint: [--no-open] [--vault <short>] [hint of
tools
One-shot headless OpenAI Codex CLI calls for background/admin AI tasks — summaries, classification, extraction, admin glue. The default engine for anything that runs AI constantly in the background (daemon-driven, per-event), because it bills the flat ChatGPT subscription instead of Claude usage or per-token API spend, and it keeps working while Claude is rate-limited. NEVER for coding — coding stays Claude. Use when a skill or daemon needs a cheap always-on AI call, when the user says "use codex", "ask codex", "codex as backup", or when building a background summarizer/classifier into a listener or loop. Reads auth from ~/.codex/auth.json (ChatGPT account, no API key).
research
Turn a warranty rejection, repair quote, or RMA email into a cited decision brief — legal read (NL/EU consumer law), is the part user-serviceable, live part and new-unit prices, repair-vs-DIY-vs-new economics, before-you-send-it checklist, deadlines. Use when the user pastes or screenshots a repair quote, warranty rejection, "not covered" email, onderzoekskosten fee, or asks "should I repair or replace this".