Skip to content

Dummies: add exclusion constraints to AnyString (DifferentFrom / Except / OneOf) #224

Description

@Reefact

Problem

AnyString is the only scalar builder with no exclusion constraints — no OneOf, no Except, no DifferentFrom. Every other scalar builder (numerics, temporal, AnyChar, AnyGuid, AnyEnum) has the exclusion trio, and DifferentFrom is documented everywhere as carrying the intent "a value different from the one I already hold" — one of the most common dummy-string needs (testing equality/inequality paths with string identifiers).

// Today — no way to express "a name different from an existing one":
string other = Any.String().NonEmpty().Generate();     // might equal existing!
// Proposed:
string other = Any.String().NonEmpty().DifferentFrom(existing).Generate();

Impact

The most-used builder has the largest hole in the constraint matrix, forcing users back to hand-rolled retry loops for a very common scenario — the kind of ad-hoc code the library exists to replace.

Direction

Strings are not ordinal-mapped, so exclusions cannot ride the interval engine (unlike numerics). Two viable implementations, both consistent with existing patterns:

  • a bounded redraw after layout that rejects an excluded result (expected collisions ≈ 0 for any non-trivial spec — the same bounded-escape pattern the distinct collections and continuous nudge already use, with a clear AnyGenerationException if the budget is exhausted, e.g. WithLength(1).Alpha() excluding most letters); or
  • spec-aware avoidance where cheap.

Add DifferentFrom(string), Except(params string[]), and OneOf(params string[]) to AnyString/StringSpec (the last is partly subsumed by a top-level Any.OneOf but is expected on the builder for symmetry). OneOf combined with other shape constraints should validate eagerly like the other builders.

Acceptance criteria

  • Any.String()…DifferentFrom(x).Generate() never returns x; Except(...) excludes each listed value; OneOf(...) draws from the listed set.
  • An over-tight exclusion that leaves the spec unsatisfiable fails with a clear, seed-bearing AnyGenerationException (bounded, never an unbounded loop).
  • Seeded reproducibility preserved; mirrored behavior documented.
  • Tests cover exclusion under length/prefix/charset constraints and the exhaustion path.

Context

Surfaced by the 2026-07-20 architecture & design audit of the standalone Dummies library (doc/handwritten/for-maintainers/audit/2026-07-20-dummies-architecture-and-design-audit.md, §8.2/§10 Must-Have; branch claude/dummies-architecture-audit-cru5aq).


Filed from the Claude Code session that produced the 2026-07-20 Dummies architecture & design audit: https://claude.ai/code/session_01GvrWFVoG74xiys3oyEft1G

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions