Skip to content

feat(dummies): add collection and tuple generators - #175

Merged
Reefact merged 2 commits into
mainfrom
claude/dummies-collections
Jul 18, 2026
Merged

feat(dummies): add collection and tuple generators#175
Reefact merged 2 commits into
mainfrom
claude/dummies-collections

Conversation

@Reefact

@Reefact Reefact commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

Extends the Dummies DSL from scalars to collections — ListOf, ArrayOf, SequenceOf, SetOf, DictionaryOf, plus PairOf/TripleOf — as combinators over any element generator, keeping the library's build-to-satisfy, fail-eagerly contract.

Type of change

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

Changes

  • Generators (Any.*, combinators deriving their random context from the element generator, ambient fallback — like Combine): ListOfList<T>, ArrayOfT[], SequenceOfIEnumerable<T> (fully materialized), SetOfHashSet<T>, DictionaryOfDictionary<K,V>, plus PairOf/TripleOf → value tuples (in-box on netstandard2.0). Comparer overloads for SetOf/DictionaryOf, and Distinct(comparer).
  • Shared count engine CountSpec, mirroring the string length algebra: WithCount/WithMinCount/WithMaxCount/WithCountBetween/NonEmpty/Empty, with eager conflict detection. An unconstrained collection holds 0..8 elements (smaller spread than a string's, since elements are themselves generated values).
  • Distinctness contract (see ADR-0013): a new internal ICardinalityHint lets small-domain generators (ordinal spec + bool/enum/char/guid pools) advertise an upper-bound cardinality, so a count beyond it fails eagerly with ConflictingAnyConstraintException; unknown/large domains use a bounded dedup-draw that surfaces a genuine shortfall as AnyGenerationException naming the seed. The bound stays sound under a custom comparer.
  • Containment: Containing(value) forces required elements in (budgeted like string fragments); ContainingAny(generator) forces a drawn value in — named apart because a library generator both is an IAny<T> and converts implicitly to its value, which would make a single overload ambiguous.
  • Tests: new AnyCollectionTests (count family + conflicts, distinct eager/fallback, comparer, containing, dictionary, pair/triple, reproducibility, As/Combine composition); the seed reproducibility batch now spans a list and a set.
  • Docs: NuGet readme gains a collections bullet; ADR-0013 drafted (EN + FR) and indexed.

Testing

  • dotnet build FirstClassErrors.sln — succeeded, 0 warnings (both netstandard2.0 and net8.0 legs), re-verified after rebase onto main.
  • dotnet test FirstClassErrors.sln — all green (Dummies 145; FCE 423; Analyzers 85; GenDoc 155; Binder 75; Cli 64).
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests) — ran as part of the full suite above.

Documentation

  • Public API / error documentation updated — full XML docs on the new public surface.
  • README / doc/ updated — Dummies/README.nuget.md and ADR-0013.
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

The handwritten EN/FR user guides are intentionally left for later, per ADR-0011's follow-up ("write the user documentation once the V1 surface stabilizes"); only the packaged NuGet readme is kept current here.

Architecture decisions

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

Rebased onto main and renumbered the ADR 0012 → 0013 after ADR-0012 (binder options, #174) landed on main.

🤖 Generated with Claude Code

claude added 2 commits July 18, 2026 09:04
Increment 3 extends the DSL from scalars to collections, composed over
any element generator rather than filled by reflection.

- New generators: Any.ListOf / ArrayOf / SequenceOf (materialized) / SetOf
  / DictionaryOf, plus Any.PairOf / TripleOf sugar over Combine. Each is a
  combinator that derives its random context from the element generator
  (falling back to the ambient source), so a seeded element keeps the
  whole collection reproducible.
- Shared count engine CountSpec mirrors the string length algebra:
  WithCount / WithMinCount / WithMaxCount / WithCountBetween / NonEmpty /
  Empty, with eager conflict detection. Unconstrained collections hold
  0..8 elements — smaller than the string spread, since elements are
  themselves generated values.
- Distinctness follows a two-layer contract. Small element domains
  advertise an upper-bound cardinality (ICardinalityHint, wired through
  the ordinal spec and the bool/enum/char/guid pools), so a count beyond
  the domain fails eagerly with ConflictingAnyConstraintException; larger
  or unknown domains use a bounded dedup-draw and surface a genuine
  shortfall as AnyGenerationException naming the seed. The bound stays
  sound under a custom comparer, which can only merge values.
- Containing(value) forces required elements into a collection (budgeted
  like string fragments); ContainingAny(generator) forces a drawn value
  in — named apart because a generator both is an IAny<T> and converts
  implicitly to its value.

Values are built to satisfy the constraints directly, never
generate-then-filter. The library stays free of FirstClassErrors and
targets netstandard2.0 and net8.0. Full suite green on both legs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhPrBqngpiSwdsF4DWvNPw
Record, as Proposed, how a distinct collection resolves a count against
its element domain: eager ConflictingAnyConstraintException when the
generator advertises a cardinality the count exceeds, and a bounded
deduplicating draw (AnyGenerationException on shortfall, seed reported)
when the domain is unbounded or unknowable. Includes the French
translation and the index entry.

Numbered 0013: ADR-0012 was taken by the binder-options decision merged
to main in the meantime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhPrBqngpiSwdsF4DWvNPw
@Reefact
Reefact force-pushed the claude/dummies-collections branch from 5252986 to fef2b24 Compare July 18, 2026 09:05
@Reefact
Reefact merged commit 2aafe80 into main Jul 18, 2026
13 checks passed
@Reefact
Reefact deleted the claude/dummies-collections branch July 18, 2026 09:59
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