From a2c154068591e6586558e8fd3921114f3575229f Mon Sep 17 00:00:00 2001
From: Piotr Zajac
Date: Wed, 22 Apr 2026 23:20:39 +0200
Subject: [PATCH 1/4] docs(backlog): register 23 historical architectural
decisions
Mines the full git history to reconstruct 22 architectural decisions
made from 2017 to 2026 and assigns each an accurate date from the
originating commit. Moves the pre-existing performance benchmark
decision to decision-23. Also adds task-19 (Add performance benchmarks).
Co-Authored-By: Claude Sonnet 4.6
---
...liminate-intermediate-array-allocations.md | 2 +-
.backlog/config.yml | 2 +-
...t-from-AutoFixture-via-provider-pattern.md | 26 ++++++
... Adopt-GitHub-Actions-as-CI-CD-platform.md | 25 ++++++
...ependabot-group-remaining-NuGet-updates.md | 24 ++++++
...egrate-Stryker-mutation-testing-into-CI.md | 24 ++++++
...e-CodeQL-for-C-static-security-analysis.md | 23 +++++
...opt-CodeRabbit-for-AI-powered-PR-review.md | 23 +++++
...- Publish-symbols-and-enable-SourceLink.md | 23 +++++
...e-FOSSA-for-license-compliance-scanning.md | 24 ++++++
...7 - Integrate-Semgrep-for-SAST-scanning.md | 23 +++++
...Add-data-narrowing-parameter-attributes.md | 29 +++++++
... Add-CustomizeWith-parameter-attributes.md | 27 ++++++
...tract-shared-logic-into-a-Core-assembly.md | 25 ++++++
... Adopt-Qodana-for-code-quality-scanning.md | 24 ++++++
...k-for-dependency-vulnerability-scanning.md | 24 ++++++
...ision-22 - Enforce-Conventional-Commits.md | 21 +++++
...formance-benchmark-tooling-and-approach.md | 86 +++++++++++++++++++
...-mocking-frameworks-as-separate-modules.md | 25 ++++++
...led-into-module-packages-not-standalone.md | 23 +++++
...get-netstandard2.0-2.1-and-net472-net48.md | 24 ++++++
...sign-assemblies-full-signing-in-CI-only.md | 24 ++++++
...ual-member-population-via-customization.md | 26 ++++++
...ture-across-member-data-rows-by-default.md | 25 ++++++
...e-GitVersion-in-ContinuousDelivery-mode.md | 24 ++++++
.../task-19 - Add-performance-benchmarks.md | 59 +++++++++++++
26 files changed, 683 insertions(+), 2 deletions(-)
create mode 100644 .backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
create mode 100644 .backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md
create mode 100644 .backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
create mode 100644 .backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md
create mode 100644 .backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md
create mode 100644 .backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
create mode 100644 .backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md
create mode 100644 .backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md
create mode 100644 .backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md
create mode 100644 .backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md
create mode 100644 .backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md
create mode 100644 .backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md
create mode 100644 .backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md
create mode 100644 .backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
create mode 100644 .backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
create mode 100644 .backlog/decisions/decision-23 - Performance-benchmark-tooling-and-approach.md
create mode 100644 .backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md
create mode 100644 .backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md
create mode 100644 .backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md
create mode 100644 .backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md
create mode 100644 .backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md
create mode 100644 .backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md
create mode 100644 .backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md
create mode 100644 .backlog/tasks/task-19 - Add-performance-benchmarks.md
diff --git a/.backlog/completed/task-12 - Eliminate-intermediate-array-allocations.md b/.backlog/completed/task-12 - Eliminate-intermediate-array-allocations.md
index 26009614..7365fc58 100644
--- a/.backlog/completed/task-12 - Eliminate-intermediate-array-allocations.md
+++ b/.backlog/completed/task-12 - Eliminate-intermediate-array-allocations.md
@@ -77,7 +77,7 @@ and allow reuse in both branches. The change:
- Eliminates all three intermediate object allocations present in the original interface-type branch
- Kills the equivalent Stryker mutation by removing the conditional that had no observable effect
-- Keeps the same logical behaviour: concrete types are found via their interfaces; types
+- Keeps the same logical behavior: concrete types are found via their interfaces; types
that are themselves `IEnumerable` are found via the fallback check
- Build passes with zero warnings; all tests pass across `net8.0`, `net472`, and `net48`
diff --git a/.backlog/config.yml b/.backlog/config.yml
index 87f416f1..e6bd274c 100644
--- a/.backlog/config.yml
+++ b/.backlog/config.yml
@@ -1,7 +1,7 @@
project_name: "AutoFixture.XUnit2.AutoMock"
default_status: "To Do"
statuses: ["To Do", "In Progress", "Done"]
-labels: ["sec", "feature", "fix", "ci-cd", "dx", "doc", "agent"]
+labels: ["sec", "feature", "fix", "ci-cd", "dx", "doc", "agent", "performance"]
date_format: yyyy-mm-dd
max_column_width: 20
auto_open_browser: true
diff --git a/.backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md b/.backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
new file mode 100644
index 00000000..fe5a6b12
--- /dev/null
+++ b/.backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
@@ -0,0 +1,26 @@
+---
+id: decision-1
+title: Decouple xUnit from AutoFixture via provider pattern
+date: '2017-01-22'
+status: accepted
+---
+## Context
+
+The library originally derived directly from AutoFixture's `AutoDataAttribute`. This created
+tight coupling between the xUnit integration and AutoFixture internals, making it difficult
+to inject custom behavior, intercept the data-generation pipeline, or add parameter-level
+customization without forking AutoFixture itself.
+
+## Decision
+
+Stop inheriting from AutoFixture attributes. Derive from xUnit's `DataAttribute` instead and
+drive fixture customization through a dedicated `IAutoFixtureAttributeProvider` abstraction.
+The provider receives the configured `IFixture` and returns a `DataAttribute` whose
+`GetData()` resolves test parameters. This separates xUnit's data-supply contract from
+AutoFixture's specimen-creation pipeline.
+
+## Consequences
+
+- Clear boundary between the xUnit layer and AutoFixture: each can evolve independently.
+- The provider pattern enables per-parameter customization via `IParameterCustomizationSource` attributes applied later in the pipeline.
+- All three mock modules (AutoMoq, AutoFakeItEasy, AutoNSubstitute) override only `Customize(IFixture)` — the rest of the pipeline is inherited from Core.
diff --git a/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md b/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md
new file mode 100644
index 00000000..8767df42
--- /dev/null
+++ b/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md
@@ -0,0 +1,25 @@
+---
+id: decision-10
+title: Adopt GitHub Actions as CI/CD platform
+date: '2023-03-24'
+status: accepted
+---
+## Context
+
+The project previously used Travis CI. Travis CI's free tier for open-source projects was
+reduced, and it lacked deep integration with GitHub security features (CodeQL, Dependabot,
+secret scanning). GitHub Actions offered native integration, a richer ecosystem of community
+actions, and free minutes for public repositories.
+
+## Decision
+
+Migrate all CI/CD pipelines from Travis CI to GitHub Actions. Organize workflows into
+reusable modules: `init.yml` (GitVersion, module detection), `build-test-pack.yml`,
+`publish.yml`, `tag.yml`. Run all jobs on `windows-latest` to support `net472`/`net48`
+test slices. Security and quality tools could run as parallel jobs in the same pipeline.
+
+## Consequences
+
+- No external CI service dependency; pipeline definition lives in the same repository.
+- Native access to GitHub security tab, Dependabot alerts, and environment secrets.
+- `windows-latest` runner is required for every job due to `net472`/`net48` constraints, which limits Linux-only optimizations.
diff --git a/.backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md b/.backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
new file mode 100644
index 00000000..780a7a4c
--- /dev/null
+++ b/.backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
@@ -0,0 +1,24 @@
+---
+id: decision-11
+title: Exclude Moq from Dependabot; group remaining NuGet updates
+date: '2023-09-21'
+status: accepted
+---
+## Context
+
+Moq introduced SponsorLink in a controversial update that embedded telemetry and caused
+significant community backlash. Automatically upgrading Moq via Dependabot could silently
+introduce this behavior without a deliberate review.
+
+## Decision
+
+Explicitly exclude `Moq` from Dependabot NuGet updates — it requires manual review and a
+deliberate upgrade decision. Group all other NuGet updates into logical Dependabot groups:
+`xUnit`, `AutoFixture`, `Analyzers`, `Testing`, `Common`, and `Other`. GitHub Actions
+dependencies are also grouped and updated weekly with a `chore(github-actions):` prefix.
+
+## Consequences
+
+- Moq version is frozen until explicitly and consciously upgraded.
+- Grouped updates reduce weekly PR noise from many individual bumps to a handful of grouped PRs.
+- Any future Moq upgrade must be reviewed for licensing, telemetry, and breaking changes before merging.
diff --git a/.backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md b/.backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md
new file mode 100644
index 00000000..2e63edf0
--- /dev/null
+++ b/.backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md
@@ -0,0 +1,24 @@
+---
+id: decision-12
+title: Integrate Stryker mutation testing into CI
+date: '2023-09-30'
+status: accepted
+---
+## Context
+
+Traditional code coverage metrics verify that tests execute code paths, not that they detect
+bugs. A test suite with 100% coverage can still pass while missing every meaningful
+regression. The project needed a higher-confidence quality gate.
+
+## Decision
+
+Integrate Stryker.NET mutation testing into the CI pipeline as a separate, slow-running
+stage. Configuration lives in `stryker-config.yml` at the repository root; the tool is
+invoked from the `src/` directory. Stryker runs are expected before raising a PR rather
+than on every commit.
+
+## Consequences
+
+- Tests are verified to actually kill mutations, raising confidence beyond coverage alone.
+- Slow step kept out of the fast feedback loop; developers opt in before submitting a PR.
+- Surviving mutants become explicit action items, not silent gaps.
diff --git a/.backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md b/.backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md
new file mode 100644
index 00000000..74cf3e72
--- /dev/null
+++ b/.backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md
@@ -0,0 +1,23 @@
+---
+id: decision-13
+title: Enable CodeQL for C# static security analysis
+date: '2023-09-21'
+status: accepted
+---
+## Context
+
+Manual code review is insufficient for systematically catching security vulnerabilities.
+As a NuGet library consumed by other projects, a vulnerability in this library propagates
+to all consumers. An automated static analysis tool integrated with GitHub's security
+infrastructure would provide continuous protection at no operational cost for public repos.
+
+## Decision
+
+Enable GitHub CodeQL for C# as a required CI check. CodeQL runs on push and pull request
+to master, analyzing the full C# codebase for vulnerabilities. Results surface in the
+GitHub Security tab and can block merges when configured as a required status check.
+
+## Consequences
+
+- Continuous vulnerability scanning without additional tooling setup for contributors.
+- Security findings are visible to maintainers in the GitHub Security tab.
\ No newline at end of file
diff --git a/.backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md b/.backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
new file mode 100644
index 00000000..faa2bb60
--- /dev/null
+++ b/.backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
@@ -0,0 +1,23 @@
+---
+id: decision-14
+title: Adopt CodeRabbit for AI-powered PR review
+date: '2026-04-12'
+status: accepted
+---
+## Context
+
+Human code review is thorough for logic but inconsistent at enforcing coding conventions,
+catching subtle anti-patterns, and ensuring every changed file receives attention. A
+first-pass automated reviewer would raise quality before human reviewers engage.
+
+## Decision
+
+Integrate CodeRabbit as an automated AI reviewer on all pull requests. CodeRabbit posts
+inline review comments and a PR summary alongside human reviewers. It is configured to
+understand the project's conventions (BDD naming, AAA structure, attribute ordering rules).
+
+## Consequences
+
+- Every PR receives a first-pass review that catches convention violations and straightforward issues before human review.
+- Human reviewers focus on higher-level concerns: design, correctness, and intent.
+- AI review comments are advisory; maintainers retain final merge authority.
diff --git a/.backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md b/.backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md
new file mode 100644
index 00000000..812197d9
--- /dev/null
+++ b/.backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md
@@ -0,0 +1,23 @@
+---
+id: decision-15
+title: Publish symbols and enable SourceLink
+date: '2023-10-13'
+status: accepted
+---
+## Context
+
+Developers using the library could not step into library code during debugging because no
+symbol packages were published. Stack traces showed only method names without source lines,
+making it difficult to diagnose failures inside the attribute pipeline.
+
+## Decision
+
+Publish `.snupkg` symbol packages to NuGet.org alongside the main `.nupkg` packages.
+Enable `Microsoft.SourceLink.GitHub` so that debuggers automatically fetch the corresponding
+source code from GitHub at the exact commit matching the installed version.
+
+## Consequences
+
+- Consumers can step into library code in their debugger without any manual setup.
+- Stack traces show full source file paths and line numbers for library frames.
+- Symbol packages are published automatically as part of the same CI release step as the main packages.
diff --git a/.backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md b/.backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md
new file mode 100644
index 00000000..f67718cd
--- /dev/null
+++ b/.backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md
@@ -0,0 +1,24 @@
+---
+id: decision-16
+title: Integrate FOSSA for license compliance scanning
+date: '2023-03-31'
+status: accepted
+---
+## Context
+
+Open-source libraries carry licensing obligations for both maintainers and consumers.
+Transitive NuGet dependencies may introduce copyleft or commercially incompatible licenses
+that must be detected before distribution. Manual license auditing does not scale as
+the dependency graph grows.
+
+## Decision
+
+Integrate FOSSA into the CI pipeline to automatically scan all direct and transitive
+NuGet dependencies for license compatibility on every build. FOSSA generates a compliance
+report and raises alerts when new dependencies introduce problematic licenses.
+
+## Consequences
+
+- License violations are caught before packages are published to NuGet.org.
+- A machine-readable compliance report is produced automatically.
+- Reduces legal risk for enterprise consumers who have strict license policies.
diff --git a/.backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md b/.backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md
new file mode 100644
index 00000000..423b9945
--- /dev/null
+++ b/.backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md
@@ -0,0 +1,23 @@
+---
+id: decision-17
+title: Integrate Semgrep for SAST scanning
+date: '2023-12-20'
+status: accepted
+---
+## Context
+
+CodeQL provides GitHub-native SAST with strong C# support, but its rule coverage is focused
+on common vulnerability classes. Semgrep offers complementary community-maintained rule sets
+and custom pattern matching that can catch additional issues CodeQL does not cover.
+
+## Decision
+
+Add Semgrep as a parallel SAST scanning step in CI alongside CodeQL. Use the default
+Semgrep ruleset for C# plus any community rulesets relevant to the project. Semgrep runs
+on push and pull request to master.
+
+## Consequences
+
+- Broader SAST coverage through two independent tools with different rule philosophies.
+- Some overlap with CodeQL is intentional — independent tools reaching the same conclusion increases confidence.
+- Semgrep findings are reviewed in CI alongside CodeQL results; both must pass before merge.
diff --git a/.backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md b/.backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md
new file mode 100644
index 00000000..fb712032
--- /dev/null
+++ b/.backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md
@@ -0,0 +1,29 @@
+---
+id: decision-18
+title: Add data-narrowing parameter attributes
+date: '2023-12-05'
+status: accepted
+---
+## Context
+
+AutoFixture generates arbitrary values for parameters. Tests for boundary conditions, enum
+subsets, or constrained numeric domains needed values from a restricted range, requiring
+manual fixture setup that negated the boilerplate reduction the library provides.
+
+## Decision
+
+Add four parameter-level attributes to Core, all implemented via `IParameterCustomizationSource`:
+
+- `[Except(v1, v2)]` — generate values excluding the specified set
+- `[PickFromValues(v1, v2)]` — pick randomly from a fixed set
+- `[PickFromRange(min, max)]` — generate within a numeric range
+- `[PickNegative]` — generate only negative numeric values
+
+All four are backed by new specimen builders (`RandomExceptValuesGenerator`,
+`RandomFixedValuesGenerator`) and request types (`ExceptValuesRequest`, `FixedValuesRequest`).
+
+## Consequences
+
+- Constrained test data without any manual fixture configuration.
+- All three mock modules benefit automatically via Core.
+- Attributes are combinable with `[Frozen]` in the same parameter list.
diff --git a/.backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md b/.backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md
new file mode 100644
index 00000000..24a3daed
--- /dev/null
+++ b/.backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md
@@ -0,0 +1,27 @@
+---
+id: decision-19
+title: Add CustomizeWith parameter attributes
+date: '2019-08-24'
+status: accepted
+---
+## Context
+
+Users occasionally need to apply a full `ICustomization` to a single parameter without
+affecting the fixture configuration for other parameters. No mechanism existed for
+per-parameter customization injection beyond the built-in `[Frozen]` attribute.
+
+## Decision
+
+Add two parameter-level attributes to Core:
+
+- `[CustomizeWith(typeof(T))]` — activates the specified `ICustomization` for a parameter
+- `[CustomizeWith]` — generic variant; equivalent in behavior, reduces casting boilerplate
+
+Both are implemented via `IParameterCustomizationSource` and apply the customization to the
+fixture immediately before the parameter is resolved.
+
+## Consequences
+
+- Fine-grained customization without polluting the fixture globally for all parameters.
+- The generic variant is the preferred form; the `typeof` variant exists for contexts where generic attributes are unavailable.
+- Combinable with `[Frozen]`, and the other data-narrowing parameter attributes in the same parameter list.
diff --git a/.backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md b/.backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md
new file mode 100644
index 00000000..19a8ba73
--- /dev/null
+++ b/.backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md
@@ -0,0 +1,25 @@
+---
+id: decision-2
+title: Extract shared logic into a Core assembly
+date: '2017-10-28'
+status: accepted
+---
+## Context
+
+As AutoNSubstitute was being added alongside AutoMoq, shared logic (base attributes,
+customizations, provider interfaces, specimen builders) was being duplicated across both
+projects. A single source of truth was needed before the duplication compounded further.
+
+## Decision
+
+Create `Objectivity.AutoFixture.XUnit2.Core` as a shared assembly containing all common
+infrastructure: `AutoDataBaseAttribute`, `InlineAutoDataBaseAttribute`,
+`MemberAutoDataBaseAttribute`, `AutoDataCommonCustomization`, provider interfaces,
+specimen builders, and parameter attributes. Core is bundled into each mock module package
+but is not published as a standalone NuGet package.
+
+## Consequences
+
+- Hub-and-spoke architecture: each mock module overrides only `Customize(IFixture)`; all shared behavior lives in one place.
+- Bugs and improvements to Core benefit all three modules simultaneously.
+- Core cannot be installed by consumers directly — it is an implementation detail.
diff --git a/.backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md b/.backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md
new file mode 100644
index 00000000..ef071401
--- /dev/null
+++ b/.backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md
@@ -0,0 +1,24 @@
+---
+id: decision-20
+title: Adopt Qodana for code quality scanning
+date: '2024-01-16'
+status: accepted
+---
+## Context
+
+The in-build analyser stack (StyleCop, Roslynator, SonarAnalyzer) catches issues at compile
+time but cannot provide holistic quality scoring, trend analysis across the codebase, or a
+consolidated view of maintainability and reliability findings separate from the build log.
+
+## Decision
+
+Integrate JetBrains Qodana into CI as a quality gate. Qodana runs a full Roslyn-based
+inspection pass independently of the build and reports results in a structured format.
+It runs on push and pull request to master and complements CodeQL (security focus) and
+Semgrep (SAST focus) with maintainability and reliability checks.
+
+## Consequences
+
+- Quality metrics are visible in CI as a distinct step, separate from build warnings.
+- Qodana's inspection coverage overlaps partially with the in-build analysers; discrepancies surface issues that build-time suppression may have hidden.
+- JetBrains Qodana supports .NET and is compatible with the `windows-latest` CI runner.
diff --git a/.backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md b/.backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
new file mode 100644
index 00000000..4a8f182d
--- /dev/null
+++ b/.backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
@@ -0,0 +1,24 @@
+---
+id: decision-21
+title: Integrate Snyk for dependency vulnerability scanning
+date: '2024-04-18'
+status: accepted
+---
+## Context
+
+CodeQL scans first-party C# code for vulnerabilities. FOSSA audits licenses. Neither tool
+specifically monitors transitive NuGet dependencies against a live CVE database. A supply
+chain vulnerability in a dependency would go undetected until a manual audit or an external
+report.
+
+## Decision
+
+Integrate Snyk into the CI pipeline to scan NuGet package dependencies for known
+vulnerabilities using Snyk's continuously updated database. Snyk runs on push and pull
+request to master and raises alerts when a dependency version matches a known CVE.
+
+## Consequences
+
+- Supply chain risk is monitored continuously alongside code quality and license compliance.
+- Snyk findings block builds when severity exceeds the configured threshold.
+- Intentional overlap with FOSSA on license scanning is acceptable given the low operational cost of running both.
diff --git a/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md b/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
new file mode 100644
index 00000000..18ccd1e4
--- /dev/null
+++ b/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
@@ -0,0 +1,21 @@
+---
+id: decision-22
+title: Enforce Conventional Commits
+date: '2026-04-22'
+status: accepted
+---
+## Context
+
+Commit messages were inconsistently formatted across contributors.
+
+## Decision
+
+Enforce Conventional Commits format (`[type]([scope]): [description]`) on every commit.
+Enforcement uses a Husky.NET `commit-msg` hook for local validation and CommitLint.Net
+in CI to block merges that contain non-conforming commit messages.
+
+## Consequences
+
+- Consistent, machine-readable commit history across all contributors.
+- Automated changelog generation becomes feasible as a follow-on step.
+- Contributors receive immediate feedback at commit time rather than discovering the issue at CI.
diff --git a/.backlog/decisions/decision-23 - Performance-benchmark-tooling-and-approach.md b/.backlog/decisions/decision-23 - Performance-benchmark-tooling-and-approach.md
new file mode 100644
index 00000000..ea4322d5
--- /dev/null
+++ b/.backlog/decisions/decision-23 - Performance-benchmark-tooling-and-approach.md
@@ -0,0 +1,86 @@
+---
+id: decision-23
+title: Performance benchmark tooling and approach
+date: '2026-04-22'
+status: accepted
+---
+## Context
+
+The library has no performance baseline. Future changes to specimen builders, customizations,
+or dependency upgrades could regress generation speed or allocation behavior with no means of
+detection. A benchmark suite was proposed to establish that baseline.
+
+Four decisions needed to be made before implementation:
+
+1. Which benchmarking tool to use
+2. Whether to benchmark only fixture creation or the full attribute pipeline
+3. Whether to use one benchmark project or one per mock module
+4. Whether to invoke the xUnit runner or call `GetData()` directly
+
+## Decision
+
+### 1. Tooling: BenchmarkDotNet
+
+| Tool | Category | Verdict |
+| --- | --- | --- |
+| BenchmarkDotNet | Micro-benchmark | **Accepted** — handles JIT warm-up, GC pressure, allocation tracking (`[MemoryDiagnoser]`), and Markdown/JSON export; de-facto standard for .NET |
+| dotnet-counters | Runtime diagnostic | Supplementary only — useful for investigating GC pressure after a benchmark reveals a problem |
+| PerfView | ETW profiler | Supplementary only — right tool to pinpoint which specimen builder caused a regression; Windows-only (consistent with CI) |
+| MiniProfiler | ASP.NET app profiler | Rejected — requires a running web host; cannot measure in-process library calls |
+| k6 | HTTP load testing | Rejected — no HTTP surface in this library |
+| Gatling | HTTP load testing | Rejected — same reason as k6 |
+
+Benchmarks are an opt-in `dotnet run --configuration Release` step, never part of `dotnet test`.
+
+### 2. Benchmark scope: full attribute pipeline, not fixture-only
+
+**Fixture-only** (`fixture.Create()` directly): simple, one project, no attribute
+involvement. Measures AutoFixture's overhead rather than this library's unique contribution.
+
+**Full attribute pipeline** (`attribute.GetData(MethodInfo)`): exercises attribute wiring,
+customization composition, and per-parameter specimen resolution — the cost uniquely
+attributable to this library on top of AutoFixture.
+
+**Decision: full attribute pipeline.**
+
+### 3. Project structure: three projects, one per mock module
+
+Benchmarking the full attribute pipeline requires referencing each module's attribute types.
+Placing all three in a single project creates namespace ambiguity and mixed `using` directives
+that obscure which attribute is under test.
+
+Three separate projects mirror the existing test project structure and keep each benchmark
+file unambiguous. The projects are **not** added to any existing `.sln` file.
+
+```text
+src/
+ Objectivity.AutoFixture.XUnit2.AutoMoq.Benchmarks/
+ Objectivity.AutoFixture.XUnit2.AutoFakeItEasy.Benchmarks/
+ Objectivity.AutoFixture.XUnit2.AutoNSubstitute.Benchmarks/
+```
+
+### 4. Execution model: direct `GetData()` calls, not the xUnit runner
+
+xUnit v2's `TheoryDiscoverer` calls `GetData(MethodInfo)` **once** during the discovery
+phase. For non-serializable data (AutoFixture objects containing Castle.Core mock proxies),
+xUnit holds the resolved objects in memory and does not call `GetData()` a second time during
+execution.
+
+The double-call concern is real at the **IDE layer**: Visual Studio Test Explorer and Rider
+each trigger an independent discovery pass on project load and again on "Run". Each benchmark
+iteration therefore calls `GetData()` **twice** to reflect this reality.
+
+`xunit.runner.utility` was considered but rejected: xUnit's invocation machinery dominates
+the numbers and is not attributable to this library, and it requires a circular build
+dependency on a separately compiled test assembly.
+
+Benchmarks use a real `MethodInfo` obtained via reflection from a representative test method
+defined in the benchmark project itself — not a synthetic stub — so that parameter type
+resolution and per-parameter customization attribute scanning are exercised realistically.
+
+## Consequences
+
+- Three new projects to create and maintain.
+- `BenchmarkDotNet.Artifacts/` must be added to `.gitignore`.
+- `CONTRIBUTING.md` needs a `## Running benchmarks` section.
+- Future CI benchmarking (PR delta comments) is a separate follow-on task.
diff --git a/.backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md b/.backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md
new file mode 100644
index 00000000..9106e0bb
--- /dev/null
+++ b/.backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md
@@ -0,0 +1,25 @@
+---
+id: decision-3
+title: Support multiple mocking frameworks as separate modules
+date: '2017-11-07'
+status: accepted
+---
+## Context
+
+AutoFixture supports multiple mock backends (Moq, NSubstitute, FakeItEasy). Teams using
+only one mocking framework should not be forced to take a transitive dependency on the
+others, and each framework should be independently versionable.
+
+## Decision
+
+Build a separate NuGet package for each mocking framework — `AutoMoq`, `AutoFakeItEasy`,
+`AutoNSubstitute` — each depending on Core. Every module exposes the same three attribute
+shapes (`[AutoMockData]`, `[InlineAutoMockData]`, `[MemberAutoMockData]`) prefixed with
+the framework name, and overrides only `Customize(IFixture)` to apply the framework-specific
+`ICustomization`.
+
+## Consequences
+
+- Users install only the package matching their mocking framework of choice.
+- Each module is independently publishable and versionable.
+- Three separate project pairs must be maintained, but the surface area per project is minimal (one `Customize` override each).
diff --git a/.backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md b/.backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md
new file mode 100644
index 00000000..b015a058
--- /dev/null
+++ b/.backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md
@@ -0,0 +1,23 @@
+---
+id: decision-4
+title: 'Publish Core bundled into module packages, not standalone'
+date: '2018-07-17'
+status: accepted
+---
+## Context
+
+Core contains all shared infrastructure but has no value in isolation — it requires a mock
+backend to function. Publishing it as a standalone NuGet package would invite consumers to
+install it alone, producing a broken setup with no data generation capability.
+
+## Decision
+
+Do not publish `Objectivity.AutoFixture.XUnit2.Core` as a standalone NuGet package. Bundle
+it into each of the three mock module packages via project reference so that installing any
+one module brings Core along transparently.
+
+## Consequences
+
+- Consumers see exactly three packages on NuGet.org; no partial installations are possible.
+- The NuGet dependency graph is clean: one package per mocking framework, nothing else required.
+- Core is an implementation detail; its public API is effectively internal to the three published packages.
diff --git a/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md b/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md
new file mode 100644
index 00000000..12da3421
--- /dev/null
+++ b/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md
@@ -0,0 +1,24 @@
+---
+id: decision-5
+title: Target netstandard2.0/2.1 and net472/net48
+date: '2018-04-17'
+status: accepted
+---
+## Context
+
+Many enterprise teams still run test suites on .NET Framework 4.7.2 and 4.8. Targeting only
+modern .NET would exclude those users, while targeting only .NET Framework would prevent
+adoption on .NET Core and later runtimes.
+
+## Decision
+
+Library projects target `netstandard2.0`, `netstandard2.1`, `net472`, and `net48`.
+Test projects target `.NET Core`, `net472`, and `net48` to validate the full compatibility
+matrix on each CI run.
+
+## Consequences
+
+- Maximum compatibility across the .NET ecosystem.
+- CI must run on `windows-latest` to execute the `net472`/`net48` test slices.
+- The test matrix is larger but covers the full surface claimed by the library.
+- When .NET Framework support is eventually dropped, all four framework targets in library `.csproj` files must be updated simultaneously.
diff --git a/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md b/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md
new file mode 100644
index 00000000..b3006aee
--- /dev/null
+++ b/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md
@@ -0,0 +1,24 @@
+---
+id: decision-6
+title: Delay-sign assemblies; full signing in CI only
+date: '2018-07-10'
+status: accepted
+---
+## Context
+
+Strong-name signing is required for .NET Framework compatibility and NuGet package trust.
+Committing the private signing key to source control would expose it to every contributor
+and fork, creating a security risk.
+
+## Decision
+
+Use delay signing locally: only `public.snk` (the public key) is committed to source
+control. Full signing is performed in CI using the `SIGNING_KEY` secret stored in GitHub
+Secrets. Developers can build and run tests locally without the private key; the fully
+signed assemblies are produced only on the CI server during the release pipeline.
+
+## Consequences
+
+- The private signing key is never exposed in source control.
+- Local builds produce delay-signed assemblies that work for development and testing but not for direct NuGet consumption.
+- Loss of the `SIGNING_KEY` secret would break releases and require re-establishing trust with a new key.
diff --git a/.backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md b/.backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md
new file mode 100644
index 00000000..a2b937aa
--- /dev/null
+++ b/.backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md
@@ -0,0 +1,26 @@
+---
+id: decision-7
+title: Suppress virtual member population via customization
+date: '2018-09-20'
+status: accepted
+---
+## Context
+
+When AutoFixture creates objects for classes that implement interfaces, the CLR marks the
+interface method implementations as `virtual sealed`. AutoFixture attempts to populate those
+virtual properties, which conflicts with mock proxy behavior: the proxy already owns those
+members, and AutoFixture's attempt to set them can throw or produce unexpected state.
+
+## Decision
+
+Introduce `IgnoreVirtualMembersSpecimenBuilder` (returning `OmitSpecimen` for virtual
+`PropertyInfo` requests) and expose it through `IgnoreVirtualMembersCustomization`. All
+three base attributes accept `IgnoreVirtualMembers = true` to activate this behavior
+per attribute instance. A `[IgnoreVirtualMembers]` parameter-level attribute applies it
+to a specific parameter.
+
+## Consequences
+
+- Mock proxies are not interfered with by AutoFixture's property-population pass.
+- Opt-in per attribute — tests that genuinely need virtual properties populated are unaffected.
+- Reduces the most common setup error for users working with interface-implementing types.
diff --git a/.backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md b/.backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md
new file mode 100644
index 00000000..18466609
--- /dev/null
+++ b/.backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md
@@ -0,0 +1,25 @@
+---
+id: decision-8
+title: Share fixture across member data rows by default
+date: '2017-01-20'
+status: accepted
+---
+## Context
+
+`[MemberAutoMockData]` combines static member data with auto-generated parameters. When a
+member provides multiple data rows, each row could receive its own fresh `IFixture` instance
+(producing different mock objects per row) or share one fixture across all rows (preserving
+mock identity). The right default was non-obvious.
+
+## Decision
+
+Default `MemberAutoDataBaseAttribute.ShareFixture = true`. The same `IFixture` instance —
+with all its customizations and registered mock objects — is reused across every data row
+produced by the member. Users can opt out with `ShareFixture = false` when independent
+fixtures per row are required.
+
+## Consequences
+
+- Related test cases receive consistent mock instances, which is the expected behavior in the majority of scenarios where member data provides complementary inputs.
+- This is the unique differentiator of `[MemberAutoMockData]` over plain `[MemberData]` with manual fixture setup.
+- Tests that need isolation between rows require explicit `ShareFixture = false`.
diff --git a/.backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md b/.backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md
new file mode 100644
index 00000000..164d9e5b
--- /dev/null
+++ b/.backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md
@@ -0,0 +1,24 @@
+---
+id: decision-9
+title: Use GitVersion in ContinuousDelivery mode
+date: '2023-08-30'
+status: accepted
+---
+## Context
+
+Manual version management in `.csproj` files is error-prone: versions can be forgotten,
+duplicated across projects, or incremented inconsistently. The project needed an automated
+versioning strategy aligned with semantic versioning and the git workflow.
+
+## Decision
+
+Adopt GitVersion in `ContinuousDelivery` mode. Versions are computed entirely from git
+history: commits on `master` produce stable semantic versions; feature branches produce
+pre-release suffixes. No `` element is set manually in any `.csproj`. The
+`Directory.Build.props` default of `1.0.0.0` is a local fallback only, never used in CI.
+
+## Consequences
+
+- Version is always derivable from git history and requires no manual intervention.
+- CI sets the version automatically before build and pack steps.
+- Developers must follow branching conventions for GitVersion to compute the correct version; ad-hoc commits directly to master advance the stable version.
diff --git a/.backlog/tasks/task-19 - Add-performance-benchmarks.md b/.backlog/tasks/task-19 - Add-performance-benchmarks.md
new file mode 100644
index 00000000..b9378bfe
--- /dev/null
+++ b/.backlog/tasks/task-19 - Add-performance-benchmarks.md
@@ -0,0 +1,59 @@
+---
+id: TASK-19
+title: Add performance benchmarks
+status: To Do
+assignee:
+ - piotrzajac
+ - claude
+created_date: '2026-04-22'
+updated_date: '2026-04-22'
+labels:
+ - performance
+dependencies: []
+priority: low
+---
+
+## Context
+
+Tooling selection, benchmark scope, project structure, and xUnit execution model analysis are
+recorded in [DECISION-1 — Performance benchmark tooling and approach](../decisions/decision-23%20-%20Performance-benchmark-tooling-and-approach.md).
+
+**Summary of decisions:**
+
+- **Tool:** BenchmarkDotNet with `[MemoryDiagnoser]`
+- **Scope:** full attribute pipeline (`GetData(MethodInfo)`) — not fixture-only
+- **Structure:** three projects, one per mock module; not added to any `.sln` file
+- **Execution model:** call `GetData()` directly twice per iteration (IDE double-discovery);
+ use a real `MethodInfo` from a representative method defined in the benchmark project
+
+### Benchmark scenarios (identical across all three projects, different attribute types)
+
+| Benchmark class | Scenario |
+| --- | --- |
+| `PocoGenerationBenchmark` | Flat POCO with 5 primitive properties |
+| `DeepGraphGenerationBenchmark` | Object graph 4 levels deep with collections |
+| `FrozenVsUnfrozenBenchmark` | Same type with and without `[Frozen]` |
+| `VirtualMembersBenchmark` | Many virtual properties, suppressed vs not |
+
+### Running benchmarks
+
+```bash
+dotnet run --project src/Objectivity.AutoFixture.XUnit2.AutoMoq.Benchmarks \
+ --configuration Release -- --filter '*'
+```
+
+(Repeat for `AutoFakeItEasy.Benchmarks` and `AutoNSubstitute.Benchmarks`.)
+
+---
+
+## Acceptance Criteria
+
+
+- [ ] #1 Three benchmark projects exist under `src/`, one per mock module, each building with `dotnet build --configuration Release`
+- [ ] #2 BenchmarkDotNet is the only benchmarking dependency; no test framework packages are added
+- [ ] #3 All four benchmark classes are implemented in each project using that module's own attribute types, each carrying `[MemoryDiagnoser]`
+- [ ] #4 Each benchmark calls `GetData()` twice per iteration using a real `MethodInfo` resolved in `[GlobalSetup]`
+- [ ] #5 Running any project with `--configuration Release -- --filter '*'` produces a valid Markdown summary table
+- [ ] #6 `BenchmarkDotNet.Artifacts/` is added to `.gitignore`
+- [ ] #7 A `## Running benchmarks` section is added to `CONTRIBUTING.md` explaining the invocation command for each project
+
From ef38ddaa4a28581e389f3a57445c4a214b1ad81c Mon Sep 17 00:00:00 2001
From: Piotr Zajac
Date: Wed, 22 Apr 2026 23:25:01 +0200
Subject: [PATCH 2/4] chore(backlog): Complete tasks
---
... Add-backlog-CLI-reference-to-AGENTS.md.md | 38 +++++++++----------
.../task-16 - Fix-CodeQL-warnings.md | 0
...nsolidate-duplicate-Facts-into-Theories.md | 0
3 files changed, 19 insertions(+), 19 deletions(-)
rename .backlog/{tasks => completed}/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md (95%)
rename .backlog/{tasks => completed}/task-16 - Fix-CodeQL-warnings.md (100%)
rename .backlog/{tasks => completed}/task-17 - Consolidate-duplicate-Facts-into-Theories.md (100%)
diff --git a/.backlog/tasks/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md b/.backlog/completed/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md
similarity index 95%
rename from .backlog/tasks/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md
rename to .backlog/completed/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md
index 1649da30..70629046 100644
--- a/.backlog/tasks/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md
+++ b/.backlog/completed/task-15 - Add-backlog-CLI-reference-to-AGENTS.md.md
@@ -1,19 +1,19 @@
----
-id: TASK-15
-title: Add backlog CLI reference to AGENTS.md
-status: Done
-assignee:
- - claude
- - piotrzajac
-created_date: '2026-04-17 12:13'
-updated_date: '2026-04-17 12:18'
-labels: []
-dependencies: []
-priority: low
----
-
-## Acceptance Criteria
-
-- [x] #1 Backlog CLI Reference section added to AGENTS.md before the MCP guidelines block
-- [x] #2 Section survives future backlog agents --update-instructions runs
-
+---
+id: TASK-15
+title: Add backlog CLI reference to AGENTS.md
+status: Done
+assignee:
+ - claude
+ - piotrzajac
+created_date: '2026-04-17 12:13'
+updated_date: '2026-04-17 12:18'
+labels: []
+dependencies: []
+priority: low
+---
+
+## Acceptance Criteria
+
+- [x] #1 Backlog CLI Reference section added to AGENTS.md before the MCP guidelines block
+- [x] #2 Section survives future backlog agents --update-instructions runs
+
diff --git a/.backlog/tasks/task-16 - Fix-CodeQL-warnings.md b/.backlog/completed/task-16 - Fix-CodeQL-warnings.md
similarity index 100%
rename from .backlog/tasks/task-16 - Fix-CodeQL-warnings.md
rename to .backlog/completed/task-16 - Fix-CodeQL-warnings.md
diff --git a/.backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md b/.backlog/completed/task-17 - Consolidate-duplicate-Facts-into-Theories.md
similarity index 100%
rename from .backlog/tasks/task-17 - Consolidate-duplicate-Facts-into-Theories.md
rename to .backlog/completed/task-17 - Consolidate-duplicate-Facts-into-Theories.md
From cbc7c04dbabb0059f22744973c70563b9db49930 Mon Sep 17 00:00:00 2001
From: Piotr Zajac
Date: Wed, 22 Apr 2026 23:53:25 +0200
Subject: [PATCH 3/4] docs(backlog): reorder decisions chronologically by date
Renumbers all 23 decisions so their IDs match the chronological
order implied by the date field. Decision-23 (Performance benchmark)
remains unchanged as the most recent entry.
Co-Authored-By: Claude Sonnet 4.6
---
...ure-across-member-data-rows-by-default.md} | 12 +++------
... Adopt-GitHub-Actions-as-CI-CD-platform.md | 10 ++-----
...-FOSSA-for-license-compliance-scanning.md} | 11 +++-----
...-GitVersion-in-ContinuousDelivery-mode.md} | 11 +++-----
...e-CodeQL-for-C-static-security-analysis.md | 23 ----------------
...pendabot-group-remaining-NuGet-updates.md} | 13 ++++------
...e-CodeQL-for-C-static-security-analysis.md | 18 +++++++++++++
...grate-Stryker-mutation-testing-into-CI.md} | 10 +++----
... Publish-symbols-and-enable-SourceLink.md} | 9 +++----
...dd-data-narrowing-parameter-attributes.md} | 11 +++-----
... - Integrate-Semgrep-for-SAST-scanning.md} | 10 +++----
... Adopt-Qodana-for-code-quality-scanning.md | 19 ++++++++++++++
...-from-AutoFixture-via-provider-pattern.md} | 14 +++-------
... Adopt-Qodana-for-code-quality-scanning.md | 24 -----------------
...-for-dependency-vulnerability-scanning.md} | 11 +++-----
...pt-CodeRabbit-for-AI-powered-PR-review.md} | 10 +++----
...ision-22 - Enforce-Conventional-Commits.md | 7 +++--
...ract-shared-logic-into-a-Core-assembly.md} | 12 +++------
...mocking-frameworks-as-separate-modules.md} | 12 +++------
...get-netstandard2.0-2.1-and-net472-net48.md | 7 ++---
...sign-assemblies-full-signing-in-CI-only.md | 9 ++-----
...ed-into-module-packages-not-standalone.md} | 10 +++----
...ual-member-population-via-customization.md | 26 -------------------
...ual-member-population-via-customization.md | 19 ++++++++++++++
...Add-CustomizeWith-parameter-attributes.md} | 11 +++-----
.../task-19 - Add-performance-benchmarks.md | 2 +-
26 files changed, 116 insertions(+), 215 deletions(-)
rename .backlog/decisions/{decision-8 - Share-fixture-across-member-data-rows-by-default.md => decision-1 - Share-fixture-across-member-data-rows-by-default.md} (55%)
rename .backlog/decisions/{decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md => decision-11 - Integrate-FOSSA-for-license-compliance-scanning.md} (53%)
rename .backlog/decisions/{decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md => decision-12 - Use-GitVersion-in-ContinuousDelivery-mode.md} (53%)
delete mode 100644 .backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md
rename .backlog/decisions/{decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md => decision-13 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md} (53%)
create mode 100644 .backlog/decisions/decision-14 - Enable-CodeQL-for-C-static-security-analysis.md
rename .backlog/decisions/{decision-12 - Integrate-Stryker-mutation-testing-into-CI.md => decision-15 - Integrate-Stryker-mutation-testing-into-CI.md} (57%)
rename .backlog/decisions/{decision-15 - Publish-symbols-and-enable-SourceLink.md => decision-16 - Publish-symbols-and-enable-SourceLink.md} (66%)
rename .backlog/decisions/{decision-18 - Add-data-narrowing-parameter-attributes.md => decision-17 - Add-data-narrowing-parameter-attributes.md} (64%)
rename .backlog/decisions/{decision-17 - Integrate-Semgrep-for-SAST-scanning.md => decision-18 - Integrate-Semgrep-for-SAST-scanning.md} (60%)
create mode 100644 .backlog/decisions/decision-19 - Adopt-Qodana-for-code-quality-scanning.md
rename .backlog/decisions/{decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md => decision-2 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md} (56%)
delete mode 100644 .backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md
rename .backlog/decisions/{decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md => decision-20 - Integrate-Snyk-for-dependency-vulnerability-scanning.md} (50%)
rename .backlog/decisions/{decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md => decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md} (57%)
rename .backlog/decisions/{decision-2 - Extract-shared-logic-into-a-Core-assembly.md => decision-3 - Extract-shared-logic-into-a-Core-assembly.md} (50%)
rename .backlog/decisions/{decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md => decision-4 - Support-multiple-mocking-frameworks-as-separate-modules.md} (51%)
rename .backlog/decisions/{decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md => decision-7 - Publish-Core-bundled-into-module-packages-not-standalone.md} (60%)
delete mode 100644 .backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md
create mode 100644 .backlog/decisions/decision-8 - Suppress-virtual-member-population-via-customization.md
rename .backlog/decisions/{decision-19 - Add-CustomizeWith-parameter-attributes.md => decision-9 - Add-CustomizeWith-parameter-attributes.md} (64%)
diff --git a/.backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md b/.backlog/decisions/decision-1 - Share-fixture-across-member-data-rows-by-default.md
similarity index 55%
rename from .backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md
rename to .backlog/decisions/decision-1 - Share-fixture-across-member-data-rows-by-default.md
index 18466609..845e7902 100644
--- a/.backlog/decisions/decision-8 - Share-fixture-across-member-data-rows-by-default.md
+++ b/.backlog/decisions/decision-1 - Share-fixture-across-member-data-rows-by-default.md
@@ -1,22 +1,16 @@
---
-id: decision-8
+id: decision-1
title: Share fixture across member data rows by default
date: '2017-01-20'
status: accepted
---
## Context
-`[MemberAutoMockData]` combines static member data with auto-generated parameters. When a
-member provides multiple data rows, each row could receive its own fresh `IFixture` instance
-(producing different mock objects per row) or share one fixture across all rows (preserving
-mock identity). The right default was non-obvious.
+`[MemberAutoMockData]` combines static member data with auto-generated parameters. When a member provides multiple data rows, each row could receive its own fresh `IFixture` instance (producing different mock objects per row) or share one fixture across all rows (preserving mock identity). The right default was non-obvious.
## Decision
-Default `MemberAutoDataBaseAttribute.ShareFixture = true`. The same `IFixture` instance —
-with all its customizations and registered mock objects — is reused across every data row
-produced by the member. Users can opt out with `ShareFixture = false` when independent
-fixtures per row are required.
+Default `MemberAutoDataBaseAttribute.ShareFixture = true`. The same `IFixture` instance — with all its customizations and registered mock objects — is reused across every data row produced by the member. Users can opt out with `ShareFixture = false` when independent fixtures per row are required.
## Consequences
diff --git a/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md b/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md
index 8767df42..bdee9faa 100644
--- a/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md
+++ b/.backlog/decisions/decision-10 - Adopt-GitHub-Actions-as-CI-CD-platform.md
@@ -6,17 +6,11 @@ status: accepted
---
## Context
-The project previously used Travis CI. Travis CI's free tier for open-source projects was
-reduced, and it lacked deep integration with GitHub security features (CodeQL, Dependabot,
-secret scanning). GitHub Actions offered native integration, a richer ecosystem of community
-actions, and free minutes for public repositories.
+The project previously used Travis CI. Travis CI's free tier for open-source projects was reduced, and it lacked deep integration with GitHub security features (CodeQL, Dependabot, secret scanning). GitHub Actions offered native integration, a richer ecosystem of community actions, and free minutes for public repositories.
## Decision
-Migrate all CI/CD pipelines from Travis CI to GitHub Actions. Organize workflows into
-reusable modules: `init.yml` (GitVersion, module detection), `build-test-pack.yml`,
-`publish.yml`, `tag.yml`. Run all jobs on `windows-latest` to support `net472`/`net48`
-test slices. Security and quality tools could run as parallel jobs in the same pipeline.
+Migrate all CI/CD pipelines from Travis CI to GitHub Actions. Organize workflows into reusable modules: `init.yml` (GitVersion, module detection), `build-test-pack.yml`, `publish.yml`, `tag.yml`. Run all jobs on `windows-latest` to support `net472`/`net48` test slices. Security and quality tools could run as parallel jobs in the same pipeline.
## Consequences
diff --git a/.backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md b/.backlog/decisions/decision-11 - Integrate-FOSSA-for-license-compliance-scanning.md
similarity index 53%
rename from .backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md
rename to .backlog/decisions/decision-11 - Integrate-FOSSA-for-license-compliance-scanning.md
index f67718cd..220b4813 100644
--- a/.backlog/decisions/decision-16 - Integrate-FOSSA-for-license-compliance-scanning.md
+++ b/.backlog/decisions/decision-11 - Integrate-FOSSA-for-license-compliance-scanning.md
@@ -1,21 +1,16 @@
---
-id: decision-16
+id: decision-11
title: Integrate FOSSA for license compliance scanning
date: '2023-03-31'
status: accepted
---
## Context
-Open-source libraries carry licensing obligations for both maintainers and consumers.
-Transitive NuGet dependencies may introduce copyleft or commercially incompatible licenses
-that must be detected before distribution. Manual license auditing does not scale as
-the dependency graph grows.
+Open-source libraries carry licensing obligations for both maintainers and consumers. Transitive NuGet dependencies may introduce copyleft or commercially incompatible licenses that must be detected before distribution. Manual license auditing does not scale as the dependency graph grows.
## Decision
-Integrate FOSSA into the CI pipeline to automatically scan all direct and transitive
-NuGet dependencies for license compatibility on every build. FOSSA generates a compliance
-report and raises alerts when new dependencies introduce problematic licenses.
+Integrate FOSSA into the CI pipeline to automatically scan all direct and transitive NuGet dependencies for license compatibility on every build. FOSSA generates a compliance report and raises alerts when new dependencies introduce problematic licenses.
## Consequences
diff --git a/.backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md b/.backlog/decisions/decision-12 - Use-GitVersion-in-ContinuousDelivery-mode.md
similarity index 53%
rename from .backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md
rename to .backlog/decisions/decision-12 - Use-GitVersion-in-ContinuousDelivery-mode.md
index 164d9e5b..6e494f4f 100644
--- a/.backlog/decisions/decision-9 - Use-GitVersion-in-ContinuousDelivery-mode.md
+++ b/.backlog/decisions/decision-12 - Use-GitVersion-in-ContinuousDelivery-mode.md
@@ -1,21 +1,16 @@
---
-id: decision-9
+id: decision-12
title: Use GitVersion in ContinuousDelivery mode
date: '2023-08-30'
status: accepted
---
## Context
-Manual version management in `.csproj` files is error-prone: versions can be forgotten,
-duplicated across projects, or incremented inconsistently. The project needed an automated
-versioning strategy aligned with semantic versioning and the git workflow.
+Manual version management in `.csproj` files is error-prone: versions can be forgotten, duplicated across projects, or incremented inconsistently. The project needed an automated versioning strategy aligned with semantic versioning and the git workflow.
## Decision
-Adopt GitVersion in `ContinuousDelivery` mode. Versions are computed entirely from git
-history: commits on `master` produce stable semantic versions; feature branches produce
-pre-release suffixes. No `` element is set manually in any `.csproj`. The
-`Directory.Build.props` default of `1.0.0.0` is a local fallback only, never used in CI.
+Adopt GitVersion in `ContinuousDelivery` mode. Versions are computed entirely from git history: commits on `master` produce stable semantic versions; feature branches produce pre-release suffixes. No `` element is set manually in any `.csproj`. The `Directory.Build.props` default of `1.0.0.0` is a local fallback only, never used in CI.
## Consequences
diff --git a/.backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md b/.backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md
deleted file mode 100644
index 74cf3e72..00000000
--- a/.backlog/decisions/decision-13 - Enable-CodeQL-for-C-static-security-analysis.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-id: decision-13
-title: Enable CodeQL for C# static security analysis
-date: '2023-09-21'
-status: accepted
----
-## Context
-
-Manual code review is insufficient for systematically catching security vulnerabilities.
-As a NuGet library consumed by other projects, a vulnerability in this library propagates
-to all consumers. An automated static analysis tool integrated with GitHub's security
-infrastructure would provide continuous protection at no operational cost for public repos.
-
-## Decision
-
-Enable GitHub CodeQL for C# as a required CI check. CodeQL runs on push and pull request
-to master, analyzing the full C# codebase for vulnerabilities. Results surface in the
-GitHub Security tab and can block merges when configured as a required status check.
-
-## Consequences
-
-- Continuous vulnerability scanning without additional tooling setup for contributors.
-- Security findings are visible to maintainers in the GitHub Security tab.
\ No newline at end of file
diff --git a/.backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md b/.backlog/decisions/decision-13 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
similarity index 53%
rename from .backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
rename to .backlog/decisions/decision-13 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
index 780a7a4c..43567881 100644
--- a/.backlog/decisions/decision-11 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
+++ b/.backlog/decisions/decision-13 - Exclude-Moq-from-Dependabot-group-remaining-NuGet-updates.md
@@ -1,21 +1,18 @@
---
-id: decision-11
+id: decision-13
title: Exclude Moq from Dependabot; group remaining NuGet updates
date: '2023-09-21'
status: accepted
---
## Context
-Moq introduced SponsorLink in a controversial update that embedded telemetry and caused
-significant community backlash. Automatically upgrading Moq via Dependabot could silently
-introduce this behavior without a deliberate review.
+Moq introduced SponsorLink in a controversial update that embedded telemetry and caused significant community backlash. Automatically upgrading Moq via Dependabot could silently introduce this behavior without a deliberate review.
## Decision
-Explicitly exclude `Moq` from Dependabot NuGet updates — it requires manual review and a
-deliberate upgrade decision. Group all other NuGet updates into logical Dependabot groups:
-`xUnit`, `AutoFixture`, `Analyzers`, `Testing`, `Common`, and `Other`. GitHub Actions
-dependencies are also grouped and updated weekly with a `chore(github-actions):` prefix.
+Explicitly exclude `Moq` from Dependabot NuGet updates — it requires manual review and a deliberate upgrade decision.
+Group all other NuGet updates into logical Dependabot groups: `xUnit`, `AutoFixture`, `Analyzers`, `Testing`, `Common`, and `Other`.
+GitHub Actions dependencies are also grouped and updated weekly with a `chore(github-actions):` prefix.
## Consequences
diff --git a/.backlog/decisions/decision-14 - Enable-CodeQL-for-C-static-security-analysis.md b/.backlog/decisions/decision-14 - Enable-CodeQL-for-C-static-security-analysis.md
new file mode 100644
index 00000000..3c467c88
--- /dev/null
+++ b/.backlog/decisions/decision-14 - Enable-CodeQL-for-C-static-security-analysis.md
@@ -0,0 +1,18 @@
+---
+id: decision-14
+title: Enable CodeQL for C# static security analysis
+date: '2023-09-21'
+status: accepted
+---
+## Context
+
+Manual code review is insufficient for systematically catching security vulnerabilities. As a NuGet library consumed by other projects, a vulnerability in this library propagates to all consumers. An automated static analysis tool integrated with GitHub's security infrastructure would provide continuous protection at no operational cost for public repos.
+
+## Decision
+
+Enable GitHub CodeQL for C# as a required CI check. CodeQL runs on push and pull request to master, analyzing the full C# codebase for vulnerabilities. Results surface in the GitHub Security tab and can block merges when configured as a required status check.
+
+## Consequences
+
+- Continuous vulnerability scanning without additional tooling setup for contributors.
+- Security findings are visible to maintainers in the GitHub Security tab.
diff --git a/.backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md b/.backlog/decisions/decision-15 - Integrate-Stryker-mutation-testing-into-CI.md
similarity index 57%
rename from .backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md
rename to .backlog/decisions/decision-15 - Integrate-Stryker-mutation-testing-into-CI.md
index 2e63edf0..7c477674 100644
--- a/.backlog/decisions/decision-12 - Integrate-Stryker-mutation-testing-into-CI.md
+++ b/.backlog/decisions/decision-15 - Integrate-Stryker-mutation-testing-into-CI.md
@@ -1,20 +1,16 @@
---
-id: decision-12
+id: decision-15
title: Integrate Stryker mutation testing into CI
date: '2023-09-30'
status: accepted
---
## Context
-Traditional code coverage metrics verify that tests execute code paths, not that they detect
-bugs. A test suite with 100% coverage can still pass while missing every meaningful
-regression. The project needed a higher-confidence quality gate.
+Traditional code coverage metrics verify that tests execute code paths, not that they detect bugs. A test suite with 100% coverage can still pass while missing every meaningful regression. The project needed a higher-confidence quality gate.
## Decision
-Integrate Stryker.NET mutation testing into the CI pipeline as a separate, slow-running
-stage. Configuration lives in `stryker-config.yml` at the repository root; the tool is
-invoked from the `src/` directory. Stryker runs are expected before raising a PR rather
+Integrate Stryker.NET mutation testing into the CI pipeline as a separate, slow-running stage. Configuration lives in `stryker-config.yml` at the repository root; the tool is invoked from the `src/` directory. Stryker runs are expected before raising a PR rather
than on every commit.
## Consequences
diff --git a/.backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md b/.backlog/decisions/decision-16 - Publish-symbols-and-enable-SourceLink.md
similarity index 66%
rename from .backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md
rename to .backlog/decisions/decision-16 - Publish-symbols-and-enable-SourceLink.md
index 812197d9..f7d2ed15 100644
--- a/.backlog/decisions/decision-15 - Publish-symbols-and-enable-SourceLink.md
+++ b/.backlog/decisions/decision-16 - Publish-symbols-and-enable-SourceLink.md
@@ -1,20 +1,17 @@
---
-id: decision-15
+id: decision-16
title: Publish symbols and enable SourceLink
date: '2023-10-13'
status: accepted
---
## Context
-Developers using the library could not step into library code during debugging because no
-symbol packages were published. Stack traces showed only method names without source lines,
-making it difficult to diagnose failures inside the attribute pipeline.
+Developers using the library could not step into library code during debugging because no symbol packages were published. Stack traces showed only method names without source lines, making it difficult to diagnose failures inside the attribute pipeline.
## Decision
Publish `.snupkg` symbol packages to NuGet.org alongside the main `.nupkg` packages.
-Enable `Microsoft.SourceLink.GitHub` so that debuggers automatically fetch the corresponding
-source code from GitHub at the exact commit matching the installed version.
+Enable `Microsoft.SourceLink.GitHub` so that debuggers automatically fetch the corresponding source code from GitHub at the exact commit matching the installed version.
## Consequences
diff --git a/.backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md b/.backlog/decisions/decision-17 - Add-data-narrowing-parameter-attributes.md
similarity index 64%
rename from .backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md
rename to .backlog/decisions/decision-17 - Add-data-narrowing-parameter-attributes.md
index fb712032..73fa5c4f 100644
--- a/.backlog/decisions/decision-18 - Add-data-narrowing-parameter-attributes.md
+++ b/.backlog/decisions/decision-17 - Add-data-narrowing-parameter-attributes.md
@@ -1,14 +1,12 @@
---
-id: decision-18
+id: decision-17
title: Add data-narrowing parameter attributes
date: '2023-12-05'
status: accepted
---
## Context
-AutoFixture generates arbitrary values for parameters. Tests for boundary conditions, enum
-subsets, or constrained numeric domains needed values from a restricted range, requiring
-manual fixture setup that negated the boilerplate reduction the library provides.
+AutoFixture generates arbitrary values for parameters. Tests for boundary conditions, enum subsets, or constrained numeric domains needed values from a restricted range, requiring manual fixture setup that negated the boilerplate reduction the library provides.
## Decision
@@ -19,11 +17,10 @@ Add four parameter-level attributes to Core, all implemented via `IParameterCust
- `[PickFromRange(min, max)]` — generate within a numeric range
- `[PickNegative]` — generate only negative numeric values
-All four are backed by new specimen builders (`RandomExceptValuesGenerator`,
-`RandomFixedValuesGenerator`) and request types (`ExceptValuesRequest`, `FixedValuesRequest`).
+All four are backed by new specimen builders (`RandomExceptValuesGenerator`, `RandomFixedValuesGenerator`) and request types (`ExceptValuesRequest`, `FixedValuesRequest`).
## Consequences
- Constrained test data without any manual fixture configuration.
- All three mock modules benefit automatically via Core.
-- Attributes are combinable with `[Frozen]` in the same parameter list.
+- Attributes are combinable with `[Frozen]` and `[CustomizeWith]` in the same parameter list.
diff --git a/.backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md b/.backlog/decisions/decision-18 - Integrate-Semgrep-for-SAST-scanning.md
similarity index 60%
rename from .backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md
rename to .backlog/decisions/decision-18 - Integrate-Semgrep-for-SAST-scanning.md
index 423b9945..1cb1f321 100644
--- a/.backlog/decisions/decision-17 - Integrate-Semgrep-for-SAST-scanning.md
+++ b/.backlog/decisions/decision-18 - Integrate-Semgrep-for-SAST-scanning.md
@@ -1,20 +1,16 @@
---
-id: decision-17
+id: decision-18
title: Integrate Semgrep for SAST scanning
date: '2023-12-20'
status: accepted
---
## Context
-CodeQL provides GitHub-native SAST with strong C# support, but its rule coverage is focused
-on common vulnerability classes. Semgrep offers complementary community-maintained rule sets
-and custom pattern matching that can catch additional issues CodeQL does not cover.
+CodeQL provides GitHub-native SAST with strong C# support, but its rule coverage is focused on common vulnerability classes. Semgrep offers complementary community-maintained rule sets and custom pattern matching that can catch additional issues CodeQL does not cover.
## Decision
-Add Semgrep as a parallel SAST scanning step in CI alongside CodeQL. Use the default
-Semgrep ruleset for C# plus any community rulesets relevant to the project. Semgrep runs
-on push and pull request to master.
+Add Semgrep as a parallel SAST scanning step in CI alongside CodeQL. Use the default Semgrep ruleset for C# plus any community rulesets relevant to the project. Semgrep runs on push and pull request to master.
## Consequences
diff --git a/.backlog/decisions/decision-19 - Adopt-Qodana-for-code-quality-scanning.md b/.backlog/decisions/decision-19 - Adopt-Qodana-for-code-quality-scanning.md
new file mode 100644
index 00000000..94b1a031
--- /dev/null
+++ b/.backlog/decisions/decision-19 - Adopt-Qodana-for-code-quality-scanning.md
@@ -0,0 +1,19 @@
+---
+id: decision-19
+title: Adopt Qodana for code quality scanning
+date: '2024-01-16'
+status: accepted
+---
+## Context
+
+The in-build analyzer stack (StyleCop, Roslynator, SonarAnalyzer) catches issues at compile time but cannot provide holistic quality scoring, trend analysis across the codebase, or a consolidated view of maintainability and reliability findings separate from the build log.
+
+## Decision
+
+Integrate JetBrains Qodana into CI as a quality gate. Qodana runs a full Roslyn-based inspection pass independently of the build and reports results in a structured format. It runs on push and pull request to master and complements CodeQL (security focus) and Semgrep (SAST focus) with maintainability and reliability checks.
+
+## Consequences
+
+- Quality metrics are visible in CI as a distinct step, separate from build warnings.
+- Qodana's inspection coverage overlaps partially with the in-build analyzers; discrepancies surface issues that build-time suppression may have hidden.
+- JetBrains Qodana supports .NET and is compatible with the `windows-latest` CI runner.
diff --git a/.backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md b/.backlog/decisions/decision-2 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
similarity index 56%
rename from .backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
rename to .backlog/decisions/decision-2 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
index fe5a6b12..0371c2bd 100644
--- a/.backlog/decisions/decision-1 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
+++ b/.backlog/decisions/decision-2 - Decouple-xUnit-from-AutoFixture-via-provider-pattern.md
@@ -1,23 +1,17 @@
---
-id: decision-1
+id: decision-2
title: Decouple xUnit from AutoFixture via provider pattern
date: '2017-01-22'
status: accepted
---
## Context
-The library originally derived directly from AutoFixture's `AutoDataAttribute`. This created
-tight coupling between the xUnit integration and AutoFixture internals, making it difficult
-to inject custom behavior, intercept the data-generation pipeline, or add parameter-level
-customization without forking AutoFixture itself.
+The library originally derived directly from AutoFixture's `AutoDataAttribute`. This created tight coupling between the xUnit integration and AutoFixture internals, making it difficult to inject custom behavior, intercept the data-generation pipeline, or add parameter-level customization without forking AutoFixture itself.
## Decision
-Stop inheriting from AutoFixture attributes. Derive from xUnit's `DataAttribute` instead and
-drive fixture customization through a dedicated `IAutoFixtureAttributeProvider` abstraction.
-The provider receives the configured `IFixture` and returns a `DataAttribute` whose
-`GetData()` resolves test parameters. This separates xUnit's data-supply contract from
-AutoFixture's specimen-creation pipeline.
+Stop inheriting from AutoFixture attributes. Derive from xUnit's `DataAttribute` instead and drive fixture customization through a dedicated `IAutoFixtureAttributeProvider` abstraction. The provider receives the configured `IFixture` and returns a `DataAttribute` whose
+`GetData()` resolves test parameters. This separates xUnit's data-supply contract from AutoFixture's specimen-creation pipeline.
## Consequences
diff --git a/.backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md b/.backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md
deleted file mode 100644
index ef071401..00000000
--- a/.backlog/decisions/decision-20 - Adopt-Qodana-for-code-quality-scanning.md
+++ /dev/null
@@ -1,24 +0,0 @@
----
-id: decision-20
-title: Adopt Qodana for code quality scanning
-date: '2024-01-16'
-status: accepted
----
-## Context
-
-The in-build analyser stack (StyleCop, Roslynator, SonarAnalyzer) catches issues at compile
-time but cannot provide holistic quality scoring, trend analysis across the codebase, or a
-consolidated view of maintainability and reliability findings separate from the build log.
-
-## Decision
-
-Integrate JetBrains Qodana into CI as a quality gate. Qodana runs a full Roslyn-based
-inspection pass independently of the build and reports results in a structured format.
-It runs on push and pull request to master and complements CodeQL (security focus) and
-Semgrep (SAST focus) with maintainability and reliability checks.
-
-## Consequences
-
-- Quality metrics are visible in CI as a distinct step, separate from build warnings.
-- Qodana's inspection coverage overlaps partially with the in-build analysers; discrepancies surface issues that build-time suppression may have hidden.
-- JetBrains Qodana supports .NET and is compatible with the `windows-latest` CI runner.
diff --git a/.backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md b/.backlog/decisions/decision-20 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
similarity index 50%
rename from .backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
rename to .backlog/decisions/decision-20 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
index 4a8f182d..e86676ac 100644
--- a/.backlog/decisions/decision-21 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
+++ b/.backlog/decisions/decision-20 - Integrate-Snyk-for-dependency-vulnerability-scanning.md
@@ -1,21 +1,16 @@
---
-id: decision-21
+id: decision-20
title: Integrate Snyk for dependency vulnerability scanning
date: '2024-04-18'
status: accepted
---
## Context
-CodeQL scans first-party C# code for vulnerabilities. FOSSA audits licenses. Neither tool
-specifically monitors transitive NuGet dependencies against a live CVE database. A supply
-chain vulnerability in a dependency would go undetected until a manual audit or an external
-report.
+CodeQL scans first-party C# code for vulnerabilities.
## Decision
-Integrate Snyk into the CI pipeline to scan NuGet package dependencies for known
-vulnerabilities using Snyk's continuously updated database. Snyk runs on push and pull
-request to master and raises alerts when a dependency version matches a known CVE.
+Integrate Snyk into the CI pipeline to scan NuGet package dependencies for known vulnerabilities using Snyk's continuously updated database. Snyk runs on push and pull request to master and raises alerts when a dependency version matches a known CVE.
## Consequences
diff --git a/.backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md b/.backlog/decisions/decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
similarity index 57%
rename from .backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
rename to .backlog/decisions/decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
index faa2bb60..3ed92839 100644
--- a/.backlog/decisions/decision-14 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
+++ b/.backlog/decisions/decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
@@ -1,20 +1,16 @@
---
-id: decision-14
+id: decision-21
title: Adopt CodeRabbit for AI-powered PR review
date: '2026-04-12'
status: accepted
---
## Context
-Human code review is thorough for logic but inconsistent at enforcing coding conventions,
-catching subtle anti-patterns, and ensuring every changed file receives attention. A
-first-pass automated reviewer would raise quality before human reviewers engage.
+Human code review is thorough for logic but inconsistent at enforcing coding conventions, catching subtle anti-patterns, and ensuring every changed file receives attention. A first-pass automated reviewer would raise quality before human reviewers engage.
## Decision
-Integrate CodeRabbit as an automated AI reviewer on all pull requests. CodeRabbit posts
-inline review comments and a PR summary alongside human reviewers. It is configured to
-understand the project's conventions (BDD naming, AAA structure, attribute ordering rules).
+Integrate CodeRabbit as an automated AI reviewer on all pull requests. CodeRabbit posts inline review comments and a PR summary alongside human reviewers. It is configured to understand the project's conventions (BDD naming, AAA structure, attribute ordering rules).
## Consequences
diff --git a/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md b/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
index 18ccd1e4..1d1462f7 100644
--- a/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
+++ b/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
@@ -1,7 +1,7 @@
---
id: decision-22
title: Enforce Conventional Commits
-date: '2026-04-22'
+date: '2026-04-10'
status: accepted
---
## Context
@@ -10,9 +10,8 @@ Commit messages were inconsistently formatted across contributors.
## Decision
-Enforce Conventional Commits format (`[type]([scope]): [description]`) on every commit.
-Enforcement uses a Husky.NET `commit-msg` hook for local validation and CommitLint.Net
-in CI to block merges that contain non-conforming commit messages.
+Enforce Conventional Commits format (`(): `) on every commit, where scope is optional.
+Enforcement uses a Husky.NET `commit-msg` hook for local validation and CommitLint.Net in CI to block merges that contain non-conforming commit messages.
## Consequences
diff --git a/.backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md b/.backlog/decisions/decision-3 - Extract-shared-logic-into-a-Core-assembly.md
similarity index 50%
rename from .backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md
rename to .backlog/decisions/decision-3 - Extract-shared-logic-into-a-Core-assembly.md
index 19a8ba73..38737e09 100644
--- a/.backlog/decisions/decision-2 - Extract-shared-logic-into-a-Core-assembly.md
+++ b/.backlog/decisions/decision-3 - Extract-shared-logic-into-a-Core-assembly.md
@@ -1,22 +1,16 @@
---
-id: decision-2
+id: decision-3
title: Extract shared logic into a Core assembly
date: '2017-10-28'
status: accepted
---
## Context
-As AutoNSubstitute was being added alongside AutoMoq, shared logic (base attributes,
-customizations, provider interfaces, specimen builders) was being duplicated across both
-projects. A single source of truth was needed before the duplication compounded further.
+As AutoNSubstitute was being added alongside AutoMoq, shared logic (base attributes, customizations, provider interfaces, specimen builders) was being duplicated across both projects. A single source of truth was needed before the duplication compounded further.
## Decision
-Create `Objectivity.AutoFixture.XUnit2.Core` as a shared assembly containing all common
-infrastructure: `AutoDataBaseAttribute`, `InlineAutoDataBaseAttribute`,
-`MemberAutoDataBaseAttribute`, `AutoDataCommonCustomization`, provider interfaces,
-specimen builders, and parameter attributes. Core is bundled into each mock module package
-but is not published as a standalone NuGet package.
+Create `Objectivity.AutoFixture.XUnit2.Core` as a shared assembly containing all common infrastructure: `AutoDataBaseAttribute`, `InlineAutoDataBaseAttribute`, `MemberAutoDataBaseAttribute`, `AutoDataCommonCustomization`, provider interfaces, specimen builders, and parameter attributes. Core is bundled into each mock module package but is not published as a standalone NuGet package.
## Consequences
diff --git a/.backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md b/.backlog/decisions/decision-4 - Support-multiple-mocking-frameworks-as-separate-modules.md
similarity index 51%
rename from .backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md
rename to .backlog/decisions/decision-4 - Support-multiple-mocking-frameworks-as-separate-modules.md
index 9106e0bb..beefbe2f 100644
--- a/.backlog/decisions/decision-3 - Support-multiple-mocking-frameworks-as-separate-modules.md
+++ b/.backlog/decisions/decision-4 - Support-multiple-mocking-frameworks-as-separate-modules.md
@@ -1,22 +1,16 @@
---
-id: decision-3
+id: decision-4
title: Support multiple mocking frameworks as separate modules
date: '2017-11-07'
status: accepted
---
## Context
-AutoFixture supports multiple mock backends (Moq, NSubstitute, FakeItEasy). Teams using
-only one mocking framework should not be forced to take a transitive dependency on the
-others, and each framework should be independently versionable.
+AutoFixture supports multiple mock backends (Moq, NSubstitute, FakeItEasy). Teams using only one mocking framework should not be forced to take a transitive dependency on the others, and each framework should be independently versionable.
## Decision
-Build a separate NuGet package for each mocking framework — `AutoMoq`, `AutoFakeItEasy`,
-`AutoNSubstitute` — each depending on Core. Every module exposes the same three attribute
-shapes (`[AutoMockData]`, `[InlineAutoMockData]`, `[MemberAutoMockData]`) prefixed with
-the framework name, and overrides only `Customize(IFixture)` to apply the framework-specific
-`ICustomization`.
+Build a separate NuGet package for each mocking framework — `AutoMoq`, `AutoFakeItEasy`, `AutoNSubstitute` — each depending on Core. Every module exposes equivalent three attribute shapes (`[AutoMockData]`, `[InlineAutoMockData]`, `[MemberAutoMockData]`) within its own module namespace, and overrides only `Customize(IFixture)` to apply the framework-specific `ICustomization`.
## Consequences
diff --git a/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md b/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md
index 12da3421..797b288e 100644
--- a/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md
+++ b/.backlog/decisions/decision-5 - Target-netstandard2.0-2.1-and-net472-net48.md
@@ -6,15 +6,12 @@ status: accepted
---
## Context
-Many enterprise teams still run test suites on .NET Framework 4.7.2 and 4.8. Targeting only
-modern .NET would exclude those users, while targeting only .NET Framework would prevent
-adoption on .NET Core and later runtimes.
+Many enterprise teams still run test suites on .NET Framework 4.7.2 and 4.8. Targeting only modern .NET would exclude those users, while targeting only .NET Framework would prevent adoption on .NET Core and later runtimes.
## Decision
Library projects target `netstandard2.0`, `netstandard2.1`, `net472`, and `net48`.
-Test projects target `.NET Core`, `net472`, and `net48` to validate the full compatibility
-matrix on each CI run.
+Test projects target `.NET Core`, `net472`, and `net48` to validate the full compatibility matrix on each CI run.
## Consequences
diff --git a/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md b/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md
index b3006aee..e052b8d0 100644
--- a/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md
+++ b/.backlog/decisions/decision-6 - Delay-sign-assemblies-full-signing-in-CI-only.md
@@ -6,16 +6,11 @@ status: accepted
---
## Context
-Strong-name signing is required for .NET Framework compatibility and NuGet package trust.
-Committing the private signing key to source control would expose it to every contributor
-and fork, creating a security risk.
+Strong-name signing is required for .NET Framework compatibility and NuGet package trust. Committing the private signing key to source control would expose it to every contributor and fork, creating a security risk.
## Decision
-Use delay signing locally: only `public.snk` (the public key) is committed to source
-control. Full signing is performed in CI using the `SIGNING_KEY` secret stored in GitHub
-Secrets. Developers can build and run tests locally without the private key; the fully
-signed assemblies are produced only on the CI server during the release pipeline.
+Use delay signing locally: only `public.snk` (the public key) is committed to source control. Full signing is performed in CI using the `SIGNING_KEY` secret stored in GitHub Secrets. Developers can build and run tests locally without the private key; the fully signed assemblies are produced only on the CI server during the release pipeline.
## Consequences
diff --git a/.backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md b/.backlog/decisions/decision-7 - Publish-Core-bundled-into-module-packages-not-standalone.md
similarity index 60%
rename from .backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md
rename to .backlog/decisions/decision-7 - Publish-Core-bundled-into-module-packages-not-standalone.md
index b015a058..9e2b9134 100644
--- a/.backlog/decisions/decision-4 - Publish-Core-bundled-into-module-packages-not-standalone.md
+++ b/.backlog/decisions/decision-7 - Publish-Core-bundled-into-module-packages-not-standalone.md
@@ -1,20 +1,16 @@
---
-id: decision-4
+id: decision-7
title: 'Publish Core bundled into module packages, not standalone'
date: '2018-07-17'
status: accepted
---
## Context
-Core contains all shared infrastructure but has no value in isolation — it requires a mock
-backend to function. Publishing it as a standalone NuGet package would invite consumers to
-install it alone, producing a broken setup with no data generation capability.
+Core contains all shared infrastructure but has no value in isolation — it requires a mock backend to function. Publishing it as a standalone NuGet package would invite consumers to install it alone, producing a broken setup with no data generation capability.
## Decision
-Do not publish `Objectivity.AutoFixture.XUnit2.Core` as a standalone NuGet package. Bundle
-it into each of the three mock module packages via project reference so that installing any
-one module brings Core along transparently.
+Do not publish `Objectivity.AutoFixture.XUnit2.Core` as a standalone NuGet package. Bundle it into each of the three mock module packages via project reference so that installing any one module brings Core along transparently.
## Consequences
diff --git a/.backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md b/.backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md
deleted file mode 100644
index a2b937aa..00000000
--- a/.backlog/decisions/decision-7 - Suppress-virtual-member-population-via-customization.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-id: decision-7
-title: Suppress virtual member population via customization
-date: '2018-09-20'
-status: accepted
----
-## Context
-
-When AutoFixture creates objects for classes that implement interfaces, the CLR marks the
-interface method implementations as `virtual sealed`. AutoFixture attempts to populate those
-virtual properties, which conflicts with mock proxy behavior: the proxy already owns those
-members, and AutoFixture's attempt to set them can throw or produce unexpected state.
-
-## Decision
-
-Introduce `IgnoreVirtualMembersSpecimenBuilder` (returning `OmitSpecimen` for virtual
-`PropertyInfo` requests) and expose it through `IgnoreVirtualMembersCustomization`. All
-three base attributes accept `IgnoreVirtualMembers = true` to activate this behavior
-per attribute instance. A `[IgnoreVirtualMembers]` parameter-level attribute applies it
-to a specific parameter.
-
-## Consequences
-
-- Mock proxies are not interfered with by AutoFixture's property-population pass.
-- Opt-in per attribute — tests that genuinely need virtual properties populated are unaffected.
-- Reduces the most common setup error for users working with interface-implementing types.
diff --git a/.backlog/decisions/decision-8 - Suppress-virtual-member-population-via-customization.md b/.backlog/decisions/decision-8 - Suppress-virtual-member-population-via-customization.md
new file mode 100644
index 00000000..a9c13da8
--- /dev/null
+++ b/.backlog/decisions/decision-8 - Suppress-virtual-member-population-via-customization.md
@@ -0,0 +1,19 @@
+---
+id: decision-8
+title: Suppress virtual member population via customization
+date: '2018-09-20'
+status: accepted
+---
+## Context
+
+When AutoFixture creates objects for classes that implement interfaces, the CLR marks the interface method implementations as `virtual sealed`. AutoFixture attempts to populate those virtual properties, which conflicts with mock proxy behavior: the proxy already owns those members, and AutoFixture's attempt to set them can throw or produce unexpected state.
+
+## Decision
+
+Introduce `IgnoreVirtualMembersSpecimenBuilder` (returning `OmitSpecimen` for virtual `PropertyInfo` requests) and expose it through `IgnoreVirtualMembersCustomization`. All three base attributes accept `IgnoreVirtualMembers = true` to activate this behavior per attribute instance. A `[IgnoreVirtualMembers]` parameter-level attribute applies it to a specific parameter.
+
+## Consequences
+
+- Mock proxies are not interfered with by AutoFixture's property-population pass.
+- Opt-in per attribute — tests that genuinely need virtual properties populated are unaffected.
+- Reduces the most common setup error for users working with interface-implementing types.
diff --git a/.backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md b/.backlog/decisions/decision-9 - Add-CustomizeWith-parameter-attributes.md
similarity index 64%
rename from .backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md
rename to .backlog/decisions/decision-9 - Add-CustomizeWith-parameter-attributes.md
index 24a3daed..8c1aa19d 100644
--- a/.backlog/decisions/decision-19 - Add-CustomizeWith-parameter-attributes.md
+++ b/.backlog/decisions/decision-9 - Add-CustomizeWith-parameter-attributes.md
@@ -1,14 +1,12 @@
---
-id: decision-19
+id: decision-9
title: Add CustomizeWith parameter attributes
date: '2019-08-24'
status: accepted
---
## Context
-Users occasionally need to apply a full `ICustomization` to a single parameter without
-affecting the fixture configuration for other parameters. No mechanism existed for
-per-parameter customization injection beyond the built-in `[Frozen]` attribute.
+Users occasionally need to apply a full `ICustomization` to a single parameter without affecting the fixture configuration for other parameters. No mechanism existed for per-parameter customization injection beyond the built-in `[Frozen]` attribute.
## Decision
@@ -17,11 +15,10 @@ Add two parameter-level attributes to Core:
- `[CustomizeWith(typeof(T))]` — activates the specified `ICustomization` for a parameter
- `[CustomizeWith]` — generic variant; equivalent in behavior, reduces casting boilerplate
-Both are implemented via `IParameterCustomizationSource` and apply the customization to the
-fixture immediately before the parameter is resolved.
+Both are implemented via `IParameterCustomizationSource` and apply the customization to the fixture immediately before the parameter is resolved.
## Consequences
- Fine-grained customization without polluting the fixture globally for all parameters.
- The generic variant is the preferred form; the `typeof` variant exists for contexts where generic attributes are unavailable.
-- Combinable with `[Frozen]`, and the other data-narrowing parameter attributes in the same parameter list.
+- Combinable with `[Frozen]` in the same parameter list.
diff --git a/.backlog/tasks/task-19 - Add-performance-benchmarks.md b/.backlog/tasks/task-19 - Add-performance-benchmarks.md
index b9378bfe..03ecf536 100644
--- a/.backlog/tasks/task-19 - Add-performance-benchmarks.md
+++ b/.backlog/tasks/task-19 - Add-performance-benchmarks.md
@@ -16,7 +16,7 @@ priority: low
## Context
Tooling selection, benchmark scope, project structure, and xUnit execution model analysis are
-recorded in [DECISION-1 — Performance benchmark tooling and approach](../decisions/decision-23%20-%20Performance-benchmark-tooling-and-approach.md).
+recorded in [DECISION-23 — Performance benchmark tooling and approach](../decisions/decision-23%20-%20Performance-benchmark-tooling-and-approach.md).
**Summary of decisions:**
From 312a6f2999c91ca6f6bf45d73f0bcd3ff14ca22c Mon Sep 17 00:00:00 2001
From: Piotr Zajac
Date: Wed, 22 Apr 2026 23:56:53 +0200
Subject: [PATCH 4/4] docs(backlog): reorder decisions chronologically by date
---
...Commits.md => decision-21 - Enforce-Conventional-Commits.md} | 2 +-
... decision-22 - Adopt-CodeRabbit-for-AI-powered-PR-review.md} | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
rename .backlog/decisions/{decision-22 - Enforce-Conventional-Commits.md => decision-21 - Enforce-Conventional-Commits.md} (97%)
rename .backlog/decisions/{decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md => decision-22 - Adopt-CodeRabbit-for-AI-powered-PR-review.md} (98%)
diff --git a/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md b/.backlog/decisions/decision-21 - Enforce-Conventional-Commits.md
similarity index 97%
rename from .backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
rename to .backlog/decisions/decision-21 - Enforce-Conventional-Commits.md
index 1d1462f7..072cedd7 100644
--- a/.backlog/decisions/decision-22 - Enforce-Conventional-Commits.md
+++ b/.backlog/decisions/decision-21 - Enforce-Conventional-Commits.md
@@ -1,5 +1,5 @@
---
-id: decision-22
+id: decision-21
title: Enforce Conventional Commits
date: '2026-04-10'
status: accepted
diff --git a/.backlog/decisions/decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md b/.backlog/decisions/decision-22 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
similarity index 98%
rename from .backlog/decisions/decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
rename to .backlog/decisions/decision-22 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
index 3ed92839..36668add 100644
--- a/.backlog/decisions/decision-21 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
+++ b/.backlog/decisions/decision-22 - Adopt-CodeRabbit-for-AI-powered-PR-review.md
@@ -1,5 +1,5 @@
---
-id: decision-21
+id: decision-22
title: Adopt CodeRabbit for AI-powered PR review
date: '2026-04-12'
status: accepted