Skip to content

feat(api): compile-time void fallback safety - #182

Merged
thomhurst merged 3 commits into
mainfrom
api-review-cleanups-5
Aug 24, 2026
Merged

feat(api): compile-time void fallback safety#182
thomhurst merged 3 commits into
mainfrom
api-review-cleanups-5

Conversation

@thomhurst

@thomhurst thomhurst commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Fifth round of pre-release API review cleanups. The +semver:minor prefix follows #176#181.

1. Compile-time void-fallback safety

Untyped Fallback(...) now changes the chain's static type from Shield to VoidShield. The chain still starts from Shield; the type changes only when a void fallback is added, and every later fluent call preserves VoidShield.

VoidShield exposes only void Execute/ExecuteAsync/context overloads. It cannot be assigned to Shield, lifted with For<TResult>(), wrapped into a typed shield, or passed to mixed Shield.Compose(...). Wrap(...) propagates the void-only state in either direction. Result-producing recovery remains Shield.For<TResult>().Fallback(...).

ShieldBuilder.Fallback(...) returns VoidShield; later handling clauses use VoidShieldBuilder. PartitionedVoidShield<TKey> and void-aware DI registry, rate-limiter adapter, testing descriptor, and state-snapshot APIs carry the type state through integrations. The former KEV005 runtime-risk analyzer is removed because these invalid uses now fail C# compilation.

Coverage includes compile-success and compile-failure contracts, all 12 public void execution forms, cancellation/deferred completion/exception identity/null guards, chaining and builder immutability, both wrap directions, partition retention, DI coexistence, rate-limiter adapters, analyzer recognition, reflection surface checks, and API baselines.

2. WhenResultIsNull() / OrResultIsNull()

New ShieldResultExtensions methods constrained with where TResult : class?. They use the existing default(TResult) predicate but render as [when null result]. WhenResultIsDefault/OrResultIsDefault remain for value types and generic code.

3. KEV010 (Info)

Reports WhenResultIsDefault/OrResultIsDefault on a known non-nullable value type, where 0, false, or an empty struct may be a legitimate result. Nullable<T> and type parameters are skipped. The new null-result clauses participate in KEV007/KEV009 handling-clause analysis.

4. KEV009 severity

No code change: KEV009 was already DiagnosticSeverity.Info, documented and tested as such.

5. Wrap and Compose

Verified and documented that outer.Wrap(inner) and Shield.Compose(outer, inner) have identical ordering, clause sealing, naming, and TimeProvider precedence. Mixed static composition with a VoidShield is intentionally rejected because C# cannot conditionally change a params Shield[] return type; use Wrap(...), which preserves VoidShield.

6. Backoff.Custom clamping

The retry path already clamps negative and over-limit delays and applies MaxDelay. XML documentation now states that behavior, with an added end-to-end MaxDelay regression test.

7. Execution docs ordering

No change needed: the docs already lead with the simple execution overload and reserve explicit state for zero-closure hot paths.

Validation

  • dotnet build Kevlar.slnx -c Release: clean, 0 warnings/errors, including net10.0 and netstandard2.0 API baselines.
  • 1,068 compiled tests passed: Kevlar.Tests 791; Analyzers 75; Integration 118; Testing 33; RateLimiting 20; Chaos 23; NetStandard 5; Allocation 3.
  • 118 package-based C# documentation snippets compiled and executed.
  • Docusaurus production build passed.

Branch note

api-review-cleanups-4 belonged to merged PR #180, so this PR uses api-review-cleanups-5.

https://claude.ai/code/session_01KAPqkMyQnM44sRnoYQ6hmg

…clauses

Rename the untyped, void-only fallback to FallbackAction on Shield's static
factories, ShieldExtensions and ShieldBuilder. It recovers void executions only
and fails a result-returning execution with InvalidOperationException, so
putting that contract in the name closes the trap for cross-assembly callers
KEV005 cannot see. Typed Shield<TResult>.Fallback is unchanged, and the strategy
still describes as "Fallback" so pipeline descriptions read as before.

Add WhenResultIsNull()/OrResultIsNull() on ShieldResultExtensions, constrained
to reference types: the null check WhenResultIsDefault/OrResultIsDefault was
really written for. Those stay for value types and generic code, where KEV010 —
a new Info hint — now asks whether default(T) is really the failure.

Document the clamping Backoff.Custom already applies to its delegate's result,
and state in the composition guide that outer.Wrap(inner) and
Shield.Compose(outer, inner) are the same operation.

Claude-Session: https://claude.ai/code/session_01KAPqkMyQnM44sRnoYQ6hmg
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 615589f4-ccb8-415f-95af-b4c402f9b87f


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

FallbackAction adds breaking churn without improving type safety. Keep the existing Fallback API while retaining the null-result and analyzer work in PR #182.
@thomhurst thomhurst changed the title +semver:minor - refactor(api): FallbackAction rename and null-result clauses +semver:minor - feat(api): null-result clauses and analyzer guidance Aug 24, 2026
Void fallbacks now transition fluent chains to VoidShield, preventing result execution or lifting at compile time.

BREAKING CHANGE: untyped Fallback overloads return VoidShield instead of Shield. Use the VoidShield-aware DI, partition, testing, and rate-limiter APIs.
@thomhurst thomhurst changed the title +semver:minor - feat(api): null-result clauses and analyzer guidance +semver:minor - feat(api): compile-time void fallback safety Aug 24, 2026
@thomhurst thomhurst changed the title +semver:minor - feat(api): compile-time void fallback safety feat(api): compile-time void fallback safety Aug 24, 2026
@thomhurst
thomhurst merged commit e916f08 into main Aug 24, 2026
5 checks passed
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.

1 participant