Developer guide
Required Outcome checks in CI
Require a signed Outcome in CI while keeping tests, receipt verification, and repository policy separate.
A repository can require a signed Outcome before a pull request merges. Tests still answer whether the code behaved as expected. Spoolis answers whether the work satisfied the agreement the repository cares about. The repository owns its merge policy; Spoolis provides the signed result that policy evaluates.
What problem this solves
Green tests and accepted work are separate facts.
A merge gate can require both without asking either check to stand in for the other.
The check this repository runs
This repository contains a reference workflow named Required Outcome reference. On pull requests and pushes to main, its Require verified Outcome job checks a committed known-good receipt with the repository-local integrations/github-require-outcome action.
The workflow supplies three inputs:
receipt_path | the receipt or receipt glob to check |
trust_path | the pinned receipt trust set |
require | the consumer policy as JSON |
Its current policy requires a passing Outcome with at least two accepted units:
require: '{"minimum_status":"pass","min_accepted_units":2}'The example proves that the action can verify a known receipt and apply repository policy. It does not prove that every pull request represents real outside work.
What the action actually checks
The action reads every matched receipt and the pinned trust entries with no network call: receipt verification first (structure, content-derived ID, Ed25519 signature, trusted key, environment, digests, aggregation rules, unit arithmetic), then requireOutcome() applies the policy.
What policy can require
- a minimum result of
passorpartial - one agreement hash
- one signing key ID, or an allowed list
- a maximum receipt age
- a minimum accepted-unit count
- a minimum earned amount
- fully satisfied evidence requirements
If verification or policy fails, the action exits unsuccessfully with machine-readable reasons such as invalid_signature, below_minimum_status, or insufficient_accepted_units. Every receipt matched by the path must pass.
Use the same policy outside GitHub Actions
requireOutcome() is exported by @spoolis/receipt-verifier 0.1.3 and later. The function returns ok, the receipt ID, result, earned amount, and an ordered list of reasons. Your application decides what to do with that answer.
const required = await requireOutcome(receipt, {
minimum_status: 'pass',
min_accepted_units: 100,
evidence_requirements: 'satisfied',
}, { trustSet })The same consumer-policy layer is available from @spoolis/cli 0.1.5 and later:
spoolis verify outcome-receipt.json --require '{"minimum_status":"pass","evidence_requirements":"satisfied"}'The library and CLI keep the roles separate. Spoolis supplies signed facts. The repository chooses the rule. GitHub decides whether the named check is required for merging.
Turn it into a merge gate
The action produces a status check. A GitHub ruleset is what makes that check a merge condition.
- Add a workflow that runs the action and keep its job name stable.
- Let the workflow run once so GitHub records the check name.
- Add that check to a required status checks rule in the applicable repository or organization ruleset.
- Pin the action code and receipt trust entries through reviewed changes.
The reference README uses the name Require verified Outcome. If you change the job name, the required check name changes too.
This repository calls the action by its local path because the implementation lives here. The reference has not been published to GitHub Marketplace. A consuming repository would need a reviewed, immutable source reference before treating it as a real gate.
What this gate does not prove
This proves that the signed Outcome satisfies the repository's declared policy. It does not independently prove that every underlying fact in the real world is correct.
It does not prove:
- that the underlying evidence perfectly represented the real world
- that a payment, deployment, or merge was authorized
- that settlement happened
- that an offline receipt has not been corrected or revoked later
Offline verification cannot discover forward supersession. A consumer that needs current correction or revocation state needs a separately governed online status check.
That narrowness is the point. The merge gate doesn't replace tests, review, or repository rules. It adds one more checkable question: did the signed Outcome meet the conditions this repository said it requires?
Read the reference action README for the complete configuration and limitations.