docs(process): require correctness tests to be demonstrated to fail - #418
Open
jamesburton wants to merge 1 commit into
Open
docs(process): require correctness tests to be demonstrated to fail#418jamesburton wants to merge 1 commit into
jamesburton wants to merge 1 commit into
Conversation
…417) Adds a 'Prove the Test Can Fail' section to CLAUDE.md. The existing rule at line 252 requires discriminating tests but gives no way to prove the requirement was met, so discrimination gets asserted rather than shown. Two accepted methods: mutation (deliberate wrong implementation, confirm the test fails) and substitution (swap the component on real data, confirm output differs). Substitution is mandatory for data-dependent tests, because a strong assertion over weak fixture data still cannot fail. Generalizes the per-type negative-control standard already mandated for quantization in #344 so the two do not drift apart. Scoped to correctness gates; smoke tests are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DzekWWxE4d52Hpa31WBYfX
There was a problem hiding this comment.
Pull request overview
Adds a new process rule to CLAUDE.md requiring authors of correctness-invariant tests to demonstrate that the test can fail (via mutation or substitution) and to record that evidence in the PR body. This strengthens the repo’s existing “tests must discriminate” mandate by adding an explicit verification method, helping prevent non-discriminating tests from being merged.
Changes:
- Adds a “Prove the Test Can Fail” section with two accepted demonstration methods (mutation/substitution).
- Explicitly requires substitution for data-dependent tests to guard against weak fixtures.
- Calls out common weak assertions to be skeptical of for correctness gates.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
jamesburton
added a commit
that referenced
this pull request
Aug 15, 2026
…ounds (#395) D3 — hybrid + --cycle died with a stack trace. CudaLayerWindowModel.LoadFromGguf was constructed with `using var` ABOVE the try, so the load-time rejection the feature advertises escaped unhandled. Every MoE / hybrid model takes that path. Model construction moves inside the try. Verified end-to-end: a synthetic Qwen3MoeHybrid GGUF now exits 1 with the one-line "mixture-of-experts FFN routing is not implemented ..." message and no stack trace. D2 — the window containing the last layer uploaded an LM head it never launches. CudaWeights derived isHybrid purely from the layer range, but CudaLayerWindowModel always builds isFinalStage: false because the head runs on the host. A new skipOutputHead flag, driven from isFinalStage rather than the layer range, drops it. Controlled A/B on the same idle baseline (757 MiB), full-trunk window, Llama-3.2-1B-Q8_0: peak 2465 -> 2325 MiB, i.e. 140 MiB. Smaller than a naive vocab x hidden estimate because this model has tied embeddings and Q8_0 dedup, so treat 140 MiB as the LOW end; an untied head is a separate allocation. Existing CudaPipelineTransformerModel behaviour is unchanged (stage 1 is isFinalStage). D4 — the CUDA tests scored random token ids at mean NLL 12.18 against a uniform floor of ln(128256) ~ 11.76, so the figure was pinned near chance and a 1% bound was satisfiable by an almost arbitrarily damaged implementation. Switched to real English through the model's own tokenizer (mean NLL ~3.0) and tightened the bounds from 1%/0.5% to 0.3%/0.2%. Added CycledScoring_DetectsAPerturbedBoundaryCheckpoint per CLAUDE.md / #418. It found two things worth recording. A UNIFORM scale of the boundary is nearly invisible (0.043%, below plain rounding) because the next layer starts with RMSNorm, which is scale-invariant — so the perturbation is element-wise with alternating sign, which rotates the checkpoint's direction instead. And the calibration is now explicit: rounding alone 0.054%, a 1% boundary error 0.123%, a 5% one 0.632%. The 0.2% bound therefore resolves a ~2%+ boundary corruption and NOT a 1% one. That limit is documented on the test rather than papered over — the CPU tests are bit-identical and carry the logic load. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DzekWWxE4d52Hpa31WBYfX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #417.
What
Adds a Prove the Test Can Fail section to
CLAUDE.md, directly after the cross-backend rules.CLAUDE.md:252already requires regression tests that discriminate between broken and fixed forms. It gives no way to prove the requirement was met — so in practice discrimination is asserted, and the assertion has been wrong three times:qwen35was misrouted toqwen2. Neither Qwen vocab contains a single ASCII multi-digit token, so\p{N}vs\p{N}{1,3}was unobservable. The assertion was fine; the fixture data was the weak link.The rule
Demonstrate the failure, and record it in the PR body. Two methods:
Substitution is called out as not optional for data-dependent tests, because #410 is precisely the case a mutation-only rule would have missed: the code was right, the fixture couldn't tell.
Scope and deliberate non-goals
Correctness gates only — tests whose name or issue claims a guarantee. Smoke tests are explicitly unaffected.
Not proposed, on purpose:
Cross-references #344's existing per-type negative-control standard so the two do not drift apart.
Verification
Documentation-only; no code paths touched. The rule's own evidence is the three cases above, each with a linked issue or PR.