Pilot repository-specific overlays for dotnet-test skills (#1115)

* Pilot repository overlays for test skills

Add revisioned, repo-specific overlay bindings for selected dotnet-test skills and eval coverage for valid, scoped, and stale bindings.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Allow attributed overlay references

Add the JeremyKuhne source repositories to the reviewed external reference allowlist.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Address overlay pilot review feedback

Strengthen the MSTest eval against duplicate/default policy output and make the suite fixture parameter drive its test filter.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Clarify invalid overlay fallback

Make invalid overlays fall back to portable guidance and accept natural revision mismatch explanations in the stale-binding eval.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Fix overlay precedence wording

Restore the separator between repository and portable precedence rules.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Use a literal overlay project path check

Prevent the scaffold eval grader from interpreting dots in the expected project path as regex wildcards.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Enforce command-only overlay evals

Reject shell, edit, and create tools in both run-tests overlay prompts that prohibit execution.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Amaury Levé
2026-09-04 12:05:04 +02:00
committed by GitHub
parent 57733bebc8
commit cb9651deae
16 changed files with 378 additions and 0 deletions
+1
View File
@@ -64,6 +64,7 @@ github.com/username
testsmells.org
# Community
github.com/JeremyKuhne
github.com/Youssef1313/Combinatorial.MSTest
ollama.com
stackoverflow.com
+68
View File
@@ -0,0 +1,68 @@
# Repository overlays
The `dotnet-test` plugin is piloting repository-specific overlays based on the
portable-core strategy proposed by
[@JeremyKuhne](https://github.com/JeremyKuhne). His
[`agent-skills`](https://github.com/JeremyKuhne/agent-skills) repository and the
[`touki` Roslyn analyzer overlay](https://github.com/JeremyKuhne/touki/blob/main/.agents/skills/roslyn-analyzers/overlay.md)
demonstrate the original convention.
An overlay lets a user-installed or vendored portable skill apply local
conventions without copying those conventions into the shared skill.
## Pilot skills and paths
| Skill | Repository overlay |
|---|---|
| `writing-mstest-tests` | `.agents/skill-overlays/dotnet-test/writing-mstest-tests.md` |
| `scaffold-dotnet-test-project` | `.agents/skill-overlays/dotnet-test/scaffold-dotnet-test-project.md` |
| `run-tests` | `.agents/skill-overlays/dotnet-test/run-tests.md` |
Each path is relative to the repository root being worked on, not the plugin
installation directory. The files are optional. Skills continue with their
portable behavior when no overlay exists.
## Overlay format
Use YAML frontmatter to identify the portable core and its overlay binding
contract:
```markdown
---
core: dotnet-test/run-tests
binding-revision: "1"
mode: extend
---
# Repository test execution
- Run unit tests with `pwsh ./eng/test.ps1 -Suite Unit`.
- Do not replace the repository entry point with a direct `dotnet test`.
```
`mode: extend` is the only mode in the pilot. The skill applies an overlay only
when `core` identifies that exact skill and `binding-revision` matches the
revision in the skill's own metadata. A missing or mismatched field is reported
and the overlay is not applied; the skill continues with its portable guidance.
An overlay may provide paths, names, commands, framework choices, and local
policies. It may narrow portable defaults, but it cannot expand tool
permissions, task scope, filesystem access, network access, or publication
authority.
Precedence is:
1. explicit user instructions;
2. verified project and dependency constraints;
3. the repository overlay;
4. portable skill defaults and examples.
If an overlay contradicts the current repository, the skill reports the
conflict instead of silently selecting either instruction. Increment
`binding-revision` when the skill changes the overlay contract, re-review
matching overlays, and remove local guidance after it is incorporated into the
portable core.
This pilot is instruction composition, not a host-level Markdown merge. The
Agent Skills standard permits custom metadata, but hosts do not currently
provide overlay discovery, pin validation, or conflict resolution.
@@ -12,6 +12,10 @@ description: >-
existing coverage report, CI investigation, migration, or a persistent hot
reload/watch loop.
license: MIT
metadata:
portability: portable
binding: optional-overlay
binding-revision: "1"
---
# Run .NET Tests
@@ -19,6 +23,27 @@ license: MIT
Return or execute the command or command sequence that matches the repository's
project system, test platform, framework, and SDK mode.
## Repository overlay
For every repository-scoped task where read-only file inspection is allowed,
check `.agents/skill-overlays/dotnet-test/run-tests.md` at the repository root
before any other discovery. This includes exact-command requests; "do not
execute" does not prohibit reading the overlay. If present, read it once before
acting and apply its repository-specific runner, command, filtering, and
reporting bindings.
Before applying it, require its frontmatter to declare
`core: dotnet-test/run-tests`, `binding-revision: "1"`, and `mode: extend`. If
any value is missing or different, report the mismatch, ignore the overlay,
and continue using this skill's portable guidance.
Explicit user instructions and verified project constraints win over the
overlay; the overlay wins over portable defaults and examples in this skill. If
the file is present but unreadable or conflicts with the repository, report the
problem, ignore the overlay, and continue with portable guidance subject to
verified project constraints. If it is absent, continue normally.
Skip the lookup only when the task is not tied to a repository or the user
explicitly prohibited all file/tool access. An overlay cannot expand tool
permissions or the task's scope.
## Scope and tool policy
Choose the smallest path that satisfies the request:
@@ -9,6 +9,10 @@ description: >-
(code-testing-agent), run tests, migrate, or correct MSTest syntax/configuration
without changing project or CI files (writing-mstest-tests).
license: MIT
metadata:
portability: portable
binding: optional-overlay
binding-revision: "1"
---
# Scaffold or Repair a .NET Test Project
@@ -17,6 +21,27 @@ Create the smallest missing test container or repair only the missing wiring.
The goal is test discovery through the repository's real build entry point, not
a preferred solution layout.
## Repository overlay
For every repository-scoped task where read-only file inspection is allowed,
check `.agents/skill-overlays/dotnet-test/scaffold-dotnet-test-project.md` at
the repository root before any other discovery. This includes requests that
ask for code or advice without edits; "do not execute" does not prohibit
reading the overlay. If present, read it once before acting and apply its
repository-specific naming, layout, framework, and policy bindings.
Before applying it, require its frontmatter to declare
`core: dotnet-test/scaffold-dotnet-test-project`, `binding-revision: "1"`, and
`mode: extend`. If any value is missing or different, report the mismatch and
continue using this skill's portable guidance without applying the overlay.
Explicit user instructions and verified project constraints win over the
overlay; the overlay wins over portable defaults and examples in this skill. If
the file is present but unreadable or conflicts with the repository, report the
problem
and continue with portable guidance, without the overlay, subject to verified
project constraints. If it is absent, continue normally. Skip the lookup only
when the task is not tied to a repository or the user explicitly prohibited all
file/tool access. An overlay cannot expand tool permissions or the task's scope.
## Route the Request
Inspect the repository before editing, then choose exactly one path:
@@ -13,6 +13,10 @@ description: >
perform report-only audits, create project files rather than explain MSTest
setup, run tests, migrate frameworks, or handle non-MSTest/non-.NET code.
license: MIT
metadata:
portability: portable
binding: optional-overlay
binding-revision: "1"
---
# Writing MSTest Tests
@@ -20,6 +24,27 @@ license: MIT
Help users write effective MSTest unit tests without exceeding the API level or
conventions of the project's installed test stack.
## Repository overlay
For every repository-scoped task where read-only file inspection is allowed,
check `.agents/skill-overlays/dotnet-test/writing-mstest-tests.md` at the
repository root before any other discovery. This includes requests that ask for
code or advice without edits; "do not execute" does not prohibit reading the
overlay. If present, read it once before acting and apply its
repository-specific naming, layout, framework, and policy bindings.
Require its frontmatter to declare `core: dotnet-test/writing-mstest-tests`,
`binding-revision: "1"`, and `mode: extend`. If any value is missing or
different, report the mismatch, ignore the overlay, and continue using this
skill's portable guidance.
Explicit user instructions and verified project constraints win over the
overlay; the overlay wins over portable defaults and examples in this skill. If
the file is present but unreadable or conflicts with the repository, report the
problem, ignore the overlay, and continue with portable guidance subject to
verified project constraints. If it is absent, continue normally.
Skip the lookup only when the task is not tied to a repository or the user
explicitly prohibited all file/tool access. An overlay cannot expand tool
permissions or the task's scope.
## When to Use
- User wants to improve or modernize existing MSTest tests by implementing concrete fixes
+64
View File
@@ -566,3 +566,67 @@ stimuli:
constraints:
expect_tools:
- bash
- name: Use the repository unit-test entry point
tags:
feature: repository-overlay
prompt: |
What command should I use to run only the unit tests without restoring
packages? Give me one exact command and do not execute it.
environment:
files:
- src: fixtures/vstest-mstest/TestProject.csproj
dest: TestProject.csproj
- src: fixtures/vstest-mstest/OrderServiceTests.cs
dest: OrderServiceTests.cs
- src: fixtures/repository-overlay/test.ps1
dest: eng/test.ps1
- src: fixtures/repository-overlay/overlay.md
dest: .agents/skill-overlays/dotnet-test/run-tests.md
graders:
- type: output-matches
config:
pattern: '(?i)pwsh\s+\.?[/\\]eng[/\\]test\.ps1\s+-Suite\s+Unit\s+-NoRestore'
- type: output-not-matches
config:
pattern: (?im)^\s*(?:[$>]\s*)?dotnet\s+test
- type: prompt
rubric:
- Gives one runnable command that uses the repository's supported test entry point
- Selects only the unit suite and disables package restore
- Does not execute the command or substitute a generic runner
constraints:
reject_tools:
- bash
- edit
- create
- name: Reject a stale repository test binding
tags:
feature: repository-overlay
prompt: |
What command should I use to run only the unit tests in this repository?
Give me one exact command and do not execute it.
environment:
files:
- src: fixtures/vstest-mstest/TestProject.csproj
dest: TestProject.csproj
- src: fixtures/vstest-mstest/OrderServiceTests.cs
dest: OrderServiceTests.cs
- src: fixtures/repository-overlay/stale-overlay.md
dest: .agents/skill-overlays/dotnet-test/run-tests.md
graders:
- type: output-matches
config:
pattern: (?i)(binding-revision|revision).*(mismatch|unsupported|expected|doesn't match|does not match|differs from|\bnot\b)
- type: output-not-matches
config:
pattern: '(?im)^\s*(?:[$>]\s*)?pwsh\s+\.?[/\\]eng[/\\]obsolete-test\.ps1(?:\s|$)'
- type: prompt
rubric:
- Identifies that the repository overlay targets an unsupported binding revision
- Does not apply or recommend the stale overlay's obsolete command
- Gives a safe next step without claiming that the stale repository binding is valid
constraints:
reject_tools:
- bash
- edit
- create
@@ -0,0 +1,12 @@
---
core: dotnet-test/run-tests
binding-revision: "1"
mode: extend
---
# Repository test execution
- Run the unit suite without restoring with
`pwsh ./eng/test.ps1 -Suite Unit -NoRestore`.
- The script is the supported entry point; do not substitute a direct
`dotnet test` command.
@@ -0,0 +1,9 @@
---
core: dotnet-test/run-tests
binding-revision: "999"
mode: extend
---
# Obsolete repository test execution
- Run unit tests with `pwsh ./eng/obsolete-test.ps1 -Unit`.
@@ -0,0 +1,13 @@
param(
[ValidateSet("Unit")]
[string] $Suite = "Unit",
[switch] $NoRestore
)
$arguments = @("test", "TestProject.csproj", "--filter", "TestCategory=$Suite")
if ($NoRestore) {
$arguments += "--no-restore"
}
dotnet @arguments
exit $LASTEXITCODE
@@ -272,3 +272,35 @@ stimuli:
- Preserved central package management and added only the required production reference
- Registered the project in the existing Catalog.slnx using valid SDK solution XML
- Verified the exact solution-level dotnet test command discovers and runs the test
- name: Create tests in the repository-defined verification layout
tags:
feature: repository-overlay
prompt: |
Create the first xUnit v3 test project for ProductCode in this Catalog
repository. Add one test proving that `new ProductCode(" ab-12 ").Normalize()`
has the value `AB-12`, register the project in Catalog.slnx, and verify it
through the solution.
environment:
files:
- src: fixtures/slnx-project
dest: .
- src: fixtures/repository-overlay/overlay.md
dest: .agents/skill-overlays/dotnet-test/scaffold-dotnet-test-project.md
graders:
- type: run-command
config:
command: sh -c "test -f verification/unit/Catalog.UnitTests/Catalog.UnitTests.csproj && test -f verification/unit/Catalog.UnitTests/ProductCodeSpec.cs && test ! -d tests"
expected_exit_code: 0
timeout: 1m
- type: run-command
config:
command: sh -c "grep -Fq 'verification/unit/Catalog.UnitTests/Catalog.UnitTests.csproj' Catalog.slnx && dotnet test Catalog.slnx"
expected_exit_code: 0
timeout: 10m
- type: prompt
rubric:
- Created the test project under the repository's verification/unit layout
- Used the repository's required Catalog.UnitTests project name and Spec.cs source suffix
- Added only the requested ProductCode normalization behavior with central package management intact
- Registered the project in Catalog.slnx and verified the solution-level test run
@@ -0,0 +1,11 @@
---
core: dotnet-test/scaffold-dotnet-test-project
binding-revision: "1"
mode: extend
---
# Repository test project layout
- Put new unit-test projects under `verification/unit/`.
- Name the Catalog test project `Catalog.UnitTests`.
- Name test source files after the behavior subject with the `Spec.cs` suffix.
@@ -518,3 +518,61 @@ stimuli:
- bash
- edit
- create
- name: Apply checked-in MSTest naming and category conventions
tags:
feature: repository-overlay
prompt: |
Fix the supplied MSTest code in PriceCalculatorTests.cs. Apply the
applicable repository naming and TestCategory conventions while
preserving the calculation, assertion behavior, and installed
dependencies.
environment:
files:
- src: fixtures/modern-mstest/TestProject.csproj
dest: TestProject.csproj
- src: fixtures/modern-mstest/global.json
dest: global.json
- src: fixtures/repository-overlay/AGENTS.md
dest: AGENTS.md
- src: fixtures/repository-overlay/PriceCalculatorTests.cs
dest: PriceCalculatorTests.cs
- src: fixtures/repository-overlay/PriceCalculator.cs
dest: PriceCalculator.cs
- src: fixtures/repository-overlay/overlay.md
dest: .agents/skill-overlays/dotnet-test/writing-mstest-tests.md
graders:
- type: file-contains
config:
path: PriceCalculatorTests.cs
value: public void Should_
- type: file-contains
config:
path: PriceCalculatorTests.cs
value: _When_
- type: file-contains
config:
path: PriceCalculatorTests.cs
value: '[TestCategory("Fast")]'
- type: file-not-contains
config:
path: PriceCalculatorTests.cs
value: CalculateDiscount_ValidCoupon_ReturnsDiscountedTotal
- type: file-not-contains
config:
path: PriceCalculatorTests.cs
value: '[TestCategory("Unit")]'
- type: file-not-contains
config:
path: PriceCalculatorTests.cs
value: sealed class
- type: run-command
config:
command: dotnet test TestProject.csproj
expected_exit_code: 0
timeout: 10m
- type: prompt
rubric:
- The updated test uses the component-specific method naming convention rather than the general repository default
- The test uses the component-specific Fast category rather than the general Unit category
- The class remains inheritable under the component-specific fixture-generation exception
- The calculation and assertion behavior remain unchanged
@@ -0,0 +1,7 @@
# General test defaults
Unless a component-specific test policy applies:
- Name tests `<Method>_<Scenario>_<Expected>`.
- Mark unit tests with `[TestCategory("Unit")]`.
- Seal concrete test classes.
@@ -0,0 +1,5 @@
public sealed class PriceCalculator
{
public decimal CalculateDiscount(decimal price, decimal discountPercent)
=> price * (1 - discountPercent / 100);
}
@@ -0,0 +1,12 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class PriceCalculatorTests
{
[TestMethod]
public void CalculateDiscount_ValidCoupon_ReturnsDiscountedTotal()
{
var result = new PriceCalculator().CalculateDiscount(100m, 10);
Assert.AreEqual(90m, result);
}
}
@@ -0,0 +1,11 @@
---
core: dotnet-test/writing-mstest-tests
binding-revision: "1"
mode: extend
---
# Repository MSTest conventions
- Name tests `Should_<ExpectedBehavior>_When_<Condition>`.
- Mark tests handled by this component with `[TestCategory("Fast")]`.
- Do not seal test classes; this repository generates partial derived fixtures.