cli-v1.1.0-beta.3
Pre-release
Pre-release
1.1.0-beta.3 — August 24, 2026
Guard reading gets wider and stricter at once — two named guard libraries and a factory-built type are read now, while three shapes where the tool was confidently wrong about a guard's reach are refused instead of guessed.
⚠️ Breaking changes
- A guard the tool cannot place above every write to its parameter, or cannot prove runs on every construction, is now marked
unread guards— so a scaffold that used to compile can block your build until you confirm the generator. It used to emit a constraint the real constructor does not hold, which is the worse of the two failures: the file compiled, the recap reported nothing worth looking at, and the draw threw inside the constructor much later.
✨ New
- The guard helpers of Ardalis.GuardClauses and CommunityToolkit.Diagnostics are read, in both their spellings —
Name = Guard.Against.NullOrWhiteSpace(name);no longer ends the scan before anything is read, so a constructor guarded in that style stops scaffolding neutral generators under a recap showing no doubt anywhere (ADR-0086). A recognised library's method outside the measured rows earnsunread guardsrather than silence. - A type with no accessible constructor now scaffolds through its own factory — the canonical validating value object, a private constructor behind a public
Create, hasGenerate()call the factory and its guards read like a constructor's, with the recap's signature line naming the call the emitted file makes (factory Email.Create(string)). An abstract type with a factory scaffolds too.
🐛 Bug Fixes
- A guard is no longer read as a bound on a value the constructor had already replaced, or never reached — a write to the parameter itself (
percent = 100 - percent), a: this(…)or: base(…)initializer that runs entire before the body, a loop,switch,usingorfinallywhose order the tool does not read, and areturnorgotoabove the guard. Each is now placed correctly or refused. - A sign guard on an unsigned parameter no longer loses its constraint —
if (size <= 0)on abyteor auintread as.Positive(), a member the unsigned generators do not carry, so it was dropped silently andAny.Byte()still drew0; it now reads as.NonZero(), which is the same constraint rather than a looser one. - The arithmetic
ArgumentOutOfRangeExceptionthrow helpers are read as guards instead of blocking the build —ThrowIfNegative,ThrowIfNegativeOrZero,ThrowIfZero,ThrowIfLessThan,ThrowIfGreaterThan,ThrowIfLessThanOrEqualandThrowIfGreaterThanOrEqualnow map to the same numeric rows a comparison already builds. - A guard followed by an
else, or anelse ifchain that throws throughout, is read rather than passed over — reading stops at the first branch that does not throw unconditionally, and that branch, with everything after it, is markedunread guards. - An enum exclusion guard is read as
AnyEnum<T>.DifferentFrom—if (status == Status.None) { throw … }, the commonest enum guard there is, used to read as.NonZero(), a memberAnyEnum<T>does not carry, and was dropped silently. - The recap no longer prints
guardfor a factory whose guards tightened nothing — the word is computed from the constraints that reach the emitted chain, on the factory path as on every other.