Skip to content

Documentation · Task

Submit CI evidence

Send the report your pipeline already produces, then gate on the decision for that exact build. The gate exits zero only for CLEARED.

Before you start

Marketplace V1 — publicly listed

  • A site administrator has enabled App REST APIs for TestWarden and access for your CI is provisioned. For guided integration setup, we can help with these permissions; see Onboarding.
  • From that setup you have the API base URL shown in Atlassian Administration → Connected apps and a short-lived access token for the custom scope write:warden-evidence:custom. Keep both in your pipeline's secret store.
  • Node.js 24 on the runner. The gate is the script scripts/warden-gate.mjs, supplied with your onboarding.
Only CI holds the token. Coding agents never do. The provider type in the payload is caller-asserted, so token custody is what establishes who produced the evidence.

1. Produce a report

The Warden accepts three formats. Each submission must name the exact repository, commit, build and environment it verifies.

FormatProvider typeUse for
JUnit XML (--format junit)ciThe complete automated test run for the build. Satisfies the default test obligation.
SARIF 2.1.0 (--format sarif)scannerStatic-analysis and security scans. Recorded as evidence; a scanner pass alone cannot satisfy the test obligation.
Provider-neutral JSON (--format json, the default)ci, agent, scanner, code_reviewer, visual_agentYour own producers. 1 to 20 observations, each passed, failed, error or skipped. Agent evidence cannot satisfy the CI-run requirement.

Compatible reports, not vendor integrations

This also applies to test-management systems such as AIO Tests, Xray, Zephyr Scale, TestRail and Azure DevOps: today, what reaches The Warden is a complete JUnit XML report that your CI produces. Read-only connectors for those platforms are in source and under acceptance, and none is live. Status is on Integrations.

Playwright, Cypress and Semgrep are not first-party integrations. They work because they can write JUnit or SARIF. The same is true of any tool that emits those formats. A tool's own green result is evidence; it is never CLEARED.

Bash
# Playwright -> JUnit
PLAYWRIGHT_JUNIT_OUTPUT_NAME=test-results/playwright.xml npx playwright test --reporter=junit

# Cypress -> JUnit (one file per spec; merge into one complete report)
npx cypress run --reporter junit --reporter-options "mochaFile=cypress/results/results-[hash].xml"

# Semgrep -> SARIF
semgrep scan --config auto --sarif --output semgrep.sarif

A multi-spec Cypress run writes several files. Merge them into one complete JUnit report before submitting. Submitting only the last spec and calling it the complete run would hide failures.

Tool-specific flags other than the three above are not documented here; check your runner's own reporter options for a JUnit or SARIF writer.

2. Configure the gate

Provide settings through secure pipeline variables. Never place the token in command arguments, source control or logs; the gate redacts it from its own output.

Bash
# Always required
export WARDEN_ENDPOINT="<API base URL from Connected apps>/warden/evidence"
export WARDEN_TOKEN="<short-lived access token from your secret store>"
export WARDEN_ENVIRONMENT="development"

# Required outside GitHub Actions (derived automatically inside it)
export WARDEN_REPOSITORY="acme/checkout-service"
export WARDEN_COMMIT="<40-character commit SHA>"
export WARDEN_BUILD="<immutable build identity>"
export WARDEN_RUN_ID="<unique run attempt>"

On GitHub Actions the repository, branch, commit, pull request, run, attempt and build are derived for you. Other systems set WARDEN_REPOSITORY, WARDEN_COMMIT, WARDEN_BUILD and WARDEN_RUN_ID. Optional: WARDEN_OBSERVED_AT (when the tests finished; defaults to now), WARDEN_PROVIDER, WARDEN_PROVIDER_VERSION, WARDEN_SOURCE_SYSTEM, WARDEN_BRANCH, WARDEN_PULL_REQUEST, WARDEN_CORRELATION_ID, WARDEN_FORMAT and WARDEN_EVIDENCE_FILE.

The endpoint must be HTTPS. Evidence is bound to the commit, build and environment you name, so use values that identify the build uniquely.

3. Run the gate

Bash
node scripts/warden-gate.mjs --file test-results/playwright.xml --format junit --json
echo "exit code: $?"

Flags: --file (the report), --format (json, junit or sarif), --json (one JSON line on stdout instead of the compact text) and --endpoint (overrides WARDEN_ENDPOINT). Make the step fail the job on a non-zero exit; do not set it to continue on error.

4. Read the result

The gate exits 0 only when the response is a structurally valid CLEARED decision that matches the exact repository, commit, build and environment you submitted. Every other outcome exits 1: held, verifying, incomplete, needs approval, unknown, malformed or unauthenticated.

CLEARED (compact output)
The Warden: CLEARED

exit code: 0
HELD (compact output)
The Warden: HELD
resolve_failing_evidence
exit code: 1

