diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b126ea..0181e40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,15 +6,17 @@ All notable changes to this project are documented here. This project adheres to
## 3.0.0
A major release focused on cryptographic correctness, a modern API, and broader use cases.
-See the [v2 → v3 migration guide](docs/v3-target/migration-v2-to-v3.md).
+See the [v2 → v3 migration guide](docs/migration-v2-to-v3.md).
### Breaking changes
- **Invalid settings now throw** `ArgumentException` from `Next()` instead of returning an error
message as the "password". Use `TryNext(out var password)` for a non-throwing path.
+- **Minimum runtime is now .NET 8.** The package targets `net8.0` and `net10.0`; `netstandard2.0`
+ has been dropped. Consumers on .NET Framework or other older runtimes should stay on the 2.x line.
### Security / correctness fixes
- Cryptographically secure RNG (`CryptoRandomSource`) with **unbiased** integer sampling
- (rejection sampling — removes modulo bias).
+ (via `RandomNumberGenerator.GetInt32` — removes modulo bias).
- Fixed an off-by-one in length handling and removed the GUID-based shuffle in favour of a
Fisher–Yates shuffle.
- Disposed/owned RNG lifecycle; removed dead code and the static RNG.
@@ -32,7 +34,7 @@ See the [v2 → v3 migration guide](docs/v3-target/migration-v2-to-v3.md).
`PasswordOptions.DefaultBatchCount`.
### Packaging
-- Multi-targets `netstandard2.0` and `net8.0`; nullable reference types enabled.
+- Multi-targets `net8.0` and `net10.0`; nullable reference types enabled.
- Single source of version truth in the csproj (removed the stale `.nuspec`).
- `PackageIcon` + `PackageReadmeFile` (clears `NU5048`), SourceLink, deterministic build, and a
`.snupkg` symbol package.
@@ -44,4 +46,4 @@ See the [v2 → v3 migration guide](docs/v3-target/migration-v2-to-v3.md).
## 2.1.0 and earlier
See the project history and the original review in
-[`docs/V3_REVIEW_AND_DOCUMENTATION.md`](docs/V3_REVIEW_AND_DOCUMENTATION.md).
+[`docs/archive/V3_REVIEW_AND_DOCUMENTATION.md`](docs/archive/V3_REVIEW_AND_DOCUMENTATION.md).
diff --git a/PasswordGenerator.Benchmarks/PasswordGenerator.Benchmarks.csproj b/PasswordGenerator.Benchmarks/PasswordGenerator.Benchmarks.csproj
index f445b44..f9c107c 100644
--- a/PasswordGenerator.Benchmarks/PasswordGenerator.Benchmarks.csproj
+++ b/PasswordGenerator.Benchmarks/PasswordGenerator.Benchmarks.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/PasswordGenerator.Tests/PasswordGenerator.Tests.csproj b/PasswordGenerator.Tests/PasswordGenerator.Tests.csproj
index d2b0146..40a0cae 100644
--- a/PasswordGenerator.Tests/PasswordGenerator.Tests.csproj
+++ b/PasswordGenerator.Tests/PasswordGenerator.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net8.0;net10.0
enable
latest
@@ -9,11 +9,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/PasswordGenerator/CryptoRandomSource.cs b/PasswordGenerator/CryptoRandomSource.cs
index 7118b26..93a220e 100644
--- a/PasswordGenerator/CryptoRandomSource.cs
+++ b/PasswordGenerator/CryptoRandomSource.cs
@@ -4,58 +4,18 @@
namespace PasswordGenerator
{
///
- /// backed by a cryptographic RNG.
- /// On modern targets it uses ; on
- /// netstandard2.0 it uses rejection sampling so the result is uniform across the whole
- /// range with no modulo bias and no off-by-one.
+ /// backed by a cryptographic RNG. Uses
+ /// , which samples uniformly across the whole
+ /// range with no modulo bias.
///
- public sealed class CryptoRandomSource : IRandomSource, IDisposable
+ public sealed class CryptoRandomSource : IRandomSource
{
-#if !NET8_0_OR_GREATER
- private readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
-#endif
-
public int NextInt(int maxExclusive)
{
if (maxExclusive <= 0)
throw new ArgumentOutOfRangeException(nameof(maxExclusive), "maxExclusive must be positive.");
-#if NET8_0_OR_GREATER
return RandomNumberGenerator.GetInt32(maxExclusive);
-#else
- if (maxExclusive == 1) return 0;
-
- var range = (uint)maxExclusive;
-
- // Largest multiple of range that is <= 2^32. Values at or above this are rejected so the
- // accepted region is a whole number of buckets, giving an unbiased result mod range.
- const ulong fullSpace = 1UL << 32;
- var limit = fullSpace - fullSpace % range;
-
- uint value;
- do
- {
- value = NextUInt32();
- } while (value >= limit);
-
- return (int)(value % range);
-#endif
- }
-
-#if !NET8_0_OR_GREATER
- private uint NextUInt32()
- {
- var buffer = new byte[4];
- _rng.GetBytes(buffer);
- return BitConverter.ToUInt32(buffer, 0);
- }
-#endif
-
- public void Dispose()
- {
-#if !NET8_0_OR_GREATER
- _rng.Dispose();
-#endif
}
}
}
diff --git a/PasswordGenerator/PasswordGenerator.csproj b/PasswordGenerator/PasswordGenerator.csproj
index 933fdf5..ae8ce7a 100644
--- a/PasswordGenerator/PasswordGenerator.csproj
+++ b/PasswordGenerator/PasswordGenerator.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;net8.0;net10.0
+ net8.0;net10.0
enable
latest
@@ -20,7 +20,7 @@
MIT
passwordgeneratorlogo.png
README.md
- Password,Passphrase,Generator,OWASP,NIST,Security,Random,Crypto,OTP,ApiKey,Entropy,dotnet,netstandard,DependencyInjection
+ Password,Passphrase,Generator,OWASP,NIST,Security,Random,Crypto,OTP,ApiKey,Entropy,dotnet,DependencyInjection
3.0.0 is a major release: cryptographically secure RNG with unbiased sampling, exception/TryNext error handling, async APIs, dependency-injection support, presets (OWASP/NIST/OTP/API key/passphrase), custom pools, exclude-ambiguous, per-class minimums and entropy estimation. See the migration guide for upgrading from 2.x.
false
@@ -41,7 +41,7 @@
-
+
diff --git a/Readme.md b/Readme.md
index 9cbd20d..e3fa558 100644
--- a/Readme.md
+++ b/Readme.md
@@ -14,12 +14,10 @@ Install via NuGet: ``` Install-Package PasswordGenerator ```
[Or click here to go to the package landing page](https://www.nuget.org/packages/PasswordGenerator)
-It targets `netstandard2.0` and `net8.0`, so it runs on .NET Framework, .NET Core and modern .NET.
-See the chart below:
+It targets `net8.0` and `net10.0`, so it requires .NET 8 or later. If you need to run on .NET
+Framework or other older runtimes, use the 2.x line (which targets `netstandard2.0`).
-
-
-> **Upgrading from 2.x?** See the [v2 → v3 migration guide](docs/v3-target/migration-v2-to-v3.md).
+> **Upgrading from 2.x?** See the [v2 → v3 migration guide](docs/migration-v2-to-v3.md).
> The v2 API still works; the one behavioural change is that invalid settings now **throw** (or use
> `TryNext`) instead of returning an error string as the "password".
@@ -82,7 +80,7 @@ var password = pwd.Next();
## Presets
Ready-made starting points; later fluent calls still override them. See the
-[standards mapping](docs/v3-target/migration-v2-to-v3.md#6-standards-mapping-for-the-presets) for the
+[standards mapping](docs/migration-v2-to-v3.md#6-standards-mapping-for-the-presets) for the
OWASP/NIST rationale.
```csharp
@@ -150,6 +148,6 @@ public class SignupService(IPasswordGenerator generator)
## Documentation
-- [v2 → v3 migration guide](docs/v3-target/migration-v2-to-v3.md)
+- [v2 → v3 migration guide](docs/migration-v2-to-v3.md)
- [Changelog](CHANGELOG.md)
- [Design & architecture docs](docs/README.md)
diff --git a/docs/README.md b/docs/README.md
index 04b8357..33d9e0a 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,53 +1,53 @@
# PasswordGenerator — Documentation
-This folder is the working reference for the package: the **shipped v3 design** and the historical
-review of the v2.1.0 code it replaced. Diagrams are written in [Mermaid](https://mermaid.js.org/) and
-render directly on GitHub.
+This folder is the working reference for the package as it ships today (**v3**). Diagrams are written
+in [Mermaid](https://mermaid.js.org/) and render directly on GitHub.
## How the docs fit together
```mermaid
flowchart LR
- subgraph Review["Review & verification"]
- A[V3_REVIEW_AND_DOCUMENTATION.md
full review of v2.1.0]
- B[V3_VERIFICATION.md
every issue re-checked vs current source]
+ subgraph Docs["the docs — current (v3)"]
+ D1[architecture.md]
+ D2[generation-flow.md]
+ D3[api-surface.md]
+ D4[configuration-and-di.md]
+ D5[migration-v2-to-v3.md]
+ D6[v3-local-nuget-test.md]
end
- subgraph Current["current-state/ — what we have"]
- C1[architecture.md]
- C2[generation-flow.md]
- C3[api-surface.md]
+ subgraph Archive["archive/ — historical"]
+ A1[V3_REVIEW_AND_DOCUMENTATION.md]
+ A2[V3_VERIFICATION.md]
+ A3[current-state/ v2.1.0 snapshot]
+ A4[before-after.md]
+ A5[roadmap.md]
+ A6[implementation-plan.md]
end
- subgraph Target["v3-target/ — where we are going"]
- T1[architecture.md]
- T2[generation-flow.md]
- T3[api-surface.md]
- T4[configuration-and-di.md]
- T5[before-after.md]
- T6[roadmap.md]
- T7[implementation-plan.md]
- T8[migration-v2-to-v3.md]
- end
- A --> B --> Current
- Current --> Target
- T5 -. compares .-> Current
- T6 --> T7
+ Docs -. superseded by .-> Archive
```
## Reading order
-1. **`V3_REVIEW_AND_DOCUMENTATION.md`** — the original full review (API, bugs, packaging, gaps).
-2. **`V3_VERIFICATION.md`** — each issue re-checked against the current `master` source, with verdicts.
-3. **`current-state/`** — diagrammed snapshot of the v2.1.0 code (now **historical**; the issues it
- documents are resolved in v3 — see the root [`CHANGELOG.md`](../CHANGELOG.md)).
-4. **`v3-target/`** — the v3 design (now **shipped**), diagrammed, with a before/after, a roadmap, a
- phased **`implementation-plan.md`**, and the user-facing **`migration-v2-to-v3.md`**.
+1. **`architecture.md`** — type relationships, the random source, and the multi-targeting strategy.
+2. **`generation-flow.md`** — how `Next()`/`Generate()` build a password, plus the async path.
+3. **`api-surface.md`** — the public fluent surface, presets, and batch/async APIs.
+4. **`configuration-and-di.md`** — `PasswordOptions`, settings resolution, and DI registration.
+5. **`migration-v2-to-v3.md`** — the user-facing upgrade guide from 2.x.
+6. **`v3-local-nuget-test.md`** — local `dotnet pack` / install verification procedure.
## Conventions
-- **Current state** describes `master` @ v2.1.0, `netstandard2.0`. Code references use
- `file:line` against that source.
-- **v3 target** describes the design that shipped in v3.0.0 (`netstandard2.0;net8.0`, nullable
- enabled). The `implementation-plan.md` records where the shipped code intentionally diverged from
- the earlier proposal (e.g. the `IPasswordBuilder` split was deferred and sync methods were not
- obsoleted).
-- Each "target" doc ends with a **Why this is better** note tied back to a verified issue.
+- These docs describe **v3.0.0** as shipped: targets `net8.0;net10.0`, nullable enabled.
+- Each doc ends with a **Why this is better** note.
+
+## Archive
+
+`archive/` keeps the material that led to v3 but no longer describes the current state:
+
+- **`V3_REVIEW_AND_DOCUMENTATION.md`** / **`V3_VERIFICATION.md`** — the original review and
+ issue-by-issue verification of the v2.1.0 code.
+- **`current-state/`** — the diagrammed snapshot of the v2.1.0 (`netstandard2.0`) code that v3 replaced.
+- **`roadmap.md`** / **`implementation-plan.md`** / **`before-after.md`** — the v3 planning documents.
+
+These are point-in-time records; where they recommend or describe `netstandard2.0` support, note that
+v3 dropped it (see the root [`CHANGELOG.md`](../CHANGELOG.md)).
diff --git a/docs/v3-target/api-surface.md b/docs/api-surface.md
similarity index 85%
rename from docs/v3-target/api-surface.md
rename to docs/api-surface.md
index 19e18fe..61723f4 100644
--- a/docs/v3-target/api-surface.md
+++ b/docs/api-surface.md
@@ -1,11 +1,11 @@
-# v3 Target — Public API Surface
+# Public API Surface
Keeps the familiar fluent feel; adds safety, presets, batch, async, and custom pools.
-> **As shipped:** the fluent builder is the existing `IPassword` (the full `IPasswordBuilder`/`Build()`
-> split from the early proposal was deferred). `Password` implements both `IPassword` and the new
-> generation contract `IPasswordGenerator`. Passphrases return an `IPasswordGenerator`
-> (`PassphraseGenerator`). See `implementation-plan.md` for the deviations.
+> The fluent builder is `IPassword` (there is no separate `IPasswordBuilder`/`Build()` split).
+> `Password` implements both `IPassword` and the generation contract `IPasswordGenerator`.
+> Passphrases return an `IPasswordGenerator` (`PassphraseGenerator`). See
+> `archive/implementation-plan.md` for how the shipped surface diverged from the early proposal.
## API map
@@ -69,7 +69,7 @@ fluent call still overrides them (resolution order is documented in `configurati
## Surfacing the broader purpose
The library is **not password-only**. The same surface generates OTPs, environment names, API keys,
-and other identifiers — so v3 deliberately keeps the per-class `Include*` methods and adds
+and other identifiers — so the library deliberately keeps the per-class `Include*` methods and adds
`WithCharacters`/`WithAllAscii` rather than forcing OWASP composition or a global 12-char minimum.
## Deprecation / migration shape
@@ -89,6 +89,6 @@ flowchart TD
> of async would be an anti-pattern and would spam every consumer with build warnings. Async exists
> for ergonomics and cancellation only.
-**Why this is better:** every verified gap in `../current-state/api-surface.md` is closed
+**Why this is better:** every gap noted in the v2.1.0 review (`archive/current-state/api-surface.md`) is closed
(`TryNext`/async/DI/presets/appSettings/custom pools), failures become explicit, and existing single
`.Next()` users still work unchanged, giving a gentle upgrade path.
diff --git a/docs/v3-target/architecture.md b/docs/architecture.md
similarity index 73%
rename from docs/v3-target/architecture.md
rename to docs/architecture.md
index 6345600..c594d81 100644
--- a/docs/v3-target/architecture.md
+++ b/docs/architecture.md
@@ -1,7 +1,6 @@
-# v3 Target — Architecture
+# Architecture
-> Multi-target `netstandard2.0;net8.0`, nullable enabled. Aligns with the adjusted plan in
-> `../V3_VERIFICATION.md` §3. As shipped, the existing `IPassword` remains the fluent builder (no
+> Multi-targets `net8.0;net10.0`, nullable enabled. `IPassword` is the fluent builder (no
> separate `IPasswordBuilder`/`Build()`); `Password` implements both `IPassword` and the generation
> contract `IPasswordGenerator`.
@@ -51,7 +50,7 @@ classDiagram
+int NextInt(int maxExclusive)
}
class CryptoRandomSource {
- GetInt32 on net8, rejection sampling on netstandard2.0
+ RandomNumberGenerator.GetInt32
}
class IEntropyEstimator {
<>
@@ -68,13 +67,13 @@ classDiagram
Password ..> PoolEntropyEstimator : EstimateEntropyBits
```
-Key shifts from today:
-- **`IRandomSource` abstraction** wraps the CSPRNG (unbiased `RandomNumberGenerator.GetInt32` on
- `net8.0`; rejection-sampling fallback on `netstandard2.0`). No `static`, disposable-aware,
- injectable. Fixes verified §5.2/§5.3/§5.6 and lets the Guid `Shuffle` be deleted (§5.5).
+Key points:
+- **`IRandomSource` abstraction** wraps the CSPRNG (unbiased `RandomNumberGenerator.GetInt32`). No
+ `static`, injectable — a deterministic `IRandomSource` can be injected in unit tests — and the
+ Guid-based `Shuffle` is gone in favour of Fisher–Yates.
- **`PasswordOptions`** is the DI config object, bindable from `IConfiguration`.
- **Presets** are static factory methods on `Password` that pre-fill the fluent builder.
-- The `[Obsolete]` v2 wrappers are **removed** in v3 (recommended in `../V3_VERIFICATION.md` §4).
+- The `[Obsolete]` v2 wrappers from earlier proposals are not present.
## Target composition (with DI)
@@ -99,19 +98,20 @@ registration is only responsible for wiring `IRandomSource` and default `Passwor
```mermaid
flowchart LR
- subgraph ns["netstandard2.0 (broad reach: .NET Framework, Umbraco)"]
- a["manual rejection sampling"]
- end
- subgraph net8["net8.0 (modern)"]
+ subgraph net8["net8.0"]
b["RandomNumberGenerator.GetInt32"]
end
- IRandomSource --> ns
+ subgraph net10["net10.0"]
+ c["RandomNumberGenerator.GetInt32"]
+ end
IRandomSource --> net8
+ IRandomSource --> net10
```
-`#if` inside `CryptoRandomSource` selects the optimal API per target while keeping one public surface.
+Both targets use the same built-in `RandomNumberGenerator.GetInt32`, so `CryptoRandomSource` needs no
+`#if` and exposes one uniform public surface. (`netstandard2.0`, which required a manual
+rejection-sampling fallback, was dropped in v3 — see the [changelog](../CHANGELOG.md).)
**Why this is better:** removes the `static`/undisposed RNG, makes randomness unbiased and testable
-(inject a deterministic `IRandomSource` in unit tests), keeps .NET Framework users supported, and
-gives modern consumers the fast built-in APIs — addressing verified issues §5.2, §5.3, §5.5, §5.6 and
-gaps §8 (async/DI/multi-target) at the architecture level.
+(inject a deterministic `IRandomSource` in unit tests), and uses the fast, allocation-free built-in
+crypto API on every supported runtime.
diff --git a/docs/V3_REVIEW_AND_DOCUMENTATION.md b/docs/archive/V3_REVIEW_AND_DOCUMENTATION.md
similarity index 98%
rename from docs/V3_REVIEW_AND_DOCUMENTATION.md
rename to docs/archive/V3_REVIEW_AND_DOCUMENTATION.md
index 5a9987d..1c56c1c 100644
--- a/docs/V3_REVIEW_AND_DOCUMENTATION.md
+++ b/docs/archive/V3_REVIEW_AND_DOCUMENTATION.md
@@ -1,5 +1,9 @@
# PasswordGenerator — Full Package Documentation & v3 Review
+> **Archived / historical.** Review of the v2.1.0 source written to plan v3, kept for reference.
+> Where it recommends multi-targeting `netstandard2.0`, note that v3 dropped `netstandard2.0` and
+> targets `net8.0;net10.0`. See the root [`CHANGELOG.md`](../../CHANGELOG.md).
+
> Purpose: a single, self-contained reference for the `PasswordGenerator` NuGet package as it
> stands today (v2.1.0). Written so it can be pasted into a Claude chat to plan v3. It covers
> what the package is, every public API, the internal implementation, confirmed bugs, design
diff --git a/docs/V3_VERIFICATION.md b/docs/archive/V3_VERIFICATION.md
similarity index 97%
rename from docs/V3_VERIFICATION.md
rename to docs/archive/V3_VERIFICATION.md
index 1127482..35f8da2 100644
--- a/docs/V3_VERIFICATION.md
+++ b/docs/archive/V3_VERIFICATION.md
@@ -1,5 +1,10 @@
# PasswordGenerator v3 — Verification Report
+> **Archived / historical (2026-05-24).** Point-in-time analysis of the v2.1.0 source, kept for
+> reference. Its target-framework recommendation (multi-target `netstandard2.0;net8.0`) was **not**
+> followed: v3 dropped `netstandard2.0` and targets `net8.0;net10.0`. See the root
+> [`CHANGELOG.md`](../../CHANGELOG.md).
+
> Companion to `V3_REVIEW_AND_DOCUMENTATION.md` and the v3 Planning Addendum.
> This document does the verification the addendum asked for: every item from the original
> review's bug list (§5) and feature gaps (§8) was re-checked against the **current source**,
diff --git a/docs/v3-target/before-after.md b/docs/archive/before-after.md
similarity index 93%
rename from docs/v3-target/before-after.md
rename to docs/archive/before-after.md
index acb93b9..e3ece99 100644
--- a/docs/v3-target/before-after.md
+++ b/docs/archive/before-after.md
@@ -1,5 +1,9 @@
# v3 Target — Before / After
+> **Archived / historical.** A v3 planning document, kept for reference and superseded by the shipped
+> v3 docs in [`../`](../README.md). The "after" column reflects the early plan; note that v3 ultimately
+> **dropped `netstandard2.0`** (targets `net8.0;net10.0`).
+
Side-by-side of the things that change most, each tied to a verified issue.
## 1. Failure handling
diff --git a/docs/current-state/api-surface.md b/docs/archive/current-state/api-surface.md
similarity index 94%
rename from docs/current-state/api-surface.md
rename to docs/archive/current-state/api-surface.md
index 7f0f6b5..84b1089 100644
--- a/docs/current-state/api-surface.md
+++ b/docs/archive/current-state/api-surface.md
@@ -1,7 +1,7 @@
# Current State — Public API Surface (v2.1.0)
> **Historical.** This describes v2.1.0. The issues noted here are resolved in v3 — see the root
-> [`CHANGELOG.md`](../../CHANGELOG.md) and the [migration guide](../v3-target/migration-v2-to-v3.md).
+> [`CHANGELOG.md`](../../../CHANGELOG.md) and the [migration guide](../../migration-v2-to-v3.md).
What a caller can do today, and how configuration is resolved.
@@ -74,4 +74,4 @@ flowchart TD
| Exclude-ambiguous, per-class minimums, entropy estimate | ✅ |
| `netstandard2.0` + `net8.0` multi-target / nullable | ✅ (netstandard2.0 only) |
-These gaps define the v3 surface in `../v3-target/api-surface.md`.
+These gaps define the v3 surface in `../../api-surface.md`.
diff --git a/docs/current-state/architecture.md b/docs/archive/current-state/architecture.md
similarity index 97%
rename from docs/current-state/architecture.md
rename to docs/archive/current-state/architecture.md
index b903995..3de1bcf 100644
--- a/docs/current-state/architecture.md
+++ b/docs/archive/current-state/architecture.md
@@ -1,7 +1,7 @@
# Current State — Architecture (v2.1.0)
> **Historical.** This describes v2.1.0. The issues noted here are resolved in v3 — see the root
-> [`CHANGELOG.md`](../../CHANGELOG.md) and the [migration guide](../v3-target/migration-v2-to-v3.md).
+> [`CHANGELOG.md`](../../../CHANGELOG.md) and the [migration guide](../../migration-v2-to-v3.md).
`master` @ v2.1.0 · target `netstandard2.0` · no third-party runtime dependencies.
diff --git a/docs/current-state/generation-flow.md b/docs/archive/current-state/generation-flow.md
similarity index 95%
rename from docs/current-state/generation-flow.md
rename to docs/archive/current-state/generation-flow.md
index 7f2afa0..ac54286 100644
--- a/docs/current-state/generation-flow.md
+++ b/docs/archive/current-state/generation-flow.md
@@ -1,7 +1,7 @@
# Current State — Generation Flow (v2.1.0)
> **Historical.** This describes v2.1.0. The issues noted here are resolved in v3 — see the root
-> [`CHANGELOG.md`](../../CHANGELOG.md) and the [migration guide](../v3-target/migration-v2-to-v3.md).
+> [`CHANGELOG.md`](../../../CHANGELOG.md) and the [migration guide](../../migration-v2-to-v3.md).
How `Next()` produces a password today (`Password.cs:114-193`).
@@ -91,4 +91,4 @@ stateDiagram-v2
```
The whole correctness contract hinges on probabilistic retry + string sentinels — the core thing v3
-replaces (see `../v3-target/generation-flow.md`).
+replaces (see `../../generation-flow.md`).
diff --git a/docs/v3-target/implementation-plan.md b/docs/archive/implementation-plan.md
similarity index 97%
rename from docs/v3-target/implementation-plan.md
rename to docs/archive/implementation-plan.md
index 7c6f8e3..56d0299 100644
--- a/docs/v3-target/implementation-plan.md
+++ b/docs/archive/implementation-plan.md
@@ -1,8 +1,12 @@
# v3 Target — Implementation Plan (phased)
-> Actionable, phase-by-phase plan to deliver the v3 design in `architecture.md`,
-> `generation-flow.md`, `api-surface.md`, `configuration-and-di.md` and `before-after.md`.
-> Sequencing follows `roadmap.md`; issue numbers (§5.x / §8) reference `../V3_VERIFICATION.md`.
+> **Archived / historical.** This is a v3 planning document, kept for reference and superseded by the
+> shipped v3 docs in [`../`](../README.md). Note that v3 ultimately **dropped `netstandard2.0`**
+> (targets `net8.0;net10.0`), contrary to the multi-target plan described here.
+
+> Actionable, phase-by-phase plan to deliver the v3 design in `../architecture.md`,
+> `../generation-flow.md`, `../api-surface.md`, `../configuration-and-di.md` and `before-after.md`.
+> Sequencing follows `roadmap.md`; issue numbers (§5.x / §8) reference `V3_VERIFICATION.md`.
## Working principles
diff --git a/docs/v3-target/roadmap.md b/docs/archive/roadmap.md
similarity index 87%
rename from docs/v3-target/roadmap.md
rename to docs/archive/roadmap.md
index 58fa049..5a95746 100644
--- a/docs/v3-target/roadmap.md
+++ b/docs/archive/roadmap.md
@@ -1,6 +1,10 @@
# v3 Target — Roadmap
-Tiered delivery from the adjusted plan in `../V3_VERIFICATION.md` §3. Sequencing only — not committed
+> **Archived / historical.** This is a v3 planning document, kept for reference. It is superseded by
+> the shipped v3 docs in [`../`](../README.md). Note that v3 ultimately **dropped `netstandard2.0`**
+> (targets `net8.0;net10.0`), contrary to the multi-target recommendation below.
+
+Tiered delivery from the adjusted plan in `V3_VERIFICATION.md` §3. Sequencing only — not committed
dates. (Delivered in v3.0.0; see `implementation-plan.md` for where the shipped code diverged.)
## Tiers as phases
@@ -76,7 +80,7 @@ flowchart TD
class D1,D2,D3 q;
```
-See `../V3_VERIFICATION.md` §4 for the reasoning behind each recommendation.
+See `V3_VERIFICATION.md` §4 for the reasoning behind each recommendation.
## Release-note discipline
diff --git a/docs/v3-target/configuration-and-di.md b/docs/configuration-and-di.md
similarity index 90%
rename from docs/v3-target/configuration-and-di.md
rename to docs/configuration-and-di.md
index 2645040..31ed2fc 100644
--- a/docs/v3-target/configuration-and-di.md
+++ b/docs/configuration-and-di.md
@@ -1,4 +1,4 @@
-# v3 Target — Configuration & Dependency Injection
+# Configuration & Dependency Injection
## Settings resolution order
@@ -49,7 +49,7 @@ sequenceDiagram
Svc->>Svc: generator.Generate(5)
```
-Two overloads (answering open question #3 in `../V3_VERIFICATION.md`):
+Two overloads:
```csharp
services.AddPasswordGenerator(options => { options.Length = 20; }); // code
@@ -72,6 +72,6 @@ The fluent API must produce identical results whether the instance is constructe
resolved from the container; DI only changes *how the dependencies are supplied*, not *what the
builder does*.
-**Why this is better:** teams can centralise password policy in `appSettings` (closing verified gap
-§8) without forcing it on every call site, the RNG dependency is wired once, and unit tests can swap
+**Why this is better:** teams can centralise password policy in `appSettings`
+without forcing it on every call site, the RNG dependency is wired once, and unit tests can swap
`IRandomSource` for a deterministic stub.
diff --git a/docs/v3-target/generation-flow.md b/docs/generation-flow.md
similarity index 75%
rename from docs/v3-target/generation-flow.md
rename to docs/generation-flow.md
index 6bd1aed..90d9f96 100644
--- a/docs/v3-target/generation-flow.md
+++ b/docs/generation-flow.md
@@ -1,8 +1,8 @@
-# v3 Target — Generation Flow
+# Generation Flow
-Replaces probabilistic retry + string sentinels with **deterministic construction + exceptions**.
+Uses **deterministic construction + exceptions** rather than probabilistic retry + string sentinels.
-## Target `Next()` / `TryNext()` flow
+## `Next()` / `TryNext()` flow
```mermaid
flowchart TD
@@ -20,14 +20,13 @@ flowchart TD
class Throw bad;
```
-**What changed vs today (`../current-state/generation-flow.md`):**
+**Design properties:**
- No retry loop, no `MaximumAttempts` gamble, **no `"Try again"` string**. Required classes are
- *guaranteed* by construction (fixes the probabilistic guarantee gap, §8).
-- Invalid configuration **throws** (`Next()`) or returns `false` (`TryNext`) — never a fake password
- (fixes §5.1 and §5.8).
+ *guaranteed* by construction.
+- Invalid configuration **throws** (`Next()`) or returns `false` (`TryNext`) — never a fake password.
- Selection uses unbiased `IRandomSource.NextInt(maxExclusive)` — no `% (len-1)` off-by-one, no
- modulo bias (fixes §5.2, §5.3).
-- Shuffle is a real crypto Fisher–Yates, replacing `orderby Guid.NewGuid()` (fixes/cleans §5.5).
+ modulo bias.
+- Shuffle is a real crypto Fisher–Yates, not `orderby Guid.NewGuid()`.
## Deterministic class-seeding (the core idea)
@@ -54,23 +53,23 @@ sequenceDiagram
RNG-->>Gen: index
end
Gen-->>App: Task>
- Note over App,Gen: sync Next()/Generate() still exist
and are fully supported (NOT obsoleted)
+ Note over App,Gen: sync Next()/Generate() also exist
and are fully supported (NOT obsoleted)
```
> Note: generation is CPU-bound, so async mainly helps large-batch ergonomics and cancellation, not
-> raw throughput — the **BenchmarkDotNet** suite (plan §10/§12) exists to prove where async actually
+> raw throughput — the **BenchmarkDotNet** suite exists to prove where async actually
> pays off, with numbers published in every release note.
-## Failure contract — before vs after
+## Failure contract — v2.1.0 vs v3
```mermaid
stateDiagram-v2
- state "v2.1.0 (today)" as Old {
+ state "v2.1.0 (previous)" as Old {
[*] --> RetryLoop
RetryLoop --> OKo: valid
RetryLoop --> StrFail: attempts exhausted → 'Try again' STRING
}
- state "v3 (target)" as New {
+ state "v3 (current)" as New {
[*] --> Validate
Validate --> BuildOK: build guarantees validity
Validate --> Throw: invalid config → exception / false
diff --git a/docs/v3-target/migration-v2-to-v3.md b/docs/migration-v2-to-v3.md
similarity index 96%
rename from docs/v3-target/migration-v2-to-v3.md
rename to docs/migration-v2-to-v3.md
index 67816c7..809e4b9 100644
--- a/docs/v3-target/migration-v2-to-v3.md
+++ b/docs/migration-v2-to-v3.md
@@ -8,6 +8,9 @@ about, plus the new capabilities you can adopt at your own pace.
> **Length range:** valid password lengths are **4–256** characters (the old "8–128" Readme claim was
> never the actual limit).
+> **Runtime requirement:** v3 targets `net8.0` and `net10.0` and **drops `netstandard2.0`**. You need
+> .NET 8 or later. Projects on .NET Framework or other older runtimes should stay on the 2.x line.
+
---
## 1. The one breaking change: error strings → exceptions / `TryNext`
diff --git a/docs/v3-local-nuget-test.md b/docs/v3-local-nuget-test.md
index 3c70469..9f6b377 100644
--- a/docs/v3-local-nuget-test.md
+++ b/docs/v3-local-nuget-test.md
@@ -21,13 +21,13 @@ dotnet pack PasswordGenerator/PasswordGenerator.csproj -c Release -o /tmp/localn
Output (trimmed):
```
-PasswordGenerator -> .../bin/Release/netstandard2.0/PasswordGenerator.dll
PasswordGenerator -> .../bin/Release/net8.0/PasswordGenerator.dll
+PasswordGenerator -> .../bin/Release/net10.0/PasswordGenerator.dll
Successfully created package '/tmp/localnuget/PasswordGenerator.3.0.0.nupkg'.
Successfully created package '/tmp/localnuget/PasswordGenerator.3.0.0.snupkg'.
```
-The package multi-targets `netstandard2.0` and `net8.0`, and a `.snupkg` symbol package is
+The package multi-targets `net8.0` and `net10.0`, and a `.snupkg` symbol package is
produced alongside it. **Expected** — this matches the packaging notes in `CHANGELOG.md`.
> The only build warnings were `SourceLink` notices that the source-control information is
@@ -375,7 +375,7 @@ passwords matching `DefaultBatchCount`.
| # | Scenario | Result | Expected? |
|---|----------|--------|-----------|
-| – | `dotnet pack` (netstandard2.0 + net8.0, .snupkg) | Built | Yes |
+| – | `dotnet pack` (net8.0 + net10.0, .snupkg) | Built | Yes |
| – | Register local feed + install package | Installed, framework-compatible | Yes |
| 1 | Basic default | length 16, all classes | Yes |
| 2 | Explicit length 32 | length 32 | Yes |