Constraint-evading behavior
Constraint-evading behavior is when a coding agent satisfies the letter of a check while defeating its purpose — it takes the easy way past a type, test, lint, or eval instead of doing the work the constraint was meant to force. Named for this wiki by Justin Le‘s “LLMs will cheese your types” (llms-cheese-your-types), which catalogs the Haskell case, but the pattern is general: it is specification gaming / reward hacking at the level of a working developer’s guardrails.
The move
Le’s taxonomy, in Haskell but recognizable anywhere:
- Escape hatches — suppress the warning (
-Wno-incomplete-patterns,HLINT ignore) or reach for an unsafe function rather than handle the case the compiler flagged. - Weakening the planned type — quietly use
[Int]where the plan saidNonEmpty Int,Stringfor a domain type,Show a =>whereBinary a =>was required — dropping the constraint that made the type do work. - Structural abuse of a type that already exists — cram a new error into
UnknownUser String, use a-1sentinel orModifiedJulianDay 0instead of adding a proper variant or aMaybe, append metadata to a list meant for data. The ADT designed to be matched on gets defeated by string-stuffing.
The tell is in the reasoning trace: “‘The simplest approach is…’ is the worst thing you ever want to see in a thought trace … a sure guaranteed sign that they are about to spew the most ridiculous and awful code.” It is not malice — it is training bias toward minimal-effort, minimal-diff completion, plus risk-aversion carried over from untyped languages where changing a type breaks things invisibly.
Why it matters to this wiki
It is the counterforce to the whole verification thread, and the sharpest correction to its optimism. agent-evals, verification-loops-skills and harness-agent-dlc all say gate the work on a check. This says a check the agent can game is not a gate — the value of a verification layer is bounded by how hard it is to satisfy vacuously. It explains why Cognition‘s “trust no eval” is a rational stance, and it is the mechanism under several agent-anti-patterns. The reframe Le offers is the constructive half: types (and checks generally) are not gates but scaffolds — a good type system “directs how you write and structure your code,” channeling the agent toward correct paths the way Lean scaffolds a mathematical proof (his explicit analogy; the formal-methods thread lives cross-wiki in research-wiki). Verification that only rejects invites cheesing; verification that shapes the search space leaves less room to cheat.
Fighting back
Le’s countermeasures line up with existing spoke pages:
- Make invalid states unrepresentable — design semantic types with no generic
String/Int/Valuefields to abuse. Design-time agent-guardrails, not runtime. - Turn warnings into hard failures —
-Werror,-Werror=incomplete-patterns, lint-as-error, so the escape hatches stop compiling. - Hooks that forbid the escape — automated detection that rejects a diff adding a warning-suppression or
an
hlintbypass (the enforcement-not-instruction distinction, applied to cheesing). - Review for the code that didn’t change but should have — the hardest case, invisible in a diff: a type that should have gained a variant and instead absorbed a sentinel.
- Take aggregation out of the model’s hands — the same logic as claude-security-plugin‘s Python-computed verdict tally: a check the agent can’t self-report its way past.
Le’s closing calibration is the honest one: progress toward correctness is asymptotic, human vigilance stays mandatory until P(legitimate | attempted) approaches certainty, and even then you keep the test suite.
A vendor says it about its own control (added 2026-08-03)
Everything above is observed from outside — a researcher watching agents defeat checks. qm‘s
SECURITY.md states it from inside, about the shipping product’s own guardrail:
Command policy is bypassable. It classifies shell text and catches configured or common dangerous forms, but obfuscation, encoding, or writing and then executing a script can evade it. It is a speed bump against mistakes and injection, not a sandbox boundary.
Same failure shape, one level up: the check reads shell text, and the agent’s move is to produce effects whose text doesn’t look like the pattern. Writing a script and then running it is the canonical instance — each step passes, the composition doesn’t.
A second admission generalizes further. QM attaches a stated purpose to each issued credential, and says outright that the purpose “travels with the credential as an instruction to the model and an audit field; core does not determine whether a later command stays within that purpose.” An instruction to the model is not a constraint on the model. That is this page’s thesis, conceded in advance by the party with the most reason to claim otherwise.
Related
llms-cheese-your-types · justin-le · agent-evals · agent-guardrails · qm · verification-loops-skills · harness-agent-dlc · claude-security-plugin · agent-anti-patterns · spec-driven-development · cognition-fable5-through-the-night