Debug Playwright Prow
quay/quay
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…
Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install openshift-eng/ai-helpers payload-snapshot --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ 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-srcUse ~/.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/
Install the "payload-snapshot" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshot into .claude/skills/payload-snapshot/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "payload-snapshot", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshotType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install openshift-eng/ai-helpers payload-snapshot --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/ci/skills/payload-snapshot .agents/skills/payload-snapshot && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "payload-snapshot" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshot into .agents/skills/payload-snapshot/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "payload-snapshot", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install openshift-eng/ai-helpers payload-snapshot --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/ci/skills/payload-snapshot .cursor/skills/payload-snapshot && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "payload-snapshot" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshot into .cursor/skills/payload-snapshot/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "payload-snapshot", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/openshift-eng/ai-helpers.git --path plugins/ci/skills/payload-snapshot--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install openshift-eng/ai-helpers payload-snapshot --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/ci/skills/payload-snapshot .gemini/skills/payload-snapshot && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "payload-snapshot" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshot into .gemini/skills/payload-snapshot/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "payload-snapshot", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install openshift-eng/ai-helpers payload-snapshotInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/ci/skills/payload-snapshot .github/skills/payload-snapshot && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "payload-snapshot" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshot into .github/skills/payload-snapshot/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "payload-snapshot", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add openshift-eng/ai-helpers --skill payload-snapshot -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install openshift-eng/ai-helpers payload-snapshot --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openshift-eng/ai-helpers.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/ci/skills/payload-snapshot .opencode/skills/payload-snapshot && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "payload-snapshot" agent skill from https://github.com/openshift-eng/ai-helpers/tree/main/plugins/ci/skills/payload-snapshot into .opencode/skills/payload-snapshot/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "payload-snapshot", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
payload-snapshotSnapshot 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. 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.
3 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit a627176. It shows what the files ask for, not the result of running them.
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.
Ships 6 files in scripts/ (Python), which the agent can run.
Shell commands in SKILL.md call:
python3jqbrewghFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
cli.github.comcloud.google.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
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.
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.
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.
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.
.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.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.
Use this skill when you need to:
Python 3 (3.10 or later)
GitHub CLI (gh) — for PR diff, comment, and job data
brew install gh (macOS) or see https://cli.github.comgh auth logingh, release controller data is still fetched; PR data is skippedGoogle Cloud SDK (gcloud) — for JUnit test result download
brew install google-cloud-sdk (macOS) or see https://cloud.google.com/sdkgcloud entirely, JUnit data is skipped and the snapshot is
reported as incomplete (see Data completeness)Container runtime (podman) — for RHCOS RPMDB extraction
brew install podman on macOSregistry.ci.openshift.org)podman, RPMDB data is skipped; all other data is still collectedrpm CLI — for the RPM changelog diffs between payloads
rpm, changelog diffs are skipped; the RPMDBs themselves are still extractedNetwork 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)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 --sippyThe script will:
ghpodmanThe output directory is structured for easy navigation:
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>.mdFind failed blocking jobs (with streaks):
jq '.blocking_jobs.failed_jobs[] | {name, state, streak: .streak.streak_length, pattern: .streak.failure_pattern}' payload/<version>/<stream>/summary.jsonCheck test failures and when they started:
jq '.[] | {test: .test_name, first_failed: .first_failed_in, payloads: .payloads_failing, jobs: .jobs}' payload/<version>/<stream>/<tag>/regressions.jsonList PRs in a payload:
jq '.changeLogJson.updatedImages[].commits[] | {component: .name, pr: .pullURL, subject: .subject}' payload/<version>/<stream>/<tag>/changelog.jsonRead a specific PR's diff:
cat payload/<version>/<stream>/<tag>/<component>/prs/<number>/code.diffCheck JUnit failures for a specific job:
jq '.[].name' payload/<version>/<stream>/<tag>/jobs/blocking/<job-name>/junit/results.jsonQuery RHCOS RPM packages (using the extracted rpmdb.sqlite):
rpm -qa --dbpath $(pwd)/payload/<version>/<stream>/<tag>/rpmdb/rhel-coreosSee what changed in the RHCOS RPMs since the baseline (inline in summary.json — no file read needed):
jq '.rpm_changelogs[] | select(.diff) | {variant, compared_tag, diff}' payload/<version>/<stream>/summary.jsonFind which payload in the chain introduced a package bump:
cat payload/<version>/<stream>/<target-tag>/rpm-changelogs/<variant>/<older-tag>.mdpython3 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 collectedstreams.jsonLists all available streams for the payload's version.
summary.jsonComprehensive stream-level triage data — start here. Contains:
payload_tag, phase, release_url, source, architecture, stream, versionchain_length, baseline_tag, hours_since_baselineblocking_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_loginforming_jobs.failed_jobs[] — job name stringstest_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 textrhcos_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>
A test result falls into exactly one of three categories, and only the last can fail a job or reject a payload:
| Category | Rule | Gates? |
|---|---|---|
| flake | the same test, in the same suite, both failed and passed | no |
| informing | the testcase carries lifecycle="informing" | no |
| failure | failed everywhere, no informing lifecycle | yes |
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.
The word "informing" appears in two places with completely different meanings. Confusing them produces wrong analysis:
| Concept | Where it lives | What it means |
|---|---|---|
| Informing job | informing_jobs.failed_jobs[] in summary.json | A CI job that runs for visibility but does not gate the payload. Job-level pass/fail. |
| Informing test | test_failures.informing[] in summary.json | An 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.
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.
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.mdDynamic 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.jsonFull release controller response including blockingJobs, informingJobs, and asyncJobs with their states, Prow URLs, and retry attempt URLs.
changelog.jsonRelease 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.
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.sqliteThe 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>.mdWritten 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:
<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.Deliberate simplifications, because the consumer is an LLM that can read a full dump when it has to:
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.jsonPer-payload regression tracking data. For each failing test in the target payload:
test_name: the failing testjobs: which jobs it fails infirst_failed_in: the earliest payload in the chain where it was failingpayloads_failing: how many consecutive payloads it has been failingfailure_message: the error messagefailure_text: full failure outputjob.jsonPer-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 onlyrhcos9 — 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 installsbuild_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 logerror_warning_count: number of lines matching error/warning patternserror_warning_lines[]: each with line_number and texttail_start_line, tail_lines[]: last 20% of the log for contextresults.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.jsonPR artifacts from GitHub (unchanged from previous version).
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 run the same underlying test multiple times with statistical analysis. The script:
aggregated- name prefixjunit-aggregated.xml which contains per-run pass/fail/skip data<system-out> to extract individual run URLsgh not authenticated: Prints a warning and continues without PR datagcloud not available: Warns, skips JUnit download, and records
gcloud_missing so the snapshot is reported incompletegcloud not authenticated: Reads the public buckets anonymouslyjob.json but no JUnit or build log)--workers flag controls parallelism for all subprocess calls (default 8)fetch-payloads (fetches recent payloads from the release controller)fetch-new-prs-in-payload (fetches PRs new in a specific payload)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
SKILL.md and 6 other files (scripts) in plugins/ci/skills/payload-snapshot of openshift-eng/ai-helpers.
Open the folder on GitHubat commit a627176
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.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Payload Snapshot this skillopenshift-eng/ai-helpers | 120 | — | ~6.4k | Automated safety check: Pass | Apache-2.0 | |
| Debug Playwright Prowquay/quay | 2.8k | — | ~2.2k | Automated safety check: Pass | Apache-2.0 | |
| Quay Prow Triagequay/quay | 2.8k | — | ~2.9k | Automated safety check: Pass | Apache-2.0 | |
| Shopware CLIshopware/shopware-cli | 123 | — | ~4.9k | Automated safety check: Pass | MIT | |
| Bootstrap Featureopenbootdotdev/openboot | 275 | — | ~1.1k | Automated safety check: Pass | MIT | |
| Debug Surefireeclipse-rdf4j/rdf4j | 420 | — | ~2.4k | Automated safety check: Pass | BSD-3-Clause |
quay/quay
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…
quay/quay
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…
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…
openbootdotdev/openboot
A skill your agent uses when adding a new CLI subcommand or feature to openboot.
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)…
GoogleCloudPlatform/DataflowTemplates
Guide for implementing a database source connector in the v2/datastream-to-spanner forward migration Dataflow template.
openshift-eng/ai-helpers
Find and independently validate actionable reliability defects across OpenShift release jobs and presubmits, then export portable issue handoffs.
openshift-eng/ai-helpers
Fetch and address all PR review comments — categorize by priority, make code changes, post replies, and push.
openshift-eng/ai-helpers
Categorize Jira issues into Red Hat Sankey Activity Type categories using MCP Jira tools.
openshift-eng/ai-helpers
Decide whether a GitHub PR has unanswered authorized review comments or new required CI failures worth a follow-up agent.
openshift-eng/ai-helpers
Analyze OpenShift must-gather diagnostic data including cluster operators, pods, nodes, and network components.
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
Works with
Categories
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.
Payload Snapshot fits situations like: tasks that involve Unit testing.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.