Reports in GitHub Actions
The Saidran Action writes a report during a GitHub Actions job. It runs on
Linux and macOS hosted runners. It verifies the release checksum and saves
HTML, Markdown and JSON reports locally. The Markdown appears in the job
summary; the HTML is uploaded as an artifact. It also creates or updates one
github-actions[bot] PR comment marked <!-- saidran -->.
The Action never invokes an agent or model. You can run the binary directly
on Windows, but this Action and the Unix installer do not support Windows runners.
Both the Action and local saidran ci need a repository with at least one
commit. Immediately after git init, review and stage your files and make the
first commit before running the check. An unborn branch exits 2 with guidance
and writes no report.
Diff-only quickstart
Save this as .github/workflows/saidran.yml:
name: Saidran report
on:
pull_request:
permissions:
contents: read
pull-requests: write
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
- uses: vincentsch/[email protected]
with:
fail-on: test_skipped,check_disabled,ci_ignores_failure
Choose a published release, or pin the Saidran reference to its reviewed commit
SHA. The binary version is embedded in that checkout's action.yml: this version
installs v0.1.0 from download/v0.1.0/, including its checksums. A SHA pin or
local uses: ./ uses that same embedded version; neither selects the latest
release or builds the checkout's source. An unavailable pinned release fails
installation without falling back. Development checkouts therefore need the
embedded release to be available, or a matching SAIDRAN_DOWNLOAD_BASE mirror.
The Action's upload dependency is pinned by SHA.
No agent or model credentials are needed for this example.
The default base is the event's PR base SHA. Keep checkout's default merge-ref
checkout and fetch-depth: 0: --base compares directly against that commit,
without inferring a merge base. If you check out the head branch instead, choose
the intended comparison commit explicitly. Saidran never fetches missing bases.
For non-PR events supply an explicit locally available base; the Action skips
comment calls and still saves the summary and artifact.
Inputs are transcript (optional CLI log), base (optional on PRs) and fail-on
(optional comma-separated policy). A policy match returns exit 10 only after
reports are written. The Action applies that status after summary, artifact and
comment handling. A report error stops publication and cannot reuse old reports:
every run gets a fresh output directory. Unexpected comment errors fail the Action
after summary and artifact preservation. A forbidden comment write (HTTP 403)
produces a warning; it does not erase a report or override a policy failure.
The token needs pull-requests: write to publish the comment. Standard
pull_request fork runs normally have read-only tokens; the report remains in the
summary and artifact when GitHub forbids the comment. Do not use
pull_request_target to execute fork code with a write token. Organization policy
can further restrict permissions. Hosted Unix runners provide Bash, Python 3,
Git, curl and GitHub CLI, which the Action uses.
Include a log from the same checkout
A transcript must come from a supported agent CLI run earlier in the same job,
inside this checkout. This example replaces the quickstart's steps. Install
Claude CLI and set up its authentication before running it. Give it the
permissions it needs for the task. ANTHROPIC_API_KEY below is a caller-managed repository
secret; it is not needed by Saidran. Pick the model and effort explicitly.
name: Saidran with a CLI log
on:
workflow_dispatch:
inputs:
base:
description: Available comparison commit
required: true
permissions:
contents: read
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
# The caller installs and authenticates Claude CLI before this step.
- name: Run Claude CLI
id: agent
shell: bash
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CONFIG_DIR: ${{ runner.temp }}/claude-config
run: |
set -euo pipefail
mkdir -p "$CLAUDE_CONFIG_DIR"
claude -p --model claude-sonnet-5-5 --effort medium --output-format json \
'Review the checkout and run its relevant checks. Do not publish anything.' \
> "$RUNNER_TEMP/claude-result.json"
python3 - <<'PY'
import json, os, pathlib
result = json.loads(pathlib.Path(os.environ['RUNNER_TEMP'], 'claude-result.json').read_text())
session_id = result['session_id']
root = pathlib.Path(os.environ['CLAUDE_CONFIG_DIR'], 'projects')
matches = list(root.glob('*/' + session_id + '.jsonl'))
if len(matches) != 1:
raise SystemExit('Expected exactly one matching CLI JSONL recording')
with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
output.write('transcript=' + str(matches[0]) + '\n')
PY
- uses: vincentsch/[email protected]
with:
transcript: ${{ steps.agent.outputs.transcript }}
base: ${{ inputs.base }}
fail-on: contradicted,stale
Saidran accepts Claude Code, Codex, OpenCode and Pi CLI recordings, not arbitrary
agent output. The Claude Code GitHub Action's execution_file is a JSON array of
SDK messages and is unsupported in this release; it is not the CLI JSONL log.
Do not upload or pass a developer-machine transcript: recorded working directories
must belong to this checkout, with existing symlinks resolved. A foreign transcript
exits 5; broken or unsupported recordings fail instead of becoming diff-only reports.
OpenCode takes storage/session/<project>/<id>.json and reads its sibling
storage/message/<session>/*.json and storage/part/<message>/*.json trees.
Preserve that complete layout when passing the session entry point.
Run the command locally
saidran ci
saidran ci --base HEAD --out reports --fail-on test_skipped
saidran ci --transcript /path/to/checkout-local-session.jsonl --json
Without --out, both modes replace report.html, report.md and report.json
under this worktree's absolute Git directory at saidran/ci/. Explicit --out
writes directly into that directory. --dry-run creates no output paths. Use --json or --format text|json|markdown|html to choose what prints to stdout.
Without a transcript no session discovery happens: JSON has session: null and
empty claims. Human views say “Diff-only report” and explain the missing log.
Changed files, findings, dismissals and evidence limits remain visible. No checks
are verified by a diff-only report. Reports summarize editable local evidence;
they do not certify correctness. Existing --fail-on values and dismissal rules
apply. Exit codes match the command guide. A successful report
without a policy match exits 0. A policy match exits 10 after saving the reports.