From 0ee2e3078d9c24b9f9c138ec805321bf5f16ebe9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 01:08:58 +0000 Subject: [PATCH 1/2] Initial plan From 5e884ded11d8b53892631b2f912154753397794b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 01:14:52 +0000 Subject: [PATCH 2/2] Align BuildMark with TemplateDotNetTool template updates - Update test-developer agent: add Test Source Filters and MSTest V4 Best Practices sections (including Assert.StartsWith antipattern from PR #45) - Update code-quality agent: fix markdownlint reference from .markdownlint.json to .markdownlint-cli2.jsonc - Update requirements agent: add Test Source Filters section - Update technical-writer agent: clarify reference-style vs inline link rules including AI agent markdown files exception - Update software-developer agent: fix XML Docs bullet with four-space indent sub-rule - Update src csproj: LangVersion to latest, GenerateDocumentationFile to lowercase true, add package comments, update SonarAnalyzer.CSharp to 10.20.0.135146, add Polyfill 9.12.0 - Update test csproj: LangVersion to latest, GenerateDocumentationFile to lowercase true, add section comments, add Polyfills implicit using, update Microsoft.NET.Test.Sdk to 18.3.0, update SonarAnalyzer.CSharp to 10.20.0.135146 --- .github/agents/code-quality-agent.md | 2 +- .github/agents/requirements-agent.md | 17 ++++++ .github/agents/software-developer.md | 3 +- .github/agents/technical-writer.md | 6 +- .github/agents/test-developer.md | 61 +++++++++++++++++++ .../DemaConsulting.BuildMark.csproj | 18 +++++- .../DemaConsulting.BuildMark.Tests.csproj | 35 ++++++++--- 7 files changed, 125 insertions(+), 17 deletions(-) diff --git a/.github/agents/code-quality-agent.md b/.github/agents/code-quality-agent.md index ea36f4f..4ca05c6 100644 --- a/.github/agents/code-quality-agent.md +++ b/.github/agents/code-quality-agent.md @@ -31,7 +31,7 @@ Ensure the project is: 1. **Build**: Zero warnings (TreatWarningsAsErrors=true) 2. **Linting**: - - markdownlint (`.markdownlint.json`) + - markdownlint (`.markdownlint-cli2.jsonc`) - cspell (`.cspell.json`) - yamllint (`.yamllint.yaml`) - dotnet format (`.editorconfig`) diff --git a/.github/agents/requirements-agent.md b/.github/agents/requirements-agent.md index 3ad52ce..c63c02c 100644 --- a/.github/agents/requirements-agent.md +++ b/.github/agents/requirements-agent.md @@ -49,6 +49,23 @@ Follow the `requirements.yaml` structure: - Linked to appropriate test(s) - Enforced via: `dotnet reqstream --requirements requirements.yaml --tests "test-results/**/*.trx" --enforce` +### Test Source Filters + +Test links in `requirements.yaml` can include a source filter prefix to restrict which test results count as +evidence. This is critical for platform and framework requirements - **never remove these filters**. + +- `windows@TestName` - proves the test passed on a Windows platform +- `ubuntu@TestName` - proves the test passed on a Linux (Ubuntu) platform +- `net8.0@TestName` - proves the test passed under the .NET 8 target framework +- `net9.0@TestName` - proves the test passed under the .NET 9 target framework +- `net10.0@TestName` - proves the test passed under the .NET 10 target framework +- `dotnet8.x@TestName` - proves the self-validation test ran on a machine with .NET 8.x runtime +- `dotnet9.x@TestName` - proves the self-validation test ran on a machine with .NET 9.x runtime +- `dotnet10.x@TestName` - proves the self-validation test ran on a machine with .NET 10.x runtime + +Without the source filter, a test result from any platform/framework satisfies the requirement. Removing a +filter invalidates the evidence for platform/framework requirements. + ## Defer To - **Software Developer Agent**: For implementing self-validation tests diff --git a/.github/agents/software-developer.md b/.github/agents/software-developer.md index c736741..05cda6f 100644 --- a/.github/agents/software-developer.md +++ b/.github/agents/software-developer.md @@ -52,7 +52,8 @@ var results = ProcessFile(options.InputFile); ### BuildMark-Specific Rules -- **XML Docs**: On ALL members (public/internal/private) with spaces after `///` in summaries +- **XML Docs**: On ALL members (public/internal/private) with spaces after `///` + - Follow standard XML indentation rules with four-space indentation - **Errors**: `ArgumentException` for parsing, `InvalidOperationException` for runtime issues - **Namespace**: File-scoped namespaces only - **Using Statements**: Top of file only diff --git a/.github/agents/technical-writer.md b/.github/agents/technical-writer.md index dc3d4cd..5fe088a 100644 --- a/.github/agents/technical-writer.md +++ b/.github/agents/technical-writer.md @@ -31,8 +31,10 @@ Invoke the technical-writer for: #### Markdown Style -- **README.md ONLY**: Use absolute URLs in the links (shipped in NuGet package) -- **All other markdown files**: Use reference-style links +- **All markdown files**: Use reference-style links `[text][ref]` with `[ref]: url` at document end +- **Exceptions**: + - **README.md**: Use absolute URLs in the links (shipped in NuGet package) + - **AI agent markdown files** (`.github/agents/*.md`): Use inline links `[text](url)` so URLs are visible in agent context - Max 120 characters per line - Lists require blank lines (MD032) diff --git a/.github/agents/test-developer.md b/.github/agents/test-developer.md index b1351a6..0bb611f 100644 --- a/.github/agents/test-developer.md +++ b/.github/agents/test-developer.md @@ -71,6 +71,67 @@ public void ClassName_MethodUnderTest_Scenario_ExpectedBehavior() - Use MSTest V4 testing framework - Follow existing naming conventions in the test suite +### Test Source Filters + +Test links in `requirements.yaml` can include a source filter prefix to restrict which test results count as +evidence. These filters are critical for platform and framework requirements - **do not remove them**. + +- `windows@TestName` - proves the test passed on a Windows platform +- `ubuntu@TestName` - proves the test passed on a Linux (Ubuntu) platform +- `net8.0@TestName` - proves the test passed under the .NET 8 target framework +- `net9.0@TestName` - proves the test passed under the .NET 9 target framework +- `net10.0@TestName` - proves the test passed under the .NET 10 target framework +- `dotnet8.x@TestName` - proves the self-validation test ran on a machine with .NET 8.x runtime +- `dotnet9.x@TestName` - proves the self-validation test ran on a machine with .NET 9.x runtime +- `dotnet10.x@TestName` - proves the self-validation test ran on a machine with .NET 10.x runtime + +Removing a source filter means a test result from any environment can satisfy the requirement, which invalidates +the evidence-based proof that the tool works on a specific platform or framework. + +### MSTest V4 Best Practices + +Common anti-patterns to avoid (not exhaustive): + +1. **Avoid Assertions in Catch Blocks (MSTEST0058)** - Instead of wrapping code in try/catch and asserting in the + catch block, use `Assert.ThrowsExactly()`: + + ```csharp + var ex = Assert.ThrowsExactly(() => SomeWork()); + Assert.Contains("Some message", ex.Message); + ``` + +2. **Avoid using Assert.IsTrue / Assert.IsFalse for equality checks** - Use `Assert.AreEqual` / + `Assert.AreNotEqual` instead, as it provides better failure messages: + + ```csharp + // ❌ Bad: Assert.IsTrue(result == expected); + // ✅ Good: Assert.AreEqual(expected, result); + ``` + +3. **Avoid non-public test classes and methods** - Test classes and `[TestMethod]` methods must be `public` or + they will be silently ignored: + + ```csharp + // ❌ Bad: internal class MyTests + // ✅ Good: public class MyTests + ``` + +4. **Avoid Assert.IsTrue(collection.Count == N)** - Use `Assert.HasCount` for count assertions: + + ```csharp + // ❌ Bad: Assert.IsTrue(collection.Count == 3); + // ✅ Good: Assert.HasCount(3, collection); + ``` + +5. **Avoid Assert.IsTrue for string prefix checks** - Use `Assert.StartsWith` instead of wrapping + `string.StartsWith` in `Assert.IsTrue`, as it produces clearer failure messages that show the expected prefix + and actual value: + + ```csharp + // ❌ Bad: Assert.IsTrue(value.StartsWith("prefix")); + // ✅ Good: Assert.StartsWith("prefix", value); + ``` + ## Defer To - **Requirements Agent**: For test strategy and coverage requirements diff --git a/src/DemaConsulting.BuildMark/DemaConsulting.BuildMark.csproj b/src/DemaConsulting.BuildMark/DemaConsulting.BuildMark.csproj index 4c3a7d7..cebcb15 100644 --- a/src/DemaConsulting.BuildMark/DemaConsulting.BuildMark.csproj +++ b/src/DemaConsulting.BuildMark/DemaConsulting.BuildMark.csproj @@ -3,7 +3,7 @@ Exe net8.0;net9.0;net10.0 - 12 + latest enable enable @@ -34,7 +34,7 @@ true - True + true true true latest @@ -50,13 +50,25 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DemaConsulting.BuildMark.Tests/DemaConsulting.BuildMark.Tests.csproj b/test/DemaConsulting.BuildMark.Tests/DemaConsulting.BuildMark.Tests.csproj index 320b0c5..09fe506 100644 --- a/test/DemaConsulting.BuildMark.Tests/DemaConsulting.BuildMark.Tests.csproj +++ b/test/DemaConsulting.BuildMark.Tests/DemaConsulting.BuildMark.Tests.csproj @@ -1,38 +1,53 @@ + + net8.0;net9.0;net10.0 - 12 + latest enable enable + + false + true + true + + true true true latest - - false - true - True + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive +