Agent skill

Payload Snapshot

by openshift-eng in openshift-eng/ai-helpers

Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis

Apache-2.0Auto-check passedTesting & QA

Install Payload Snapshot

skills CLI
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install openshift-eng/ai-helpers payload-snapshot --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/ci/skills/payload-snapshot .claude/skills/payload-snapshot && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
payload-snapshot
GitHub stars
120
Token cost
~6.4k tokens
SKILL.md length
2,796 words
Files
7 (incl. scripts)
Skills in repo
118
Repo updated
First seen
Licence
Apache-2.0

At a glance

Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis

  • Works in 3 steps: Run the Snapshot Script → Navigate the Snapshot → Use the Data
  • Tasks that involve Unit testing
  • SKILL.md covers When to Use This Skill, Prerequisites, Implementation Steps and CLI Reference, plus 6 more sections
  • Runs Python scripts from its folder; calls python3, jq and brew

What it does

Payload Snapshot is an agent skill from openshift-eng/ai-helpers. Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis

Its SKILL.md is about 6.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 7 other files, including scripts (for example `scripts/payload_snapshot.py`, `scripts/test_collection_completeness.py` and `scripts/test_gcs_bucket_remap.py`).

It sits in Testing & QA, covering Unit testing. It works with JUnit, Google Cloud, Homebrew and macOS. The repository describes itself as: Developer productivity tools for Claude Code & other AI assistants. The licence is Apache-2.0.

When your agent uses it

  • Tasks that involve Unit testing

Example prompts

  • “/payload-snapshot”

Requirements

  • Python 3

Workflow steps

3 steps, taken from the step headings in SKILL.md.

  1. Run the Snapshot Script
  2. Navigate the Snapshot
  3. Use the Data

What it can do on your machine

Read from SKILL.md and the folder at commit a627176. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships 6 files in scripts/ (Python), which the agent can run.

    Shell commands in SKILL.md call:

    • python3
    • jq
    • brew
    • gh

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • cli.github.com
    • cloud.google.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Payload Snapshot loads about 6.4k tokens when it runs. Until then it costs about 44 tokens; SKILL.md has 2,796 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~44
When it runs · the whole SKILL.md, loaded when a task matches
~6.4k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.

SKILL.md

The full file from openshift-eng/ai-helpers at commit a627176, republished under its Apache-2.0 licence (© openshift-eng). 2,796 words, ~6,438 tokens.

