git:20260211.57a837d to git:20260817.04b2411

51 added, 106 removed. Audit A to A.

---
name: property-based-testing
- description: Provides guidance for property-based testing across multiple languages and smart contracts. Use when writing tests, reviewing code with serialization/validation/parsing patterns, designing features, or when property-based testing would provide stronger coverage than example-based tests.
+ effort: low
+ description: "Writes, reviews, and debugs property-based tests — Hypothesis, fast-check, proptest, jqwik, rapid, and Echidna or Medusa for Solidity invariants. Use whenever tests should cover a whole input domain instead of a hand-picked list of examples: encode/decode and serialize/deserialize pairs, parsers, canonicalizers and normalizers, validators, numeric and Decimal types, comparators and sort order, data structures, and smart-contract state invariants. Also use when adding cases to an existing @given, fast-check, or proptest suite, when judging whether existing property tests assert anything real, and when a generator has shrunk a counterexample and you need to tell a wrong property from a genuine bug. Not for coverage-guided binary fuzzing (libFuzzer, AFL), mutation-testing campaigns, static analysis, benchmarking, or end-to-end UI tests."
---
- # Property-Based Testing Guide
-
- Use this skill proactively during development when you encounter patterns where PBT provides stronger coverage than example-based tests.
-
- ## When to Invoke (Automatic Detection)
-
- **Invoke this skill when you detect:**
-
- - **Serialization pairs**: `encode`/`decode`, `serialize`/`deserialize`, `toJSON`/`fromJSON`, `pack`/`unpack`
- - **Parsers**: URL parsing, config parsing, protocol parsing, string-to-structured-data
- - **Normalization**: `normalize`, `sanitize`, `clean`, `canonicalize`, `format`
- - **Validators**: `is_valid`, `validate`, `check_*` (especially with normalizers)
- - **Data structures**: Custom collections with `add`/`remove`/`get` operations
- - **Mathematical/algorithmic**: Pure functions, sorting, ordering, comparators
- - **Smart contracts**: Solidity/Vyper contracts, token operations, state invariants, access control
-
- **Priority by pattern:**
-
- | Pattern | Property | Priority |
- |---------|----------|----------|
- | encode/decode pair | Roundtrip | HIGH |
- | Pure function | Multiple | HIGH |
- | Validator | Valid after normalize | MEDIUM |
- | Sorting/ordering | Idempotence + ordering | MEDIUM |
- | Normalization | Idempotence | MEDIUM |
- | Builder/factory | Output invariants | LOW |
- | Smart contract | State invariants | HIGH |
-
- ## When NOT to Use
-
- Do NOT use this skill for:
- - Simple CRUD operations without transformation logic
- - One-off scripts or throwaway code
- - Code with side effects that cannot be isolated (network calls, database writes)
- - Tests where specific example cases are sufficient and edge cases are well-understood
- - Integration or end-to-end testing (PBT is best for unit/component testing)
-
- ## Property Catalog (Quick Reference)
-
- | Property | Formula | When to Use |
- |----------|---------|-------------|
- | **Roundtrip** | `decode(encode(x)) == x` | Serialization, conversion pairs |
- | **Idempotence** | `f(f(x)) == f(x)` | Normalization, formatting, sorting |
- | **Invariant** | Property holds before/after | Any transformation |
- | **Commutativity** | `f(a, b) == f(b, a)` | Binary/set operations |
- | **Associativity** | `f(f(a,b), c) == f(a, f(b,c))` | Combining operations |
- | **Identity** | `f(x, identity) == x` | Operations with neutral element |
- | **Inverse** | `f(g(x)) == x` | encrypt/decrypt, compress/decompress |
- | **Oracle** | `new_impl(x) == reference(x)` | Optimization, refactoring |
- | **Easy to Verify** | `is_sorted(sort(x))` | Complex algorithms |
- | **No Exception** | No crash on valid input | Baseline property |
-
- **Strength hierarchy** (weakest to strongest):
- No Exception → Type Preservation → Invariant → Idempotence → Roundtrip
-
- ## Decision Tree
-
- Based on the current task, read the appropriate section:
-
- ```
- TASK: Writing new tests
- → Read [{baseDir}/references/generating.md]({baseDir}/references/generating.md) (test generation patterns and examples)
- → Then [{baseDir}/references/strategies.md]({baseDir}/references/strategies.md) if input generation is complex
-
- TASK: Designing a new feature
- → Read [{baseDir}/references/design.md]({baseDir}/references/design.md) (Property-Driven Development approach)
-
- TASK: Code is difficult to test (mixed I/O, missing inverses)
- → Read [{baseDir}/references/refactoring.md]({baseDir}/references/refactoring.md) (refactoring patterns for testability)
-
- TASK: Reviewing existing PBT tests
- → Read [{baseDir}/references/reviewing.md]({baseDir}/references/reviewing.md) (quality checklist and anti-patterns)
-
- TASK: Test failed, need to interpret
- → Read [{baseDir}/references/interpreting-failures.md]({baseDir}/references/interpreting-failures.md) (failure analysis and bug classification)
-
- TASK: Need library reference
- → Read [{baseDir}/references/libraries.md]({baseDir}/references/libraries.md) (PBT libraries by language, includes smart contract tools)
- ```
+ # Property-Based Testing
- ## How to Suggest PBT
+ An example test asserts one point. A property asserts a rule over the whole input
+ domain and lets the generator hunt for the counterexample. That trade is worth making
+ when the code has an algebraic shape — an inverse, an invariant, an oracle — and not
+ otherwise. Code with no such shape gets example tests; saying so is a valid outcome.
- When you detect a high-value pattern while writing tests, **offer PBT as an option**:
+ Check first whether the shape is missing or merely buried. A calculation wrapped in I/O,
+ a string built by concatenation, an in-place mutation — each has a property and no seam
+ to assert it through. See [references/refactoring.md](references/refactoring.md) before
+ concluding there is nothing to assert.
- > "I notice `encode_message`/`decode_message` is a serialization pair. Property-based testing with a roundtrip property would provide stronger coverage than example tests. Want me to use that approach?"
+ ## Property catalog
- **If codebase already uses a PBT library** (Hypothesis, fast-check, proptest, Echidna), be more direct:
+ | Property | Formula | Where it applies |
+ |---|---|---|
+ | Roundtrip | `decode(encode(x)) == x` | Serialization, conversion pairs |
+ | Inverse | `f(g(x)) == x` | encrypt/decrypt, compress/decompress |
+ | Oracle | `new(x) == reference(x)` | Optimization, refactoring, reimplementation |
+ | Idempotence | `f(f(x)) == f(x)` | Normalization, formatting, sorting |
+ | Invariant | Holds before and after | Any transformation, contract state |
+ | Easy to verify | `is_sorted(sort(x))` | Complex algorithms with cheap checkers |
+ | Commutativity | `f(a, b) == f(b, a)` | Binary and set operations |
+ | Associativity | `f(f(a,b), c) == f(a, f(b,c))` | Combining operations |
+ | Identity | `f(x, e) == x` | Operations with a neutral element |
- > "This codebase uses Hypothesis. I'll write property-based tests for this serialization pair using a roundtrip property."
+ Strength ordering, weakest to strongest:
+ `no crash → type preservation → invariant → idempotence → roundtrip / oracle`.
- **If user declines**, write good example-based tests without further prompting.
+ Assert the strongest property the code supports. "No crash" alone rarely justifies the
+ dependency — if that is all you can find, either a small rearrangement exposes something
+ stronger, or the honest report is that this code is a poor PBT candidate. Rule out the
+ first before settling for the second.
- ## When NOT to Use PBT
+ ## The two ways a property test asserts nothing
- - Simple CRUD without complex validation
- - UI/presentation logic
- - Integration tests requiring complex external setup
- - Prototyping where requirements are fluid
- - User explicitly requests example-based tests only
+ - **Tautology.** `assert add(a, b) == a + b` restates the implementation; no bug they
+ share can fail it. Pick a property that constrains the function without recomputing
+ it. Note the exception: `f(x) == f(x)` is a genuine determinism property when `f`
+ is not obviously pure — serializers over dicts or sets, hashing, anything reading
+ the clock.
+ - **Vacuity.** `assume()` that filters out nearly every input passes without
+ exercising anything, and self-contradictory `assume()` passes having run zero cases.
+ Push constraints into the strategy so the generator produces valid inputs directly.
- ## Red Flags
+ ## Where to look next
- - Recommending trivial getters/setters
- - Missing paired operations (encode without decode)
- - Ignoring type hints (well-typed = easier to test)
- - Overwhelming user with candidates (limit to top 5-10)
- - Being pushy after user declines
+ Load the one that matches the task in front of you:
- ## Rationalizations to Reject
+ | Task | File |
+ |---|---|
+ | Writing new tests, designing strategies | [references/generating.md](references/generating.md) |
+ | The code has no property to assert yet | [references/refactoring.md](references/refactoring.md) |
+ | Reviewing existing property tests | [references/reviewing.md](references/reviewing.md) |
+ | A property test just failed | [references/interpreting-failures.md](references/interpreting-failures.md) |
+ | Library choice, Echidna and Medusa | [references/libraries.md](references/libraries.md) |
- Do not accept these shortcuts:
+ ## Introducing PBT to a project that lacks it
- - **"Example tests are good enough"** - If serialization/parsing/normalization is involved, PBT finds edge cases examples miss
- - **"The function is simple"** - Simple functions with complex input domains (strings, floats, nested structures) benefit most from PBT
- - **"We don't have time"** - PBT tests are often shorter than comprehensive example suites
- - **"It's too hard to write generators"** - Most PBT libraries have excellent built-in strategies; custom generators are rarely needed
- - **"The test failed, so it's a bug"** - Failures require validation; see [interpreting-failures.md]({baseDir}/references/interpreting-failures.md)
- - **"No crash means it works"** - "No exception" is the weakest property; always push for stronger guarantees
+ If the project already uses a PBT library, just write the tests in it. If it does not,
+ adding one is a dependency decision that belongs to the user — offer it once with the
+ specific property you would write, and take the answer either way.