Skip to content

feat(justdummies): add JD023-JD024 for scalar chain satisfiability - #375

Merged
Reefact merged 1 commit into
mainfrom
claude/justdummies-analyzers-analysis-z9q1xd
Jul 29, 2026
Merged

feat(justdummies): add JD023-JD024 for scalar chain satisfiability#375
Reefact merged 1 commit into
mainfrom
claude/justdummies-analyzers-analysis-z9q1xd

Conversation

@Reefact

@Reefact Reefact commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Two rules sharing one walk over a ScalarConstraintState, because they read the same state from opposite sides: JD023 asks whether anything remains, JD024 whether anything changed.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Analyzer / diagnostic change
  • Tests
  • Documentation
  • Build / CI / tooling

Changes

  • JD023 ScalarChainAdmitsNoValue (Warning) — an integer chain narrowed to nothing: an empty interval, an empty lattice (Between(1, 10).MultipleOf(20)), an emptied allow-list (OneOf(5).Except(5)), or a finite range emptied by its exclusions (Zero().NonZero()).
  • JD024 ConstraintWithNoEffect (Info) — a constraint that narrows nothing: an exclusion of a value the domain could never produce, or a bound already implied.
  • ScalarConstraintState — the shared domain model. 26 new analyzer tests (198 total), 4 documentation pages (EN + FR), both index tables, release-tracking rows.

JD024 is the only silent one

Every other contradiction in the constraint family throws eventually and loudly. An inert constraint leaves the test green while it exercises a domain the author did not write. The dangerous shape is an exclusion of a sentinel the generator could never draw: it misses silently, and starts mattering the day someone widens the range, at which point the sentinel begins appearing and a distant test starts flaking.

Info rather than Warning, because a defensive exclusion kept so the intent survives a future widening is a real and reasonable style. The rule states the fact without insisting it is a defect.

Dogfooding found a genuine bug in the model

Not a misread of the library this time — a bug in my arithmetic. Any.Int64().LessThanOrEqualTo(long.MinValue) is legal and yields exactly that value, yet the first version declared it empty: the state used -long.MaxValue as its "unbounded" sentinel to keep Maximum - Minimum from overflowing, which made long.MinValue unrepresentable and condemned a chain AnySignedIntegerTests asserts is legal.

The bounds now run to the true extremes, and a bound genuinely beyond the range — GreaterThan(long.MaxValue) — is carried by an explicit saturation flag rather than by arithmetic that would overflow. Four tests pin the extremes in both directions.

This was also the first wave run under the corrected dogfooding protocol from #373: the Info rule was escalated to warning before the sweep, not after. JD024 would have been invisible otherwise.

Zero hits across JustDummies.UnitTests, JustDummies.PropertyTests, JustDummies.Xunit.UnitTests, FirstClassErrors.Testing.UnitTests and FirstClassErrors.UnitTests.

Scope, deliberately narrow

Integer generators only, one expression, every argument folded. A constraint the model has never seen ends the walk rather than being guessed at — a rule claiming a chain is unsatisfiable has to be certain.

Testing

  • dotnet build FirstClassErrors.sln — 0 warnings, 0 errors
  • dotnet test FirstClassErrors.sln — 2 115 tests, 0 failures across all 13 test projects
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests, 132) — the rules added here live in JustDummies.Analyzers.UnitTests (198)

Mutation testing was not run locally.

Documentation

  • Public API / error documentation updated — no public API change
  • README / doc/ updated — JD023/JD024 pages and the rule index
  • French translation updated — JD023.fr.md, JD024.fr.md and the French index
  • No documentation change required

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR drafted as Proposed: ADR-____
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

One deviation worth your eye. My original study recommended rejecting a rule for argument-free structural pairs (Positive() + Negative(), Empty() + NonEmpty()), on the grounds that ADR-0035 assigns structural illegality to the type system. JD023 reports Positive().Negative() anyway — not as a special case, but because it falls out of the same emptiness computation as everything else.

I think that is right: the typed progression ADR-0035 would prescribe is not warranted for a numeric domain (it does not split into fixed shapes), so the choice is analyzer or nothing. But JD016 already did the same for Empty().NonEmpty() without my flagging it, and I would rather say so than let the pattern spread silently. If you read ADR-0035 as forbidding it, both are one if away from being excluded.

Remaining

Four rules I can build alone — duplicate pool values, the empty relative URI, the unused Combine operand, Distinct over reference equality — which will follow as the Composition wave. Then the two that need your decision: the unsupported-regex rule (grammar shared with the library) and the production-code rule (IsTestProject oracle).

🤖 Generated with Claude Code

https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq


Generated by Claude Code

The two rules share one walk over a ScalarConstraintState because they
read the same state from opposite sides: JD023 asks whether anything
remains, JD024 whether anything changed.

JD023 reports an integer chain narrowed to nothing — an empty interval,
an empty lattice, an emptied allow-list. The library computes this with
one emptiness test; the rule runs the same test over the constants
written at the call site and stays silent for every argument it cannot
fold.

JD024 is the only member of the constraint family the run time NEVER
reports. Every other contradiction throws eventually and loudly, while an
inert constraint leaves the test green exercising a domain the author did
not write. The dangerous case is an exclusion of a sentinel the generator
could never draw: it misses silently, and starts mattering the day
someone widens the range. Info rather than Warning, because a defensive
exclusion kept so the intent survives a future widening is a real and
reasonable style.

Dogfooding caught a genuine bug in the model, not a misread of the
library. Any.Int64().LessThanOrEqualTo(long.MinValue) is legal and yields
exactly that value, yet the first version declared it empty: the state
used -long.MaxValue as its "unbounded" sentinel, which made long.MinValue
unrepresentable. The bounds now run to the true extremes, and a bound
genuinely beyond the range — GreaterThan(long.MaxValue) — is carried by
an explicit saturation flag rather than by arithmetic that would
overflow. Four tests pin the extremes in both directions.

Scope is deliberately narrow: integer generators only, one expression,
every argument folded. A constraint the model has never seen ends the
walk rather than being guessed at — a rule claiming a chain is
unsatisfiable has to be certain.

Rebased onto main after #374, which carried the analyzer library to
collection expressions. The two new files were written before that
convention landed, so they are brought to it here the same way: `dotnet
format style --diagnostics IDE0028`, run to a fixed point, five single
initializers, nothing else touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq
@Reefact
Reefact force-pushed the claude/justdummies-analyzers-analysis-z9q1xd branch from 0031dfb to 0682fef Compare July 29, 2026 16:26
@Reefact
Reefact merged commit d839a95 into main Jul 29, 2026
28 checks passed
@Reefact
Reefact deleted the claude/justdummies-analyzers-analysis-z9q1xd branch July 29, 2026 16:35
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