Download SKILL.mdSave it as .claude/skills/payload-snapshot/SKILL.md (or your agent's skills folder). This skill also uses 6 other files; get the full folder from GitHub.
name
payload-snapshot
description
Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis

Payload Snapshot

This skill downloads all data needed to analyze an OpenShift payload into a local directory tree. The resulting snapshot can be navigated entirely via file reads — no live API calls required during analysis.

When to Use This Skill

Use this skill when you need to:

  • Analyze a rejected payload and want all data available locally before starting
  • Create a reproducible snapshot of payload state at a point in time
  • Track test failure regressions across multiple payloads
  • Work offline or reduce API calls during payload analysis

Prerequisites

  1. Python 3 (3.10 or later)

    • Uses only standard library (no external dependencies)
  2. GitHub CLI (gh) — for PR diff, comment, and job data

    • Install: brew install gh (macOS) or see https://cli.github.com
    • Authenticate: gh auth login
    • Without gh, release controller data is still fetched; PR data is skipped
  3. Google Cloud SDK (gcloud) — for JUnit test result download

    • Install: brew install google-cloud-sdk (macOS) or see https://cloud.google.com/sdk
    • Authentication is not required — the CI artifact buckets are public and are read anonymously when no account is configured
    • Without gcloud entirely, JUnit data is skipped and the snapshot is reported as incomplete (see Data completeness)
  4. Container runtime (podman) — for RHCOS RPMDB extraction

    • Install: available in most Linux distributions; brew install podman on macOS
    • Requires access to the release image registry (e.g., registry.ci.openshift.org)
    • Without podman, RPMDB data is skipped; all other data is still collected
  5. rpm CLI — for the RPM changelog diffs between payloads

    • Queries the already-extracted RPMDBs locally; no network, no root
    • Without rpm, changelog diffs are skipped; the RPMDBs themselves are still extracted
  6. Network access to:

    • *.ocp.releases.ci.openshift.org (release controller)
    • sippy.dptools.openshift.org (historical payload fallback)
    • api.github.com (via gh CLI)
    • storage.googleapis.com (via gcloud CLI)

Implementation Steps

Step 1: Run the Snapshot Script
bash
script_path="plugins/ci/skills/payload-snapshot/scripts/payload_snapshot.py"

# Snapshot a specific payload
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806

# Custom output directory
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806 --output-dir .work/snapshot

# Limit chain depth
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806 --max-chain 5

# Skip JUnit download (faster, still generates job structure and summary)
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806 --no-junit

# Skip RPMDB extraction
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806 --no-rpmdb

# Skip the RPM changelog diffs (RPMDBs are still extracted)
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806 --no-rpm-changelogs

# Force Sippy for all payload metadata (normally fallback is automatic)
python3 "$script_path" 4.22.0-0.nightly-2026-02-25-152806 --sippy

The script will:

  1. Parse the payload tag to determine version, stream, and architecture
  2. Probe all available streams for the version (nightly, ci, across architectures)
  3. Chain backwards through Sippy's time-ordered release tag list until finding one where all blocking jobs passed, restoring tags garbage collected from the release controller
  4. For each payload in the chain, prefer release controller data and changelogs; fall back per historical tag or cross-tag diff to Sippy payload, PR, and job data
  5. Split jobs into blocking/informing directories with metadata and GCS browser links
  6. For each failed blocking job, download and parse JUnit XML test results
  7. For each failed blocking job, download build-log.txt from GCS and extract error/warning lines + log tail
  8. Track test failure regressions — when did each failure first appear?
  9. Track per-job failure streaks — consecutive failures, originating payload, failure pattern
  10. For each unique PR across all changelogs, fetch the git diff, comments, and CI jobs via gh
  11. For each payload in the chain, extract the full RPM database from RHCOS images via podman
  12. Diff the target payload's RPM changelogs against every older payload in the chain, using the extracted RPMDBs
  13. Generate summary.json with comprehensive triage data, plus AGENTS.md/CLAUDE.md for agent orientation
Step 2: Navigate the Snapshot

The output directory is structured for easy navigation:

text
payload/
  <version>/
    <stream>/
      summary.json                         # START HERE — full triage data
      CLAUDE.md                            # Imports AGENTS.md for Claude Code
      AGENTS.md                            # Dynamic snapshot orientation doc
      streams.json                         # All streams for this version
      <tag>/                               # Each payload in the chain
        payload.json                       # Release controller API response
        changelog.json                     # PRs that changed vs. previous payload
        regressions.json                   # Test failure regression tracking
        jobs/
          blocking/
            <job-name>/
              job.json                     # Job metadata (state, URLs, GCS link, retries)
              build_log.json               # Error/warning lines + log tail (failed only)
              junit/                       # Only for failed jobs
                junit_operator.xml         # CI phase results
                junit-aggregated.xml       # Aggregated jobs only
                results.json               # Parsed test failures (full output)
          informing/
            <job-name>/
              job.json                     # Job metadata only (no JUnit/build log)
        <component>/                       # e.g., machine-config-operator
          prs/
            <pr_number>/
              code.diff                    # Git diff of the PR
              comments.json                # PR comments and reviews
              jobs.json                    # CI check runs
        rpmdb/                             # RPMDB from RHCOS images
          rhel-coreos/                     # queryable with rpm --dbpath
            rpmdb.sqlite
          rhel-coreos-10/
            rpmdb.sqlite
        rpm-changelogs/                    # Target payload only
          rhel-coreos-10/                  # One diff per older payload
            <older-payload-tag>.md
Step 3: Use the Data

Find failed blocking jobs (with streaks):

bash
jq '.blocking_jobs.failed_jobs[] | {name, state, streak: .streak.streak_length, pattern: .streak.failure_pattern}' payload/<version>/<stream>/summary.json

Check test failures and when they started:

bash
jq '.[] | {test: .test_name, first_failed: .first_failed_in, payloads: .payloads_failing, jobs: .jobs}' payload/<version>/<stream>/<tag>/regressions.json

List PRs in a payload:

bash
jq '.changeLogJson.updatedImages[].commits[] | {component: .name, pr: .pullURL, subject: .subject}' payload/<version>/<stream>/<tag>/changelog.json

Read a specific PR's diff:

bash
cat payload/<version>/<stream>/<tag>/<component>/prs/<number>/code.diff

Check JUnit failures for a specific job:

bash
jq '.[].name' payload/<version>/<stream>/<tag>/jobs/blocking/<job-name>/junit/results.json

Query RHCOS RPM packages (using the extracted rpmdb.sqlite):

bash
rpm -qa --dbpath $(pwd)/payload/<version>/<stream>/<tag>/rpmdb/rhel-coreos

See what changed in the RHCOS RPMs since the baseline (inline in summary.json — no file read needed):

bash
jq '.rpm_changelogs[] | select(.diff) | {variant, compared_tag, diff}' payload/<version>/<stream>/summary.json

Find which payload in the chain introduced a package bump:

bash
cat payload/<version>/<stream>/<target-tag>/rpm-changelogs/<variant>/<older-tag>.md

CLI Reference

text
python3 payload_snapshot.py <payload_tag> [OPTIONS]

Positional:
  payload_tag          Payload tag (e.g., 4.22.0-0.nightly-2026-02-25-152806)

Options:
  --output-dir DIR     Base output directory (default: payload)
  --max-chain N        Maximum backward chain depth (default: 20)
  --workers N          Parallel workers for API calls (default: 8)
  --no-junit           Skip JUnit download and regression tracking
  --no-rpmdb           Skip RHCOS RPMDB extraction
  --no-rpm-changelogs  Skip the RPM changelog diffs between the target payload
                       and the older payloads in the chain
  --sippy              Force Sippy for all payload metadata instead of using
                       the automatic release-controller-first fallback
  --fail-on-incomplete Exit 1 if any requested data could not be collected

Output Files

streams.json

Lists all available streams for the payload's version.

summary.json

Comprehensive stream-level triage data — start here. Contains:

  • Payload metadata: payload_tag, phase, release_url, source, architecture, stream, version
  • Chain data: chain_length, baseline_tag, hours_since_baseline
  • blocking_jobs.failed_jobs[] — detailed objects with name, state, prow_url, gcs_url, and relative path job_json. May include: rhcos_version, streak (with streak_length, originating_payload, is_new_failure, failure_pattern), build_log_errors, test_failure_count, and relative paths junit_results, build_log
  • informing_jobs.failed_jobs[] — job name strings
  • test_failures.blocking[] — gating failures only: test_name, jobs, first_failed_in, payloads_failing, failure_message, failure_text (full, not truncated). These are the failures that can fail a job and therefore reject the payload.
  • test_failures.informing[] / test_failures.flakes[] — test_name, jobs. Neither can fail a job. No onset is tracked for them, because an onset implies there is a culprit to find.
  • payloads[] — per-payload entries with tag, phase, source, changelog_source, relative file paths, prs[] with component/diff/comments paths, rhcos_changes[] with RPM diffs per RHCOS variant (package versions only — no changelog text), and, on every payload but the target, rpm_changelogs[] pointing at the report holding that text
  • rhcos_rpms[] — RPMDB metadata for the target payload's RHCOS variants: tag, name, pullspec, rpmdb (relative path to rpmdb.sqlite)
  • rpm_changelogs[] — one RPM diff per (RHCOS variant, older payload in the chain): variant, compared_tag, is_baseline, changed/added/removed counts, and changelogs (relative path to the full report). The oldest surviving comparison per variant — normally the chain baseline — also carries diff inline, so target-vs-baseline needs no file read: diff.changed[] with package, old, new and changelog (the entries that version added), plus diff.added[] / diff.removed[] with package and version. The intermediate hops are a subset of that diff and stay behind their changelogs path; read them to find which hop introduced a given package bump. If the true chain baseline's RPMDB couldn't be read for a variant, the next-oldest readable comparison takes over is_baseline/diff instead, flagged with baseline_rpmdb_missing: true so consumers know the diff doesn't reach all the way back to the chain's actual start.
  • data_complete — true when all requested data was ultimately collected, including via a fallback after an initial read failed. false means some requested data could not be read at all.
  • collection_errors[] — every read failure encountered. Each entry has reason, command, and optionally detail, stage, job, payload_tag, recovered. Reasons: auth, timeout, gcloud_missing, command_failed, junit_unavailable (nothing readable), junit_missing (nothing discovered), junit_unparseable (corrupt XML), junit_partial (some files unread), build_log_unavailable.
    • recovered: true means a fallback subsequently obtained the data. These entries are diagnostic only (useful for spotting a timeout that needs tuning) and do not make data_complete false.
    • data_complete is false only when at least one error was not recovered.

<a id="data-completeness"></a>

Only gating results count as failures

A test result falls into exactly one of three categories, and only the last can fail a job or reject a payload:

CategoryRuleGates?
flakethe same test, in the same suite, both failed and passedno
informingthe testcase carries lifecycle="informing"no
failurefailed everywhere, no informing lifecycleyes

Informing tests are run to stabilize them and are not expected to gate. A missing lifecycle attribute means the test does gate — the attribute exists only to opt a test out.

results.json records all three so nothing is hidden, each entry carrying status (failed, error, flake) and test_lifecycle (blocking, informing).

test_failure_count on a failed job counts only gating results — it is the failure count, and flakes and informing tests are not added to it. Those two are listed by name under test_failures.flakes[] and test_failures.informing[]. Regression onset (first_failed_in) is derived from gating failures alone.

"Informing job" vs "informing test" — two unrelated concepts

The word "informing" appears in two places with completely different meanings. Confusing them produces wrong analysis:

ConceptWhere it livesWhat it means
Informing jobinforming_jobs.failed_jobs[] in summary.jsonA CI job that runs for visibility but does not gate the payload. Job-level pass/fail.
Informing testtest_failures.informing[] in summary.jsonAn individual test case whose lifecycle="informing" attribute opts it out of gating. Can appear inside any job — blocking or informing.

An informing test can run inside a blocking job. An informing job can contain blocking tests. They are orthogonal. Never combine them in the same section or count.

gcloud credentials are not required

The CI artifact buckets are public. When gcloud has no active account it is run in anonymous mode automatically, so an unauthenticated environment still produces a complete snapshot. Authenticate only if you also need private buckets.

Missing data is absent, never empty

When a collection step fails, the affected file is not written and the corresponding summary field is omitted — it is never emitted as an empty list or a zero count. Specifically:

  • If JUnit could not be read for a job, results.json is not created, the job entry has no test_failure_count and junit_results, and instead carries junit_collection_failed: true.

  • An absent test_failure_count therefore means unknown, whereas 0 means verified clean.

  • If some JUnit files were read and others were not, results.json holds the real results that were obtained and the job entry adds junit_collection_partial: true. test_failure_count is then a lower bound, not a total.

  • A failed job with no readable JUnit at all — none discovered (junit_missing), or nothing that parsed (junit_unparseable) — is left without a count entirely. A job whose tests may never have run is unknown, not clean. A parse failure alongside other readable files is the partial case above, not this one.

Consumers must distinguish these. Treating unreadable data as "no test failures" makes a broken job look like it failed for some other reason, which misdirects root-cause analysis. Check data_complete before drawing any conclusion from an absence of test failures. Use --fail-on-incomplete in automation to exit non-zero rather than emit a partial snapshot.

State is persisted to collection_errors.json beside summary.json, so a later process can tell that an existing results.json came from an incomplete read.

AGENTS.md / CLAUDE.md

Dynamic orientation document generated at snapshot time. Contains the specific payload tag, chain, failed jobs, file layout, key concepts, and summary.json schema. CLAUDE.md imports AGENTS.md via @AGENTS.md.

payload.json

Full release controller response including blockingJobs, informingJobs, and asyncJobs with their states, Prow URLs, and retry attempt URLs.

changelog.json

Release controller diff response with changeLogJson.updatedImages listing every PR that changed between this payload and its predecessor. Also contains nodeImageStreams at the top level — RPM diffs for each RHCOS variant showing which packages changed between payloads, as package names and versions only.

For the target payload, _rpm_changelogs[] is injected alongside _source: variant, compared_tag (the predecessor, the same comparison this file describes) and changelogs, a path relative to the payload directory pointing at the report that carries the changelog text nodeImageStreams lacks. payload.json gets the same key.

Show full SKILL.md (1,140 more words)Show less
rhcos_changes[] (in payloads[] within summary.json)

Per-payload RHCOS RPM diff data extracted from the changelog's nodeImageStreams. Each entry contains:

  • name: Human-readable RHCOS version name (e.g., "Red Hat Enterprise Linux CoreOS 10.2")
  • tag: RHCOS image stream tag (rhel-coreos for RHCOS 9, rhel-coreos-10 for RHCOS 10)
  • changed: Dict of {package_name: {"old": old_version, "new": new_version}}
  • added: Dict of newly added packages {package_name: new_version} (when present)
  • removed: Dict of removed packages {package_name: old_version} (when present)

Only variants with non-empty rpmDiff are included. When the snapshot is created from Sippy-based changelogs (which lack nodeImageStreams), this field is absent.

rpmdb/<variant>/rpmdb.sqlite

The rpmdb.sqlite file extracted from RHCOS images via podman. One directory per RHCOS variant (e.g., rpmdb/rhel-coreos/, rpmdb/rhel-coreos-10/), each containing rpmdb.sqlite. Queryable with rpm -qa --dbpath <absolute-path-to-variant-dir> or directly with sqlite3.

Extracted for every payload in the chain. Skipped when podman is unavailable or --no-rpmdb is passed.

rpm-changelogs/<variant>/<older-payload-tag>.md

Written for the target payload only, one file per (RHCOS variant, older payload in the chain) — including the baseline. Each file answers "what do this variant's RPMs have in the target that they did not have in that older payload?", by querying both already-extracted RPMDBs with rpm locally. No API calls and no image pulls are involved, and rhcos_changes[] (which the release controller computes) is not consulted.

Each file has three sections:

  • Changed packages — <old version> -> <new version> plus only the changelog entries the new version added. RPM changelogs are additive and grow from the top, so the cut is at the first entry the old version already had. (Not a common-suffix subtraction: rpm trims stale entries off the bottom at build time, so two builds of the same package weeks apart routinely disagree about their oldest entries.) A version bump with no new entry is reported as a rebuild.
  • New packages — packages the older payload did not have at all, with their full changelog.
  • Removed packages — name and version only. Nothing to read a changelog from.

Deliberate simplifications, because the consumer is an LLM that can read a full dump when it has to:

  • Each older payload is compared against the target independently. No attempt is made to follow a package's evolution hop by hop. A package removed and later re-added shows up as "no difference" against the baseline and as some difference against an intermediate payload — that is the honest answer to each of those two questions, and no further reconciliation is done.
  • A changelog whose history was rewritten degrades to a full dump. When the two versions share no entries, the whole changelog is emitted rather than guessing a cut point from version strings.
  • A variant missing from the older payload is skipped, rather than reported as several hundred new packages.

Indexed by summary.json's rpm_changelogs[], whose baseline entry repeats the same content inline under diff. Read these files for the intermediate hops, or when the prose layout is easier than the JSON.

Skipped when rpm is unavailable, --no-rpm-changelogs is passed, the RPMDBs were not extracted, or the chain has no older payload (chain_length < 2).

regressions.json

Per-payload regression tracking data. For each failing test in the target payload:

  • test_name: the failing test
  • jobs: which jobs it fails in
  • first_failed_in: the earliest payload in the chain where it was failing
  • payloads_failing: how many consecutive payloads it has been failing
  • failure_message: the error message
  • failure_text: full failure output
job.json

Per-job metadata including name, state, lifecycle (blocking/informing), Prow URL, GCS browser URL (gcs_url), retry count, whether it's an aggregated job, GCS bucket path, and rhcos_version.

The rhcos_version field is determined from the job name and OCP version:

  • rhcos9_10 — heterogeneous cluster (mixed RHCOS 9 and 10 node pools)
  • rhcos10 — RHCOS 10 only
  • rhcos9 — RHCOS 9 (explicit)
  • rhcos9-default — no explicit fragment; defaults to RHCOS 9 for OCP 4.x installs (including major upgrades to 5.x)
  • rhcos10-default — no explicit fragment; defaults to RHCOS 10 for OCP 5.x fresh installs
build_log.json (failed blocking jobs only)

Extracted from build-log.txt in GCS (handles gzip decompression). Contains:

  • total_lines: total line count of the build log
  • error_warning_count: number of lines matching error/warning patterns
  • error_warning_lines[]: each with line_number and text
  • tail_start_line, tail_lines[]: last 20% of the log for context
results.json (in junit/ subdirectory)

Parsed JUnit test failures for a specific job. Only includes failed/error tests. For aggregated jobs, includes per-run pass/fail/skip data with Prow URLs for each run.

code.diff, comments.json, jobs.json

PR artifacts from GitHub (unchanged from previous version).

Chain Logic

The script chains backwards from the target payload until it finds a payload where all blocking jobs succeeded. This is stricter than the Accepted phase — a payload can be force-accepted with failed blocking jobs, which does not count as a stop point.

Sippy's release tag list, sorted by release_time, is used to identify every preceding assembled payload. If a tag is still retained, its payload details and changelog come from the release controller. If it has been garbage collected, the script constructs compatible payload.json and changelog.json files from Sippy's release tags, pull requests, and job runs APIs. A changelog that crosses a garbage-collected tag also comes from Sippy because the release controller can no longer compute that diff.

The generated source and changelog_source fields expose this provenance. Sippy-backed data is intentionally partial: RHCOS nodeImageStreams, async jobs, and previousAttemptURLs are unavailable.

For terminal payloads (Accepted/Rejected), jobs showing Pending on the release controller are cross-checked against the actual Prow prowjob.json artifact to get their real state.

Aggregated Jobs

Aggregated jobs run the same underlying test multiple times with statistical analysis. The script:

  • Detects aggregated jobs by the aggregated- name prefix
  • Downloads junit-aggregated.xml which contains per-run pass/fail/skip data
  • Parses the YAML in <system-out> to extract individual run URLs

Error Handling

  • Tag not found: Exits with code 2 and a descriptive error
  • Release controller unreachable: Exits with code 1
  • Historical tag missing from release controller: Automatically uses Sippy
  • gh not authenticated: Prints a warning and continues without PR data
  • gcloud not available: Warns, skips JUnit download, and records gcloud_missing so the snapshot is reported incomplete
  • gcloud not authenticated: Reads the public buckets anonymously
  • Individual job/PR fetch failure: Logs a warning, records a collection error, and continues
  • Idempotent: Re-running skips files that already exist — except JUnit output a previous run recorded as incomplete, which is discarded and re-collected so a partial snapshot cannot be inherited as complete

Notes

  • The script uses only Python standard library — no pip dependencies
  • PR data is deduplicated across payloads — each PR is fetched once
  • JUnit and build-log download are scoped to failed blocking jobs only (informing jobs get job.json but no JUnit or build log)
  • The --workers flag controls parallelism for all subprocess calls (default 8)
  • Summary is always regenerated on re-run (not skipped like other files)
  • Progress is printed to stderr; the script produces no stdout output

See Also

  • Related Skill: fetch-payloads (fetches recent payloads from the release controller)
  • Related Skill: fetch-new-prs-in-payload (fetches PRs new in a specific payload)
  • Related Skill: payload-analysis (analyzes a payload snapshot for revert candidates)

© openshift-eng, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 6 other files (scripts) in plugins/ci/skills/payload-snapshot of openshift-eng/ai-helpers.

  • SKILL.md
  • scripts/payload_snapshot.py
  • scripts/test_collection_completeness.py
  • scripts/test_gcs_bucket_remap.py
  • scripts/test_rpm_changelogs.py
  • scripts/test_sippy_fallback.py
  • scripts/test_test_classification.py

Open the folder on GitHubat commit a627176

Compare with similar skills

Payload Snapshot next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Payload Snapshot compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Payload Snapshot this skillopenshift-eng/ai-helpers120—~6.4kAutomated safety check: PassApache-2.0
Debug Playwright Prowquay/quay2.8k—~2.2kAutomated safety check: PassApache-2.0
Quay Prow Triagequay/quay2.8k—~2.9kAutomated safety check: PassApache-2.0
Shopware CLIshopware/shopware-cli123—~4.9kAutomated safety check: PassMIT
Bootstrap Featureopenbootdotdev/openboot275—~1.1kAutomated safety check: PassMIT
Debug Surefireeclipse-rdf4j/rdf4j420—~2.4kAutomated safety check: PassBSD-3-Clause

Similar skills

  • Deep-dive diagnosis of a Playwright test failure already isolated to one Quay Prow/OpenShift CI run: downloads its GCS artifacts (results.json, JUnit, build/pod logs, Jaeger traces), classifies real…

    2.8k GitHub stars~2.2k tokensUpdated today
    Testing & QAAuto-check passed
  • Diagnose any Quay Prow job failure end to end: prowjob.json - top-level build log - JUnit - resolved failing step - Playwright results.json when the failing step is Playwright, continuing through…

    2.8k GitHub stars~2.9k tokensUpdated today
    Testing & QAAuto-check passed
  • Shopware CLI

    shopware/shopware-cli

    Use Shopware CLI for Shopware project, extension, and account workflows — create and install new projects (project create, project dev install), validate projects or extensions (with…

    123 GitHub stars~4.9k tokensUpdated yesterday
    Testing & QAAuto-check passed
  • Bootstrap Feature

    openbootdotdev/openboot

    A skill your agent uses when adding a new CLI subcommand or feature to openboot.

    275 GitHub stars~1.1k tokensUpdated 15 days ago
    Testing & QAAuto-check passed
  • Debug Surefire

    eclipse-rdf4j/rdf4j

    Debug Maven Surefire unit tests by running them in JDWP "wait for debugger" mode (-Dmaven.surefire.debug) and attaching to the forked test JVM using jdb (preferred for CLI/agent debugging)…

    420 GitHub stars~2.4k tokensUpdated yesterday
    Testing & QAAuto-check passed
  • Add Source Datastream To Spanner

    GoogleCloudPlatform/DataflowTemplates

    Guide for implementing a database source connector in the v2/datastream-to-spanner forward migration Dataflow template.

    1.3k GitHub stars~1.7k tokensUpdated yesterday
    Testing & QAAuto-check: notes

More from openshift-eng/ai-helpers

All 118 skills in this repo
  • Investigate CI Reliability

    openshift-eng/ai-helpers

    Find and independently validate actionable reliability defects across OpenShift release jobs and presubmits, then export portable issue handoffs.

    120 GitHub stars~1.9k tokensUpdated 2 days ago
    Auto-check passed
  • Address Review PR

    openshift-eng/ai-helpers

    Fetch and address all PR review comments — categorize by priority, make code changes, post replies, and push.

    120 GitHub stars~2.9k tokensUpdated 2 days ago
    Auto-check passed
  • Categorize Activity Types

    openshift-eng/ai-helpers

    Categorize Jira issues into Red Hat Sankey Activity Type categories using MCP Jira tools.

    120 GitHub stars~2.4k tokensUpdated 2 days ago
    Auto-check passed
  • Has Review Work

    openshift-eng/ai-helpers

    Decide whether a GitHub PR has unanswered authorized review comments or new required CI failures worth a follow-up agent.

    120 GitHub stars~1.9k tokensUpdated 2 days ago
    Auto-check passed
  • Must Gather Analyzer

    openshift-eng/ai-helpers

    Analyze OpenShift must-gather diagnostic data including cluster operators, pods, nodes, and network components.

    120 GitHub stars~2.3k tokensUpdated 2 days ago
    Auto-check passed
  • Payload Autodl JSON

    openshift-eng/ai-helpers

    Schema for the autodl JSON data file produced by payload-analysis for database ingestion — you must use this skill whenever generating the autodl JSON file

    120 GitHub stars~2.6k tokensUpdated 2 days ago
    Auto-check passed

Categories

Questions about Payload Snapshot

What does Payload Snapshot do?

Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis. Payload Snapshot is an agent skill from openshift-eng/ai-helpers.

When should I use Payload Snapshot?

Payload Snapshot fits situations like: tasks that involve Unit testing.

How do I install Payload Snapshot in Claude Code?

Run `npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a claude-code`. Or copy the skill folder (plugins/ci/skills/payload-snapshot in openshift-eng/ai-helpers) into .claude/skills/payload-snapshot in your project. Claude Code loads it when a task matches its description.

How do I install Payload Snapshot in Codex?

Run `npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a codex`. Or copy the skill folder (plugins/ci/skills/payload-snapshot in openshift-eng/ai-helpers) into .agents/skills/payload-snapshot in your project. Codex loads it when a task matches its description.

Can I use Payload Snapshot in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/payload-snapshot, .gemini/skills/payload-snapshot, .github/skills/payload-snapshot and .opencode/skills/payload-snapshot in your project.

What does Payload Snapshot need to run?

Going by SKILL.md and its folder, Payload Snapshot needs Python for the scripts in its folder and the command-line tools its instructions call (python3, jq, brew and gh). Our summary lists: Python 3.

Does Payload Snapshot access the network?

SKILL.md names 2 domains. As links in the text: cli.github.com and cloud.google.com. This is read from the text; nothing was executed.

Is Payload Snapshot safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.

What licence does Payload Snapshot use?

Payload Snapshot is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Payload Snapshot use?

About 6.4k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Payload Snapshot?

Skills that share tags, products or a category with Payload Snapshot: Debug Playwright Prow (quay/quay, 2.8k stars), Quay Prow Triage (quay/quay, 2.8k stars), Shopware CLI (shopware/shopware-cli, 123 stars) and Bootstrap Feature (openbootdotdev/openboot, 275 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Payload Snapshot?

openshift-eng (a GitHub organization) maintains it in openshift-eng/ai-helpers, which has 120 GitHub stars. The repository holds 118 skills in this directory. The repository was last updated on October 6, 2026.

Source: openshift-eng/ai-helpers on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.