Skip to content

Repository files navigation

RunReplay inspects a failed GitHub Actions job and returns its commit SHA, runner, failed step, and artifacts.

CI status Latest release Apache-2.0 license

Inspect any GitHub Actions job from your terminal.
Turn a job URL into the facts a maintainer needs before attempting a fix.

Quick start · What you get · Compare · JSON output · First contributions · Impact · Roadmap · Contributing

Start with the failed job

npx runreplay inspect https://github.com/OWNER/REPO/actions/runs/RUN_ID/job/JOB_ID

# Resolve the workflow source and Action revisions for the same historical job
npx runreplay resolve https://github.com/OWNER/REPO/actions/runs/RUN_ID/job/JOB_ID

# Compare a failed job with the last strictly comparable successful job
npx runreplay compare https://github.com/OWNER/REPO/actions/runs/RUN_ID/job/JOB_ID --baseline last-successful

npx downloads the public CLI when needed; no clone or global install is required. RunReplay works without a token for public repositories, subject to GitHub's anonymous API limits. For private repositories, set a fine-grained GITHUB_TOKEN with read access to Actions and Contents:

GITHUB_TOKEN=github_pat_... npx runreplay inspect <job-url>

Do not paste tokens into an issue, shared shell history, or CI log.

For GitHub Enterprise Server, pass the browser job URL and explicitly select the REST API base. RunReplay does not infer API hosts from job URLs:

GITHUB_TOKEN=github_pat_... npx runreplay inspect \
  https://ghe.example.com/OWNER/REPO/actions/runs/RUN_ID/job/JOB_ID \
  --api-base https://ghe.example.com/api/v3

Use an Enterprise token with read access to Actions and Contents for the target repository. --api-base must be an HTTPS API base URL and must not include credentials, query strings, or fragments.

When GitHub rejects a request, RunReplay keeps the original status code and safe API message, then adds a short next step for common cases: invalid tokens, missing Actions or Contents read access, rate limits, missing jobs, and expired logs or artifacts.

To install it once instead:

npm install --global runreplay
runreplay inspect <job-url>

See the inspection

This is a real inspection of a public failed GitHub Actions job. RunReplay identifies the commit, hosted runner, failed step, and attached artifacts before anyone proposes a patch.

RunReplay inspecting a public failed GitHub Actions job; it reports a failed Dependabot step and no artifacts.

What you get

Evidence Why it matters
Commit SHA and branch Anchor the investigation to the code that actually ran.
Workflow event and job conclusion Separate a failing job from a broader workflow summary.
Runner labels and timing Show the available execution context GitHub exposes.
Per-step results Point directly to the failed command stage.
Logs API URL and artifacts Preserve the available trail for deeper investigation.

Machine-readable output

Use --json when the inspection feeds Claude, jq, a CI bot, or another tool:

node dist/cli.js inspect <job-url> --json > inspection.json

The public schema is versioned from day one:

{
  "schemaVersion": "1.0",
  "repository": "actions/checkout",
  "runId": 123,
  "jobId": 456,
  "commitSha": "",
  "event": "push",
  "runner": { "labels": ["ubuntu-latest"] },
  "steps": [],
  "artifacts": [
    {
      "id": 7,
      "name": "test-results",
      "sizeInBytes": 42,
      "expired": false,
      "availability": "available",
      "createdAt": "2026-07-26T10:02:00Z",
      "updatedAt": "2026-07-26T10:03:00Z",
      "expiresAt": "2026-08-25T10:02:00Z",
      "digest": "sha256:…",
      "workflowRun": {
        "id": 123,
        "repositoryId": 99,
        "headRepositoryId": 100,
        "headBranch": "main",
        "headSha": ""
      },
      "apiUrl": "https://api.github.com/repos/actions/checkout/actions/artifacts/7",
      "archiveDownloadUrl": "https://api.github.com/artifacts/7/zip"
    }
  ],
  "redactions": []
}

Artifact metadata is additive within schema 1.0: older consumers can ignore the extra fields, and missing GitHub fields are emitted as null. availability is derived from GitHub's expired flag so human and JSON output use the same state.

Resolve manifest: what actually ran