With --json the output carries state, progressionAllowed, changeSetId, qualityDecisionId, reasonCodes, nextActions, obligations, verification counts and a local submission record. See the full HELD example in held-response.json and the meaning of each code in Decisions and approvals.

A failing JUnit report holds the change with evidence_failed and the next action resolve_failing_evidence. The decision in Jira names up to 10 failing tests in report order. Fix the cause and let CI submit evidence for the new build. The earlier HELD decision stays in history; fresh passing evidence produces a new decision.

Evidence for an older build than the current target returns evidence_target_superseded and can never clear the change. Re-running only the gate for an earlier attempt resubmits that attempt unchanged, so always re-run the whole job for a fresh build.

Announce a run (optional)

A CI producer can tell The Warden that a run has started. While the run is open and the only gap is missing evidence for that exact build, the change reads VERIFYING with verification_run_in_progress. If the window lapses without evidence, it reads HELD with verification_run_overdue and the next action investigate_overdue_verification_run.

Bash
export WARDEN_STARTED_AT="2026-10-18T09:20:00Z"   # when the run began
export WARDEN_EXPECTED_MINUTES="30"                # 1 to 360, default 60
node scripts/warden-gate.mjs --announce --json

An announcement is never evidence and never clears anything. The command exits 0 when the announcement is accepted and its output never carries progressionAllowed, so do not use it as a gate. Only ci providers can announce. Use the same run, build and environment identity as the evidence that follows.

GitHub Actions

Developer preview — live acceptance pending

The reusable composite action warden-verify wraps the same gate. It announces the run (without failing the job if the announcement is refused), submits the report, writes a job summary with the obligations to resolve, and keeps a single sticky pull-request comment current. Inputs: endpoint, evidence-file, access-token (required), environment (default development), format (default junit), expected-minutes (default 15), pr-comment (default true) and check-run (default false; needs checks: write). The comment needs issues: write.

Workflow step
- name: Submit evidence to The Warden
  uses: ./.github/actions/warden-verify
  with:
    endpoint: ${{ vars.WARDEN_ENDPOINT }}
    environment: development
    evidence-file: test-results/junit.xml
    format: junit
    access-token: ${{ secrets.WARDEN_TOKEN }}

The action package is delivered during pilot onboarding along with the endpoint and access. Download the complete workflow: github-workflow.yml.

Limits and retries

  • Each request is limited to 256 KiB. JSON envelopes carry 1 to 20 observations; metadata is limited to 8 KiB.
  • A JUnit report whose complete XML exceeds 256 KiB is sent losslessly as a gzip-base64 object, verified by SHA-256 and byte length, up to 2 MiB of raw XML. Larger or incompressible reports stay INCOMPLETE with evidence_too_large.
  • JUnit XML must not contain DOCTYPE or ENTITY declarations. SARIF must be version 2.1.0 with an explicit successful invocation (executionSuccessful: true); every finding holds the scan unless its kind is pass, informational or notApplicable.
  • Identifiers beginning with testwarden: are reserved for server-generated receipts and are rejected from external observations.
  • The gate retries only transient outcomes (network failures, interrupted responses, HTTP 408, 409, 425, 429 and 5xx) with jittered backoff, honouring Retry-After, for at most 6 attempts within 150 seconds. Rejected, unauthenticated, oversized and superseded submissions fail immediately.
  • Exact retries are idempotent and keep the original observation time and run identity. Changed evidence is stored as a new receipt; conflicting evidence is never overwritten.

Troubleshooting

Gate errorMeaning and fix
authentication_failedHTTP 401 or 403. The token is missing, expired or lacks write:warden-evidence:custom, App REST APIs are not enabled, or the caller is not a site administrator. Fix access; do not retry.
evidence_rejectedHTTP 400. Check exact repository, commit (40 hex characters), build and environment; format and provider type pairing; 1 to 20 observations; no caller-supplied state, progression flag or approval fields.
evidence_too_largeThe report exceeds the limits above. Split or reduce the report at its source.
evidence_target_supersededA newer build is already the target for this change. Re-run the whole job for the current build.
decision_target_mismatchThe response described a different repository, commit, build or environment than you submitted. The gate refuses to pass. Check your WARDEN_* identity values.
service_unreachable, service_http_5xxTransient. The gate already retried; re-run the job.
invalid_or_missing_evidence_configurationA required WARDEN_* value or the report file is missing, or the file could not be read.
HELD after a fixThe fix has not produced fresh evidence for a new build. Submit evidence for the new build.

Still stuck? Email support with the qualityDecisionId from the gate output; see Support.

Download fixtures

All fixtures use the fictional repository acme/checkout-service and contain no real identifiers. Replace the repository, commit, build and timestamps with your own; observedAt cannot be in the future and evidence older than 24 hours is stale under the default contract.

Related tasks