Skip to content

feat(justdummies): add JD007-JD010 for draws that escape the seed scope - #369

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

feat(justdummies): add JD007-JD010 for draws that escape the seed scope#369
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

Second wave of the JustDummies analyzer study: four rules covering the ways an arbitrary value is drawn outside the scope [Reproducible] pins. Each rests on a claim about xUnit's lifecycle, and each claim was verified against xunit.v3 3.2.2 by probe before the rule was written — not inferred from the adapter's code.

Type of change

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

Changes

  • JD007 DrawOutsideThePinnedScope (Warning) — xUnit constructs the test-class instance, and awaits IAsyncLifetime.InitializeAsync, before running the before/after hooks the adapter pins the seed from. The failure mode is worse than no reproducibility: the test advertises it, the reader pins the reported seed, the arrangement still differs, and the seed looks broken.
  • JD008 ArbitraryValueInTheoryData (Warning) — a theory's data provider is evaluated at discovery, before any case runs. One draw serves every case, replayable from no seed, in a theory that reads as if it enumerated arbitrary cases.
  • JD009 DrawInStaticInitializer (Warning) — a type initializer draws once, lazily, under whichever test first touched the type; the value is then shared by every test in the class, making them order-dependent.
  • JD010 ReproducibleOnNonTestMethod (Warning) — [Reproducible] on a method with no IFactAttribute is never read. It earns a rule because a working attribute is silent by design, so the inert form is indistinguishable from the working one until a failure that should have named a seed does not.
  • XunitFacts — lifecycle and data-provider shapes, all resolved by metadata name, so a compilation referencing neither xUnit nor the adapter never reaches these rules and JustDummies stays standalone.
  • GeneratorFacts.IsGenerateCall and RootsAtAmbientAny. The latter is deliberately conservative: it reports only a chain proven to start at the ambient Any, so a draw from an isolated AnyContext — which no ambient scope governs — can never be reported. That under-reports a generator held in a local or field, and the help pages say so.
  • 23 new analyzer tests (66 total), 8 documentation pages (EN + FR), both index tables, release-tracking rows.

The lifecycle claims, and how they were checked

A probe under [Reproducible(Seed = 555)] compared each draw against the first draw of a fresh Any.UseSeed(555) scope:

Draw site Matches the pinned scope?
Test body yesbodyDraw == firstDrawOfSeed555
Instance field initializer no
Test class constructor no
[MemberData] provider no

The probe was removed before commit; what it established is now pinned by the rules' own tests and stated in the help pages.

Dogfooding

Run over JustDummies.UnitTests, JustDummies.PropertyTests, JustDummies.Xunit.UnitTests, FirstClassErrors.Testing.UnitTests and FirstClassErrors.UnitTests: no hits. Unlike the previous wave, nothing here changed the design.

Because a clean sweep is also what a rule that never runs produces, a planted positive control was built and checked: all four fired at the expected locations, then the control was removed. The clean sweep is the suites' doing.

Testing

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

Mutation testing was not run locally.

Documentation

  • Public API / error documentation updated — no public API change
  • README / doc/ updated — JD007JD010 rule pages and the rule index
  • French translation updated — JD007.fr.mdJD010.fr.md and the French entries of doc/handwritten/for-users/analyzers/README.fr.md
  • 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-____

These four apply decisions already recorded — ADR-0044 (first-party analyzers for a mistake the type system cannot express) and ADR-0059 (enforcement follows what the mechanism can know). No new lasting decision.

Open question

JD007 exists because the adapter cannot pin earlier. BeforeAfterTestAttribute.Before runs after construction, which is xUnit's design, not the adapter's choice — so the analyzer is the available answer rather than the only conceivable one. If a v3 extensibility point could open the scope before the test class is constructed, JD007 would become unnecessary and the adapter would simply be more correct. I did not investigate that: it changes JustDummies.Xunit's behaviour rather than adding a diagnostic, and it is your call whether it is worth exploring.

Related issues

None. Second wave of the analyzer-opportunity study; the first shipped in #368.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq


Generated by Claude Code

The four rules cover the ways a value is drawn outside the scope
[Reproducible] pins, each verified against xunit.v3 3.2.2 by probe before
the rule was written rather than inferred from the adapter's code.

JD007 — xUnit constructs the test-class instance, and awaits
IAsyncLifetime.InitializeAsync, before running the before/after hooks the
adapter pins the seed from. The probe confirmed it: under
[Reproducible(Seed = 555)] a constructor draw and a field-initializer
draw both differed from the first draw of a fresh UseSeed(555) scope,
while the body's draw matched it exactly. The test therefore advertises
full reproducibility and delivers it only for the body.

JD008 — a theory's data provider is evaluated at discovery, before any
case runs. The probe's [MemberData] value likewise fell outside the
pinned scope. One draw serves every case, replayable from no seed.

JD009 — a static initializer draws once, lazily, under whichever test
first touched the type; the value is then shared, making the tests
order-dependent. RandomSource resolves the source at Generate() time, so
a shared *generator* is safe — the compliant shape the message names.

JD010 — [Reproducible] on a method with no IFactAttribute is never read.
It matters because a working attribute is silent by design, so the inert
form is indistinguishable from the working one until a failure that
should have named a seed does not.

Three shared helpers carry the rules: XunitFacts for the lifecycle and
provider shapes, plus GeneratorFacts.IsGenerateCall and RootsAtAmbientAny.
The latter is deliberately conservative — it reports only a chain proven
to start at the ambient Any, so a draw from an isolated AnyContext, which
no ambient scope governs, can never be reported.

Dogfooded across JustDummies.UnitTests, JustDummies.PropertyTests,
JustDummies.Xunit.UnitTests, FirstClassErrors.Testing.UnitTests and
FirstClassErrors.UnitTests: no hits. A planted positive control confirmed
all four fire at the expected locations, so the clean sweep is the
suites' doing, not a rule that never runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq
@Reefact
Reefact merged commit 376ca09 into main Jul 29, 2026
28 checks passed
@Reefact
Reefact deleted the claude/justdummies-analyzers-analysis-z9q1xd branch July 29, 2026 12:57
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