Problem
"Arbitrary yet valid values" is JustDummies' defining constraint, and it is not recorded in any ADR. It is asserted in JustDummies/README.nuget.md (l.3), repeated across XML docs, and used as the audit's closing formula — but none of the 16 JustDummies ADRs (0011, 0013, 0015, 0020, 0025, 0026, 0030–0033, 0035–0040) states it.
That matters because it is already a load-bearing rule, applied consistently and enforced as an input guard, not merely as an output property:
- Floating point.
ContinuousIntervalSpec.EnsureFinite (ContinuousIntervalSpec.cs:35) is called from 27 argument sites across AnyDouble, AnySingle and AnyHalf — every bound, every allow-list entry, every exclusion. Any.Double().Except(double.NaN) throws; the library refuses to discuss NaN at all. Locked in by ContinuousIntervalProperties.cs:236/:253 ("rejected as argument errors by every entry point taking a bound") and AnyModernTypeTests.cs:101.
- Enums.
AnyEnum.OneOf rejects an undeclared numeric value (AnyEnum.cs:77), locked in by AnySetTypeTests.cs:138.
- As an admission criterion. The audit's Out of Scope list already refuses
Index/Range on exactly this ground — "validity is contextual, so 'arbitrary yet valid' cannot hold standalone" (audit §10, l.877).
So the same decision has been taken at least twice in code and once as a design filter, with no record — and it is exactly the class of decision a future maintainer would question.
And the principle is not a global invariant of the library. The generic entry points do not carry it, by construction:
AnyOneOf<T>.FromPool (AnyOneOf.cs:39-40) validates only non-empty pool and no null element. Any.OneOf(double.NaN, 1.0) therefore compiles and yields NaN today.
AnyExtensions.As (AnyExtensions.cs:36) projects to anything.
That is correct — T is opaque, the library cannot judge its semantics — but an ADR that claimed a library-wide invariant would be false the day it is written. The boundary has to be stated at the level where it actually holds: the typed builders.
Impact
Three concrete costs, all already observed:
- The rule cannot be invoked. A proposal to add
Any.Enum<T>().Undeclared(), Any.Double().WithNaN(), Any.String().NotMatching(regex) or Any.DateTime().OutOfRange() contradicts no accepted ADR. The refusal is currently an intuition, re-derived from scratch each time.
- A legitimate near-neighbour is indistinguishable from the refused ones. A
[Flags] combination (Read|Write) is undeclared and perfectly valid — it passes the criterion, whereas an "undeclared enum member" generator does not. Without the criterion written down, the audit's AllowingCombinations() proposal and an Undeclared() proposal look like the same request.
- The escape hatch is undiscoverable. The one legitimate shape of the need — a domain where a non-finite double genuinely means something (
NaN = "missing measurement"), or where a specific out-of-domain value is the subject of a test — is already served by Any.OneOf(...) / .As(...). Nothing says so, so users hit the ArgumentException wall and conclude the library is missing a feature.
Direction
Draft one Proposed ADR stating the boundary where it is true:
A typed builder draws, and accepts as an argument, only valid values of the domain it represents. The generic entry points (OneOf<T>, ElementOf<T>, As) carry no such guarantee: the caller's pool is the whole specification, and the library does not judge T.
with, as rationale, the two independent lines the code already supports:
- The dummy criterion. A dummy is "a value a test needs but never asserts on" (README l.3). A value chosen for being out of domain is the subject of the assertion, however arbitrary it is within that class — arbitrariness is not insignificance. Such a value is a hostile fixture, and its correct form is a literal at the call site.
- The engine constraint (floats, independent of doctrine).
ContinuousIntervalSpec is an ordered interval model: bounds comparison, midpoint sampling (:184), and an exclusion nudge walking the representable ladder. Every comparison with NaN is false, so NaN is not one more value in the interval, it is a value outside the model. Compounding it, EqualityComparer<double>.Default.Equals(NaN, NaN) is true while NaN == NaN is false, so a NaN in a pool would dedupe under Distinct() while user code comparing with == sees two different values — the literal "sabotage arithmetic" of ContinuousIntervalSpec.cs:8.
And record the two corollaries that make the criterion usable:
[Flags] combinations remain admissible (a combined value is undeclared and valid) — pre-answering the audit's AllowingCombinations() proposal without pre-empting its own decision.
decimal has neither NaN nor infinity. System.Decimal has no such representation; AnyDecimal runs on DecimalIntervalSpec, which contains no finiteness guard because there is nothing to guard. Worth one documented sentence so nobody looks for the symmetry with AnyDouble, or files it as a gap.
User-facing documentation, in the same unit of work:
- One paragraph in
JustDummies/README.nuget.md stating the boundary and naming the escape hatch (Any.OneOf / As) for the legitimate case.
- The same in the JustDummies user guide when it lands (audit recommendation §11.6).
- A sentence on
EnsureFinite's user-visible message path and on AnyEnum's XML docs pointing at the escape hatch, so the wall explains its own exit.
Per repo convention, an agent drafts as Proposed; @reefact accepts.
Acceptance criteria
Context
Surfaced while assessing whether Any.Enum<T>() should be able to produce an undeclared numeric value (e.g. (OrderStatus)6) for tests of defensive boundary code — the conclusion being no, but that the repository had no recorded reason to say so, and no recorded reason to keep saying so for the next such request.
Sibling structural-gap ADR issues: #216 (determinism contract), #217 (ordinal engine). Related: #228 (foundational ADR backfill, core library).
Problem
"Arbitrary yet valid values" is JustDummies' defining constraint, and it is not recorded in any ADR. It is asserted in
JustDummies/README.nuget.md(l.3), repeated across XML docs, and used as the audit's closing formula — but none of the 16 JustDummies ADRs (0011, 0013, 0015, 0020, 0025, 0026, 0030–0033, 0035–0040) states it.That matters because it is already a load-bearing rule, applied consistently and enforced as an input guard, not merely as an output property:
ContinuousIntervalSpec.EnsureFinite(ContinuousIntervalSpec.cs:35) is called from 27 argument sites acrossAnyDouble,AnySingleandAnyHalf— every bound, every allow-list entry, every exclusion.Any.Double().Except(double.NaN)throws; the library refuses to discuss NaN at all. Locked in byContinuousIntervalProperties.cs:236/:253("rejected as argument errors by every entry point taking a bound") andAnyModernTypeTests.cs:101.AnyEnum.OneOfrejects an undeclared numeric value (AnyEnum.cs:77), locked in byAnySetTypeTests.cs:138.Index/Rangeon exactly this ground — "validity is contextual, so 'arbitrary yet valid' cannot hold standalone" (audit §10, l.877).So the same decision has been taken at least twice in code and once as a design filter, with no record — and it is exactly the class of decision a future maintainer would question.
And the principle is not a global invariant of the library. The generic entry points do not carry it, by construction:
AnyOneOf<T>.FromPool(AnyOneOf.cs:39-40) validates only non-empty pool and no null element.Any.OneOf(double.NaN, 1.0)therefore compiles and yields NaN today.AnyExtensions.As(AnyExtensions.cs:36) projects to anything.That is correct —
Tis opaque, the library cannot judge its semantics — but an ADR that claimed a library-wide invariant would be false the day it is written. The boundary has to be stated at the level where it actually holds: the typed builders.Impact
Three concrete costs, all already observed:
Any.Enum<T>().Undeclared(),Any.Double().WithNaN(),Any.String().NotMatching(regex)orAny.DateTime().OutOfRange()contradicts no accepted ADR. The refusal is currently an intuition, re-derived from scratch each time.[Flags]combination (Read|Write) is undeclared and perfectly valid — it passes the criterion, whereas an "undeclared enum member" generator does not. Without the criterion written down, the audit'sAllowingCombinations()proposal and anUndeclared()proposal look like the same request.NaN= "missing measurement"), or where a specific out-of-domain value is the subject of a test — is already served byAny.OneOf(...)/.As(...). Nothing says so, so users hit theArgumentExceptionwall and conclude the library is missing a feature.Direction
Draft one
ProposedADR stating the boundary where it is true:with, as rationale, the two independent lines the code already supports:
ContinuousIntervalSpecis an ordered interval model: bounds comparison, midpoint sampling (:184), and an exclusion nudge walking the representable ladder. Every comparison with NaN is false, so NaN is not one more value in the interval, it is a value outside the model. Compounding it,EqualityComparer<double>.Default.Equals(NaN, NaN)istruewhileNaN == NaNisfalse, so a NaN in a pool would dedupe underDistinct()while user code comparing with==sees two different values — the literal "sabotage arithmetic" ofContinuousIntervalSpec.cs:8.And record the two corollaries that make the criterion usable:
[Flags]combinations remain admissible (a combined value is undeclared and valid) — pre-answering the audit'sAllowingCombinations()proposal without pre-empting its own decision.decimalhas neither NaN nor infinity.System.Decimalhas no such representation;AnyDecimalruns onDecimalIntervalSpec, which contains no finiteness guard because there is nothing to guard. Worth one documented sentence so nobody looks for the symmetry withAnyDouble, or files it as a gap.User-facing documentation, in the same unit of work:
JustDummies/README.nuget.mdstating the boundary and naming the escape hatch (Any.OneOf/As) for the legitimate case.EnsureFinite's user-visible message path and onAnyEnum's XML docs pointing at the escape hatch, so the wall explains its own exit.Per repo convention, an agent drafts as
Proposed;@reefactaccepts.Acceptance criteria
ProposedADR records the boundary scoped to typed builders, explicitly disclaiming it forOneOf<T>/ElementOf<T>/As.[Flags]admissible;decimalhas no non-finite values).adr/README.mdwith its FR twin, and cross-linked with Dummies: settle and document the determinism contract, and record it in a Proposed ADR #216 and Dummies: record the ordinal-engine architecture in a Proposed ADR #217 (the two other JustDummies structural-gap ADRs).README.nuget.mdstates the boundary and names the escape hatch.Context
Surfaced while assessing whether
Any.Enum<T>()should be able to produce an undeclared numeric value (e.g.(OrderStatus)6) for tests of defensive boundary code — the conclusion being no, but that the repository had no recorded reason to say so, and no recorded reason to keep saying so for the next such request.Sibling structural-gap ADR issues: #216 (determinism contract), #217 (ordinal engine). Related: #228 (foundational ADR backfill, core library).