Dummies: cross-engine reachability suite + decimal full-range overflow fix - #274
Merged
Conversation
DecimalIntervalSpec.Generate sampled a bounded draw as mid ± half around the interval midpoint. On the unconstrained domain [decimal.MinValue, decimal.MaxValue] that overflows: the domain is symmetric, so max/2 rounds up and half = max/2 - min/2 doubles to just past decimal.MaxValue, throwing an OverflowException. An unconstrained Any.Decimal().Generate() therefore never returned a value at all — the whole advertised domain was unreachable, a sibling of the #206 upper-half defect. Interpolate as a convex combination instead: min*(1 - fraction) + max*fraction stays within [min, max] for fraction in [0, 1], and no intermediate ever leaves the decimal range. Bounded ranges are unchanged in behaviour (still uniform across [min, max], still nudged off exclusions, still reproducible within a version); only the full-range crash is fixed. Surfaced by the cross-engine reachability suite added for #213, whose full-range scenario exercises every builder's unconstrained domain — the one draw no existing test performed for decimal. Refs: #213 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tsv9tNCWpTENBTQUmpDUgP
The Dummies tests asserted membership (a generated value satisfies its constraints) but never reachability (the whole declared domain is generable). That blind spot let two defects ship green — AnyDecimal never reaching the upper half of a range (#206) and the AnySingle/AnyHalf exclusion nudge stalling on satisfiable specs (#207) — and no test ran the same scenario across every builder, so an engine-level regression could hide in the one type-facade a hand-written test happened not to cover. Add one parameterized suite that runs a shared battery against every interval-backed builder (the discrete integers, the two 128-bit integers, the binary-floating trio, decimal, and the five temporal types) through a small per-type adapter. A new builder is covered by adding one row to AllCases, not a new file. Each builder is a serializable theory row, so it shows and re-runs as an isolated test. Scenarios: an unconstrained draw reaches both halves of its domain; a wide Between reaches both halves; both inclusive bounds are reachable (exactly for discrete types, to within 1% for continuous ones); a point excluded from a narrow range still generates and is never returned; OneOf then Except yields only the survivors; contradictory constraints throw naming both sides. The suite is deterministic: one fixed seed, a fixed draw count large enough that a correct uniform generator reaches the asserted region while a stuck one fails every time — a stable CI guard, not a flaky one. Reintroducing the #206 fraction and the #207 static-ulp step was verified to turn it red (the #207 revert reddens Single/Half but not Double, whose quantize is the identity — the engine defect isolated to the affected facades). The dedicated #206 and #207 regressions are kept as focused, commented guards. Refs: #213 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tsv9tNCWpTENBTQUmpDUgP
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.
Summary
Add a single parameterized reachability suite that runs a shared scenario battery against every interval-backed Dummies builder (#213), and fix the unconstrained
AnyDecimalfull-rangeOverflowExceptionthat suite surfaced. This closes the "membership-tested but reachability-blind" gap the 2026-07-20 audit named (§9.2/§9.3) — the blind spot that let #206 and #207 ship green.Type of change
Changes
Dummies.UnitTests/CrossEngineReachabilityTests.cs: one[Theory]per scenario, run against every interval-backed builder (the 8 discrete integers,Int128/UInt128,Double/Single/Half,Decimal, and the five temporal types) through a per-type adapter — a new builder is one row inAllCases, not a new file. Scenarios: an unconstrained draw reaches both halves of its domain; a wideBetweenreaches both halves; both inclusive bounds are reachable (exactly for discrete types, to within 1% for continuous ones); a point excluded from a narrow range still generates and is never returned;OneOfthenExceptyields only the survivors; contradictory constraints throw naming both sides. Deterministic (one fixed seed, a fixed draw count); each builder is a serializable, individually-runnable theory row.Dummies/DecimalIntervalSpec.cssamples bounded decimals as a convex combinationmin*(1 - fraction) + max*fractioninstead ofmid ± half, which overflowed on the symmetric full domain and made unconstrainedAny.Decimal().Generate()throwOverflowException. Bounded-range behaviour is unchanged (uniform, exclusion-nudged, reproducible within a version).DecimalBetweenReachesBothHalves) and Dummies: AnySingle/AnyHalf exclusion nudge stalls on satisfiable specs #207 (ContinuousExclusionNudgeTests) regressions are kept as focused, commented guards.Scope note: the suite covers the interval-backed builders. The non-interval scalars (
bool,guid,string,enum,char) are excluded — they expose no user-bounded ordered range, so "both halves / both endpoints" is undefined for them. This matches the issue's Direction ("numeric/temporal"); the acceptance criterion's "every scalar builder" was read down accordingly — flagging for confirmation.Testing
dotnet build FirstClassErrors.slndotnet test FirstClassErrors.sln— all projects green;Dummies.UnitTests351/351FirstClassErrors.Analyzers.UnitTests— 132/132)Guard validation: reintroducing the #206 fraction and the #207 static-ulp step was verified to turn the suite red (the #207 revert reddens
Single/Halfbut notDouble, whose quantize is the identity — the engine defect isolated to the affected facades), and reverting the decimal fix reddens the full-rangeDecimalscenario with the originalOverflowException.Documentation
doc/updateddoc/handwritten/for-users/README.fr.md) updated if user-facing behavior changedArchitecture decisions
Related issues
Closes #213
🤖 Generated with Claude Code
https://claude.ai/code/session_01Tsv9tNCWpTENBTQUmpDUgP