Every pull request makes a claim. This cross-examines it.
Quick Start · What It Verifies · Modes · Inputs & Outputs · Verification Context v1.0 · Why a Deterministic Judge · Security
One file. One step. No account required to start.
# .github/workflows/qwed.yml
name: QWED Verification
on: [pull_request]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: QWED-AI/qwed-verification-action@v1
with:
action: scan-secrets
paths: "**/*.env,**/*.json"
output_format: sarif
fail_on_findings: "true"From this commit forward, nothing merges to main on the strength of its own testimony.
Ask a model to calculate compound interest on $100,000 at 5% over ten years, and a fluent, confident, wrong answer comes back — simple interest, dressed as compound. Nothing in the tone gives it away. That's the actual failure mode: fluency and correctness are different claims, and only one of them is checkable.
In our benchmarks, a frontier model held 73% accuracy on financial calculations — number-moving tasks, run through a solver instead of trusted on delivery. QWED caught the remaining errors before they reached anywhere that mattered. On unverified financial output, that gap has priced out as high as $12,889 per transaction.
QWED doesn't try to make the model smarter. It makes the model accountable — every claim gets handed to something that can't guess.
One action, five jurisdictions:
| Mode | Catches | Why It's on Trial |
|---|---|---|
scan-secrets |
Leaked API keys, tokens, SSH keys | Your secrets, in someone else's repo, before you've noticed |
scan-code |
eval(), exec(), subprocess, unsafe imports |
The RCE that was one merge away |
verify-shell |
curl | bash, rm -rf, sudo escapes |
The script that owns the box it runs on |
verify |
Hallucinated math, logic, SQL, code | Output that reads correctly and isn't |
verify-process |
Skipped reasoning steps, missing IRAC structure | The chain-of-thought that jumped straight to the answer |
A linter tells you your code doesn't match the style guide. QWED tells you your code doesn't match reality — using SymPy, Z3, and SQLGlot, the same class of engine used to prove theorems, not to guess at them.
Secret scanning
- uses: QWED-AI/qwed-verification-action@v1
with:
action: scan-secrets
paths: "**/*.env,**/*.json,**/*.py"
fail_on_findings: "true"Code security
- uses: QWED-AI/qwed-verification-action@v1
with:
action: scan-code
paths: "**/*.py"
output_format: sarif # surfaces directly in the GitHub Security tabShell verification
- uses: QWED-AI/qwed-verification-action@v1
with:
action: verify-shell
paths: "**/*.sh"LLM output verification (requires a QWED backend — see note below)
- uses: QWED-AI/qwed-verification-action@v1
with:
action: verify
engine: math
query: "Integral of x^2"
llm_output: "x^3/3"
api_key: ${{ secrets.QWED_API_KEY }} # optional in local modeResult: REJECTED — the integral of x² is x³/3 + C, proven by SymPy, cited in the ruling.
Reasoning process verification (IRAC structure + milestones)
- uses: QWED-AI/qwed-verification-action@v1
with:
action: verify-process
query: "Derivative of x^2"
llm_output: "Issue: differentiate x^2. Rule: power rule. Application: 2*x^(2-1). Conclusion: 2x."
milestones: "issue,rule,application,conclusion"
fail_on_findings: "true"verify-process checks how the model reasoned, not just what it concluded. Every trace must satisfy the IRAC structure (Issue, Rule, Application, Conclusion) and contain each comma-separated entry in milestones (matched case-insensitively). Missing IRAC steps or missed milestones deny admission (admission: DENY) and are reported by name, with a decimal process_rate scoring what fraction of milestones held. Omitting milestones checks IRAC structure only.
Verification Context output (emit VC v1.0 JSON)
- uses: QWED-AI/qwed-verification-action@v1
with:
action: scan-code
paths: "**/*.py"
output_format: verification-context
fail_on_findings: "true"
# outputs: verdict, admission, proof_ref, verification_contextNote on
verifymode: secret scanning, code scanning, and shell verification run entirely inside the runner — no backend needed. Theverifymode (LLM output cross-examination) calls the QWED verification API; pass anapi_key(or run a QWED backend locally) for that mode. Self-hosted deployments can use theapi_urlinput to point to their own backend.
Every QWED verification result is emitted as a Verification Context v1.0 document — a machine-readable, schema-validated protocol with:
| Output | Values | Meaning |
|---|---|---|
verdict |
VERIFIED · UNVERIFIABLE · BLOCKED |
The truth judgment |
admission |
ADMIT · DENY |
Safe to merge? (truth ≠ admission) |
proof_ref |
sha256:<64-hex> or empty |
Cryptographic evidence commitment |
verification_context |
JSON | Full VC v1.0 document (with output_format: verification-context or json) |
verified |
true · false |
Backward-compatible boolean (true only when verdict=VERIFIED and admission=ADMIT) |
Fail-closed guarantees:
UNVERIFIABLEandBLOCKEDalways produceadmission: DENYVERIFIEDrequires a resolvableproof_ref- Schema validation failure fails closed
fail_on_findings: "true"gates onadmission == "ADMIT", not just a boolean
Inputs
| Input | Default | Description |
|---|---|---|
action |
verify |
verify · scan-secrets · scan-code · verify-shell · verify-process |
engine |
math |
math · logic · code · sql · shell (for verify) |
query |
— | The original user query, e.g. "Derivative of x²" |
llm_output |
— | The output being cross-examined |
paths |
. |
Glob patterns to scan, e.g. **/*.py,**/*.env |
output_format |
text |
text · json · sarif · verification-context |
fail_on_findings |
true |
Fail the build on any finding (admission != ADMIT) |
api_key |
— | Optional — local mode requires nothing |
api_url |
https://api.qwedai.com |
QWED API base URL for self-hosted deployments |
mask_pii |
false |
Redact PII in inputs and outputs |
milestones |
— | Comma-separated required process steps for verify-process (matched case-insensitively; missed ones deny and are named) |
Outputs
| Output | Description |
|---|---|
verdict |
VERIFIED · UNVERIFIABLE · BLOCKED (Verification Context v1.0) |
admission |
ADMIT · DENY — gate execution/shipping on admission == "ADMIT" |
proof_ref |
sha256:<64-hex> evidence commitment, or empty when not verified |
verification_context |
Full VC v1.0 JSON document (when output_format: verification-context or json) |
verified |
true if verdict=VERIFIED and admission=ADMIT (backward-compatible) |
explanation |
The proof, or the reason it didn't hold |
findings_count |
Number of issues found |
process_rate |
Fraction of required milestones present, 0.0–1.0 (verify-process mode) |
sarif_file |
Path to the SARIF report |
badge_url |
URL for your QWED verified badge |
| QWED | Most guardrails | |
|---|---|---|
| The judge | A deterministic solver (Z3 / SymPy) | Another model, or an embedding distance |
| Verdict basis | Mathematical proof | Resemblance to a "good" answer |
| Result | VERIFIED with verdict, admission, and proof_ref |
"Looks fine" |
| Latency | Under 100ms for most checks | Variable |
| Data handling | Never leaves the runner (except verify mode) |
Usually a round trip to the cloud |
QWED isn't in competition with the models it checks. It's what lets you ship them.
- Scan modes stay on the runner.
scan-secrets,scan-code,verify-shell, andverify-processexecute entirely inside your CI environment or VPC — no external call, no exception. - Verify mode calls a backend.
verify(LLM output cross-examination) calls the configured QWED API unless you pointapi_urlat a self-hosted or local deployment — so only the query and output under examination traverse that boundary, and only to the backend you chose. - Nothing is learned from. QWED is a deterministic execution engine, not a model. There is no training loop for your data to enter.
- Every passing result includes an evidence commitment. A passing result ships with
verdict=VERIFIED,admission=ADMIT, and aproof_refbinding the ruling to the evidence that produced it. - SARIF native. Findings land directly in the GitHub Security tab — no separate dashboard to check.
- uses: QWED-AI/qwed-verification-action@v1 # tracks latest v1
- uses: QWED-AI/qwed-verification-action@v1.0.0 # pinned, reproducibleThe action's version tags are decoupled from the engine's release train — action fixes ship on their own cadence. v1 is a floating alias: the release workflow moves it to every new v1.x.y, so it always resolves to the latest action release (and whatever engine that release pins). For a fixed build, pin the full version.
| Action | Engine image |
|---|---|
v1 (alias) |
Tracks latest v1.x.y — see rows below |
v1.0.0 |
:latest (unpinned — resolves to the current latest engine) |
main already carries a digest pin (sha256:be5d26f1…, engine 3.2.0); it ships as v1.0.1, which will get its own row here.
- When the engine releases a new version, the action is consciously bumped to reference it (new action minor version, pin updated, matrix row added below).
- Patch action releases (
1.x.y) never change the engine pin. - Minor engine releases that change verification behavior require an action minor bump (
1.x). - The matrix above lists published tags only and is updated in the same PR as every pin change — a pin without a matrix row fails review.
- Verify any row: the full digest is in
action.ymlunderruns.image; Docker Hub tags map it to the engine release.
This action runs on the open-source QWED Protocol — eleven-plus verification engines, agent-security guards, and SDKs for Python, TypeScript, Go, and Rust.
| Resource | Link |
|---|---|
| Core repository | QWED-AI/qwed-verification |
| Verification Context spec | spec/v1.0/verification-context.md |
| QWED Security (GitHub App) | QWED-AI/qwed-security |
| Documentation | docs.qwedai.com |
| Verification course | QWED-AI/qwed-learning |
| Sponsor | github.com/sponsors/QWED-AI |
Found a bypass, or have a new engine in mind? Read CONTRIBUTING.md and SECURITY.md first — most good PRs start as a failed attempt to fool the verifier.
Where an argument ends, and a proof begins.