Characterization
- Pins observed output
- Misses a guard that never jumped
Topic · Characterization testing
Proof treats the old tree as source and the rewrite as target. The audit fails if the new tree dropped a test, an MC/DC row, or a known-issue reproducer the old tree still had.
proof audit --check mirror_complete
Characterization tests pin observed output. They do not notice a guard that never made the jump. A thinner suite on the new code still goes green.
01 · What characterization testing is
Michael Feathers called it characterization testing: write tests against the behavior you observe, not the behavior you wish you had, so a rewrite cannot quietly drift. Golden-master testing is the same idea with captured output. ISO 26262 back-to-back testing runs the same inputs through a reference (the old version, or a model) and the new implementation, and compares the results.
That oracle is real work. It catches an implementation that no longer matches the old bytes. It does not catch the other failure of a port: the feature looks done, the new tests pass, and the malformed-input rejection, the MC/DC row, or the Reproduces: KI-x guard never made the jump. Nothing fails, because the new code is graded against its own thinner suite. The loss is invisible until the old bug comes back.
Proof's answer is not another output dump. It is a mirror: a contract that the target file-set must reproduce the verification ledger of the source, for every requirement in scope. Coverage parity, keyed by cell, not by test name.
02 · The exhibit
Mistral OCR, LiteLLM. Source is the authority. The campaign splits what the target inherited, what it introduced, what diverged, and what it already fixed. A cell the target dropped still fails the mirror.
Characterization
The ledger
MC/DC row and KI reproducer can vanish.
Invisible lossGolden master
A thinner suite on the new tree still goes green.
Still greenProof
Same rewrite. Two oracles. Click the tabs.
03 · The command
Source is the authority. Target must be a superset on each enabled dimension. Extra target cells never fail. Status in_progress tracks gaps as warnings. Status complete turns remaining gaps into errors that gate the audit.
proof mirror add --id MIR-001 \ --source "litellm/llms/mistral/ocr/**" \ --target "litellm-rust/crates/core/src/providers/mistral/ocr/**" \ --status in_progress proof audit --check mirror_complete proof mirror status MIR-001
A Python test and a Rust test count as the same cell if they witness the same requirement and discriminant. The names can change. The cell cannot vanish.
04 · The honest loss
Mirror proves the refactor reproduced the same verification obligations and coverage: no dropped obligation-evidence cell, no dropped regression guard, no dropped MC/DC row. It does not, by itself, prove the two implementations produce identical output. Both sides can carry a malformed_input:negative cell and still reject different inputs, because each test was written against its own implementation.
Characterization tests have the opposite limit. They pin the old behavior, including bugs you meant to drop. A golden master that records a wrong answer will fail the rewrite that finally corrects it. You still need a requirement to say which behaviors were intended and which were incidental. A mirror of an under-covered source is meaningless; it only copies the holes.
True input-output agreement is a different tool (proof differential fuzz on a declared transform). It is not what mirror_complete checks, and it is not a characterization suite for a whole legacy system. If the rewrite has no approved requirements yet, recover them and have the owners sign them before you treat either ledger as the bar. The traceability matrix is the other direction of the same problem: links that are asserted once and never re-read.