Skip to content

Support classic .NET test projects in dotnet-test - #993

Open
Evangelink wants to merge 10 commits into
mainfrom
dev/amauryleve/legacy-test-compatibility
Open

Support classic .NET test projects in dotnet-test#993
Evangelink wants to merge 10 commits into
mainfrom
dev/amauryleve/legacy-test-compatibility

Conversation

@Evangelink

@Evangelink Evangelink commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

  • detect classic non-SDK projects and packages.config before choosing build, test, or coverage commands
  • preserve explicit <Compile Include> registration, legacy dependency versions, custom fixture bases, and repository MSBuild/VSTest/MSTest workflows
  • gate MSTest authoring advice by the installed version, including MSTest 3.5.x fallbacks
  • prevent SDK-style coverage package injection into classic projects and safely partition mixed SDK/classic solutions
  • require explicit, repository-policy-compliant opt-in before testability migrations
  • add regression evals and realistic net472 fixtures for generation, execution, coverage, and MSTest authoring

Review

Three independent review rounds were completed. Findings fixed included unsafe packages.config classification, mixed-solution coverage execution, unconditional testability migration routing, legacy validation wording, MSTest version-gating contradictions, and fixture adapter integration.

Evaluation follow-up

Run 31012795023 produced three non-passes with distinct causes:

  • code-testing-agent — routing/content: 5W/6T/1L (p=0.109), with activation only 1/5. Its frontmatter simultaneously claimed mandatory test generation and excluded MSTest. The only loss said the baseline was better because it covered the exact 0% boundary while the skilled run substituted 50%. The description now keeps MSTest generation at this entry point, and the classic stimulus explicitly requires <0, 0, 100, and >100.
  • crap-score — statistical power: 4W/2T/0L (p=0.063). Two ties left only four discordant trials, so no record could have passed. Two distinct scenarios now measure classic-project coverage safety and async MoveNext-to-source line mapping rather than repeating existing tasks.
  • platform-detection — eval design/activation: 0/5 activation because the skill set disable-model-invocation: true; the isolated arm randomly lost the SDK 10 precedence case while the plugin arm answered it correctly. Platform detection is now directly invocable and its expanded eval measures harder project-system, runner, bridge, and precedence decisions. The guidance also now distinguishes dotnet test command mode from the platform actually executed through the MTP bridge.

Run 31039546010 was inconclusive because three trials errored at session.idle:

  • Two writing-mstest-tests plugin trials stalled at the exact 5-minute budget—one before any response and one after two successful globs. This was classified as transient reliability; the eval now allows 6 minutes without changing skill content.
  • One isolated classic coverage trial ignored the existing stop instruction, created a temporary .coverage-run SDK project, collected coverage from the substitute assembly, and timed out while writing that misleading report. The skill now has a prominent hard stop forbidding temporary wrappers or substitute coverage. The eval snapshots every project file and rejects any newly generated Cobertura report.
  • To restore runtime headroom, coverage-analysis now uses two runs instead of three. Its eight distinct scenarios still produce 16 counted trials; the prior measured record was 17W/6T/1L over 24.
  • Review also corrected the legacy modernization fixture from MSTest 3.6.3 to 3.8.0 and scoped every crap-score collection fallback to SDK-style projects.

Run 31083033945 exhausted the fixed 155-minute Vally step after 233 of 234 expected trajectories; all recorded trajectories succeeded and no verdict phase ran. Runtime analysis showed the largest costs in broad generation/build scenarios. The evals now retain every unique requirement while replacing expensive near-ties with focused no-tool stimuli:

  • coverage still checks branch gaps and multi-member 80% arithmetic; the retained measured subset was 16W/1T/1L
  • run-tests still covers classic/VSTest, bridged/native MTP, multi-TFM, NUnit, xUnit, TUnit, TRX, combined filters, diagnostics, and props detection; the unchanged measured subset was 10W/1T
  • writing-mstest now tests API review/modernization rather than generation owned by code-testing-agent, including cooperative timeout syntax and correct IsInRange(min, max, value) ordering; the unchanged measured subset was 11W/1T
  • expected workload is 219 trajectories, with the expensive restore/build/generation paths converted to inline read-only tasks and unreferenced fixtures removed

Validation

  • dotnet run --no-build --no-restore --project eng/skill-validator/src/SkillValidator.csproj -- check --plugin ./plugins/dotnet-test
  • python eng/eval-quality/check_eval_quality.py
  • XML parsing and internal Cobertura consistency checks for all added fixtures
  • exercised the classic coverage safety grader under Git Bash
  • git diff --check

Run 31112078844 confirmed the structural bottleneck: all six changed skills were still assigned to the synthetic default execution shard, so one Vally job owned every baseline/isolated/plugin trajectory. The workflow already supports executionShard: and merges shard artifacts downstream. The six evals are now split into four domain shards aligned with max-parallel: 4:

  • generation: code-testing-agent
  • risk: coverage-analysis + crap-score
  • execution: platform-detection + run-tests
  • mstest: writing-mstest-tests

This changes only scheduling; each skill retains the same scenarios, variants, comparison, and statistical gate. The obsolete monolithic run bound to 8c638d083 was cancelled after the sharded commit was pushed.

Run 31112078844 completed comparisons and narrowed the remaining work to two skills:

  • code-testing-agent — eval discrimination: 2W/10T/0L (p=0.250). Only the broad Vitest scenario activated the skill consistently; focused generation was baseline-equivalent. Three distinct project-wide scenarios now exercise the orchestration/checklist advantage across multi-module Python, classic multi-class MSTest, and multi-package Go. At two runs each, the eval now has 18 trials and six new broad trial opportunities without relying on extra repetition.
  • platform-detection — output contract + boundary coverage: 3W/5T/1L (p=0.312). The sole loss was semantically correct—“VSTest command mode executing MTP”—but put VSTest on the requested Platform: line. The skill now defines Platform as the executed platform and reports command mode separately. Two baseline-easy framework cases were replaced by harder, valid SDK 9 boundaries: MTP runner without the dotnet-test bridge, and MSTest.Sdk explicitly opting back into VSTest with UseVSTest=true.

