Skip to content

docs(process): require correctness tests to be demonstrated to fail - #418

Open
jamesburton wants to merge 1 commit into
devfrom
issue/417-demonstrate-test-failure
Open

docs(process): require correctness tests to be demonstrated to fail#418
jamesburton wants to merge 1 commit into
devfrom
issue/417-demonstrate-test-failure

Conversation

@jamesburton

Copy link
Copy Markdown
Owner

Closes #417.

What

Adds a Prove the Test Can Fail section to CLAUDE.md, directly after the cross-backend rules.

CLAUDE.md:252 already 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:

Case How it went wrong
#410 pre-tokenizer parity Passed CI, author testing and an advisor review. Forcibly swapping the pipeline on real GGUFs produced byte-identical token ids — the test could not fail when qwen35 was misrouted to qwen2. 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.
#411 Jinja loop vars A reviewer applied 7 mutants; 6 died, 1 survived — behaviour correct in code but guarded by nothing. Became a one-line test.
#372 NemotronH RoPE Survived months behind "logits are finite and non-constant".

The rule

Demonstrate the failure, and record it in the PR body. Two methods:

  • Mutation — deliberate wrong implementation, re-run, confirm failure. Best for logic.
  • Substitution — swap the component for a plausible wrong alternative on real data, confirm output differs. Best for data-dependent paths.

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.

…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
Copilot AI lite review requested due to automatic review settings August 14, 2026 23:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants