From a963571a5b4fdd89a97c22a6e28d9d3591d7fac6 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Fri, 5 Jun 2026 06:37:24 -0700 Subject: [PATCH 1/4] Bump dependencies on develop to match main (#325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Small PR to sync develop's dependency versions up to main (follow-up after the dual-publish landing) and to exercise the new PR pipeline end-to-end. Touches `Directory.Packages.props` + `.config/dotnet-tools.json` (the workflow's shared paths-filter anchor), so the smoke build should run. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .config/dotnet-tools.json | 4 ++-- Directory.Packages.props | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index e4aa460..856590e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,14 +3,14 @@ "isRoot": true, "tools": { "csharpier": { - "version": "1.2.5", + "version": "1.2.6", "commands": [ "csharpier" ], "rollForward": false }, "husky": { - "version": "0.8.0", + "version": "0.9.1", "commands": [ "husky" ], diff --git a/Directory.Packages.props b/Directory.Packages.props index a052ee0..ded83a3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,11 +1,11 @@ - - - + + + - + From 34318f45e4ff3a5898967bf885a63d94032a2f56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:20:25 -0700 Subject: [PATCH 2/4] Bump Microsoft.NET.Test.Sdk and Microsoft.SourceLink.GitHub (#326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 18.4.0 to 18.6.0.
Release notes _Sourced from [Microsoft.NET.Test.Sdk's releases](https://github.com/microsoft/vstest/releases)._ ## 18.6.0 ## What's Changed * Revert removal of Video Recorder by @​nohwnd in https://github.com/microsoft/vstest/pull/15336 * Speed up blame by filtering non-.NET processes from dump collection by @​nohwnd in https://github.com/microsoft/vstest/pull/15518 * Add README.md to NuGet packages by @​nohwnd in https://github.com/microsoft/vstest/pull/15550 * Report child process info on connection timeout by @​nohwnd in https://github.com/microsoft/vstest/pull/15603 ### Changes to tests and infra * Brand as 18.6 by @​nohwnd in https://github.com/microsoft/vstest/pull/15423 * Upgrading code coverage version to 18.5.1, by @​fhnaseer in https://github.com/microsoft/vstest/pull/15422 * Updating System.Collections.Immutable to 9.0.11 by @​MSLukeWest in https://github.com/microsoft/vstest/pull/15425 * Fix attachVS when used for debugging integration tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15451 * Replace dotnet.config, with global.json by @​nohwnd in https://github.com/microsoft/vstest/pull/15449 * Document debugging integration tests with AttachVS by @​Copilot in https://github.com/microsoft/vstest/pull/15452 * Fix stack overflow tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15461 * Make TestAssets.sln buildable locally by @​Youssef1313 in https://github.com/microsoft/vstest/pull/15466 * Try filtering out tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15463 * Build just once when tfms run in parallel by @​nohwnd in https://github.com/microsoft/vstest/pull/15465 * Review simplify compatibility sources, deduplicate tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15472 * Cleanup dead TRX code by @​Youssef1313 in https://github.com/microsoft/vstest/pull/15474 * Update .NET runtimes to 8.0.25, 9.0.14, and 10.0.4 by @​nohwnd in https://github.com/microsoft/vstest/pull/15481 * Compat matrix checker by @​nohwnd in https://github.com/microsoft/vstest/pull/15480 * Add trx analysis skill by @​nohwnd in https://github.com/microsoft/vstest/pull/15486 * Split integration tests to single tfm and multi tfm project by @​nohwnd in https://github.com/microsoft/vstest/pull/15484 * Update matrix by @​nohwnd in https://github.com/microsoft/vstest/pull/15477 * Break infinite restore loop in VS by @​nohwnd in https://github.com/microsoft/vstest/pull/15503 * Use global package cache for build, and local for running integration tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15500 * Update contributing by @​nohwnd in https://github.com/microsoft/vstest/pull/15505 * Reduce test wall-clock time by increasing minThreads by @​drognanar in https://github.com/microsoft/vstest/pull/15502 * Indicator flakiness by @​nohwnd in https://github.com/microsoft/vstest/pull/15513 * Fix ci build by @​nohwnd in https://github.com/microsoft/vstest/pull/15515 * Fix thread safety issues by @​Evangelink in https://github.com/microsoft/vstest/pull/15512 * Optimize DotnetSDKSimulation_PostProcessing test (163s → 61s) by @​nohwnd in https://github.com/microsoft/vstest/pull/15516 * Build isolated test assets for single TFM instead of 7 by @​nohwnd in https://github.com/microsoft/vstest/pull/15517 * Remove unused dependencies from Library.IntegrationTests by @​nohwnd in https://github.com/microsoft/vstest/pull/15527 * Remove printing _attachments content to console by @​nohwnd in https://github.com/microsoft/vstest/pull/15520 * Add Linux/macOS test filtering guide to CONTRIBUTING.md by @​nohwnd in https://github.com/microsoft/vstest/pull/15521 * Change integration test parallelization from ClassLevel to MethodLevel by @​nohwnd in https://github.com/microsoft/vstest/pull/15526 * Unify target framework checks with IsNetFrameworkTarget/IsNetTarget by @​nohwnd in https://github.com/microsoft/vstest/pull/15523 * Add unattended work instructions to copilot-instructions.md by @​nohwnd in https://github.com/microsoft/vstest/pull/15531 * Reduce code style rule severity from warning to suggestion by @​nohwnd in https://github.com/microsoft/vstest/pull/15522 * Remove Debug/Release line number branching from tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15519 * Revise unattended work instructions in copilot-instructions.md by @​nohwnd in https://github.com/microsoft/vstest/pull/15532 * Improve CompatibilityRowsBuilder error message with diagnostic details by @​nohwnd in https://github.com/microsoft/vstest/pull/15529 * docs: add git worktree and upstream sync workflow to copilot-instructions.md by @​nohwnd in https://github.com/microsoft/vstest/pull/15538 * Add VSIX runner to smoke tests by @​nohwnd in https://github.com/microsoft/vstest/pull/15541 * Remove deprecated WebTest and TMI test methods by @​nohwnd in https://github.com/microsoft/vstest/pull/15525 * Fix compatibility test failures for legacy vstest.console and MSTest adapter by @​nohwnd in https://github.com/microsoft/vstest/pull/15534 * Convert TestPlatform.sln to slnx format by @​nohwnd in https://github.com/microsoft/vstest/pull/15551 * Convert test/TestAssets .sln files to .slnx format by @​nohwnd in https://github.com/microsoft/vstest/pull/15557 ... (truncated) ## 18.5.1 ## What's Changed * Fix System.Collections.Immutable binding mismatch in Common.dll (rel/18.5) by @​nohwnd in https://github.com/microsoft/vstest/pull/15720 * Port verify-binding-redirects.ps1 to rel/18.5 by @​nohwnd in https://github.com/microsoft/vstest/pull/15719 * Bump to 18.5.1 by @​nohwnd in https://github.com/microsoft/vstest/pull/15721 **Full Changelog**: https://github.com/microsoft/vstest/compare/v18.5.0...v18.5.1 ## 18.5.0 ⚠️ Unlisted on Nuget, because of #​15718 ## What's Changed * Add runtime configs by @​nohwnd in https://github.com/microsoft/vstest/pull/15377 * Add net8.0 target for TranslationLayer by @​nohwnd in https://github.com/microsoft/vstest/pull/15375 * Determine architecture of remote process on windows by @​nohwnd in https://github.com/microsoft/vstest/pull/15396 * Updating System.Collections.Immutable package reference to version 9.0.0 by @​MSLukeWest in https://github.com/microsoft/vstest/pull/15392 * Dump via netcore tool on windows by @​nohwnd in https://github.com/microsoft/vstest/pull/15397 * Fix answer file splitting by @​nohwnd in https://github.com/microsoft/vstest/pull/15381 * Run tests against vsix runner by @​nohwnd in https://github.com/microsoft/vstest/pull/15419 **Full Changelog**: https://github.com/microsoft/vstest/compare/v18.4.0...v18.5.0 Commits viewable in [compare view](https://github.com/microsoft/vstest/compare/v18.4.0...v18.6.0).
Updated [Microsoft.SourceLink.GitHub](https://github.com/dotnet/dotnet) from 10.0.201 to 10.0.300.
Release notes _Sourced from [Microsoft.SourceLink.GitHub's releases](https://github.com/dotnet/dotnet/releases)._ ## 10.0.300 You can build .NET 10.0 from the repository by cloning the release tag `v10.0.300` and following the build instructions in the [main README.md](https://github.com/dotnet/dotnet/blob/v10.0.300/README.md#building). Alternatively, you can build from the sources attached to this release directly. More information on this process can be found in the [dotnet/dotnet repository](https://github.com/dotnet/dotnet/blob/v10.0.300/README.md#building-from-released-sources). Attached are PGP signatures for the GitHub generated tarball and zipball. You can find the public key at https://dot.net/release-key-2023 ## 10.0.204 You can build .NET 10.0 from the repository by cloning the release tag `v10.0.204` and following the build instructions in the [main README.md](https://github.com/dotnet/dotnet/blob/v10.0.204/README.md#building). Alternatively, you can build from the sources attached to this release directly. More information on this process can be found in the [dotnet/dotnet repository](https://github.com/dotnet/dotnet/blob/v10.0.204/README.md#building-from-released-sources). Attached are PGP signatures for the GitHub generated tarball and zipball. You can find the public key at https://dot.net/release-key-2023 ## 10.0.203 You can build .NET 10.0 from the repository by cloning the release tag `v10.0.203` and following the build instructions in the [main README.md](https://github.com/dotnet/dotnet/blob/v10.0.203/README.md#building). Alternatively, you can build from the sources attached to this release directly. More information on this process can be found in the [dotnet/dotnet repository](https://github.com/dotnet/dotnet/blob/v10.0.203/README.md#building-from-released-sources). Attached are PGP signatures for the GitHub generated tarball and zipball. You can find the public key at https://dot.net/release-key-2023 ## 10.0.202 You can build .NET 10.0 from the repository by cloning the release tag `v10.0.202` and following the build instructions in the [main README.md](https://github.com/dotnet/dotnet/blob/v10.0.202/README.md#building). Alternatively, you can build from the sources attached to this release directly. More information on this process can be found in the [dotnet/dotnet repository](https://github.com/dotnet/dotnet/blob/v10.0.202/README.md#building-from-released-sources). Attached are PGP signatures for the GitHub generated tarball and zipball. You can find the public key at https://dot.net/release-key-2023 Commits viewable in [compare view](https://github.com/dotnet/dotnet/compare/v10.0.201...v10.0.300).
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ded83a3..b16b9bc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ - - + + From d89dc893912378140e8610589ef64678a5ca2920 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:55:05 +0000 Subject: [PATCH 3/4] Bump the nuget-deps group with 1 update (#327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated [csharpier](https://github.com/belav/csharpier) from 1.2.6 to 1.3.0.
Release notes _Sourced from [csharpier's releases](https://github.com/belav/csharpier/releases)._ ## 1.3.0 # 1.3.0 ## Breaking Changes ### Change xml formatting to return error when it runs into syntax error so it is consistent with c# [#​1854](https://github.com/belav/csharpier/pull/1854) Previously CSharpier treated an invalid xml file as a warning instead of an error. This was inconsistent with how it treated c# files. Invalid c# or xml files are not treated as errors. The `--compilation-errors-as-warnings` argument has been renamed to `--syntax-errors-as-warnings` and can be used to return warnings instead of errors when encountering invalid files. ## What's Changed ### Feature: Configurable whitespace handling for xml [#​1790](https://github.com/belav/csharpier/issues/1790) CSharpier now supports two types of xml whitespace formatting strict or ignore. By default all xml except `xaml` or `axaml` is treated as strict whitespace. See [details](https://csharpier.com/docs/Configuration#xml-whitespace-sensitivity) ### Feature: Move closing bracket for xml elements to the same line. [#​1598](https://github.com/belav/csharpier/issues/1598) With strict xml whitespace handling, csharpier now keeps the closing bracket for an element on the same line instead of breaking it to a new line. ```xml TextValue TextValue ``` ### Feature: Support for csharpier-ignore with XML formatter [#​1788](https://github.com/belav/csharpier/issues/1788) CSharpier now supports `csharpier-ignore` in xml files. See [details](https://csharpier.com/docs/Ignore#xml) ### Feature: Add MSBuild transitive and multi-target support [#​1833](https://github.com/belav/csharpier/issues/1833) CSharpier.MSBuild can now work as a transitive dependency. ### Feature: allow checking formatting with cache [#​1830](https://github.com/belav/csharpier/pull/1830) The `csharpier check` command now supports a `--use-cache` option. ### Feature: remove dependency on Microsoft.AspNetCore.App [#​1508](https://github.com/belav/csharpier/issues/1508) Previously CSharpier required that Microsoft.AspNetCore.App be installed. CSharpier has been modified to use an HttpListener when it is run using `server` to remove the need for this dependency. ### Fix: csharpier-ignore comment removes linespaces before block [#​1867](https://github.com/belav/csharpier/issues/1867) CSharpier was removing blank lines before csharpier-ignore comments in some cases ```c# // input and expected output var x = 1; // csharpier-ignore var y=1; /// 1.2.6 var x = 1; // csharpier-ignore var y=1; ... (truncated) Commits viewable in [compare view](https://github.com/belav/csharpier/compare/1.2.6...1.3.0).
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=csharpier&package-manager=nuget&previous-version=1.2.6&new-version=1.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 856590e..94d9553 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "csharpier": { - "version": "1.2.6", + "version": "1.3.0", "commands": [ "csharpier" ], From a9fc0c0383abbc787c240976ac48efa594ac2e13 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Mon, 8 Jun 2026 09:34:46 -0700 Subject: [PATCH 4/4] Adopt shared markdownlint config and lint-clean docs (#330) Addresses #329 - realign with ptr727/ProjectTemplate. - **Shared lint config:** carry `.markdownlint-cli2.jsonc` verbatim; normalize blank lines / label a fence so docs pass clean. - **Review-loop contract:** add the template's **PR Review Etiquette** section to `AGENTS.md`. - **Copilot runbook:** append the **GitHub Copilot Review Runbook** to `.github/copilot-instructions.md` (owner/repo adapted to `ptr727`/`Utilities`). - **Line endings:** normalize both touched files to CRLF per `.editorconfig` (they had pre-existing mixed CRLF/LF), and drop a whitespace-only line in a code example. Per your confirmation, this repo adopts the full review-loop docs despite being NuGet-only. Its `.editorconfig` already governs line endings, so no editorconfig rule change was needed. --- .github/copilot-instructions.md | 166 +++++++++++++++++++++++++++++++- .markdownlint-cli2.jsonc | 14 +++ AGENTS.md | 44 +++++++++ 3 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 .markdownlint-cli2.jsonc diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index cb80fe8..5f39aaf 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -7,6 +7,7 @@ This is a .NET utility library that provides generally useful C# classes and ext ## Code Style and Standards ### General Guidelines + - Follow C# coding conventions and .NET best practices - Use meaningful variable and method names - Keep methods focused and single-purpose @@ -22,6 +23,7 @@ This is a .NET utility library that provides generally useful C# classes and ext 2. **dotnet format** is run second for style enforcement #### Formatting Workflow + ```bash # Always format with CSharpier FIRST after editing code dotnet csharpier . @@ -34,16 +36,19 @@ dotnet format --verify-no-changes ``` #### Key Formatting Rules (from .editorconfig) + - **No `var` keyword**: Use explicit types everywhere + ```csharp // ✅ CORRECT string text = "hello"; List numbers = []; - + // ❌ WRONG var text = "hello"; var numbers = new List(); ``` + - **Indentation**: 4 spaces (not tabs) - **Line endings**: CRLF (Windows) - **Charset**: UTF-8 @@ -53,13 +58,16 @@ dotnet format --verify-no-changes - **Collection expressions**: Preferred `[]` over `new List()` #### Pre-Commit Hook + The Husky.Net pre-commit hook automatically runs: + 1. `dotnet csharpier .` - Code formatting 2. `dotnet format` - Style enforcement **Commits will be rejected if formatting fails!** ### .NET 10 and AOT Considerations + - The project targets .NET 10 with PublishAot enabled - Avoid reflection where possible (not AOT-friendly) - Use source generators instead of runtime reflection when applicable @@ -105,12 +113,15 @@ The Husky.Net pre-commit hook automatically runs: ## Common Tasks ### Building + Run the ".NET Build" task or use: `dotnet build` ### Publishing + Run the ".NET Publish" task or use: `dotnet publish` ### Formatting (REQUIRED before commit) + ```bash # Step 1: Format with CSharpier dotnet csharpier . @@ -123,19 +134,24 @@ dotnet format --verify-no-changes ``` ### Running Tests + Use: `dotnet test` ## Commit Guidelines ### Pre-Commit Process (Automated by Husky.Net) + The following happens automatically on every commit: + 1. ✅ CSharpier formats all C# files 2. ✅ dotnet format applies .editorconfig rules 3. ✅ Commit proceeds if formatting passes 4. ❌ Commit is rejected if formatting fails ### Manual Pre-Commit Checklist + Before committing, ensure: + - [ ] Code formatted with CSharpier (`dotnet csharpier .`) - [ ] Style rules applied (`dotnet format`) - [ ] No formatting issues (`dotnet format --verify-no-changes`) @@ -146,15 +162,19 @@ Before committing, ensure: - [ ] Commit message is clear and descriptive ### Commit Message Format + Follow conventional commit format: -``` + +```text (): [optional body] [optional footer] ``` + Examples: + - `feat(download): add async download methods` - `fix(fileex): correct boundary condition in DeleteDirectory` - `docs(readme): update async method examples` @@ -165,7 +185,7 @@ Examples: - **Package ID**: InsaneGenius.Utilities - **Namespace**: InsaneGenius.Utilities - **License**: MIT -- **Repository**: https://github.com/ptr727/Utilities +- **Repository**: - **Target Framework**: .NET 10 - **C# Version**: 14.0 - **Version**: 3.5 (managed by Nerdbank.GitVersioning) @@ -186,6 +206,7 @@ Examples: ## Code Generation Preferences ### Modern C# Features (C# 14) + - Prefer modern C# language features (pattern matching, records, file-scoped namespaces, etc.) - Use nullable reference types consistently with `ArgumentNullException.ThrowIfNull()` - Leverage expression-bodied members where appropriate @@ -195,6 +216,7 @@ Examples: - Use primary constructors where appropriate ### Async/Await Patterns + - **Always use `ConfigureAwait(false)` in library code** - Provide async versions of I/O-bound methods - Use `CancellationToken` parameters (default to `default`) @@ -204,17 +226,20 @@ Examples: - Use `Memory` and `Span` for async I/O operations ### Input Validation + - Use `ArgumentNullException.ThrowIfNull()` for null checks - Validate parameters early in methods - Document all exceptions in XML comments ### Resource Management + - Use `using` statements for proper disposal - Use `await using` for async disposal - Avoid explicit `.Close()` calls (using handles it) - Use `leaveOpen` parameter when appropriate ### Thread Safety + - Use `Lazy` for thread-safe initialization - Use `Lock` (C# 13+) instead of `object` for locks - Avoid static mutable state @@ -243,6 +268,7 @@ Examples: ## Common Patterns in This Project ### Error Handling + ```csharp try { @@ -259,18 +285,21 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) ``` ### Retry Logic + - Use `Options.RetryCount` for retry attempts - Use `Options.Cancel.IsCancellationRequested` for cancellation - Use `Task.Delay()` for async waits - Log retry attempts with `LogOptions.Logger.Information()` ### Method Signatures + - I/O methods return `bool` for success/failure - Async methods have `Async` suffix - Async methods include optional `CancellationToken cancellationToken = default` - Use `out` parameters for additional return values ### XML Documentation + - Always include `` for all public members - Document all `` with descriptions - Document `` with descriptions @@ -295,24 +324,28 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) ## File-Specific Notes ### Download.cs + - Uses thread-safe `Lazy` initialization - Provides both sync and async versions - Returns tuples from async methods for multiple values - Uses `HttpCompletionOption.ResponseHeadersRead` for efficiency ### FileEx.cs + - All I/O methods have async versions - Uses `Options` for retry configuration - Returns `bool` for success/failure - Supports cancellation via `Options.Cancel` and method parameter ### StringCompression.cs + - Supports configurable compression levels - Has both sync and async versions - Uses `leaveOpen` for stream management - Proper error documentation ### Extensions.cs + - Uses C# 14 `extension` keyword - Must be inside static class - Provides extension methods for string compression and logger error handling @@ -320,6 +353,7 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) ## Development Workflow ### Making Changes + 1. Edit code 2. **Run CSharpier**: `dotnet csharpier .` 3. **Run dotnet format**: `dotnet format` @@ -328,6 +362,7 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) 6. Commit (Husky.Net pre-commit hook will verify formatting) ### Before Committing + ```bash # Format code (REQUIRED ORDER) dotnet csharpier . @@ -345,6 +380,7 @@ git commit -m "feat: your message" ``` ### If Pre-Commit Hook Fails + ```bash # Hook will show formatting errors # Re-run formatters @@ -365,9 +401,133 @@ git commit -m "feat: your message" ## EditorConfig Integration The project uses `.editorconfig` for style enforcement. Key rules: + - `csharp_style_var_*` = **false** (no var keyword) - `csharp_style_namespace_declarations` = **file_scoped** - `csharp_prefer_system_threading_lock` = **true** - `dotnet_style_prefer_collection_expression` = **when_types_loosely_match** Visual Studio and Rider automatically apply these settings. VS Code requires the EditorConfig extension. + +## GitHub Copilot Review Runbook + +Use this section for provider-specific mechanics. The expected review loop *contract* (request review on every push, verify head-SHA coverage, triage findings, reply + resolve, escalate when stuck) is defined in [AGENTS.md -> PR Review Etiquette](../AGENTS.md#pr-review-etiquette). This section only describes how to make GitHub Copilot reliably execute it. + +### Triggering and Polling + +Auto-review on push is configured (via the branch ruleset's `copilot_code_review` rule with `review_on_push: true`) but fires inconsistently in practice - treat it as best-effort, not guaranteed. After every push, **re-request a review programmatically** via the GraphQL `requestReviews` mutation, passing the Copilot reviewer's bot node id in `botIds`. This now works reliably (it previously did not - a maintainer had to click "re-request review" in the UI; the agent can now drive the loop end-to-end without that hand-off). + +> **The reviewer login differs by API - this is intentional, not a typo.** In **GraphQL** (`gh api graphql` and `gh pr view --json reviews`, which is GraphQL-backed) the `Bot.login` is `copilot-pull-request-reviewer` - **no `[bot]` suffix**. In the **REST** API (`gh api repos/.../issues|pulls/...`) the same account's `user.login` is `copilot-pull-request-reviewer[bot]` - **with** the suffix. Each query below uses the correct form for its API; match the API, not a single spelling, when adapting them. + +```sh +# 1. PR node id + the Copilot reviewer's bot node id (read from any existing +# Copilot review; the reviewer login is `copilot-pull-request-reviewer`). +PR_NODE=$(gh pr view --json id --jq '.id') +BOT_ID=$(gh api graphql -f query=' +{ + repository(owner: "ptr727", name: "Utilities") { + pullRequest(number: ) { + reviews(first: 50) { nodes { author { __typename login ... on Bot { id } } } } + } + } +}' --jq '[.data.repository.pullRequest.reviews.nodes[] + | select(.author.login == "copilot-pull-request-reviewer") + | .author.id] | first') + +# 2. Re-request a Copilot review on the current head. +gh api graphql -f query=' +mutation($pr: ID!, $bot: ID!) { + requestReviews(input: { pullRequestId: $pr, botIds: [$bot], union: true }) { + pullRequest { id } + } +}' -F pr="$PR_NODE" -F bot="$BOT_ID" +``` + +The bot node id is read from an existing Copilot review, so step 1 needs at least one prior review on the PR - the auto-review-on-open normally supplies the first one. If no Copilot review exists yet and auto-review didn't fire, request `Copilot` once through the GitHub PR UI to seed it, then use the mutation for every subsequent re-request. + +**Do NOT post `@Copilot review` as a PR comment.** That comment triggers the Copilot *coding agent* (`copilot-swe-agent[bot]`), which makes code changes rather than posting a review. + +Known non-working request paths (don't rely on them - use the `requestReviews` mutation above instead): + +- `POST /requested_reviewers` with `reviewers=[Copilot]` can return 200 but no-op. +- `copilot-pull-request-reviewer` as a requested reviewer slug returns 422. + +### Verify Review Covered Current Head + +Before merging, confirm Copilot reviewed the current PR head SHA. Copilot may respond as either a formal review (carries an exact commit SHA) or an issue comment (no SHA - use the most recent Copilot comment for manual confirmation). Check both. + +```sh +PR_HEAD=$(gh pr view --json headRefOid --jq '.headRefOid') + +# 1. Formal review - exact SHA match. +gh pr view --json reviews --jq \ + '.reviews[] | select(.author.login=="copilot-pull-request-reviewer") | .commit.oid' \ + | grep -q "$PR_HEAD" && echo "covered via formal review" + +# 2. Issue comment - show the most recent Copilot comment for manual +# confirmation. This is the REST API, so the login carries the `[bot]` suffix. +gh api repos/ptr727/Utilities/issues//comments --jq \ + '[.[] | select(.user.login=="copilot-pull-request-reviewer[bot]")] | last | {created_at, body: .body[:200]}' +``` + +Coverage is confirmed when (1) exits 0. For issue comments (path 2), body content is the only reliable signal - `created_at` is not: `git log -1 --format=%cI` is the **commit** timestamp, not the push timestamp, so amended or rebased commits can have an earlier timestamp and an older Copilot comment could satisfy a time check even though Copilot never saw the current head. Treat path (2) as confirmed only when the comment body explicitly refers to the current changes. + +### Bounded Retry Workflow + +If a review did not run on the current head, retry: + +1. Wait briefly and check head-SHA coverage (see above). +1. Re-request the review via the `requestReviews` mutation (see "Triggering and Polling"); fall back to the GitHub PR UI only if the mutation no-ops. +1. Retry up to two more times (three total). +1. If still missing, mark review as blocked and escalate to the user/maintainer with what was attempted. + +### Reply and Thread Resolution Workflow + +List unresolved threads. Use `first: 100` with cursor-based pagination; if `hasNextPage` is true, re-run with `after: ""` to retrieve the next page: + +```sh +gh api graphql -f query=' +{ + repository(owner: "ptr727", name: "Utilities") { + pullRequest(number: ) { + reviewThreads(first: 100) { + nodes { + id isResolved path + comments(first: 1) { nodes { author { login } body } } + } + pageInfo { hasNextPage endCursor } + } + } + } +}' | jq ' + .data.repository.pullRequest.reviewThreads | + (.pageInfo | "hasNextPage=\(.hasNextPage) endCursor=\(.endCursor)"), + (.nodes[] | select(.isResolved == false)) +' +``` + +Reply on a thread, then resolve it: + +```sh +gh api graphql -f query=' +mutation($threadId: ID!, $body: String!) { + addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) { + comment { id } + } +}' -F threadId="PRRT_..." -F body="Fixed in : ." + +gh api graphql -f query=' +mutation($threadId: ID!) { + resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } } +}' -F threadId="PRRT_..." +``` + +Issue-level Copilot comments (those in `issues//comments`) have no resolution action - GitHub provides no API or UI to resolve them. Reply if the finding warrants it; no resolution step is needed or possible. + +Reply-body conventions: + +- Accepted bug/style fix: include fixing commit SHA and a one-line summary. +- Declined style comment: cite the rule (AGENTS.md or language CODESTYLE) and the existing-tree precedent. +- Declined architecture proposal: one-sentence rationale. + +After the final push, sweep-resolve stale older threads for removed code paths. diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 0000000..c6a5714 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,14 @@ +{ + "config": { + // Prose paragraphs and data-heavy tables/URLs are intentionally long; + // reflowing at 80 cols hurts readability and churns diffs. + "MD013": false, + // Inline HTML is used for reference-link section dividers. + "MD033": false, + // Require fenced code blocks over the legacy 4-space-indented style. + "MD046": { "style": "fenced" }, + // Wide tables are intentional where wrapping cells breaks GitHub rendering. + "MD060": false + }, + "gitignore": true +} diff --git a/AGENTS.md b/AGENTS.md index 489e747..c7101cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,6 +54,50 @@ The repo uses a **two-phase model by default**: PRs build fast, publishing is ba - Don't add `Co-Authored-By:` lines unless the developer explicitly asks. - Use US English spelling. +## PR Review Etiquette + +The repo runs a review loop on every PR: local agent iteration plus remote automated review (GitHub Copilot is the configured reviewer). Treat this as a contract regardless of which local agent authored the changes. + +### Expected Review Loop + +1. Push changes to the PR branch. +2. Re-request a review for the **current head SHA**. Auto-trigger is unreliable, so request it explicitly via the `requestReviews` GraphQL mutation (now reliable end-to-end - see the runbook); the UI is only a fallback. +3. Wait for review activity on that head. +4. Triage findings. +5. Apply fixes or write a rationale for declines. +6. Reply to each thread and resolve what was addressed. +7. Re-run the loop after every fix push until no actionable findings remain. + +`mergeStateStatus: CLEAN` only checks required statuses; it does not block on bot review comments. Drive the loop to green - review confirmed on the latest head SHA and every actionable finding closed - and then **wait for the maintainer's explicit permission to merge**. The agent does not merge on its own (consistent with "default to staging"; merging is maintainer-authorized). + +For provider-specific mechanics (how to request review, query review state, post replies, resolve threads), see the **GitHub Copilot Review Runbook** in [.github/copilot-instructions.md](./.github/copilot-instructions.md). This file owns the contract; that file owns the mechanics. + +### Triaging Review Comments + +For each comment, classify before responding: + +- **Bug** - wrong behavior, missing test coverage, or a real divergence between code and docs. Fix it. Reply with the fixing commit SHA when done. +- **Style/convention** - the comment cites a rule from this file or a language-specific style guide. Two cases: + - The cited rule matches what the existing codebase already does -> fix the offending code. + - The cited rule contradicts what's in the tree, or industry norm -> **update the rule instead of the code**. The rule is wrong, not the code. Bouncing the same code across rounds is the symptom of a wrong rule. Heuristic: three rounds on the same style category means the rule needs adjusting and the user should authorize the rule change. +- **Architectural opinion** - the comment proposes a different design ("constrain this to disabled-by-default", "move it elsewhere", "add a runtime guardrail"). This is judgment, not a bug. Surface it to the user with a recommendation; don't apply unilaterally. + +### Responding and Resolution Expectations + +Reply inline with either the fixing commit SHA (for accepted issues) or a concise rationale (for declines). Resolve review threads when addressed or intentionally declined with rationale. Issue-level comments (those at `repos/.../issues//comments` rather than tied to a specific line) have no resolution action - acknowledge with a reply if needed and move on. + +After the final push on a PR, sweep older threads from earlier rounds whose code paths no longer exist; otherwise stale unresolved markers remain in the review UI. + +### Escalating to the User + +Bring the user in when: + +- **Genuine design trade-off** surfaces (fail-open vs fail-closed, narrow vs broad refactor scope, "should we add a guardrail or trust the docstring"). Triage, recommend, ask. +- **Repeated friction** across rounds without convergence - that's the rule-needs-updating signal. Stop, summarize the pattern, and let the user authorize the rule change. +- **Architectural redesign** is requested rather than a bug fix. Surface with a recommendation; never apply unilaterally. + +Anti-pattern: don't keep flipping the code on the same style point. Flip the rule once and stick to the rule. + ## Maintainer Setup (GitHub) - **Secrets**: `NUGET_API_KEY` (NuGet.org push); `CODEGEN_APP_CLIENT_ID` + `CODEGEN_APP_PRIVATE_KEY` for the merge-bot's GitHub App token — add these to **both** the Actions and Dependabot secret stores.