diff --git a/.docfx/Dockerfile.docfx b/.docfx/Dockerfile.docfx index 9d42262..465ff57 100644 --- a/.docfx/Dockerfile.docfx +++ b/.docfx/Dockerfile.docfx @@ -1,4 +1,4 @@ -ARG NGINX_VERSION=1.31.0-alpine +ARG NGINX_VERSION=1.31.2-alpine FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base RUN rm -rf /usr/share/nginx/html/* diff --git a/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.Console.md b/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.Console.md index 150ba58..d95c029 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.Console.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.Console.md @@ -2,8 +2,10 @@ uid: Codebelt.Extensions.BenchmarkDotNet.Console summary: *content --- -The `Codebelt.Extensions.BenchmarkDotNet.Console` namespace contains types that provide a structured and opinionated console-hosted execution model for `BenchmarkDotNet`. +The `Codebelt.Extensions.BenchmarkDotNet.Console` namespace removes the boilerplate of turning a console application into a BenchmarkDotNet host. Use it when you want a single static call from `Main` to wire up the generic host, register a `BenchmarkContext` for the command-line arguments, register the default `BenchmarkWorkspace` (or your own `IBenchmarkWorkspace` implementation) through `AddBenchmarkWorkspace`, run every discovered benchmark assembly, and then post-process the generated artifacts — all without writing the hosting setup by hand. -Use `BenchmarkProgram.Run` for synchronous benchmark hosts and `BenchmarkProgram.RunAsync` for asynchronous benchmark hosts; both entry points support the default `BenchmarkWorkspace` and custom `IBenchmarkWorkspace` implementations. +Start with `BenchmarkProgram.Run` from your `Main` for a synchronous host, or `BenchmarkProgram.RunAsync` when your entry point is async. Both forward the command-line arguments into a `BenchmarkContext`, resolve the registered `IBenchmarkWorkspace` and `BenchmarkWorkspaceOptions` from the service provider, and then hand the loaded assemblies to `BenchmarkRunner` (when no arguments are supplied) or to `BenchmarkSwitcher` (when selective filtering is required). The generic `Run` / `RunAsync` overloads let you plug in a custom `IBenchmarkWorkspace` without rewriting the host. + +If you need to suppress status messages in Release, register additional services, or surface a different `IHost` lifecycle, you can either rely on the `setup` delegate that mutates the resolved `BenchmarkWorkspaceOptions` or the optional `serviceConfigurator` delegate that mutates the `IServiceCollection` before the host is built. [!INCLUDE [availability-modern](../../includes/availability-modern.md)] diff --git a/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.md b/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.md index a1e8fc1..a71afc1 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.BenchmarkDotNet.md @@ -1,14 +1,18 @@ ---- -uid: Codebelt.Extensions.BenchmarkDotNet -summary: *content ---- -The `Codebelt.Extensions.BenchmarkDotNet` namespace contains types that provide a uniform, opinionated, and extensible way of working with `BenchmarkDotNet`. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -### Extension Methods - -|Type|Ext|Methods| -|--:|:-:|---| -|BenchmarkWorkspaceOptions|⬇️|`ConfigureBenchmarkDotNet`| -|IServiceCollection|⬇️|`AddBenchmarkWorkspace`, `AddBenchmarkWorkspace`| +--- +uid: Codebelt.Extensions.BenchmarkDotNet +summary: *content +--- +The `Codebelt.Extensions.BenchmarkDotNet` namespace solves the recurring friction of running BenchmarkDotNet from a host application: discovery of the benchmark assemblies, the per-TFM and per-build-configuration filtering that comes with that, and the post-run cleanup of the generated report artifacts. Use it when you want a workspace that scans a `tuning` folder, loads every `*.Benchmarks.dll` for the current `Debug|Release` build and target framework moniker, hands the assemblies to a `BenchmarkRunner` / `BenchmarkSwitcher`, and then moves the per-run output out of the per-run `results` directory and into the long-lived `tuning` directory of your repository. + +Start with the default registration: add `AddBenchmarkWorkspace` to an `IServiceCollection`, build a service provider, and resolve `IBenchmarkWorkspace`. That returns the built-in `BenchmarkWorkspace` implementation, which already wires up a sensible `ManualConfig` derived from `BenchmarkWorkspaceOptions.Slim` and BenchmarkDotNet's recommended settings. If you need to extend or replace the configuration — adding a job, attaching a custom exporter, tightening the iteration count — call `ConfigureBenchmarkDotNet` on a `BenchmarkWorkspaceOptions` instance to do it fluently. Reach for `AddBenchmarkWorkspace` only when you implement `IBenchmarkWorkspace` yourself to override assembly discovery or post-processing. + +The two extension surfaces map directly to the two configuration moments you care about. `BenchmarkWorkspaceOptionsExtensions.ConfigureBenchmarkDotNet` lets you mutate the BenchmarkDotNet `IConfig` on an options instance while the fluent `IConfig` API normally forces a manual reassignment. `ServiceCollectionExtensions.AddBenchmarkWorkspace` and `AddBenchmarkWorkspace` register the workspace and the resolved options into the DI container so that any consumer — including a console host — can resolve `IBenchmarkWorkspace` and `BenchmarkWorkspaceOptions` straight from the service provider. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +### Extension Members + +|Type|Ext|Methods| +|--:|:-:|---| +|BenchmarkWorkspaceOptions|⬇️|`ConfigureBenchmarkDotNet`| +|IServiceCollection|⬇️|`AddBenchmarkWorkspace`, `AddBenchmarkWorkspace`| diff --git a/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspace.md b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspace.md new file mode 100644 index 0000000..e2f0c18 --- /dev/null +++ b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspace.md @@ -0,0 +1,38 @@ +--- +uid: Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspace +example: +- *content +--- +The following example builds a default `BenchmarkWorkspace` against the current repository and walks through the two phases the workspace actually performs: discovering every `*.Benchmarks.dll` under the configured `tuning` folder for the current build configuration and target framework moniker, then moving the per-run `results` directory into the long-lived `tuning` directory once the benchmark run finishes. + +```csharp +using System; +using System.IO; +using Codebelt.Extensions.BenchmarkDotNet; + +namespace MyBenchmarks; + +public static class Program +{ + public static void Main() + { + var repositoryPath = Directory.GetCurrentDirectory(); + var options = new BenchmarkWorkspaceOptions + { + RepositoryPath = repositoryPath, + TargetFrameworkMoniker = "net10.0" + }; + + var workspace = new BenchmarkWorkspace(options); + + // Discovers and loads every *.Benchmarks.dll that matches + // //bin//. + var assemblies = workspace.LoadBenchmarkAssemblies(); + Console.WriteLine($"Loaded {assemblies.Length} benchmark assemblies from {repositoryPath}."); + + // After a benchmark run, PostProcessArtifacts moves the per-run results directory + // into the configured tuning folder and removes the now-empty results directory. + workspace.PostProcessArtifacts(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptions.md b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptions.md new file mode 100644 index 0000000..b8ce36b --- /dev/null +++ b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptions.md @@ -0,0 +1,39 @@ +--- +uid: Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptions +example: +- *content +--- +The following example configures a `BenchmarkWorkspaceOptions` instance with the repository layout and discovery knobs the workspace will use, then runs the two lifecycle methods that the workspace invokes before the benchmark run (`PostConfigureOptions`) and during construction (`ValidateOptions`). This is the same shape every consumer follows: build an options instance, optionally override one or more defaults, and let the workspace validate the state. + +```csharp +using System; +using Codebelt.Extensions.BenchmarkDotNet; + +namespace MyBenchmarks; + +public static class Program +{ + public static void Main() + { + var options = new BenchmarkWorkspaceOptions + { + // pin the workspace to a specific repository layout + RepositoryPath = @"C:\Repos\MyBenchmarkRepo", + RepositoryTuningFolder = "tuning", + RepositoryReportsFolder = "reports", + TargetFrameworkMoniker = "net10.0", + BenchmarkProjectSuffix = "Benchmarks", + AllowDebugBuild = false, + SkipBenchmarksWithReports = true + }; + + // late-bind the BenchmarkDotNet artifacts path against the configured repository + options.PostConfigureOptions(); + + // throws InvalidOperationException if any required property is missing or whitespace + options.ValidateOptions(); + + Console.WriteLine($"ArtifactsPath: {options.Configuration.ArtifactsPath}"); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptionsExtensions.md b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptionsExtensions.md new file mode 100644 index 0000000..bf8deac --- /dev/null +++ b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptionsExtensions.md @@ -0,0 +1,35 @@ +--- +uid: Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspaceOptionsExtensions +example: +- *content +--- +The following example shows `ConfigureBenchmarkDotNet` being used to add a second BenchmarkDotNet job to the default `IConfig` carried by `BenchmarkWorkspaceOptions`. The helper takes care of forcing the default configuration, passing the current `IConfig` to the delegate, and assigning the returned configuration back onto the options instance — which is otherwise awkward because BenchmarkDotNet's `AddJob` / `AddColumn` / `AddDiagnoser` methods return a new configuration object rather than mutating the receiver. + +```csharp +using System; +using System.Linq; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using Codebelt.Extensions.BenchmarkDotNet; +using Perfolizer.Horology; + +namespace MyBenchmarks; + +public static class Program +{ + public static void Main() + { + var options = new BenchmarkWorkspaceOptions(); + + // fluent IConfig mutations normally require explicit reassignment; this helper does it for you + options.ConfigureBenchmarkDotNet(c => c.AddJob( + Job.Default + .WithWarmupCount(2) + .WithIterationTime(TimeInterval.FromMilliseconds(500)) + .WithMaxIterationCount(25) + .WithId("LongRunning"))); + + Console.WriteLine($"Jobs: {string.Join(", ", options.Configuration.GetJobs().Select(j => j.Id))}"); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkContext.md b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkContext.md new file mode 100644 index 0000000..b109175 --- /dev/null +++ b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkContext.md @@ -0,0 +1,27 @@ +--- +uid: Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkContext +example: +- *content +--- +The following example constructs a `BenchmarkContext` from the command-line arguments passed to the entry point. When the host resolves `BenchmarkContext` from the service provider, it inspects `Args.Length` to decide whether to run every benchmark in every loaded assembly (`BenchmarkRunner.Run`) or to forward the args to `BenchmarkSwitcher` for selective execution. A `null` array is normalized to an empty array so downstream code never has to guard against `null`. + +```csharp +using System; +using Codebelt.Extensions.BenchmarkDotNet.Console; + +namespace MyBenchmarks; + +public static class Program +{ + public static void Main(string[] args) + { + var context = new BenchmarkContext(args); + // context.Args is the same array passed to Main, or an empty array when args is null + Console.WriteLine($"BenchmarkContext received {context.Args.Length} argument(s)."); + foreach (var arg in context.Args) + { + Console.WriteLine($" - {arg}"); + } + } +} +``` diff --git a/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkProgram.md b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkProgram.md new file mode 100644 index 0000000..a862dea --- /dev/null +++ b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkProgram.md @@ -0,0 +1,25 @@ +--- +uid: Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkProgram +example: +- *content +--- +The following example shows the typical `Program.cs` of a benchmark host project: the entry point forwards the command-line arguments to `BenchmarkProgram.RunAsync` and supplies a `setup` delegate that customizes the resolved `BenchmarkWorkspaceOptions` before the host is built. `BenchmarkProgram` derives from `Codebelt.Bootstrapper.Console.MinimalConsoleProgram`, so the `Main` plumbing — host configuration, service registration, lifecycle — is inherited; the only thing the host project has to write is the call site and the workspace configuration. + +```csharp +using System.Threading.Tasks; +using Codebelt.Extensions.BenchmarkDotNet; +using Codebelt.Extensions.BenchmarkDotNet.Console; + +namespace MyBenchmarks; + +public static class Program +{ + // minimal benchmark host: forwards args into the auto-discovered benchmark workspace + public static Task Main(string[] args) => BenchmarkProgram.RunAsync(args, setup: options => + { + options.BenchmarkProjectSuffix = "MyBench"; + options.AllowDebugBuild = false; + options.SkipBenchmarksWithReports = true; + }); +} +``` diff --git a/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.ServiceCollectionExtensions.md b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.ServiceCollectionExtensions.md new file mode 100644 index 0000000..d56eb1f --- /dev/null +++ b/.docfx/api/types/Codebelt.Extensions.BenchmarkDotNet.ServiceCollectionExtensions.md @@ -0,0 +1,48 @@ +--- +uid: Codebelt.Extensions.BenchmarkDotNet.ServiceCollectionExtensions +example: +- *content +--- +The following example registers a benchmark workspace through the non-generic `AddBenchmarkWorkspace` overload — which defaults to the built-in `BenchmarkWorkspace` implementation — and the generic `AddBenchmarkWorkspace` overload — which lets a consumer plug in a custom `IBenchmarkWorkspace` (here, a `FakeWorkspace` that simply returns an empty assembly set). Both overloads register the workspace and the resolved options as singletons, so any consumer can resolve `IBenchmarkWorkspace` and `BenchmarkWorkspaceOptions` straight from the built `IServiceProvider`. + +```csharp +using System; +using System.Reflection; +using Codebelt.Extensions.BenchmarkDotNet; +using Microsoft.Extensions.DependencyInjection; + +namespace MyBenchmarks; + +// a custom workspace registered through the generic overload +public sealed class FakeWorkspace : IBenchmarkWorkspace +{ + public Assembly[] LoadBenchmarkAssemblies() => Array.Empty(); + public void PostProcessArtifacts() { } +} + +public static class Program +{ + public static void Main() + { + // default registration: registers BenchmarkWorkspace and the resolved options as singletons + var services = new ServiceCollection(); + services.AddBenchmarkWorkspace(setup: o => o.BenchmarkProjectSuffix = "Benchmarks"); + using (var provider = services.BuildServiceProvider()) + { + var defaultWorkspace = provider.GetRequiredService(); + var defaultOptions = provider.GetRequiredService(); + Console.WriteLine($"default: {defaultWorkspace.GetType().Name} / {defaultOptions.BenchmarkProjectSuffix}"); + } + + // generic registration: any IBenchmarkWorkspace implementation + var typed = new ServiceCollection(); + typed.AddBenchmarkWorkspace(setup: o => o.RepositoryPath = @"C:\Repos\MyRepo"); + using (var typedProvider = typed.BuildServiceProvider()) + { + var customWorkspace = typedProvider.GetRequiredService(); + var customOptions = typedProvider.GetRequiredService(); + Console.WriteLine($"custom: {customWorkspace.GetType().Name} / {customOptions.RepositoryPath}"); + } + } +} +``` diff --git a/.docfx/docfx.json b/.docfx/docfx.json index 291ae32..f09678b 100644 --- a/.docfx/docfx.json +++ b/.docfx/docfx.json @@ -27,12 +27,13 @@ { "files": [ "api/**/*.yml", - "api/**/*.md", "packages/**/*.md", "toc.yml", "*.md" ], "exclude": [ + "api/namespaces/**", + "api/types/**", "bin/**", "obj/**" ] @@ -69,7 +70,8 @@ "overwrite": [ { "files": [ - "api/namespaces/**.md" + "api/namespaces/**/*.md", + "api/types/**/*.md" ], "exclude": [ "obj/**", diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index dd0cf8b..37b90a2 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -203,7 +203,8 @@ jobs: security-events: write deploy: - if: github.event_name != 'pull_request' + # Avoid skipped optional jobs (for example disabled macOS matrix runs) from suppressing deployment. + if: ${{ always() && github.event_name != 'pull_request' && needs.build.result == 'success' && needs.pack.result == 'success' && needs.test_qualitygate.result == 'success' && needs.sonarcloud.result == 'success' && needs.codecov.result == 'success' && needs.codeql.result == 'success' }} name: call-nuget needs: [build, pack, test_qualitygate, sonarcloud, codecov, codeql] uses: codebeltnet/jobs-nuget-push/.github/workflows/default.yml@v3 diff --git a/.nuget/Codebelt.Extensions.BenchmarkDotNet.Console/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.BenchmarkDotNet.Console/PackageReleaseNotes.txt index 4c952aa..722c697 100644 --- a/.nuget/Codebelt.Extensions.BenchmarkDotNet.Console/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.BenchmarkDotNet.Console/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 1.3.1 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 1.3.0 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.BenchmarkDotNet/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.BenchmarkDotNet/PackageReleaseNotes.txt index 2bb3972..be68542 100644 --- a/.nuget/Codebelt.Extensions.BenchmarkDotNet/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.BenchmarkDotNet/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 1.3.1 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 1.3.0 Availability: .NET 10 and .NET 9 diff --git a/AGENTS.md b/AGENTS.md index a9e7a11..bb874f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,3 +64,63 @@ Agents must never automatically commit code changes or push to remote repositori - Public API conventions belong in `.docfx/api/namespaces/` and should be treated as the official documentation source for library behavior and naming vocabulary. - When adding or renaming public APIs, update the relevant namespace page in `.docfx/api/namespaces/` if the change introduces or clarifies a convention. - Keep internal reasoning, exploratory notes, and agent discussion out of DocFX pages; summarize only stable public guidance. + + +## DocFX Documentation Maintenance + +When changing public .NET APIs, keep the DocFX documentation current in the same change set. + +Documentation updates must cover public API only. Do not document private or internal types or members. Do not create namespace overview pages for namespaces that contain no public API. + +Public non-abstraction types — including enums, structs, records, plain classes, and static extension containers — are valid documentation targets. Generic public types and generic extension methods are valid documentation targets too. Do not exclude a type solely because it is generic or because reflection reports it as abstract and sealed (that is the IL pattern for a static class). + +For public non-abstraction types, include at least one realistic, copy/paste-ready usage example on the generated type page/overwrite section for that type UID. For example, a public `Class1` requires an example on the `Class1` API page, not only on the namespace page. Prefer deriving examples from existing unit, functional, or integration tests, but convert test code into real-life consumer-oriented usage. + +Missing type examples must be added through per-type DocFX overwrite files under `.docfx/api/types/{TypeUid}.md` in Codebelt repositories. Namespace overview text and `Extension Members` tables are not substitutes for type-page examples. + +Public extension methods must have examples too. Listing an extension method in an `Extension Members` table is required, but it is not enough. + +All added or changed code samples must be deterministic and verified to compile. Do not add pseudo-code, ellipses, hidden test helpers, or examples that rely on unverified behavior. + +Compilation is necessary but not sufficient. Do not present runtime implementation names such as `services.GetType().Name` or `host.GetType().FullName` as the example outcome. Show application behavior, configured state, a resolved domain service, an HTTP response, or another result that explains why a caller uses the API. Application-entry-point examples must not declare an empty local `Program` type merely to compile; show a real entry point or clearly identify the referenced application project. + +Every namespace containing public API must have a DocFX namespace overview page named after the namespace, such as `X.Y.Z.md`, under `.docfx/api/namespaces/`, using DocFX overwrite front matter with the namespace `uid`. + +Namespace pages must identify key entry points from release notes, package documentation, public factories/builders, and strong functional tests, then help readers choose among adjacent workflows. When the package complements a well-known upstream API, compare concrete acquisition, customization, lifecycle, and sharing tradeoffs from current official guidance; do not claim drop-in replacement compatibility without evidence. + +Namespaces exposing public extension methods must document those extension members at namespace level. The namespace page must include an `Extension Members` table listing the extended type, the extension marker, and the public extension methods. Extension members are rendered under the heading `Extension Members`. + +Both namespace overwrite files and type overwrite files are required deliverables in the same run. Generating only namespace pages or only type pages is incomplete. + +`docfx.json` must keep namespace and type overwrite files in separate subdirectories. `build.overwrite` must include both `api/namespaces/**/*.md` (for namespace pages) and `api/types/**/*.md` (for type pages). `build.content` must exclude both `api/namespaces/**` and `api/types/**` to prevent overwrite Markdown from being treated as conceptual content. Do not use `api/**/*.md` under `build.overwrite` or `build.content`. + +Availability must be documented by referencing the appropriate include file when one exists, or by adding explicit availability text when no suitable include exists. Availability must reflect the actual target frameworks, conditional compilation, and project configuration. + +For conditionally compiled APIs, choose the executable test framework from the asset that contains the API. Inspect the preprocessor condition, project TFMs, package `lib/` assets, and resolved consumer asset before changing a sample. For APIs under `NETSTANDARD2_0` or `NETSTANDARD2_0_OR_GREATER`, when modern `lib/netX.0/` assets also exist, use `net48` (or another supported .NET Framework target from `net462` onward) so the consumer selects `lib/netstandard2.0/`. Never use `netstandard*` as an executable target, and never use a modern `netX.0` target when it selects an asset where the API is absent. For other TFM guards, select a runnable consumer TFM that resolves to the containing asset and confirm that selection from restore or build evidence. + +Preserve manual documentation edits. Prefer additive changes, but correct stale or contradictory information so documentation remains accurate. + +Preserve working Markdown links, `Related:` references, and historical URL citations during prose rewrites. Remove or replace a URL only after directly verifying that the current destination returns HTTP 404. Timeouts, 403s, rate limits, DNS failures, and other lookup problems are not removal evidence. + +Interim scratch artifacts do not belong in the repository working tree. Store assessment queues, project manifests, review reports, captured validator output, progress notes, and one-off helper scripts in temp or session storage instead. New working-tree files are only legitimate when they are the managed `AGENTS.md` block, the active `docfx.json`, the deterministic `skip-compile-allowlist.json` waiver file when one is truly required, or DocFX-authored namespace/type Markdown that maps to a real public namespace or type. Everything else is blocking cleanup work, not a documentation deliverable. The validator auto-detects generic-arity type families (such as `MutableTuple`1`..`MutableTuple`N`) and skips redundant sibling examples from the public API surface alone, so no family-skip manifest is ever written into the repository. + +Skip markers are waivers, not fixes. A skip marker only suppresses compilation when it both existed before the current run and matches an entry in `.docfx/skip-compile-allowlist.json`. Each allowlist entry must include `diagnosticCode`, `filePath`, `uid` or `symbol`, `reason`, `approval`, and `lifetime` (`temporary` or `permanent`). Newly introduced or unallowlisted skip markers remain fail-level diagnostics and do not permit a completion claim. + +Do not emit a final report, audit result, completion summary, or handoff while `summary.canClaimCompletion` is false, `summary.remainingWorkItems` is greater than zero, `summary.remainingGates` is non-empty, `summary.fullVerificationRan` is false, fail-level diagnostics remain, `summary.newlyIntroducedSkipMarkers` is non-zero, or `summary.interimArtifacts` is non-zero. Large queues, many changed files, repetitive next steps, long runtimes, context pressure, session length, task size, or a "stable queue" are not valid stop reasons; the next action must be another remediation batch, a validator rerun, a validator/tooling fix, or a true blocker with exact evidence. + +Context pressure is not a completion condition. If the session feels constrained while work remains, continue with a smaller deterministic batch, regenerate deterministic queue state such as `--assessment-queue`, `--project-manifest`, or the active dry-run manifest/review pair, or report a true tooling failure with the exact command, exit code, and output. When naming a queue-state regeneration command, resolve it to a concrete temp/session path instead of leaving `` as a placeholder. Do not stop with phrases like "given context constraints", "best done in a follow-up", "remaining work requires authoring", "this is a massive task", or "I will provide a focused summary". A context-sized handoff while work remains is `FAIL_CONTEXT_HANDOFF_WITH_REMAINING_WORK`; the remediation is to continue with a smaller deterministic batch. + +Before completing documentation work, run the relevant verification commands, normally: + +```bash +dotnet build +dotnet test +dotnet run --file /scripts/docfx.cs -- --repo-root . --build-api-model --validate-samples --verify-docfx-build +``` + +Codebelt repositories are normally strong-name signed with a `.snk` file in the repository root on the main author's codespace. Preserve and copy that root `.snk` file when building a temporary copy. If the repository or temp copy has no root `.snk`, run build and test verification with `-p:SkipSignAssembly=true`, for example `dotnet build -p:SkipSignAssembly=true` and `dotnet test -p:SkipSignAssembly=true`. + +The final DocFX verification must run outside the working tree when possible. The `--verify-docfx-build` option copies the repository to a temp workspace, runs DocFX against the resolved `docfx.json` there, and removes the temp workspace afterward so generated API YAML, manifest files, and site output do not flood git status. Do not call the work complete until the final JSON reports `summary.fullVerificationRan: true`, `summary.canClaimCompletion: true`, `summary.remainingWorkItems: 0`, an empty `summary.remainingGates`, an empty `summary.remainingDiagnosticsByCode`, `summary.newlyIntroducedSkipMarkers: 0`, and `summary.interimArtifacts: 0`. + +If a command cannot be run, report the exact limitation or failure instead of claiming the documentation was verified. + diff --git a/CHANGELOG.md b/CHANGELOG.md index cc6cfab..64f6190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), For more details, please refer to `PackageReleaseNotes.txt` on a per assembly basis in the `.nuget` folder. +## [1.3.1] - 2026-06-29 + +This is a patch release focused on documentation infrastructure improvements, CI/CD pipeline hardening, and keeping dependencies current. + +### Added + +- Type-level DocFX documentation files (`.docfx/api/types/`) for all public types including `BenchmarkWorkspace`, `BenchmarkWorkspaceOptions`, `BenchmarkWorkspaceOptionsExtensions`, `BenchmarkContext`, `BenchmarkProgram`, and `ServiceCollectionExtensions`, with copy/paste-ready usage examples for each type, +- Comprehensive DocFX documentation maintenance guidelines in AGENTS.md covering documentation standards, public API documentation requirements, compilation validation, availability documentation, and documentation artifact management. + +### Changed + +- DocFX configuration updated to properly segregate and manage namespace-level and type-level overwrite files, preventing documentation duplication and improving build clarity, +- CI/CD deployment condition improved to use explicit job result checking with `always()` to ensure that skipped optional jobs (such as disabled macOS matrix runs) do not suppress deployment, +- Upgraded `Microsoft.NET.Test.Sdk` from 18.6.0 to 18.7.0, +- Updated Dockerfile configuration for DocFX builds. + ## [1.3.0] - 2026-06-05 This is a minor release focused on code organization improvements, test coverage expansion, CI/CD pipeline hardening, and comprehensive agent guidance. The release refactors target framework parsing logic for better testability, expands the test suite for core workspace classes, extends CI/CD support to macOS environments, and establishes clear conventions and standards for AI agent collaboration in the repository. @@ -108,7 +124,8 @@ This is the initial stable release of the `Codebelt.Extensions.BenchmarkDotNet` - ADDED `BenchmarkProgram` class in the Codebelt.Extensions.BenchmarkDotNet.Console namespace that provides the main entry point for hosting and running benchmarks using BenchmarkDotNet, - ADDED `BenchmarkWorker` class in the Codebelt.Extensions.BenchmarkDotNet.Console namespace that is responsible for executing benchmarks within the console host. -[Unreleased]: https://github.com/codebeltnet/benchmarkdotnet/compare/v1.3.0...HEAD +[Unreleased]: https://github.com/codebeltnet/benchmarkdotnet/compare/v1.3.1...HEAD +[1.3.1]: https://github.com/codebeltnet/benchmarkdotnet/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/codebeltnet/benchmarkdotnet/compare/v1.2.7...v1.3.0 [1.2.7]: https://github.com/codebeltnet/benchmarkdotnet/compare/v1.2.6...v1.2.7 [1.2.6]: https://github.com/codebeltnet/benchmarkdotnet/compare/v1.2.5...v1.2.6 diff --git a/Directory.Packages.props b/Directory.Packages.props index 4de402c..5db172c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,10 +5,10 @@ - - - - + + + +