Problem
pv lint contracts/ reports 0 PV-VER-001 errors today even when contracts cite test names that don't exist in the source tree. The current PV-VER-001 check only validates that the test: field is non-empty and syntactically a cargo invocation; it does NOT verify the cited test function actually exists.
This caused 6 dangling test references across §50.4 cascade contracts that survived multiple contract-bump cycles. Each was caught manually via grep fn <name> audits and fixed in 5 sequential PRs:
Drift classes the current lint MISSES
- Function-name suffix drift:
_init_matches_constructor cited; actual is _init_matches_input. Fragment-grep gives false-negative because both end in _matches_<word>.
- Module-path drift:
transformer::attention::tests::foo cited; actual is in transformer::model::tests::foo (same fn name, wrong module).
- Convention drift:
_encoder_init_errors cited (anticipated name); actual is validate_pretrain_init_arch_rejects_encoder (different convention).
- "Or equivalent" placeholder: contract uses prose-style "or equivalent" instead of a real cargo test invocation.
Proposed fix
Add a new pv lint --strict-test-binding flag that:
- For each
falsification_test entry in every contract:
- Parse the
test: field as a cargo test ... invocation.
- Extract the fully-qualified test name (
<package>::<module>::tests::<fn_name>).
- Search the source tree (configurable, default
crates/) for fn <fn_name> in a #[test] block.
- If not found, report
PV-VER-002: dangling test reference at <contract>:<line> — cited <name> not found in source.
- Default mode: WARNING (so existing CI doesn't break).
- With
--strict: ERROR (so future PR CI catches drift at merge time).
- Document in
apr lint README + feedback_falsifier_first_cascade_pattern.md.
Acceptance criteria
Five Whys
- Why did this drift accumulate? Contracts authored alongside their impl PRs stamped anticipated test names; impl PRs landed with different conventions; no automated cross-check ran at contract-merge time.
- Why didn't
pv lint catch it? The PV-VER-001 check validates test: field shape, not its referent.
- Why does referent verification matter? A contract citing a non-existent test is unfalsifiable — operators reading it think the falsifier is bound when it isn't.
- Why now? Five sequential drift PRs in one day is a smell that the lint is too lax; codifying the strict check prevents recurrence.
- Why a separate flag rather than promoting to default error? Backward compatibility — existing CI runs
pv lint and would break if the new check were ERROR-by-default. Two-stage rollout: (a) add flag, (b) fix all warnings, (c) flip default. Per Toyota Way kaizen.
Related
Stack location
- CLI:
crates/aprender-contracts-cli/src/commands/lint.rs (or wherever the lint command lives)
- Contract: new contract or extend existing
apr-pv-lint-v1.yaml
🤖 Generated with Claude Code
Problem
pv lint contracts/reports 0 PV-VER-001 errors today even when contracts cite test names that don't exist in the source tree. The current PV-VER-001 check only validates that thetest:field is non-empty and syntactically a cargo invocation; it does NOT verify the cited test function actually exists.This caused 6 dangling test references across §50.4 cascade contracts that survived multiple contract-bump cycles. Each was caught manually via
grep fn <name>audits and fixed in 5 sequential PRs:Drift classes the current lint MISSES
_init_matches_constructorcited; actual is_init_matches_input. Fragment-grep gives false-negative because both end in_matches_<word>.transformer::attention::tests::foocited; actual is intransformer::model::tests::foo(same fn name, wrong module)._encoder_init_errorscited (anticipated name); actual isvalidate_pretrain_init_arch_rejects_encoder(different convention).Proposed fix
Add a new
pv lint --strict-test-bindingflag that:falsification_testentry in every contract:test:field as acargo test ...invocation.<package>::<module>::tests::<fn_name>).crates/) forfn <fn_name>in a#[test]block.PV-VER-002: dangling test reference at <contract>:<line> — cited <name> not found in source.--strict: ERROR (so future PR CI catches drift at merge time).apr lintREADME +feedback_falsifier_first_cascade_pattern.md.Acceptance criteria
pv lint --strict-test-binding contracts/flags all known dangling references that PRs contract(apr-pretrain-arch-polymorphic-v1): v1.3 → v1.4 — bind FALSIFY-APR-PRETRAIN-INIT-CUDA-001 + drift-prevention test #1502/contract(apr-pretrain-from-init-v1): v1.1 → v1.2 — test-reference drift correction #1504/contract(apr-pretrain-arch-polymorphic-v1): v1.4 → v1.5 — fix FALSIFY-005/006 test-reference drift #1505/contract+test(apr-cli-tokenize-import-hf-v1): v1.0 → v1.1 — bind FALSIFY-001 with real integration test #1506/contract(apr-pretrain-arch-polymorphic-v1): v1.5 → v1.6 — fix FALSIFY-003/004/007 drift (round 2) #1509 fixed (regression test).apr-pv-lint-v1.yaml(or appropriate contract) bind the new lint behavior.feedback_full_problems_pmat_contracts.md.Five Whys
pv lintcatch it? The PV-VER-001 check validatestest:field shape, not its referent.pv lintand would break if the new check were ERROR-by-default. Two-stage rollout: (a) add flag, (b) fix all warnings, (c) flip default. Per Toyota Way kaizen.Related
feedback_falsifier_first_cascade_pattern.mdfeedback_no_guessing.md(contract claims must be verifiable)Stack location
crates/aprender-contracts-cli/src/commands/lint.rs(or wherever the lint command lives)apr-pv-lint-v1.yaml🤖 Generated with Claude Code