# adversarial — Ensures test suites catch intentional sabotage.
The `adversarial` module enforces the theorem that a valid test suite must be capable of rejecting a deliberately broken CPU reference. Passing a correct implementation is insufficient; the gauntlet guarantees that if an adversarial implementor (the "Defender") introduces a plausible, targeted mutation, the test suite (the "Prosecutor") will detect the sabotage and fail.
This module exists to defeat the "tests passed" false-confidence syndrome. Without it, a test suite might achieve 100% code coverage while structurally failing to enforce edge-case invariants. For example, historically, tests for `fnv1a` buffer hashes passed because they only used small inputs; an adversarial implementation hardcoded to fail on buffers >1KB would pass undetected. This module injects known sabotages (like `OffByOne`, `HardcodedZero`, or `DropLsb`) to force the test suite to prove it actually checks boundaries.
```
adversarial/
├── mod.rs — defines the Implementor/Prosecutor/Defender roles
├── gauntlet.rs — evaluates Defendants against a target op's law set
├── defender/ — the Defendant struct and built-in mutation classes
│ ├── mod.rs — defines DefendantClass (HardcodedZero, DropLsb, etc)
│ ├── macros.rs — helpers for quickly instantiating Defendants
│ └── catalogs/ — directory of hand-crafted sabotages for primitives
└── defenders/ — auto-discovered corpus of adversarial CPU functions
```
The adversarial gauntlet runs during the test evaluation phase (CPU only, no wgpu execution) as part of the overall verification suite. It receives a target `OpSpec` with its declared laws and a catalog of `Defendant` objects (deliberately broken functions). It executes the `algebra::checker::verify_laws` logic against the Defendant. If the test suite fails to detect the break, it emits a `GauntletFinding::Escaped`.
To add a new Defendant (a new type of sabotage), define a function in the CPU reference style that is wrong in a specific way. Add it to `defender/catalogs/<target_op>.rs` or create a new file if it's a new op. Use the `defendant!` macro, specifying the name, the `DefenderClass` (e.g., `DefenderClass::SignFlip`), the broken function pointer, and the exact list of law IDs that this sabotage expects to fail. Finally, register the new catalog in `defender/catalogs/mod.rs` so it is picked up by `full_catalog()`.
Failure Modes:
- **Escaped Defendant**: The malicious CPU reference passed the test suite. Cause: The test suite is weak, missing adversarial inputs or boundary checks. Fix: Write stronger properties in the Prosecutor suite that cover the broken behavior.
- **Over-catch (Informational)**: A law failed that was not in the `fails_laws` list. Cause: The sabotage was broader than expected, or laws overlap. Fix: Informational only, consider refining the `fails_laws` expectation or the sabotage precision.
Example:
```rust
use vyre_conform::adversarial::{Defendant, DefendantCatalog, MutationClass};
// Example of defining a Defendant in a catalog
let catalog = DefendantCatalog {
target_op_id: "spec:primitive:bitwise:and".into(),
defendants: vec![
Defendant {
id: "and_hardcoded_zero".into(),
class: MutationClass::HardcodedZero,
func: |args| vec![Value::U32(0)], // Always returns 0
fails_laws: vec!["law:and:identity".into()],
}
],
};
```