Skip to content

feat(dummies): add top-level OneOf/ElementOf choice combinator#282

Merged
Reefact merged 6 commits into
mainfrom
claude/issue-223-discussion-kbd5nm
Jul 21, 2026
Merged

feat(dummies): add top-level OneOf/ElementOf choice combinator#282
Reefact merged 6 commits into
mainfrom
claude/issue-223-discussion-kbd5nm

Conversation

@Reefact

@Reefact Reefact commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Add a top-level, seed-aware choice combinator to Dummies: Any.OneOf<T>(params T[]) and Any.ElementOf<T>(...) draw one value uniformly from a caller-supplied pool, so picking "any of these" no longer needs a hand-rolled pool[new Random().Next(...)] that ignores the ambient seed and silently breaks Reproducibly. This is the audit's highest-leverage Must-Have (§10).

Type of change

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

Changes

  • Add AnyOneOf<T> — a terminal generator drawing uniformly from an explicit pool (IAny<T>, IHasRandomSource, ICardinalityHint<T>); the pool is deduplicated, sized and membership-tested under EqualityComparer<T>.Default.
  • Add the factories Any.OneOf<T>(params T[]), Any.ElementOf<T>(IReadOnlyList<T>) and Any.ElementOf<T>(IEnumerable<T>) (the sequence overload materializes once), mirrored on AnyContext for the seeded surface.
  • Two names dodge the params-generic inference footgun: OneOf for inline literals, ElementOf for a held collection.
  • An empty pool throws eagerly; a null element is refused with a message pointing at OrNull(), keeping nullability orthogonal and symmetric with Any.String().OneOf (ADR-0030).
  • Cover with Dummies.UnitTests/AnyOneOfTests.cs; the AnyAnyContext mirror is enforced by the existing SurfaceParityTests.
  • Record the decision as ADR-0032 (EN + FR + index) and document the combinator in the Dummies package README.

Testing

  • dotnet build FirstClassErrors.sln
  • dotnet test FirstClassErrors.sln
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests)

Ran dotnet test Dummies.UnitTests440/440 passed (the new tests plus the parity / naming / cross-engine / architecture guards). Built Dummies on both target frameworks (netstandard2.0 + net8.0) clean, 0 warnings. Did not run the full-solution test suite or the analyzer tests — no analyzer change in this PR.

Documentation

  • Public API / error documentation updated
  • README / doc/ updated
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

XML docs on the new factories and AnyOneOf<T>; the Dummies package README (README.nuget.md) gains a bullet with an example. ADR-0032 ships bilingual (EN + FR). README.fr.md needs no change — the root README does not reference the Dummies package.

Architecture decisions

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

ADR-0032 was drafted as Proposed and accepted by the maintainer (@Reefact) during this session — recorded as Accepted. It complements ADR-0030 (the string sibling) and relies on ADR-0013 (the ICardinalityHint cardinality gate); no accepted ADR is contradicted.

Related issues

Closes #223


Generated by Claude Code

claude and others added 6 commits July 21, 2026 21:38
Picking an arbitrary element from a caller-held set was the one common
dummy need with no seed-aware answer: users hand-rolled
pool[new Random().Next(...)], which ignores the ambient source and
silently breaks Reproducibly for that draw. The typed builders' OneOf
only narrows within a scalar's own domain; there was no way to draw from
a pool of arbitrary domain objects.

Add AnyOneOf<T>, a terminal generator drawing one value uniformly from an
explicit pool, and the top-level factories Any.OneOf<T>(params T[]),
Any.ElementOf<T>(IReadOnlyList<T>) and Any.ElementOf<T>(IEnumerable<T>)
(the sequence overload materializes once), mirrored on AnyContext so the
seeded surface carries them too. Two names avoid the params-generic
inference footgun: OneOf for inline literals, ElementOf for a held
collection.

The pool is deduplicated, sized and membership-tested under
EqualityComparer<T>.Default - the type-agnostic analogue of the string
generator's ordinal dedup, and a sound upper bound for the distinct-
collection cardinality gate (ADR-0013). An empty pool throws eagerly; a
null element is rejected and the message points at OrNull(), keeping
nullability orthogonal and symmetric with Any.String().OneOf (ADR-0030).

The Any<->AnyContext mirror is enforced by SurfaceParityTests; the
generic, parameterized factories are excluded from the CLR-naming and
cross-engine guards by construction. Covered by AnyOneOfTests.

Record the decision as ADR-0032 (Proposed): draw arbitrary values from an
explicit, top-level choice pool.

Refs: #223
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQ15y5om7Gagin9ZpYXDWf
Document the top-level choice combinator in the Dummies package README:
Any.OneOf(...) and Any.ElementOf(...) as the seed-aware answer to "any of
these", mirroring where the string value-set generator is documented.

Record the maintainer's acceptance of ADR-0032 (draw arbitrary values
from an explicit, top-level choice pool): flip its status from Proposed to
Accepted in the English and French records and in the ADR index.

The Dummies CHANGELOG [Unreleased] section is drafted automatically from
merged pull requests (changelog.yml), so it is left untouched here.

Refs: #223
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQ15y5om7Gagin9ZpYXDWf
Merging main brought in the per-target-framework public API baselines
(#221): PublicApiAnalyzers now raises RS0016 for any public symbol absent
from PublicAPI.Unshipped.txt, a warning locally and an error in CI. The
new top-level choice combinator was therefore undeclared and reddened the
build, pack and packaged-asset jobs.

Declare the eight new public symbols - AnyOneOf<T>, its Generate(), and
the OneOf/ElementOf factories on Any and AnyContext - in both the net8.0
and netstandard2.0 baselines. The entries are identical across the two
assets because the combinator is not gated by #if NET8_0_OR_GREATER.

Verified locally: dotnet pack -c Release is clean (no RS0016, package
validation passes) and the full solution builds warning-free.

Refs: #223
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQ15y5om7Gagin9ZpYXDWf
@Reefact
Reefact enabled auto-merge July 21, 2026 22:04
@Reefact
Reefact merged commit 8739803 into main Jul 21, 2026
16 checks passed
@Reefact
Reefact deleted the claude/issue-223-discussion-kbd5nm branch July 21, 2026 22:05
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.

Dummies: add a top-level choice combinator Any.OneOf<T> / Any.ElementOf<T>

2 participants