Both replacement platform fixtures restore successfully, and the generation fixtures compile/build. The generation shard now evaluates 18 trials; platform detection remains at 9 trials.

Run 31117103578, the first sharded retry, never reached Vally: prepare-validator failed while downloading a GitHub Action with repeated Service Unavailable errors. This was transient infrastructure, not an eval result.

Teach dotnet-test skills and agents to preserve non-SDK projects, packages.config dependencies, explicit compile registration, legacy runners, and version-compatible MSTest APIs. Add regression evals for generation, execution, coverage, and authoring.

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

Copilot-Session: fdfec89f-b610-479c-a6c7-c2936b300e68
Copilot AI review requested due to automatic review settings August 5, 2026 12:41
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Skill Coverage Report

Plugin Skill Covered Coverage
dotnet-test assertion-quality 22/22 100%
dotnet-test code-testing-agent 4/4 100%
dotnet-test crap-score 6/6 100%
dotnet-test detect-static-dependencies 22/22 100%
dotnet-test filter-syntax 1/1 100%
dotnet-test generate-testability-wrappers 24/24 100%
dotnet-test grade-tests 29/29 100%
dotnet-test migrate-static-to-wrapper 26/26 100%
dotnet-test mtp-hot-reload 16/16 100%
dotnet-test platform-detection 2/2 100%
dotnet-test run-tests 18/18 100%
dotnet-test test-anti-patterns 25/25 100%
dotnet-test test-gap-analysis 30/30 100%
dotnet-test test-smell-detection 26/26 100%
dotnet-test test-tagging 28/28 100%
dotnet-test writing-mstest-tests 44/46 95.7%
Uncovered: dotnet-test/writing-mstest-tests
  • [CodePattern] [TestMethod] (line 147)
  • [CodePattern] [TestClass] (line 147)

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/evaluate

@Evangelink
Evangelink temporarily deployed to copilot-pat-pool August 5, 2026 12:44 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands the dotnet-test plugin to correctly handle classic (non-SDK-style) .NET test projects—especially packages.config-based net472 MSTest solutions—by updating platform/project-system detection, ensuring version-compatible MSTest authoring guidance, and adding regression fixtures/evals to prevent accidental modernization (e.g., SDK-style coverage injection or implicit migration advice).

Changes:

  • Add classic non-SDK project-system awareness (including packages.config) across run/test/coverage guidance and agent workflows.
  • Gate MSTest authoring recommendations to the installed MSTest version (with explicit fallbacks for MSTest 3.5.x).
  • Add new realistic classic net472 MSTest fixtures and regression eval scenarios for running tests, coverage analysis, and test generation/registration.
Show a summary per file
File Description
tests/dotnet-test/writing-mstest-tests/eval.yaml Adds an eval stimulus to enforce MSTest-version-compatible exception assertions (MSTest 3.5.2).
tests/dotnet-test/run-tests/fixtures/classic-mstest/TestProject.csproj Introduces a classic net472 MSTest packages.config-style test project fixture.
tests/dotnet-test/run-tests/fixtures/classic-mstest/packages.config Pins MSTest adapter/framework dependencies for the classic run-tests fixture.
tests/dotnet-test/run-tests/fixtures/classic-mstest/OrderServiceTests.cs Adds a classic MSTest test file fixture used by run-tests evaluation.
tests/dotnet-test/run-tests/eval.yaml Adds an eval scenario ensuring classic projects route to MSBuild + VSTest/MSTest runner guidance.
tests/dotnet-test/coverage-analysis/fixtures/classic-packages-config/packages.config Adds a classic packages.config fixture for coverage-analysis scenarios.
tests/dotnet-test/coverage-analysis/fixtures/classic-packages-config/LegacyTests.cs Adds a basic MSTest test file for the classic coverage-analysis fixture.
tests/dotnet-test/coverage-analysis/fixtures/classic-packages-config/Legacy.Tests.csproj Adds a classic net472 test project fixture for coverage-analysis.
tests/dotnet-test/coverage-analysis/eval.yaml Adds a regression scenario ensuring classic projects are not modified when coverage is absent.
tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/packages.config Adds classic test dependencies (MSTest/Moq/NBuilder) for code-testing-agent fixture.
tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/FixtureBase.cs Adds a base fixture pattern to enforce preserving legacy suite conventions.
tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/DiscountServiceTests.cs Adds a representative classic MSTest test using Moq/NBuilder conventions.
tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/tests/Discounts.Tests.csproj Adds a classic test project requiring explicit <Compile Include> registration.
tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/src/DiscountService.cs Adds production-like code to exercise boundary/missing-product test generation in classic projects.
tests/dotnet-test/code-testing-agent/fixtures/classic-mstest/src/Discounts.csproj Adds a classic net472 production project fixture referenced by the tests project.
tests/dotnet-test/code-testing-agent/eval.yaml Adds a scenario requiring classic test-file registration and MSTest 3.5.2-compatible assertions.
plugins/dotnet-test/skills/writing-mstest-tests/SKILL.md Updates MSTest authoring guidance to respect installed MSTest version and classic project constraints.
plugins/dotnet-test/skills/run-tests/SKILL.md Updates run-tests guidance to support classic non-SDK projects (MSBuild + runner) in addition to dotnet test.
plugins/dotnet-test/skills/platform-detection/SKILL.md Extends detection instructions to classify SDK-style vs classic project systems and use packages.config signals.
plugins/dotnet-test/skills/crap-score/SKILL.md Prevents classic project modification/injection and requires repo-owned coverage workflow or existing Cobertura.
plugins/dotnet-test/skills/coverage-analysis/SKILL.md Updates coverage workflow to partition SDK vs classic projects and avoid SDK-style injection into classic projects.
plugins/dotnet-test/skills/coverage-analysis/references/test-execution.md Updates execution reference to exclude classic projects from automatic provider/test execution paths.
plugins/dotnet-test/skills/coverage-analysis/references/setup-discovery.md Adds classic vs SDK test project partitioning logic and corresponding branching guidance.
plugins/dotnet-test/skills/coverage-analysis/references/guidelines.md Clarifies modification rules: only SDK-style projects may get provider additions; classic must remain untouched.
plugins/dotnet-test/skills/code-testing-extensions/extensions/dotnet.md Adds .NET extension guidance for classic project detection, command selection, and explicit file registration.
plugins/dotnet-test/skills/code-testing-agent/SKILL.md Adds explicit guardrails for classic non-SDK .NET projects (preserve versions, register files, avoid modernizing).
plugins/dotnet-test/README.md Updates plugin README to document classic non-SDK support and constraints.
plugins/dotnet-test/agents/testability-migration.agent.md Adds an explicit “check repository policy” phase before any testability refactor actions.
plugins/dotnet-test/agents/test-quality-auditor.agent.md Removes default testability-migration handoff and tightens policy gating around production refactors.
plugins/dotnet-test/agents/code-testing-tester.agent.md Updates tester agent to use repository runner commands for classic projects and avoid substituting dotnet test.
plugins/dotnet-test/agents/code-testing-researcher.agent.md Updates researcher agent to detect classic projects and record authoritative classic build/test commands.
plugins/dotnet-test/agents/code-testing-implementer.agent.md Updates implementer agent to explicitly register new files in classic .csproj compile items.
plugins/dotnet-test/agents/code-testing-generator.agent.md Updates generator agent to require repository-appropriate full-workspace build validation (no forced dotnet build).
plugins/dotnet-test/agents/code-testing-builder.agent.md Updates builder agent to use MSBuild for classic projects and avoid implicitly migrating to SDK-style builds.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 34/34 changed files
  • Comments generated: 3

Comment thread tests/dotnet-test/writing-mstest-tests/eval.yaml Outdated
Comment thread tests/dotnet-test/run-tests/fixtures/classic-mstest/OrderServiceTests.cs Outdated
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

❌ Evaluation ran but produced no results.

The evaluate job completed but no results.json verdicts were generated. This is usually a transient infrastructure failure — commonly an LLM-session auth error (Session was not created with authentication info or custom provider) — not a problem with your skill. Check the workflow run logs, then re-post /evaluate to try again.

@github-actions github-actions Bot added the waiting-on-author PR state label label Aug 5, 2026
Tighten the MSTest version grader, make the runner fixture assertion behavioral, and use nameof for the guarded parameter.

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

Copilot-Session: fdfec89f-b610-479c-a6c7-c2936b300e68
Copilot AI review requested due to automatic review settings August 5, 2026 13:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Suppressed comments (2)

tests/dotnet-test/writing-mstest-tests/eval.yaml:227

  • The new stimulus can still pass if the answer includes newer APIs like Assert.ThrowsExactly alongside the required Assert.ThrowsException call, even though the rubric says not to recommend newer APIs. Add an explicit negative grader, and tighten the version regex with word boundaries so it can't match e.g. "13.5.2".
    tests/dotnet-test/coverage-analysis/eval.yaml:45
  • This new classic-project stimulus relies on the rubric to forbid SDK-style provider injection, but the graders don't currently fail answers that recommend commands like dotnet add ... coverlet.collector (even if the files remain unchanged). Adding an output-not-matches grader would prevent false positives.
    graders:
      - type: run-command
        config:
          command: sh -c "diff -u .eval-baseline/Legacy.Tests.csproj Legacy.Tests.csproj && diff -u .eval-baseline/packages.config packages.config"
          expected_exit_code: 0
  • Files reviewed: 34/34 changed files
  • Comments generated: 0 new

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/evaluate

@Evangelink
Evangelink temporarily deployed to copilot-pat-pool August 5, 2026 13:58 — with GitHub Actions Inactive
@github-actions github-actions Bot added pr-state/ready-for-eval PR is mergeable and awaiting evaluation and removed waiting-on-author PR state label labels Aug 5, 2026
@Evangelink
Evangelink enabled auto-merge (squash) August 5, 2026 15:07
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📊 Skill Evaluation Results

6 skill(s) evaluated — ✅ 3 improved, ❌ 3 no credible change, 🔻 0 regressed.

A skill passes only on a credible net win over baseline: more wins than losses, by an exact one-sided sign test at p ≤ 0.05.

Skill Result Net win p Δ Pref W/T/L Quality Baseline Overfit Skills Loaded
code-testing-agent +33.3% 0.109 +13.3% 5/6/1 4.1/5 4.1/5 ✅ 0.16 ⚠️ 1/5 · 1/5 (plugin)
coverage-analysis +66.7% 0.000 +31.7% 17/6/1 4.6/5 3.4/5 ✅ 0.10 ⚠️ 6/8 · 7/8 (plugin)
crap-score +66.7% 0.063 +36.7% 4/2/0 4.2/5 4.1/5 ✅ 0.09 6/6 · 6/6 (plugin)
platform-detection -20.0% 0.500 -20.0% 0/4/1 3.4/5 4.1/5 🟡 0.21 ⚠️ 0/5 · 0/5 (plugin)
run-tests +50.0% 0.019 +42.5% 10/4/2 4.4/5 3.1/5 ✅ 0.16 ⚠️ 14/16 · 11/16 (plugin)
writing-mstest-tests +76.5% 0.000 +37.6% 14/2/1 4.1/5 3.6/5 🟡 0.27 ⚠️ 16/17 · 12/17 (plugin)
ℹ️ Column legend
  • Net win(wins − losses) / trials for skilled vs baseline, judged head-to-head by vally compare. This is the effect the gate decides on.
  • p — one-sided exact sign test over the discordant (non-tie) trials. A skill passes only at p ≤ 0.05, which needs at least 5 winning trials.
  • Δ Pref — the same comparison weighted by how decisive each win was (much-better ±100%, slightly-better ±40%). Reported for triage only: weighting the statistic by magnitude made a skill fail for winning harder, which is why the gate deliberately ignores this column.
  • W/T/L — wins / ties / losses across trials.
  • ⚠️ — the gate withheld a verdict. Either the eval has fewer trials than any result needs to reach p ≤ 0.05 (underpowered — the skill was never actually measured, so this is not a regression; add scenarios or raise defaults.runs), or the comparison didn't complete.
  • 🔻 — a credible regression: the losses themselves clear the same bar the gate uses for wins.
  • Quality / Baseline — mean absolute judge score 0–5 (skilled isolated vs skill-free control).
  • Overfit — overfitting-judge severity (✅ Low, 🟡 Moderate, 🔴 High, — none) with its score.
  • Skills Loaded — of the scenarios that expect activation, how many actually activated / that total (plugin run shown when present); ⚠️ marks a scenario that expected activation but didn't activate.
❌ code-testing-agent — details

Reason: Net win +33.3% (5W/6T/1L over 12 trial(s), sign test p=0.109), mean preference +13.3% — not credible (sign test p=0.109 > 0.05)

Scenario Net win Δ Pref Trials (W/T/L)
= Diagnose failing tests without generating a new suite +0.0% +0.0% 0/2/0
= Does not revert a gutted-looking workspace (workspace integrity) +0.0% +0.0% 0/2/0
▲ Extend an existing suite to the untested method only +50.0% +20.0% 1/1/0
▲ Generate Vitest tests for the shopping-cart library (TypeScript polyglot) +100.0% +40.0% 2/0/0
▲ Keep a single-function request proportional +100.0% +40.0% 2/0/0
▼ Register tests in a classic project without modernizing it -50.0% -20.0% 0/1/1
❌ crap-score — details

Reason: Net win +66.7% (4W/2T/0L over 6 trial(s), sign test p=0.063), mean preference +36.7% — not credible — 2 of 6 trial(s) tied, leaving only 4 discordant trial(s). The sign test conditions on non-tie trials and cannot reach 0.05 below 5, so no record could have passed here — this is not a measured null. Either the skill is inert on these scenarios (make them discriminate) or the eval needs more trials to clear the ties (more scenarios or defaults.runs)

Scenario Net win Δ Pref Trials (W/T/L)
▲ Calculate CRAP score for a single method with partial coverage +100.0% +40.0% 1/0/0
▲ Generate coverage then compute CRAP score +100.0% +100.0% 1/0/0
▲ Identify riskiest methods across a file +100.0% +40.0% 1/0/0
= Recognize when complexity alone blocks the CRAP threshold +0.0% +0.0% 0/1/0
▲ Recompute complexity instead of trusting a stale source comment +100.0% +40.0% 1/0/0
= Report a fully covered method at its complexity floor +0.0% +0.0% 0/1/0
❌ platform-detection — details

Reason: Net win -20.0% (0W/4T/1L over 5 trial(s), sign test p=0.500), mean preference -20.0% — no improvement

Scenario Net win Δ Pref Trials (W/T/L)
= MTP signal set in Directory.Build.props rather than the project file +0.0% +0.0% 0/1/0
= Microsoft.NET.Test.Sdk alongside an MTP runner property +0.0% +0.0% 0/1/0
= TUnit project is MTP-only +0.0% +0.0% 0/1/0
= global.json opts a plain xUnit v3 project into MTP on SDK 10 +0.0% +0.0% 0/1/0
▼ global.json runner outranks TestingPlatformDotnetTestSupport on SDK 10 -100.0% -100.0% 0/0/1

Per-scenario details for 3 skill(s) were omitted to keep this comment under GitHub's 65,536-character limit — open the job's step summary or Full Results for the complete breakdown.

🔍 Full Results - additional metrics and failure investigation steps

To investigate failures, paste this to your AI coding agent:

For PR 993 in dotnet/skills, download eval artifacts with gh run download 31012795023 --repo dotnet/skills --pattern "vally-results-*" --dir ./eval-results, then fetch https://raw.githubusercontent.com/dotnet/skills/a48fd80a24f95ea71a10beef9e277e2c0056e514/eng/vally-adapter/InvestigatingResults.md and follow it to analyze the results.json files. Diagnose each failure, suggest fixes to the eval.yaml and skill content, and tell me what to fix first.

▶ Sessions Visualisation -- interactive replay of all evaluation sessions
📊 Session Analytics (preview) -- aggregated metrics across evaluation sessions

@github-actions github-actions Bot added waiting-on-review PR state label and removed pr-state/ready-for-eval PR is mergeable and awaiting evaluation labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

✅ Evaluation passed for a48fd80. cc @dotnet/dotnet-testing — please review.

Fix test-generation routing, add discriminating CRAP scenarios, and make platform detection directly measurable with accurate dotnet-test mode and MTP bridge semantics.

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

Copilot-Session: fdfec89f-b610-479c-a6c7-c2936b300e68
Copilot AI review requested due to automatic review settings August 5, 2026 19:27

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/evaluate

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Suppressed comments (3)

plugins/dotnet-test/skills/coverage-analysis/references/test-execution.md:160

  • Same issue in the mixed-project snippet: $major -ge 10 isn’t sufficient to decide native-MTP argument placement. In SDK 10 VSTest mode (runner unset/VSTest), --coverage must still be passed after --. Update this conditional to use a global.json runner check (native MTP vs VSTest mode).
        if ($major -ge 10) {
            dotnet test --project $tp.FullName --results-directory $rawDir --coverage --coverage-output-format cobertura --coverage-output $rawDir
        } else {
            dotnet test $tp.FullName --results-directory $rawDir -- --coverage --coverage-output-format cobertura --coverage-output $rawDir
        }

tests/dotnet-test/writing-mstest-tests/eval.yaml:226

  • The MSTest version grader still allows a bare "3.5.2" match because the "MSTest" prefix is optional. This reintroduces the earlier issue where responses can pass without explicitly acknowledging MSTest 3.5.2. Require an explicit MSTest + 3.5.2 mention in the regex.
    plugins/dotnet-test/skills/coverage-analysis/references/test-execution.md:126
  • This section selects --coverage vs -- --coverage purely from the SDK major version, but elsewhere in this PR you distinguish SDK 10 VSTest mode (including the MTP bridge) from native MTP mode. On SDK 10 in VSTest mode, MTP arguments still must go after --. The guidance and snippet should key off global.json test.runner (native MTP vs VSTest mode), not just $major.

This issue also appears on line 156 of the same file.

The command syntax depends on the .NET SDK version. In .NET 9, Microsoft.Testing.Platform arguments
must be passed after the `--` separator. In .NET 10+, `--coverage` is a top-level `dotnet test` flag.

```powershell
$rawDir = Join-Path "<COVERAGE_DIR>" "raw"
  • Files reviewed: 56/56 changed files
  • Comments generated: 1

@github-actions github-actions Bot removed the waiting-on-review PR state label label Aug 5, 2026
@github-actions github-actions Bot added the pr-state/ready-for-eval PR is mergeable and awaiting evaluation label Aug 7, 2026
github-actions Bot added a commit that referenced this pull request Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📊 Skill Evaluation Results

4 skill(s) evaluated — ✅ 2 improved, ❌ 2 no credible change, 🔻 0 regressed.

A skill passes only on a credible net win over baseline: more wins than losses, by an exact one-sided sign test at p ≤ 0.05.

Skill Result Net win p Δ Pref W/T/L Quality Baseline Overfit Skills Loaded
code-testing-agent -16.7% 0.227 -10.0% 2/11/5 3.8/5 3.9/5 ✅ 0.20 ⚠️ 3/8 · 1/8 (plugin)
platform-detection +11.1% 0.500 -2.2% 2/6/1 4.5/5 4.3/5 🟡 0.30 ⚠️ 8/9 · 3/9 (plugin)
run-tests +42.9% 0.035 +34.3% 7/6/1 4.2/5 3.1/5 ✅ 0.16 ⚠️ 9/14 · 5/14 (plugin)
writing-mstest-tests +57.1% 0.004 +27.1% 8/6/0 4.2/5 3.6/5 🟡 0.21 ⚠️ 10/14 · 7/14 (plugin)
ℹ️ Column legend
  • Net win(wins − losses) / trials for skilled vs baseline, judged head-to-head by vally compare. This is the effect the gate decides on.
  • p — one-sided exact sign test over the discordant (non-tie) trials. A skill passes only at p ≤ 0.05, which needs at least 5 winning trials.
  • Δ Pref — the same comparison weighted by how decisive each win was (much-better ±100%, slightly-better ±40%). Reported for triage only: weighting the statistic by magnitude made a skill fail for winning harder, which is why the gate deliberately ignores this column.
  • W/T/L — wins / ties / losses across trials.
  • ⚠️ — the gate withheld a verdict. Either the eval has fewer trials than any result needs to reach p ≤ 0.05 (underpowered — the skill was never actually measured, so this is not a regression; add scenarios or raise defaults.runs), or the comparison didn't complete.
  • 🔻 — a credible regression: the losses themselves clear the same bar the gate uses for wins.
  • Quality / Baseline — mean absolute judge score 0–5 (skilled isolated vs skill-free control).
  • Overfit — overfitting-judge severity (✅ Low, 🟡 Moderate, 🔴 High, — none) with its score.
  • Skills Loaded — of the scenarios that expect activation, how many actually activated / that total (plugin run shown when present); ⚠️ marks a scenario that expected activation but didn't activate.
❌ code-testing-agent — details

Reason: Net win -16.7% (2W/11T/5L over 18 trial(s), sign test p=0.227), mean preference -10.0% — no improvement

Scenario Net win Δ Pref Trials (W/T/L)
▼ Diagnose failing tests without generating a new suite -50.0% -50.0% 0/1/1
▼ Does not revert a gutted-looking workspace (workspace integrity) -50.0% -20.0% 0/1/1
= Extend an existing suite to the untested method only +0.0% +0.0% 0/2/0
▼ Generate Vitest tests for the shopping-cart library (TypeScript polyglot) -50.0% -20.0% 0/1/1
= Generate a project-wide Go suite across collaborating packages +0.0% +0.0% 0/2/0
▲ Generate a project-wide pytest suite across multiple modules +50.0% +20.0% 1/1/0
▼ Generate project-wide tests for a classic MSTest library -100.0% -40.0% 0/0/2
▲ Keep a single-function request proportional +50.0% +20.0% 1/1/0
= Register tests in a classic project without modernizing it +0.0% +0.0% 0/2/0
❌ platform-detection — details

Reason: Net win +11.1% (2W/6T/1L over 9 trial(s), sign test p=0.500), mean preference -2.2% — not credible — 6 of 9 trial(s) tied, leaving only 3 discordant trial(s). The sign test conditions on non-tie trials and cannot reach 0.05 below 5, so no record could have passed here — this is not a measured null. Either the skill is inert on these scenarios (make them discriminate) or the eval needs more trials to clear the ties (more scenarios or defaults.runs)

Scenario Net win Δ Pref Trials (W/T/L)
▲ Classic packages.config project requires the standalone VSTest toolchain +100.0% +40.0% 1/0/0
= MSTest.Sdk explicitly opts back into VSTest +0.0% +0.0% 0/1/0
= MTP signal set in Directory.Build.props rather than the project file +0.0% +0.0% 0/1/0
= Microsoft.NET.Test.Sdk alongside an MTP runner property +0.0% +0.0% 0/1/0
▲ SDK 10 VSTest mode bridges to an MSTest.Sdk MTP project +100.0% +40.0% 1/0/0
= SDK 10 explicit VSTest mode still bridges to MTP +0.0% +0.0% 0/1/0
= SDK 9 MTP signal and framework versions live in Directory.Packages.props +0.0% +0.0% 0/1/0
▼ SDK 9 runner without bridge remains on VSTest -100.0% -100.0% 0/0/1
= xUnit runner property outranks Microsoft.NET.Test.Sdk on SDK 9 +0.0% +0.0% 0/1/0

Per-scenario details for 2 skill(s) were omitted to keep this comment under GitHub's 65,536-character limit — open the job's step summary or Full Results for the complete breakdown.

🔍 Full Results - additional metrics and failure investigation steps

To investigate failures, paste this to your AI coding agent:

For PR 993 in dotnet/skills, download eval artifacts with gh run download 31173760947 --repo dotnet/skills --pattern "vally-results-*" --dir ./eval-results, then fetch https://raw.githubusercontent.com/dotnet/skills/43f7d77644250d741d72615ba3db581102cc9960/eng/vally-adapter/InvestigatingResults.md and follow it to analyze the results.json files. Diagnose each failure, suggest fixes to the eval.yaml and skill content, and tell me what to fix first.

▶ Sessions Visualisation -- interactive replay of all evaluation sessions
📊 Session Analytics (preview) -- aggregated metrics across evaluation sessions

Reset mutable classic fixtures between trials, remove noisy generation scenarios, clarify MSTest runner bridge semantics, and add discriminating platform precedence cases.

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

Copilot-Session: fdfec89f-b610-479c-a6c7-c2936b300e68
Copilot AI review requested due to automatic review settings August 7, 2026 12:52

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/evaluate

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Suppressed comments (2)

tests/dotnet-test/run-tests/eval.yaml:87

  • This no-tool NUnit filtering stimulus asks for the exact dotnet test command, but the current graders only check for --filter and the filter expression. An answer that never includes dotnet test (or that provides only the filter snippet) could still pass. Add a dotnet test output-matches grader to align the checks with the prompt requirement.
    tests/dotnet-test/writing-mstest-tests/eval.yaml:48
  • The async-timeout stimulus can currently pass while leaving CancellationToken.None untouched. The regex graders only require that the output mention CancellationToken somewhere, and the prompt grader isn’t guaranteed to reject the unchanged argument. Since the prompt explicitly requires flowing TestContext.CancellationToken, add an explicit grader for TestContext.CancellationToken and reject CancellationToken.None to prevent false positives.
  • Files reviewed: 88/92 changed files
  • Comments generated: 0 new

github-actions Bot added a commit that referenced this pull request Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📊 Skill Evaluation Results

4 skill(s) evaluated — ✅ 1 improved, ❌ 3 no credible change, 🔻 0 regressed.

A skill passes only on a credible net win over baseline: more wins than losses, by an exact one-sided sign test at p ≤ 0.05.

Skill Result Net win p Δ Pref W/T/L Quality Baseline Overfit Skills Loaded
code-testing-agent +16.7% 0.250 +6.7% 2/10/0 4.0/5 3.7/5 🟡 0.23 ⚠️ 4/6 · 0/6 (plugin)
platform-detection +25.0% 0.125 +20.0% 3/9/0 4.8/5 4.1/5 ✅ 0.15 ⚠️ 11/12 · 4/12 (plugin)
run-tests +57.1% 0.011 +44.3% 9/4/1 4.4/5 3.3/5 ✅ 0.10 ⚠️ 13/14 · 8/14 (plugin)
writing-mstest-tests +35.7% 0.090 +18.6% 7/5/2 3.8/5 3.5/5 🟡 0.27 ⚠️ 6/14 · 5/14 (plugin)
ℹ️ Column legend
  • Net win(wins − losses) / trials for skilled vs baseline, judged head-to-head by vally compare. This is the effect the gate decides on.
  • p — one-sided exact sign test over the discordant (non-tie) trials. A skill passes only at p ≤ 0.05, which needs at least 5 winning trials.
  • Δ Pref — the same comparison weighted by how decisive each win was (much-better ±100%, slightly-better ±40%). Reported for triage only: weighting the statistic by magnitude made a skill fail for winning harder, which is why the gate deliberately ignores this column.
  • W/T/L — wins / ties / losses across trials.
  • ⚠️ — the gate withheld a verdict. Either the eval has fewer trials than any result needs to reach p ≤ 0.05 (underpowered — the skill was never actually measured, so this is not a regression; add scenarios or raise defaults.runs), or the comparison didn't complete.
  • 🔻 — a credible regression: the losses themselves clear the same bar the gate uses for wins.
  • Quality / Baseline — mean absolute judge score 0–5 (skilled isolated vs skill-free control).
  • Overfit — overfitting-judge severity (✅ Low, 🟡 Moderate, 🔴 High, — none) with its score.
  • Skills Loaded — of the scenarios that expect activation, how many actually activated / that total (plugin run shown when present); ⚠️ marks a scenario that expected activation but didn't activate.
❌ code-testing-agent — details

Reason: Net win +16.7% (2W/10T/0L over 12 trial(s), sign test p=0.250), mean preference +6.7% — not credible — 10 of 12 trial(s) tied, leaving only 2 discordant trial(s). The sign test conditions on non-tie trials and cannot reach 0.05 below 5, so no record could have passed here — this is not a measured null. Either the skill is inert on these scenarios (make them discriminate) or the eval needs more trials to clear the ties (more scenarios or defaults.runs)

Scenario Net win Δ Pref Trials (W/T/L)
▲ Does not revert a gutted-looking workspace (workspace integrity) +50.0% +20.0% 1/1/0
= Extend an existing suite to the untested method only +0.0% +0.0% 0/2/0
= Generate a project-wide Go suite across collaborating packages +0.0% +0.0% 0/2/0
▲ Generate a project-wide pytest suite across multiple modules +50.0% +20.0% 1/1/0
= Generate project-wide tests for a classic MSTest library +0.0% +0.0% 0/2/0
= Keep a single-function request proportional +0.0% +0.0% 0/2/0
❌ platform-detection — details

Reason: Net win +25.0% (3W/9T/0L over 12 trial(s), sign test p=0.125), mean preference +20.0% — not credible — 9 of 12 trial(s) tied, leaving only 3 discordant trial(s). The sign test conditions on non-tie trials and cannot reach 0.05 below 5, so no record could have passed here — this is not a measured null. Either the skill is inert on these scenarios (make them discriminate) or the eval needs more trials to clear the ties (more scenarios or defaults.runs)

Scenario Net win Δ Pref Trials (W/T/L)
▲ Classic packages.config project requires the standalone VSTest toolchain +100.0% +40.0% 1/0/0
= Conditional properties produce mixed platforms by target framework +0.0% +0.0% 0/1/0
= MSTest.Sdk explicitly opts back into VSTest +0.0% +0.0% 0/1/0
▲ MTP signal set in Directory.Build.props rather than the project file +100.0% +100.0% 1/0/0
= Microsoft.NET.Test.Sdk alongside an MTP runner property +0.0% +0.0% 0/1/0
= Native MTP mode conflicts with MSTest.Sdk UseVSTest opt-out +0.0% +0.0% 0/1/0
= Project-level bridge opt-out overrides Directory.Build.props +0.0% +0.0% 0/1/0
▲ SDK 10 VSTest mode bridges to an MSTest.Sdk MTP project +100.0% +100.0% 1/0/0
= SDK 10 explicit VSTest mode still bridges to MTP +0.0% +0.0% 0/1/0
= SDK 9 MTP signal and framework versions live in Directory.Packages.props +0.0% +0.0% 0/1/0
= SDK 9 runner without bridge remains on VSTest +0.0% +0.0% 0/1/0
= xUnit runner property outranks Microsoft.NET.Test.Sdk on SDK 9 +0.0% +0.0% 0/1/0
❌ writing-mstest-tests — details

Reason: Net win +35.7% (7W/5T/2L over 14 trial(s), sign test p=0.090), mean preference +18.6% — not credible (sign test p=0.090 > 0.05)

Scenario Net win Δ Pref Trials (W/T/L)
▲ Configure conditional execution, retry, and cleanup +100.0% +40.0% 1/0/0
▲ Configure test parallelization and MSTest.Sdk project +100.0% +40.0% 1/0/0
▼ Fix swapped Assert.AreEqual arguments -100.0% -40.0% 0/0/1
= Keep exception assertions compatible with the installed MSTest version +0.0% +0.0% 0/1/0
▲ Modernize an async test with cooperative cancellation +100.0% +40.0% 1/0/0
= Modernize legacy test patterns +0.0% +0.0% 0/1/0
▼ Replace ExpectedException with Assert.Throws -100.0% -40.0% 0/0/1
▲ Replace generic IsTrue checks for null, identity, emptiness, and absence +100.0% +40.0% 1/0/0
= Replace manual numeric bounds with MSTest 3.10 assertions +0.0% +0.0% 0/1/0
= Replace manual string checks with MSTest 3.10 assertions +0.0% +0.0% 0/1/0
▲ Set up test lifecycle correctly +100.0% +100.0% 1/0/0
▲ Use DynamicData with ValueTuples over object arrays +100.0% +40.0% 1/0/0
▲ Use proper collection assertions +100.0% +40.0% 1/0/0
= Use proper type assertions instead of casts +0.0% +0.0% 0/1/0

Per-scenario details for 1 skill(s) were omitted to keep this comment under GitHub's 65,536-character limit — open the job's step summary or Full Results for the complete breakdown.

🔍 Full Results - additional metrics and failure investigation steps

To investigate failures, paste this to your AI coding agent:

For PR 993 in dotnet/skills, download eval artifacts with gh run download 31180075357 --repo dotnet/skills --pattern "vally-results-*" --dir ./eval-results, then fetch https://raw.githubusercontent.com/dotnet/skills/18e6653d665ea4692a1d02dc050b196dca3bd0bc/eng/vally-adapter/InvestigatingResults.md and follow it to analyze the results.json files. Diagnose each failure, suggest fixes to the eval.yaml and skill content, and tell me what to fix first.

▶ Sessions Visualisation -- interactive replay of all evaluation sessions
📊 Session Analytics (preview) -- aggregated metrics across evaluation sessions

Align the generation pipeline contract with its eval, improve MSTest routing and response semantics, increase platform evidence, and isolate coverage and CRAP execution shards.

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

Copilot-Session: fdfec89f-b610-479c-a6c7-c2936b300e68
Copilot AI review requested due to automatic review settings August 7, 2026 16:21

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/evaluate

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Suppressed comments (3)

tests/dotnet-test/crap-score/eval.yaml:5

  • The PR description says the evals are split into four execution shards (generation, risk, execution, mstest) with coverage-analysis + crap-score both in the risk shard. This eval uses executionShard: risk-crap, which creates an extra shard name and may defeat the intended max-parallel: 4 scheduling split. Consider using the shared risk shard name here to match the described scheduling.
    tests/dotnet-test/coverage-analysis/eval.yaml:5
  • The PR description calls out a single risk execution shard containing both coverage-analysis and crap-score. This eval currently uses executionShard: risk-coverage, which creates a separate shard name and may reintroduce the single-shard bottleneck (or exceed the intended max-parallel: 4). Rename this shard to risk to align with the described scheduling.
    tests/dotnet-test/crap-score/eval.yaml:27
  • This grader asserts the classic project files are unchanged, but it doesn't currently prevent the skill from creating additional *.csproj files (for example, a temporary SDK wrapper) or emitting a Cobertura report, both of which would violate the scenario's intent but could still pass this check. Consider extending the run-command to fail if any extra project files or Cobertura XML are created.
  • Files reviewed: 92/96 changed files
  • Comments generated: 0 new

github-actions Bot added a commit that referenced this pull request Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📊 Skill Evaluation Results

6 skill(s) evaluated — ✅ 4 improved, ❌ 1 no credible change, 🔻 0 regressed.

⚠️ 1 could not be judged: 1 inconclusive — the comparison didn't complete (errored, unmatched, or self-contradictory trials).

A skill passes only on a credible net win over baseline: more wins than losses, by an exact one-sided sign test at p ≤ 0.05.

Skill Result Net win p Δ Pref W/T/L Quality Baseline Overfit Skills Loaded
code-testing-agent -40.0% 0.109 -16.0% 1/4/5 2.9/5 2.8/5 🟡 0.33 ⚠️ 2/5 · 0/5 (plugin)
coverage-analysis ⚠️ +53.3% 0.004 +25.3% 8/7/0 4.6/5 3.7/5 ✅ 0.11 ⚠️ 5/8 · 6/8 (plugin)
crap-score +75.0% 0.016 +45.0% 6/2/0 4.3/5 3.8/5 ✅ 0.10 8/8 · 8/8 (plugin)
platform-detection +29.2% 0.008 +19.2% 7/17/0 4.9/5 4.4/5 ✅ 0.19 ⚠️ 11/12 · 5/12 (plugin)
run-tests +57.1% 0.011 +35.7% 9/4/1 4.1/5 3.2/5 ✅ 0.18 ⚠️ 12/14 · 9/14 (plugin)
writing-mstest-tests +50.0% 0.033 +20.0% 9/3/2 4.0/5 3.2/5 🟡 0.22 ⚠️ 10/14 · 5/14 (plugin)
ℹ️ Column legend
  • Net win(wins − losses) / trials for skilled vs baseline, judged head-to-head by vally compare. This is the effect the gate decides on.
  • p — one-sided exact sign test over the discordant (non-tie) trials. A skill passes only at p ≤ 0.05, which needs at least 5 winning trials.
  • Δ Pref — the same comparison weighted by how decisive each win was (much-better ±100%, slightly-better ±40%). Reported for triage only: weighting the statistic by magnitude made a skill fail for winning harder, which is why the gate deliberately ignores this column.
  • W/T/L — wins / ties / losses across trials.
  • ⚠️ — the gate withheld a verdict. Either the eval has fewer trials than any result needs to reach p ≤ 0.05 (underpowered — the skill was never actually measured, so this is not a regression; add scenarios or raise defaults.runs), or the comparison didn't complete.
  • 🔻 — a credible regression: the losses themselves clear the same bar the gate uses for wins.
  • Quality / Baseline — mean absolute judge score 0–5 (skilled isolated vs skill-free control).
  • Overfit — overfitting-judge severity (✅ Low, 🟡 Moderate, 🔴 High, — none) with its score.
  • Skills Loaded — of the scenarios that expect activation, how many actually activated / that total (plugin run shown when present); ⚠️ marks a scenario that expected activation but didn't activate.
❌ code-testing-agent — details

Reason: Net win -40.0% (1W/4T/5L over 10 trial(s), sign test p=0.109), mean preference -16.0% — no improvement

Scenario Net win Δ Pref Trials (W/T/L)
▼ Does not revert a gutted-looking workspace (workspace integrity) -50.0% -20.0% 0/1/1
= Generate a project-wide Go suite across collaborating packages +0.0% +0.0% 1/0/1
▼ Generate a project-wide pytest suite across multiple modules -50.0% -20.0% 0/1/1
= Generate project-wide tests for a classic MSTest library +0.0% +0.0% 0/2/0
▼ Keep a single-function request proportional -100.0% -40.0% 0/0/2
⚠️ coverage-analysis — details

Reason: Net win +53.3% (8W/7T/0L over 15 trial(s), sign test p=0.004), mean preference +25.3%, 1 unmatched — inconclusive (unmatched trajectories)

Scenario Net win Δ Pref Trials (W/T/L)
▲ Analyse a CI Cobertura report without re-running tests or installing tools +50.0% +20.0% 1/1/0
▲ Coverage plateau diagnosis +100.0% +40.0% 2/0/0
= Distinguish partially covered branches from covered lines +0.0% +0.0% 0/2/0
▲ Preserve a classic packages.config project when coverage data is absent +50.0% +20.0% 1/1/0
▲ Project-wide coverage analysis with existing Cobertura data +100.0% +70.0% 2/0/0
= Reconcile a coverage target spread across several members +0.0% +0.0% 0/2/0
= Refactoring safety assessment from coverage data +0.0% +0.0% 0/1/0
▲ Run coverage from scratch without existing data +100.0% +40.0% 2/0/0

Per-scenario details for 4 skill(s) were omitted to keep this comment under GitHub's 65,536-character limit — open the job's step summary or Full Results for the complete breakdown.

🔍 Full Results - additional metrics and failure investigation steps

To investigate failures, paste this to your AI coding agent:

For PR 993 in dotnet/skills, download eval artifacts with gh run download 31197234575 --repo dotnet/skills --pattern "vally-results-*" --dir ./eval-results, then fetch https://raw.githubusercontent.com/dotnet/skills/191ed06c10bca672155fea6f69d89d9daf577500/eng/vally-adapter/InvestigatingResults.md and follow it to analyze the results.json files. Diagnose each failure, suggest fixes to the eval.yaml and skill content, and tell me what to fix first.

▶ Sessions Visualisation -- interactive replay of all evaluation sessions
📊 Session Analytics (preview) -- aggregated metrics across evaluation sessions

@github-actions github-actions Bot added waiting-on-review PR state label and removed pr-state/ready-for-eval PR is mergeable and awaiting evaluation labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-review PR state label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants