# Predicate: `static-scan-verdict` (v1)

```
https://getprobity.dev/predicate/v1/static-scan-verdict
```

The **Tier-1 detection verdict**: the outcome of a STATIC scan of an MCP server / A2A agent —
the scanner connects over the transport and analyses the *declared surface* (protocol/schema
conformance, tool descriptions, annotations, known-bad static patterns) **without booting a
microVM and without executing the server's tools**. It is the cheap, CI-safe, pre-merge rung of
the containment ladder (the Tier-2 [adversarial-execution-evidence](adversarial-execution-evidence.md)
attestation runs the thing in the box; this one never does).

## Why it is a distinct type

`adversarial-execution-evidence` (Tier 2) REQUIRES runtime-only bindings a static scan cannot
honestly fill — the `observationEnvironment` substrate / catchPolicy / networkPosture bindings, the
runtime corpus manifest, `attackResults`, and the drained `interceptRecords`. A pure static scan
boots nothing and drains nothing, so reusing `adversarial-execution-evidence` would force
fabricated/empty substrate bindings — a verifier could not tell a real static scan from a
defenses-off runtime gate. `static-scan-verdict` carries only the fields a static scan genuinely
produces.

## Predicate body

| Field | Req | Meaning |
|-------|-----|---------|
| `verdict` | REQUIRED | `PASS` / `DEGRADED` / `FAIL`. DEGRADED when a layer was `NOT_RUNNABLE`. |
| `tier` | REQUIRED | always `"static"` — the containment-ladder rung this verdict was earned at. |
| `scan_engine` | REQUIRED | the static scanner identity (e.g. `mcp-test-toolkit`). |
| `coverage_qualifier` | REQUIRED | plain-English scope, e.g. "static surface only — no microVM, no runtime execution". |
| `layers` | REQUIRED | array of the static pyramid layers, each `{id, status, is_scorable, detail, findings[], metrics}`; a `finding` is `{check_id, severity, message}`. |
| `coverage_map` | REQUIRED | `{static, runtime, hosting}` booleans — which ladder rungs this verdict covers (`static:true`, the rest `false`). |
| `corpus_digest` | OPTIONAL | SHA-256 of the static payload corpus the scan used (informational; pins the static check set). |
| `scorecard_ref` | OPTIONAL | a reference to the full Tier-2 runtime evidence attestation. |

A producer MAY add fields additively within v1; a verifier MUST ignore unknown fields.

## Binding contract

`static-scan-verdict` is **verdict-bearing** (it is in the admission-eligible predicate-name set) but
carries **NO catch-policy binding** — there is no substrate catch-policy in a static scan. It is
therefore in the verdict-bearing registry but NOT in the catch-policy-bound subset (the verdict
subset that MUST bind a 64-hex catch-policy digest). The verifier accepts a `static-scan-verdict`
whose DSSE signature verifies and whose `predicateType` is registered; it does NOT require any
runtime binding. The optional additive `egress_posture` field, if present, MUST be a known posture
(fail-closed), exactly as the Tier-2 evidence predicate's `networkPosture.posture`.

## Honesty scope

Tier 1 is the BUILT, most-mature pillar (the `mcp-test-toolkit` static pyramid). A
`static-scan-verdict` says only what was visible **without executing the server** — it never claims a
runtime catch. The cross-tier story (a payload that PASSES static here and FAILS runtime in the
Tier-2 gate) is exactly why Tier 2 exists; this verdict is honest about being the static half.

## Related

- [`adversarial-execution-evidence`](adversarial-execution-evidence.md) — the Tier-2 runtime evidence attestation (the box).
- [`compliance-crosswalk`](compliance-crosswalk.md) — how a signed verdict maps to control catalogs.
- [`README`](README.md) — the predicate standard index + the predicate-name registry.
