Skip to content

feat(dummies): add DifferentFrom/Except exclusions to AnyString#283

Merged
Reefact merged 2 commits into
mainfrom
claude/issue-224-discussion-tjw4sk
Jul 22, 2026
Merged

feat(dummies): add DifferentFrom/Except exclusions to AnyString#283
Reefact merged 2 commits into
mainfrom
claude/issue-224-discussion-tjw4sk

Conversation

@Reefact

@Reefact Reefact commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

AnyString was the only scalar builder without exclusion constraints. This adds DifferentFrom(string) and Except(params string[]), closing the last gap in the scalar exclusion matrix and serving the common "a value different from the one I already hold, same shape" dummy-string need. (OneOf already shipped as a terminal generator, so it is not re-added here.)

Type of change

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

Changes

  • Add AnyString.DifferentFrom(string) and AnyString.Except(params string[]) (empty string is a valid value to exclude; a null element is not).
  • Extend StringSpec with an accumulating, ordinal-deduplicated exclusion set. Generation stays constructive; when exclusions are present it takes a bounded redraw of the layout, and an exclusion that leaves the shape unsatisfiable fails at Generate() with a seed-bearing AnyGenerationException — mirroring the distinct-collection escape (ADR-0013), never an unbounded loop.
  • OneOf stays terminal (returns AnyStringOneOf) and cannot combine with an exclusion.
  • Declare the two new methods in the per-TFM public-API baselines (Dummies/PublicAPI/{netstandard2.0,net8.0}/PublicAPI.Unshipped.txt).
  • Update SurfaceParityTests algebra table (adds Except/DifferentFrom for AnyString) and its stale comment.
  • Update the Dummies NuGet readme's "built to satisfy, never filtered" note and the StringSpec doc to record the one deferred-failure carve-out.
  • Add ADR-0033 (English + French) recording the bounded-redraw decision.

Testing

  • dotnet build FirstClassErrors.sln — succeeds (also verified in CI-equivalent mode: -c Release with the warnings-as-errors ratchet, so the public-API analyzer RS0016/RS0017 pass; Dummies builds for netstandard2.0 and net8.0).
  • dotnet test FirstClassErrors.sln — full solution green (Dummies.UnitTests includes 8 new tests for exclusions; nothing else regressed).
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests).
  • dotnet pack Dummies — package validation passes.

Documentation

  • Public API / error documentation updated (XML docs on the new methods and StringSpec).
  • README / doc/ updated (Dummies NuGet readme; ADR-0033 EN + FR).
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

The only user-facing doc affected is the Dummies NuGet readme, which is English-only (no .fr.md counterpart); ArbitraryTestValues.* does not enumerate the string vocabulary or the "never filter" invariant, so it needed no change. The ADR carries its French translation per the ADR conventions.

Architecture decisions

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

ADR-0033 records that string exclusions are met by a bounded redraw and that an unsatisfiable exclusion is deferred to a seed-bearing generation failure — the one departure from the constructive "built to satisfy" rule for scalars, sibling to ADR-0013. Drafted as Proposed and accepted by the maintainer (@Reefact) in this PR. (Renumbered from 0032 after #223 landed its own ADR-0032 on main; this branch is up to date with main.)

Related issues

Closes #224

🤖 Generated with Claude Code

https://claude.ai/code/session_01MvjmrU4bRweSaj2SriJAdS

AnyString was the only scalar builder without exclusion constraints,
forcing hand-rolled retry loops for the common "a value different from
the one I already hold" case. Add DifferentFrom(string) and
Except(params string[]), the exclusion pair every other scalar builder
already exposes, and declare them in the per-TFM public-API baselines.

Strings are not ordinal-mapped, so an exclusion cannot be built into the
constructive layout the way it is for numerics. It is met instead by a
bounded redraw of the layout (expected collisions are ~0 for any
non-trivial shape) — the same bounded escape distinct collections use to
skip a duplicate. An exclusion tight enough to leave the shape
unsatisfiable surfaces at generation as a seed-bearing
AnyGenerationException, never as an unbounded loop; this is documented as
the one case where a string generator that exists may still fail to
generate.

OneOf stays terminal (it returns AnyStringOneOf) and cannot combine with
an exclusion.

Refs: #224
@Reefact
Reefact force-pushed the claude/issue-224-discussion-tjw4sk branch from e4cee7c to efe4cf4 Compare July 22, 2026 06:16
Record the decision behind #224 (English canonical plus French
translation, indexed): AnyString exclusions are met by a bounded redraw
and an unsatisfiable exclusion fails at generation with a seed-bearing
error, the one departure from the constructive "built to satisfy" rule
for scalars. Sibling to ADR-0013. Accepted by the maintainer.

Refs: #224
@Reefact
Reefact force-pushed the claude/issue-224-discussion-tjw4sk branch from efe4cf4 to 5b17aa9 Compare July 22, 2026 06:24
@Reefact
Reefact merged commit 1d3b9e7 into main Jul 22, 2026
16 checks passed
@Reefact
Reefact deleted the claude/issue-224-discussion-tjw4sk branch July 22, 2026 06:30
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 exclusion constraints to AnyString (DifferentFrom / Except / OneOf)

2 participants