A Rule Without a Mutation Probe is a Decoration: Why Gates Need Selftest
Engineering teams adopting AI coding agents frequently assemble extensive .cursorrules, AGENTS.md, or custom validator scripts containing dozens of assertions:
- "Never cast with as any"
- "Every finite state machine transition must define a guard"
- "Never edit protected schema files outside contract windows"
This looks rigorous in documentation until you ask the fundamental question:
How do you know that this rule actually works and is capable of failing red?
1. The Phenomenon of Decorative Quality Gates
In real-world repositories, validator rules frequently degrade silently:
- A regular expression contains a subtle syntax error (e.g. unescaped quantifier or missing newline flag).
- The checked AST node was refactored three commits ago and no longer matches.
- The check was never executed against actual negative, broken code.
The result: broken code passes through CI with a cheerful green checkmark. Developers operate under the illusion of safety while their quality gates are purely decorative.
2. The CDAL Doctrine: Every Rule Must Have a Positive Mutation
In CDAL, every invariant rule in a ContractKind and every forbidden pattern must declare an explicit mutation probe:
// packages/kinds/src/state-machine.ts
{
rule: 'STATE-REACHABILITY',
file: 'state-machine.contract.mjs',
find: "from: 'REQUESTED'",
replace: "from: 'UNREACHABLE_ORPHAN'",
}
The cdal selftest command clones contracts and test files into an isolated environment, applies each mutation sequentially, and verifies that the validator fails with specifically that exact rule ID.
If a mutation does not trigger an error, cdal selftest fails the build immediately.
3. Negative and Evasion Probes
The same rigor applies to forbidden patterns. The selftest engine checks:
- Positive probe: Does
@ts-ignorein a production.tsfile get caught? - Negative probe: Does legitimate text mentioning "ignore" get allowed without false alarms?
- Gate evasion probe: Does an agent attempting to split strings (
['@ts', 'ignore'].join('')) get detected?
Only when your gates pass mutation testing can you trust them to protect your repository.