mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
042a9d2b2c
* Reorganize plugins per agreed structure (#271) Split the dotnet plugin into domain-specific plugins per discussion in #271: - dotnet: narrowed to common everyday C#/.NET coding tasks - dotnet-diag: perf investigations, debugging, incident analysis - dotnet-data: data access and Entity Framework - dotnet-aspnet, dotnet-wpf, dotnet-winforms, dotnet-maui: scaffolded empty to convey intent for future framework-specific skills Moved skills, tests, and CODEOWNERS entries accordingly. No file content was changed; all skill/test moves are pure renames. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Improve dotnet-diag and dotnet-winforms descriptions - dotnet-diag: add '.NET' qualifier to clarify scope - dotnet-winforms: use official 'Windows Forms (WinForms)' name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add .NET qualifier to dotnet-data description Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove empty plugins; list them in CONTRIBUTING.md as future options Remove the four empty placeholder plugins (dotnet-aspnet, dotnet-wpf, dotnet-winforms, dotnet-maui) that had only plugin.json and no skills. Add a 'Plugin organization' section to CONTRIBUTING.md that describes the domain-specific plugin groupings introduced in this PR and lists the four removed plugin names as reserved candidates for future contributions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Create dotnet-upgrade plugin with migration/upgrade skills Move migrate-nullable-references, thread-abort-migration, and dotnet-aot-compat from dotnet to the new dotnet-upgrade plugin. Update CODEOWNERS paths (same owners), CONTRIBUTING.md plugin table, and fix the thread-abort-migration SKILL.md link in CONTRIBUTING.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
588 lines
24 KiB
YAML
588 lines
24 KiB
YAML
scenarios:
|
||
- name: "Enable NRT in a small library with mixed nullability"
|
||
prompt: |
|
||
Enable nullable reference types in this C# project and resolve all warnings.
|
||
The project is at TaskManager/TaskManager.csproj. Make sure the public API
|
||
surface accurately reflects nullability.
|
||
setup:
|
||
files:
|
||
- path: "TaskManager/TaskManager.csproj"
|
||
content: |
|
||
<Project Sdk="Microsoft.NET.Sdk">
|
||
<PropertyGroup>
|
||
<TargetFramework>net8.0</TargetFramework>
|
||
<ImplicitUsings>enable</ImplicitUsings>
|
||
<WarningsAsErrors>nullable</WarningsAsErrors>
|
||
</PropertyGroup>
|
||
</Project>
|
||
- path: "TaskManager/Models/TaskItem.cs"
|
||
content: |
|
||
namespace TaskManager.Models;
|
||
|
||
public class TaskItem
|
||
{
|
||
public int Id { get; set; }
|
||
public string Title { get; set; }
|
||
public string Description { get; set; }
|
||
public string AssignedTo { get; set; }
|
||
public DateTime? DueDate { get; set; }
|
||
public bool IsCompleted { get; set; }
|
||
public List<string> Tags { get; set; } = new();
|
||
|
||
public TaskItem(string title)
|
||
{
|
||
Title = title;
|
||
}
|
||
}
|
||
- path: "TaskManager/Models/Project.cs"
|
||
content: |
|
||
namespace TaskManager.Models;
|
||
|
||
public class Project
|
||
{
|
||
public string Name { get; set; }
|
||
public string Owner { get; set; }
|
||
public List<TaskItem> Tasks { get; } = new();
|
||
|
||
public Project(string name, string owner)
|
||
{
|
||
Name = name;
|
||
Owner = owner;
|
||
}
|
||
|
||
public TaskItem FindTaskByTitle(string title)
|
||
{
|
||
return Tasks.FirstOrDefault(t => t.Title == title);
|
||
}
|
||
}
|
||
- path: "TaskManager/Services/ITaskRepository.cs"
|
||
content: |
|
||
using TaskManager.Models;
|
||
|
||
namespace TaskManager.Services;
|
||
|
||
public interface ITaskRepository
|
||
{
|
||
TaskItem GetById(int id);
|
||
IReadOnlyList<TaskItem> GetAll();
|
||
void Add(TaskItem task);
|
||
void Remove(int id);
|
||
TaskItem FindByTitle(string title);
|
||
}
|
||
- path: "TaskManager/Services/InMemoryTaskRepository.cs"
|
||
content: |
|
||
using TaskManager.Models;
|
||
|
||
namespace TaskManager.Services;
|
||
|
||
public class InMemoryTaskRepository : ITaskRepository
|
||
{
|
||
private readonly List<TaskItem> _tasks = new();
|
||
private int _nextId = 1;
|
||
|
||
public TaskItem GetById(int id)
|
||
{
|
||
return _tasks.FirstOrDefault(t => t.Id == id);
|
||
}
|
||
|
||
public IReadOnlyList<TaskItem> GetAll() => _tasks.AsReadOnly();
|
||
|
||
public void Add(TaskItem task)
|
||
{
|
||
task.Id = _nextId++;
|
||
_tasks.Add(task);
|
||
}
|
||
|
||
public void Remove(int id)
|
||
{
|
||
var task = GetById(id);
|
||
if (task != null)
|
||
_tasks.Remove(task);
|
||
}
|
||
|
||
public TaskItem FindByTitle(string title)
|
||
{
|
||
return _tasks.FirstOrDefault(t =>
|
||
string.Equals(t.Title, title, StringComparison.OrdinalIgnoreCase));
|
||
}
|
||
}
|
||
- path: "TaskManager/Services/TaskService.cs"
|
||
content: |
|
||
using TaskManager.Models;
|
||
|
||
namespace TaskManager.Services;
|
||
|
||
public class TaskService
|
||
{
|
||
private readonly ITaskRepository _repository;
|
||
private string _defaultAssignee;
|
||
|
||
public TaskService(ITaskRepository repository, string defaultAssignee)
|
||
{
|
||
_repository = repository;
|
||
_defaultAssignee = defaultAssignee;
|
||
}
|
||
|
||
public TaskItem CreateTask(string title, string description, string assignedTo = null)
|
||
{
|
||
var task = new TaskItem(title)
|
||
{
|
||
Description = description,
|
||
AssignedTo = assignedTo ?? _defaultAssignee
|
||
};
|
||
_repository.Add(task);
|
||
return task;
|
||
}
|
||
|
||
public TaskItem GetTask(int id) => _repository.GetById(id);
|
||
|
||
public bool CompleteTask(int id)
|
||
{
|
||
var task = _repository.GetById(id);
|
||
if (task == null) return false;
|
||
task.IsCompleted = true;
|
||
return true;
|
||
}
|
||
|
||
public IReadOnlyList<TaskItem> GetTasksByAssignee(string assignee)
|
||
{
|
||
return _repository.GetAll()
|
||
.Where(t => string.Equals(t.AssignedTo, assignee, StringComparison.OrdinalIgnoreCase))
|
||
.ToList();
|
||
}
|
||
}
|
||
- path: "TaskManager/Services/TaskFormatter.cs"
|
||
content: |
|
||
using TaskManager.Models;
|
||
|
||
namespace TaskManager.Services;
|
||
|
||
public static class TaskFormatter
|
||
{
|
||
public static string FormatTask(TaskItem task)
|
||
{
|
||
var status = task.IsCompleted ? "Done" : "Pending";
|
||
var assignee = task.AssignedTo ?? "Unassigned";
|
||
var due = task.DueDate.HasValue
|
||
? task.DueDate.Value.ToShortDateString()
|
||
: "No due date";
|
||
return $"[{status}] {task.Title} - {assignee} (Due: {due})";
|
||
}
|
||
|
||
public static string FormatProject(Project project)
|
||
{
|
||
var lines = new List<string>
|
||
{
|
||
$"Project: {project.Name} (Owner: {project.Owner})",
|
||
$"Tasks: {project.Tasks.Count}"
|
||
};
|
||
foreach (var task in project.Tasks)
|
||
{
|
||
lines.Add($" - {FormatTask(task)}");
|
||
}
|
||
return string.Join(Environment.NewLine, lines);
|
||
}
|
||
}
|
||
- path: "TaskManager/Services/CsvExporter.cs"
|
||
content: |
|
||
using TaskManager.Models;
|
||
|
||
namespace TaskManager.Services;
|
||
|
||
public class CsvExporter
|
||
{
|
||
private string _separator;
|
||
private string _header;
|
||
|
||
public CsvExporter(string separator = ",")
|
||
{
|
||
_separator = separator;
|
||
}
|
||
|
||
public string Export(IEnumerable<TaskItem> tasks)
|
||
{
|
||
_header = string.Join(_separator, "Id", "Title", "AssignedTo", "Completed");
|
||
var lines = new List<string> { _header };
|
||
foreach (var task in tasks)
|
||
{
|
||
var assignee = task.AssignedTo ?? "";
|
||
lines.Add(string.Join(_separator,
|
||
task.Id, task.Title, assignee, task.IsCompleted));
|
||
}
|
||
return string.Join(Environment.NewLine, lines);
|
||
}
|
||
}
|
||
assertions:
|
||
- type: "exit_success"
|
||
- type: "file_contains"
|
||
path: "TaskManager/TaskManager.csproj"
|
||
value: "<Nullable>enable</Nullable>"
|
||
- type: "file_contains"
|
||
path: "TaskManager/Services/ITaskRepository.cs"
|
||
value: "TaskItem?"
|
||
- type: "file_contains"
|
||
path: "TaskManager/Models/Project.cs"
|
||
value: "TaskItem?"
|
||
- type: "file_contains"
|
||
path: "TaskManager/Services/TaskService.cs"
|
||
value: "string?"
|
||
rubric:
|
||
- "Did the agent avoid adding `?` to value types? `int Id`, `bool IsCompleted`, `int id` parameters, and `int _nextId` must NOT get `?`. `DateTime? DueDate` already has `?` and must not become `DateTime??`. Check edits carefully for this mistake."
|
||
- "Did the agent correctly annotate methods that return null? `GetById`, `FindByTitle`, and `FindTaskByTitle` all use `FirstOrDefault` which returns null <20> their return types must be `TaskItem?`, not `TaskItem`. Both the interface `ITaskRepository` and implementation `InMemoryTaskRepository` must match."
|
||
- "Did the agent correctly handle the `_header` field in CsvExporter? It is set inside `Export()`, not the constructor <20> it should be `string? _header` or `string _header = null!`, not left as `string _header` which would produce CS8618."
|
||
- "Did the agent keep interface and implementation return types consistent? If `ITaskRepository.GetById` returns `TaskItem?`, then `InMemoryTaskRepository.GetById` must also return `TaskItem?`. Same for `FindByTitle`. Check both files."
|
||
- "Does the project build with zero CS86xx warnings after the agent's changes? The agent should have resolved all nullable warnings introduced by enabling NRT."
|
||
timeout: 300
|
||
|
||
- name: "File-by-file migration: only modify the targeted file"
|
||
prompt: |
|
||
This project has nullable reference types enabled project-wide, but each file
|
||
still has #nullable disable at the top. Remove #nullable disable from
|
||
UserProfile.cs ONLY and fix all resulting nullable warnings in that file.
|
||
Do NOT modify any other files.
|
||
setup:
|
||
files:
|
||
- path: "FileByFile/FileByFile.csproj"
|
||
content: |
|
||
<Project Sdk="Microsoft.NET.Sdk">
|
||
<PropertyGroup>
|
||
<TargetFramework>net8.0</TargetFramework>
|
||
<ImplicitUsings>enable</ImplicitUsings>
|
||
<Nullable>enable</Nullable>
|
||
</PropertyGroup>
|
||
</Project>
|
||
- path: "FileByFile/UserProfile.cs"
|
||
content: |
|
||
#nullable disable
|
||
|
||
namespace FileByFile;
|
||
|
||
public class UserProfile
|
||
{
|
||
public string Name { get; set; }
|
||
public string Bio { get; set; }
|
||
public string Email { get; set; }
|
||
|
||
public UserProfile(string name, string email)
|
||
{
|
||
Name = name;
|
||
Email = email;
|
||
}
|
||
|
||
public string GetDisplayName()
|
||
{
|
||
return Bio != null ? $"{Name} - {Bio}" : Name;
|
||
}
|
||
}
|
||
- path: "FileByFile/OrderProcessor.cs"
|
||
content: |
|
||
#nullable disable
|
||
|
||
namespace FileByFile;
|
||
|
||
public class OrderProcessor
|
||
{
|
||
private string _connectionString;
|
||
|
||
public OrderProcessor(string connectionString)
|
||
{
|
||
_connectionString = connectionString;
|
||
}
|
||
|
||
public string ProcessOrder(string orderId)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
- path: "FileByFile/NotificationService.cs"
|
||
content: |
|
||
#nullable disable
|
||
|
||
namespace FileByFile;
|
||
|
||
public class NotificationService
|
||
{
|
||
private string _endpoint;
|
||
|
||
public NotificationService(string endpoint)
|
||
{
|
||
_endpoint = endpoint;
|
||
}
|
||
|
||
public void Send(string message, string recipient)
|
||
{
|
||
}
|
||
|
||
public string GetLastError()
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
assertions:
|
||
- type: "exit_success"
|
||
- type: "file_not_contains"
|
||
path: "FileByFile/UserProfile.cs"
|
||
value: "#nullable disable"
|
||
- type: "file_contains"
|
||
path: "FileByFile/UserProfile.cs"
|
||
value: "string?"
|
||
- type: "file_contains"
|
||
path: "FileByFile/OrderProcessor.cs"
|
||
value: "#nullable disable"
|
||
- type: "file_contains"
|
||
path: "FileByFile/NotificationService.cs"
|
||
value: "#nullable disable"
|
||
rubric:
|
||
- "Did the agent follow the skill's file-by-file migration strategy, migrating UserProfile.cs completely (removing #nullable disable, annotating correctly) before moving on to other files?"
|
||
- "Did the agent correctly annotate Bio as string? (nullable) since it is never set in the constructor and can legitimately be null, while keeping Name and Email as non-nullable string since they are set via the constructor?"
|
||
- "Does the project build with zero errors after the agent's changes?"
|
||
timeout: 300
|
||
|
||
- name: "Enable NRT in ASP.NET Core Web API with EF Core"
|
||
prompt: |
|
||
This small project (6 files) needs `<Nullable>enable</Nullable>` added and all resulting
|
||
warnings fixed. It's a straightforward task. The project is at BookStore/BookStore.csproj.
|
||
It uses Entity Framework Core with an in-memory database and has a Web API controller.
|
||
Make sure annotations are correct for both EF Core entities and API models.
|
||
setup:
|
||
files:
|
||
- path: "BookStore/BookStore.csproj"
|
||
content: |
|
||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||
<PropertyGroup>
|
||
<TargetFramework>net9.0</TargetFramework>
|
||
<ImplicitUsings>enable</ImplicitUsings>
|
||
<WarningsAsErrors>nullable</WarningsAsErrors>
|
||
</PropertyGroup>
|
||
<ItemGroup>
|
||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
|
||
</ItemGroup>
|
||
</Project>
|
||
- path: "BookStore/Models/Author.cs"
|
||
content: |
|
||
namespace BookStore.Models;
|
||
|
||
public class Author
|
||
{
|
||
public int Id { get; set; }
|
||
public string Name { get; set; }
|
||
public string Biography { get; set; }
|
||
public List<Book> Books { get; set; } = new();
|
||
}
|
||
- path: "BookStore/Models/Book.cs"
|
||
content: |
|
||
namespace BookStore.Models;
|
||
|
||
public class Book
|
||
{
|
||
public int Id { get; set; }
|
||
public string Title { get; set; }
|
||
public string Isbn { get; set; }
|
||
public decimal Price { get; set; }
|
||
public string Summary { get; set; }
|
||
public int AuthorId { get; set; }
|
||
public Author Author { get; set; }
|
||
public int? CategoryId { get; set; }
|
||
public Category Category { get; set; }
|
||
}
|
||
- path: "BookStore/Models/Category.cs"
|
||
content: |
|
||
namespace BookStore.Models;
|
||
|
||
public class Category
|
||
{
|
||
public int Id { get; set; }
|
||
public string Name { get; set; }
|
||
public List<Book> Books { get; set; } = new();
|
||
}
|
||
- path: "BookStore/Data/BookStoreContext.cs"
|
||
content: |
|
||
using Microsoft.EntityFrameworkCore;
|
||
using BookStore.Models;
|
||
|
||
namespace BookStore.Data;
|
||
|
||
public class BookStoreContext : DbContext
|
||
{
|
||
public BookStoreContext(DbContextOptions<BookStoreContext> options) : base(options) { }
|
||
|
||
public DbSet<Book> Books { get; set; }
|
||
public DbSet<Author> Authors { get; set; }
|
||
public DbSet<Category> Categories { get; set; }
|
||
|
||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||
{
|
||
modelBuilder.Entity<Book>()
|
||
.HasOne(b => b.Category)
|
||
.WithMany(c => c.Books)
|
||
.HasForeignKey(b => b.CategoryId)
|
||
.IsRequired(false);
|
||
}
|
||
}
|
||
- path: "BookStore/Dtos/BookDto.cs"
|
||
content: |
|
||
namespace BookStore.Dtos;
|
||
|
||
public class CreateBookRequest
|
||
{
|
||
public string Title { get; set; }
|
||
public string Isbn { get; set; }
|
||
public decimal Price { get; set; }
|
||
public string Summary { get; set; }
|
||
public int AuthorId { get; set; }
|
||
public int? CategoryId { get; set; }
|
||
}
|
||
|
||
public class BookResponse
|
||
{
|
||
public int Id { get; set; }
|
||
public string Title { get; set; }
|
||
public string Isbn { get; set; }
|
||
public decimal Price { get; set; }
|
||
public string AuthorName { get; set; }
|
||
public string CategoryName { get; set; }
|
||
}
|
||
- path: "BookStore/Controllers/BooksController.cs"
|
||
content: |
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using BookStore.Data;
|
||
using BookStore.Dtos;
|
||
using BookStore.Models;
|
||
|
||
namespace BookStore.Controllers;
|
||
|
||
[ApiController]
|
||
[Route("api/[controller]")]
|
||
public class BooksController : ControllerBase
|
||
{
|
||
private readonly BookStoreContext _context;
|
||
|
||
public BooksController(BookStoreContext context)
|
||
{
|
||
_context = context;
|
||
}
|
||
|
||
[HttpGet]
|
||
public async Task<ActionResult<List<BookResponse>>> GetAll()
|
||
{
|
||
var books = await _context.Books
|
||
.Include(b => b.Author)
|
||
.Include(b => b.Category)
|
||
.ToListAsync();
|
||
|
||
return books.Select(b => new BookResponse
|
||
{
|
||
Id = b.Id,
|
||
Title = b.Title,
|
||
Isbn = b.Isbn,
|
||
Price = b.Price,
|
||
AuthorName = b.Author.Name,
|
||
CategoryName = b.Category != null ? b.Category.Name : "Uncategorized"
|
||
}).ToList();
|
||
}
|
||
|
||
[HttpGet("{id}")]
|
||
public async Task<ActionResult<BookResponse>> GetById(int id)
|
||
{
|
||
var book = await _context.Books
|
||
.Include(b => b.Author)
|
||
.Include(b => b.Category)
|
||
.FirstOrDefaultAsync(b => b.Id == id);
|
||
|
||
if (book == null) return NotFound();
|
||
|
||
return new BookResponse
|
||
{
|
||
Id = book.Id,
|
||
Title = book.Title,
|
||
Isbn = book.Isbn,
|
||
Price = book.Price,
|
||
AuthorName = book.Author.Name,
|
||
CategoryName = book.Category != null ? book.Category.Name : "Uncategorized"
|
||
};
|
||
}
|
||
|
||
[HttpPost]
|
||
public async Task<ActionResult<BookResponse>> Create(CreateBookRequest request)
|
||
{
|
||
var author = await _context.Authors.FindAsync(request.AuthorId);
|
||
if (author == null) return BadRequest("Author not found");
|
||
|
||
var book = new Book
|
||
{
|
||
Title = request.Title,
|
||
Isbn = request.Isbn,
|
||
Price = request.Price,
|
||
Summary = request.Summary,
|
||
AuthorId = request.AuthorId,
|
||
Author = author,
|
||
CategoryId = request.CategoryId
|
||
};
|
||
|
||
_context.Books.Add(book);
|
||
await _context.SaveChangesAsync();
|
||
|
||
return CreatedAtAction(nameof(GetById), new { id = book.Id }, new BookResponse
|
||
{
|
||
Id = book.Id,
|
||
Title = book.Title,
|
||
Isbn = book.Isbn,
|
||
Price = book.Price,
|
||
AuthorName = author.Name,
|
||
CategoryName = "Uncategorized"
|
||
});
|
||
}
|
||
|
||
[HttpGet("search")]
|
||
public async Task<ActionResult<List<BookResponse>>> Search(string query)
|
||
{
|
||
var books = await _context.Books
|
||
.Include(b => b.Author)
|
||
.Where(b => b.Title.Contains(query) || b.Author.Name.Contains(query))
|
||
.ToListAsync();
|
||
|
||
return books.Select(b => new BookResponse
|
||
{
|
||
Id = b.Id,
|
||
Title = b.Title,
|
||
Isbn = b.Isbn,
|
||
Price = b.Price,
|
||
AuthorName = b.Author.Name,
|
||
CategoryName = b.Category != null ? b.Category.Name : "Uncategorized"
|
||
}).ToList();
|
||
}
|
||
}
|
||
- path: "BookStore/Program.cs"
|
||
content: |
|
||
using Microsoft.EntityFrameworkCore;
|
||
using BookStore.Data;
|
||
|
||
var builder = WebApplication.CreateBuilder(args);
|
||
builder.Services.AddControllers();
|
||
builder.Services.AddDbContext<BookStoreContext>(options =>
|
||
options.UseInMemoryDatabase("BookStore"));
|
||
|
||
var app = builder.Build();
|
||
app.MapControllers();
|
||
app.Run();
|
||
assertions:
|
||
- type: "exit_success"
|
||
- type: "file_contains"
|
||
path: "BookStore/BookStore.csproj"
|
||
value: "<Nullable>enable</Nullable>"
|
||
- type: "file_contains"
|
||
path: "BookStore/Models/Book.cs"
|
||
value: "Category?"
|
||
- type: "file_contains"
|
||
path: "BookStore/Dtos/BookDto.cs"
|
||
value: "string?"
|
||
rubric:
|
||
- "Did the agent handle the required vs optional navigation property distinction? `Book.Author` is a required navigation (non-nullable FK `AuthorId`) and should use `= null!` or stay non-nullable. `Book.Category` is an optional navigation (nullable FK `CategoryId?`) and MUST be `Category?`. Check that the agent did not make both nullable or both non-nullable."
|
||
- "Did the agent keep `DbSet<T>` properties non-nullable in BookStoreContext? EF Core always initializes these. They should remain `DbSet<Book>`, `DbSet<Author>`, `DbSet<Category>` without `?`. The agent may add `= null!` or use `=> Set<T>()` but must NOT make them nullable."
|
||
- "Did the agent correctly identify that `BookResponse.CategoryName` can be null (category is optional) and make it `string?`, while `BookResponse.AuthorName` should remain non-nullable (author is always present)?"
|
||
- "Did the agent handle `Author.Biography` and `Book.Summary` appropriately? These are descriptive text fields that logically could be null. The agent should make them `string?` (nullable) since not every book/author necessarily has a summary/biography, OR use `= string.Empty` / `= null!` with a comment. It should NOT silently leave them as `string` which would make them NOT NULL columns in EF Core."
|
||
- "Did the agent recognize the ASP.NET model validation impact? With NRTs enabled, non-nullable string properties on `CreateBookRequest` (like `Title`, `Isbn`) become implicitly `[Required]`. The agent should either accept this (correct for Title/Isbn) or annotate `Summary` as `string?` if it should be optional. Check that the agent did not blindly make all DTO strings nullable."
|
||
- "Does the project build with zero CS86xx warnings after the agent's changes?"
|
||
timeout: 300
|