Skip to content

JustDummies: flag-enum combinations behind an opt-in, and a pin for the offset/OneOf divergence - #307

Merged
Reefact merged 3 commits into
mainfrom
claude/justdummies-v1-readiness-x8u9yw
Jul 26, 2026
Merged

JustDummies: flag-enum combinations behind an opt-in, and a pin for the offset/OneOf divergence#307
Reefact merged 3 commits into
mainfrom
claude/justdummies-v1-readiness-x8u9yw

Conversation

@Reefact

@Reefact Reefact commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

Any.Enum<T>() draws from the declared members, so a [Flags] dummy carries at most one bit and a branch reading two is never exercised — the hidden assumption the library exists to surface, installed by the generator itself. This adds AllowingCombinations(), an explicit opt-in widening the draw to every combination, and leaves the default untouched. It also pins an accepted risk recorded in ADR-0037 that no test held.

Type of change

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

Changes

  • AnyEnum<TEnum>.AllowingCombinations() widens the draw to the OR-closure of the declared members, plus the zero value when the enum declares one. The whole class already worked over an "universe" field, so OneOf, Except, the pool, ICardinalityHint and Generate follow unchanged.
  • Taking the declared members as the generating set — rather than the individual bits — absorbs a declared composite (ReadWrite = Read | Write adds nothing) without deciding which members "are" bits, and never invents a zero value for an enum that declares none ({ Left, Right } yields three values, not four).
  • The universe is materialized rather than drawn bit by bit. A per-member coin flip is uniform over subsets, not over values: with a declared composite, several subsets collapse onto one value, which is then drawn far more often. Enumeration also keeps ICardinalityHint exact, so a distinct collection over combinations still fails eagerly instead of falling back to a bounded draw.
  • Enumeration is exponential, so it is refused past a ceiling rather than silently degraded into a second, non-uniform regime — as it is on an enum that is not [Flags]. Both refusals happen at declaration and name their cause.
  • Except/DifferentFrom keep comparing by equality under the opt-in: Except(Read) forbids Read and leaves Read | Write drawable. A bit-level exclusion, if ever wanted, gets its own name rather than mutating this one.
  • OneOf accepts a combination once the opt-in is applied; before it, the ArgumentException names the missing constraint instead of failing flatly.
  • Parity-matrix entry, public-API baselines for both target frameworks, and a line in the package README.
  • Separate commit: a regression test pinning the ADR-0037 accepted risk that WithOffset does not rewrite a OneOf value's own offset, in both declaration orders. The divergence was documented but untested, so a change in either direction would have gone unnoticed.

Testing

  • dotnet build FirstClassErrors.sln — succeeded, 0 warnings, 0 errors
  • dotnet test FirstClassErrors.sln — 13 projects, 1772 tests, 0 failures
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests) — 132 passed, within the solution run

New tests: 13 examples in JustDummies.UnitTests (533 total, was 519) and 5 properties in JustDummies.PropertyTests (215 total, was 210), placed per ADR-0040 — the universe of each named enum shape and the conflict wording as examples, the invariants quantified over the constraint sets as properties. One of them, FlagsEnumDrawsDeclaredMembersByDefault, exists to lock the default rather than the new behaviour.

Only the net10.0 leg was run locally; the net472 floor runs in CI.

Documentation

  • Public API / error documentation updated — XML docs on the new member and the members whose contract it touches, plus the committed baselines
  • README / doc/ updated — package README, and ADR-0041 in English and French
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

doc/handwritten/for-users/README.fr.md was not touched: it does not cover JustDummies at all (the package is still absent from the repository README and has no user guide — issue #218). The user-facing surface for this change is the package README, which has no French counterpart; the French half of the change is the ADR translation.

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR-0041, drafted as Proposed and since accepted by @Reefact (status flipped in 62e606a)
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

ADR-0041 records four choices that outlive the implementation: the default stays declared-members-only even for a [Flags] enum (making it depend on the attribute would make the draw a function of type metadata rather than of what the test wrote — the class of implicitness ADR-0020 removed); Except keeps equality semantics; the universe is the OR-closure of the members with the zero-value rule; and the ceiling refuses rather than degrades.

Related issues

Refs #226 — the demand-driven backlog entry this resolves. Not closing it: the entry covers several other nice-to-haves that remain open.


🤖 Generated with Claude Code

https://claude.ai/code/session_01CQjueXGjtdbzdpLs1fQi3y

claude added 3 commits July 26, 2026 21:06
Any.Enum<T>() draws from the declared members, so a [Flags] dummy carries
at most one bit and a branch reading two is never exercised — the hidden
assumption the library exists to surface, installed by the generator
itself.

AllowingCombinations() widens the draw to the OR-closure of the declared
members, plus the zero value when the enum declares one. Taking the
members as the generating set absorbs a declared composite without
deciding which members "are" bits, and never invents a zero the type does
not define.

The universe is materialized rather than drawn bit by bit: a per-member
coin flip is uniform over subsets, not over values, so a declared
composite would skew the draw, and it could report no distinct
cardinality — dropping distinct collections from an eager conflict to a
bounded draw. Enumeration is exponential, so it is refused past a ceiling
instead of silently degrading, as it is on a non-[Flags] enum.

Exclusions keep comparing by equality: Except(Read) forbids Read and
leaves Read | Write drawable.

Refs: #226
WithOffset governs the constructed draw only: OneOf returns its values
verbatim, offset included, because it is a terminal enumeration of exact
values. ADR-0037 records that as an accepted risk, but no test held it,
so a future change in either direction would have gone unnoticed.

Refs: #297
@Reefact
Reefact merged commit 2b345ce into main Jul 26, 2026
16 checks passed
@Reefact
Reefact deleted the claude/justdummies-v1-readiness-x8u9yw branch July 26, 2026 21:17
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