feat(justdummies): add JD016-JD017 for collection and enum constraints - #372
Merged
Merged
Conversation
JD016 reports a collection chain whose constant count constraints cannot all hold, or which asks for more distinct elements than its element generator can produce — the cardinality gate ADR-0013 records. Both throw at declaration time, so this moves an arrange-time red to a build-time red rather than closing a silent green; the chain usually sits in a helper several call frames from the test that dies on it. JD017 keeps the enum surface separate, because its domain is metadata — the declared members — rather than interval arithmetic, and its mistake has its own model. Any.Enum draws only declared members, which is deliberate and surprising in one place: on a [Flags] enum, writing a combination in OneOf is the natural gesture and the generator refuses it unless AllowingCombinations() is declared, so the message carries that hint rather than leaving it to be guessed. Twelve behaviours were probed against the built library before either rule was written, and dogfooding then corrected the cardinality helper. AllowingCombinations() WIDENS an enum's universe to the OR-closure of its declared members — eight values for four flags — so counting declared members condemned a chain AnyEnumCombinationTests asserts is legal. The helper now stands down when it sees that constraint rather than computing the closure: an unprovable domain must never be treated as a small one, and a deliberate false negative is the right side to err on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq
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
Continues the
JustDummies.Constraintscategory with the collection and enum surfaces, both built on the helpers #371 landed.Type of change
Changes
CollectionConstraintsAdmitNoValue(Warning) — count constraints that cannot all hold (WithCount(0).NonEmpty(),WithMinCount(5).WithMaxCount(2), moreContainingitems than the cap allows), and the cardinality gate ADR-0013 records: a distinct collection asking for more elements than its element generator can produce.EnumUniverseViolation(Warning) — an enum constraint outside the declared members: a[Flags]combination withoutAllowingCombinations(), an undeclared numeric value, or an exclusion that empties the universe.Both throw at declaration time, so these move an arrange-time red to a build-time red rather than closing a silent green. That is still worth it: the chain usually sits in an arrange helper several call frames away from the test that dies on it, where the message names two constraints the reader must go hunting for.
Why the enum surface is its own rule
Its domain is metadata — the declared members — rather than interval arithmetic, and its mistake has a distinct model: a value outside the declared set is not a narrowing that happens to be empty, it is a category error. The
[Flags]case gets its own hint in the message, becauseAllowingCombinations()is not something a reader would guess from "not a declared member".Dogfooding corrected the cardinality helper
Twelve behaviours were probed against the built library before either rule was written — count contradictions, the
Containingoverflow, theSetOf(Boolean)andDistinct()cardinality gates, the enum universe in all four shapes. All twelve matched the design.Dogfooding then found what the probe had not covered:
AllowingCombinations()widens an enum's universe to the OR-closure of its declared members — eight values for four flags — and my helper walked the element chain to its factory and counted declared members, condemningAny.SetOf(Any.Enum<Permissions>().AllowingCombinations()).WithCount(8), whichAnyEnumCombinationTestsasserts succeeds.The helper now stands down when it sees that constraint rather than computing the closure. Its own doc comment already said an unprovable domain must never be treated as a small one; I had violated that by looking only at the factory. A deliberate false negative is the right side to err on, and two tests now pin both directions — the widened chain stays silent, the un-widened one still reports.
After the fix, JD016 and JD017 produce zero hits across
JustDummies.UnitTests,JustDummies.PropertyTests,JustDummies.Xunit.UnitTests,FirstClassErrors.Testing.UnitTestsandFirstClassErrors.UnitTests.Testing
dotnet build FirstClassErrors.sln— 0 warnings, 0 errorsdotnet test FirstClassErrors.sln— 2 070 tests, 0 failures across all 13 test projectsFirstClassErrors.Analyzers.UnitTests, 132) — the rules added here live inJustDummies.Analyzers.UnitTests(153)Mutation testing was not run locally.
Documentation
doc/updated —JD016/JD017pages and the rule indexJD016.fr.md,JD017.fr.mdand the French indexArchitecture decisions
Proposed: ADR-____JD016 applies ADR-0013's cardinality gate at build time rather than deciding anything about it; JD017 applies ADR-0041's opt-in for flag combinations the same way.
Remaining
Thirteen catalogued rules: the scalar-chain emptiness test, no-effect constraints, duplicate pool values, the empty relative URI, the unused
Combineoperand,Distinctover reference equality, and the reproducibility long tail (nested scopes, committed replay seeds, parallel draws, shared static contexts, blank replay snippets). The two that carry their own decision are still waiting on you — the unsupported-regex rule needs the grammar shared with the library, and the production-code rule needs theIsTestProjectMSBuild oracle.🤖 Generated with Claude Code
https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq
Generated by Claude Code