inspect tells you what GitHub still exposes about a job. resolve adds the workflow source from the commit that ran and identifies the revision behind each supported uses: declaration:

npx runreplay resolve <job-url> --json > manifest.json

RunReplay never resolves a mutable tag today and calls it historical truth. Every Action record declares its evidence level:

Evidence Meaning
runtime-log GitHub Runner recorded the exact SHA downloaded by this job. This is the strongest historical evidence.
declared-full-sha The workflow declared an immutable 40-character SHA.
github-api-current-ref GitHub resolved a mutable branch or tag now. It is useful context, not proof of the old run.
unresolved RunReplay lacks trustworthy evidence and says why instead of guessing.

Version 0.2 supports repository Actions, including actions declared from repository subdirectories. It explicitly reports local Actions, Docker Actions, dynamic expressions, and reusable workflows as unresolved where their execution cannot yet be proven.

Compare a failure with its baseline

compare turns two historical jobs into a CI diff. It reports changes in workflow source, declared and historically observed Action revisions, runner labels, steps, artifacts, timing, commits, and changed files.

# Explicit baseline: first URL is the failed/target job, second is its baseline
npx runreplay compare <failed-job-url> <baseline-job-url>

# Automatic baseline: only an exact earlier successful match is accepted
npx runreplay compare <failed-job-url> --baseline last-successful

# Stable machine-readable report
npx runreplay compare <failed-job-url> --baseline last-successful --json > comparison.json

For last-successful, RunReplay requires the same repository workflow, job name, event, branch, and runner labels. If no such completed successful job exists, it returns:

{
  "schemaVersion": "1.0",
  "baseline": null,
  "reason": "no-comparable-successful-job"
}

RunReplay reads successful workflow runs page by page, up to 1,000 runs. If that limit is reached before an exact match is found, it reports baseline-search-limit-reached and searchedRuns: 1000 rather than claiming that no baseline exists.

changedInputs is deliberately descriptive, not an AI diagnosis: a changed Action SHA or runner image is an investigation lead, not proof of the failure's cause.

Public investigations

RunReplay is used on public CI failures before a fix is proposed. The first documented investigation is actalog case 001: a timezone-dependent Vue test, diagnosed from a failed-vs-successful CI comparison and fixed in an external PR.

RunReplay comparing a failed GitHub Actions job to its successful baseline and identifying a timezone-dependent frontend test.

The current evidence counters live in IMPACT.md. They deliberately count only public, verifiable outcomes.

Scope: facts first, replay later

RunReplay is an inspector, not a VM time machine. It does not claim to restore a completed runner's filesystem, caches, secrets, service-container state, or other data GitHub did not retain.

The resolve manifest finds the workflow source at the inspected commit and identifies supported Action revisions with explicit evidence. Matrix expansion, reusable workflow traversal, nested/composite Actions, and a best-effort local replay path remain future work. Every replay claim must be backed by explicit evidence and verification. See the roadmap.

Architecture

job URL
  │
  ├── URL parser ── validates owner, repository, run, and job IDs
  ├── GitHub API ── retrieves job, workflow run, and artifact metadata
  ├── inspection ── formats evidence for humans or the versioned JSON schema
  └── resolve manifest ── historical workflow source + Action SHA evidence
      └── compare ── strict baseline matching + factual CI diff

First contributions available

These small, unclaimed issues are a good way to get started. See CONTRIBUTING.md for the full contribution workflow; maintainers review focused pull requests promptly.

Issue Typical size
#12: malformed GitHub Actions job URL tests One test file
#13: artifact formatting edge-case tests Two test files
#14: expired-log (410) fixture scenario Fixture plus integration test
#15: rate-limit fixture scenarios Fixture plus integration test
#19: first-time contributor walkthrough One short documentation section

Before opening a pull request, run:

npm ci
npm test
npm run check
git diff --check

Contribute

The first public contribution paths are intentionally small and useful:

Read CONTRIBUTING.md, run the checks, and keep each pull request focused.

git clone https://github.com/shleder/runreplay.git
cd runreplay
npm install
npm test
npm run check

License

Apache-2.0

About

Inspect any GitHub Actions job from your terminal

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages