Files
Abhitej John 78054c1161 Migrate LLM evals to the Vally harness (#877)
* Make Vally the sole LLM eval engine, retiring skill-validator evaluate

Collapses the parallel skill-validator + Vally eval pipeline into a single
Vally-only path while preserving every capability skill-validator provided:
PR gate, PR comment, dotnet/skills-data historical push, and the dashboard.
The skill-validator `check` linter is retained (skill-check.yml) pending
microsoft/vally #463.

- evaluation.yml: delete build-validator and the skill-validator `evaluate`
  job; single `evaluate:` job now uses vally-evaluation.yml. Forward the
  COPILOT_PAT_0..9 pool onto it (from #911) so the reusable workflow's token
  gate is satisfied. Rewire comment-on-pr / report-status / publish-* /
  deploy-dashboard onto `evaluate` and vally-results-* artifacts.
- vally-evaluation.yml: pin @microsoft/vally-cli@0.9.0; keep the #911 prepare
  guard; write the generated per-plugin experiment to $GITHUB_WORKSPACE so
  relative eval globs resolve.
- adapt.mjs: reconcile #887's runCompareWithRetry + conclusive/unmatched
  hardening with the consolidation helpers (nonActivation, compareByStim,
  roleToDashboard, etc.). Add consolidate.mjs and gen-experiment.mjs.
- build-replay-sessions.ps1: read Vally executor-session-logs/events.jsonl
  instead of skill-validator session output.
- Rename all tests/*/*/eval.vally.yaml to eval.yaml (Vally format is now the
  only eval format); author Vally evals for the four skills that lacked one.
- CONTRIBUTING.md + InvestigatingResults: point contributors at the Vally
  harness; drop non-public links.

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

* Fix setup-local-sdk eval for Vally schema

The hand-authored setup-local-sdk eval used skill-validator grader/environment
schema that Vally rejects at validation (no stimuli executed):
- file-contains/file-not-contains graders require 'value:' not 'substring:'
  (output-contains correctly keeps 'substring:').
- environment.files entries require 'src:'/'dest:' (copy a fixture) rather than
  inline 'path:'/'content:'. Moved the global.json body into fixtures/global.json.

Verified with 'vally lint --eval-spec' (0 errors) across all 97 eval.yaml.

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

* Fix markdownlint violations in migration docs

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

* Address PR review feedback on eval migration

- Consolidate run-vally-evals.sh into a single neutral entrypoint
  eng/run-skill-evals.sh (deletes the thin wrapper; adapt.mjs stays in
  eng/vally-adapter/ since CI invokes it directly)
- Rename reusable workflow vally-evaluation.yml -> evaluation-run.yml and
  update all references/self-trigger regexes
- Rename local output dir vally-results -> eval-results
- Bake prerequisite preflight (Node 20+, GITHUB_TOKEN/gh) into the runner
- Simplify CONTRIBUTING "Running tests locally" prerequisites
- Scrub harness name from consumer-facing surfaces

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

* Reinstate overfitting detection under the Vally harness

Overfitting detection previously ran inside skill-validator's `evaluate`
command, which the Vally migration replaced, so it silently stopped running.
Reinstate it as a standalone step wired into the Vally pipeline:

- Add `skill-validator overfitting` command that discovers skills, parses
  each eval.yaml, runs the existing OverfittingJudge, and emits
  [{plugin, skill, overfittingResult}] JSON (per-skill failures non-fatal,
  bounded parallelism).
- Add a Vally-format parser bridge (ParseEvalConfigFlexible) so the judge
  handles the current stimuli/graders eval.yaml format; the legacy
  scenarios-only parser rejected all 98 evals, so the judge never ran.
- adapt.mjs: merge overfitting results onto each verdict via
  --overfitting <file> (keyed by plugin/skill); output is byte-identical
  when the flag is absent.
- evaluation-run.yml: build skill-validator (shared cache with check),
  run the judge per leg reading GITHUB_TOKEN, and pass --overfitting to
  adapt.mjs so the dashboard/skills-data consume it unchanged.

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

* Reconcile #919 Vally eval configs into the sole eval.yaml convention

PR #919 landed on main adding fresh Vally specs for three coverage-gap
skills (setup-local-sdk, convert-blazor-server-to-webapp, dotnet-webapi)
under the interim eval.vally.yaml filename. This branch collapses to a
single Vally spec named eval.yaml, so the merge left each skill with both
my migrated eval.yaml and #919's newer eval.vally.yaml (a git-clean but
semantic duplicate the pipeline would not discover).

Adopt #919's reviewed Vally content as the canonical eval.yaml for all
three skills and drop the redundant eval.vally.yaml files, preserving the
'sole eval.yaml, zero eval.vally.yaml' invariant. Content is byte-identical
to #919's blobs (b14121c6 / 47d43823 / 18a9362f), which already passed
vally-evaluate on main.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-21 15:22:31 -07:00

163 lines
6.2 KiB
YAML

name: dotnet-webapi
description: Evaluates the dotnet-aspnetcore/dotnet-webapi skill
type: capability
config:
timeout: 15m
stimuli:
- name: Create a CRUD Web API with minimal APIs, OpenAPI, and proper HTTP semantics
prompt: |
Create an ASP.NET Core Web API (.NET 10) project for managing products.
Each product has a name, price, category (an enum: Electronics, Clothing,
Food, Books), and a createdAt timestamp.
The API should have these endpoints:
- GET /api/products (list all)
- GET /api/products/{id} (get by ID)
- POST /api/products (create a product)
Use an in-memory service behind an interface for data storage.
Add OpenAPI documentation with rich metadata on each endpoint.
graders:
- type: output-matches
config:
pattern: (MapGet|MapPost)
- type: output-matches
config:
pattern: (TypedResults|Results<)
- type: output-matches
config:
pattern: sealed
- type: output-contains
config:
substring: DateTimeOffset
- type: output-matches
config:
pattern: '/// <summary>'
- type: output-contains
config:
substring: AddOpenApi
- type: output-contains
config:
substring: MapOpenApi
- type: output-matches
config:
pattern: (WithName|WithSummary|WithDescription)
- type: output-not-contains
config:
substring: Swashbuckle
- type: output-not-matches
config:
pattern: (UseSwagger|UseSwaggerUI)
- type: output-contains
config:
substring: JsonStringEnumConverter
- type: output-contains
config:
substring: CancellationToken
- type: prompt
rubric:
- Uses minimal APIs (MapGet, MapPost, etc.) rather than controllers for new projects
- POST endpoint returns 201 Created with a Location header, not 200 OK
- Uses TypedResults with explicit Results<T1, T2> return types for compile-time type safety
- DTOs are sealed records, not mutable classes
- Uses a service layer with interfaces instead of injecting a data store directly into endpoints
- CancellationToken is accepted in endpoint signatures and forwarded through all async calls
- Date/time properties use DateTimeOffset, not DateTime
- All request and response DTOs include XML doc summary comments
- Enum properties serialize as strings by default using JsonStringEnumConverter
- Uses builder.Services.AddOpenApi() and app.MapOpenApi() (built-in .NET 9+ support)
- Does NOT add any Swashbuckle NuGet packages
- Chains .WithName(), .WithSummary(), and .WithDescription() on endpoints for OpenAPI metadata
- name: Add error handling with ProblemDetails and IExceptionHandler
prompt: |
I have an existing ASP.NET Core minimal API project. I need to add
global error handling so that:
- All error responses use RFC 7807 Problem Details format
- KeyNotFoundException maps to 404
- ArgumentException maps to 400
- InvalidOperationException maps to 409
- Unhandled exceptions return 500 with no sensitive details
Show me how to implement this without adding try-catch blocks in
every endpoint.
graders:
- type: output-contains
config:
substring: IExceptionHandler
- type: output-contains
config:
substring: ProblemDetails
- type: output-contains
config:
substring: AddProblemDetails
- type: output-contains
config:
substring: UseExceptionHandler
- type: prompt
rubric:
- Implements IExceptionHandler (the modern .NET 8+ approach) rather than convention-based middleware
- Returns ProblemDetails for all error responses (RFC 7807)
- Maps exception types to appropriate HTTP status codes (404, 400, 409)
- Does not expose internal exception details in production error responses
- Registers the handler with AddExceptionHandler<T>() and AddProblemDetails()
- Exception handler class is sealed
- Exception handler is placed in a Middleware/ folder
- name: Add a new API endpoint to an existing controller-based project
prompt: |
I have an existing ASP.NET Core Web API that uses controllers. Here is
one of the existing controllers:
```csharp
[ApiController]
[Route("api/[controller]")]
public class CustomersController : ControllerBase
{
private readonly ICustomerService _service;
public CustomersController(ICustomerService service)
{
_service = service;
}
[HttpGet]
public async Task<ActionResult<List<CustomerResponse>>> GetAll()
{
return Ok(await _service.GetAllAsync());
}
}
```
Add a new OrdersController with a GET endpoint to list orders and a
POST endpoint to create an order. Each order has a customer ID, a
placedAt timestamp, a total amount, and an OrderStatus (Pending,
Processing, Shipped, Delivered, Cancelled).
graders:
- type: output-contains
config:
substring: ControllerBase
- type: output-contains
config:
substring: '[ApiController]'
- type: output-contains
config:
substring: CancellationToken
- type: output-not-matches
config:
pattern: 'app\.(MapGet|MapPost|MapPut|MapDelete|MapPatch)|\b(MapGet|MapPost|MapPut|MapDelete|MapPatch)\b'
- type: output-contains
config:
substring: DateTimeOffset
- type: output-matches
config:
pattern: '/// <summary>'
- type: prompt
rubric:
- Continues with the controller pattern since the existing project uses controllers (does not mix minimal APIs)
- Includes CancellationToken in all endpoint signatures
- POST create returns CreatedAtAction with 201 status, not Ok with 200
- Uses sealed record DTOs with proper naming (CreateOrderRequest, OrderResponse)
- Date/time properties use DateTimeOffset, not DateTime
- All request and response DTOs include XML doc summary comments