Back to skills
SKILL.md
Csharp Developer
ASecurityUse when designing, implementing, auditing, and hardening csharp developer server logic, APIs, background jobs, and error boundaries.
- 5 stars
- 0 votes
- 0 copies
- 0 views
- Added September 27, 2026
Works with
Security analysis
100/100npx -y skills add Harmitx7/tribunal-kit --skill csharp-developer --agent claude-codeAre you the author of Csharp Developer?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/harmitx7-csharp-developer)---
name: csharp-developer
description: "Use when designing, implementing, auditing, and hardening csharp developer server logic, APIs, background jobs, and error boundaries."
version: 6.0.0
last-updated: 2026-09-29
skills:
- clean-code
- database-design
- api-patterns
tools: Read, Grep, Glob, Bash, Edit, Write
scripts-binding:
- .agent/scripts/lint_runner.js
- .agent/scripts/verify_all.js
---
# C# / .NET Pro β .NET 9+ & C# 13 Mastery
## Mandatory Pre-Flight Context Inspection
Before reading, generating, or refactoring code in the `csharp-developer` domain, inspect these 5 critical parameters:
1. **System Boundaries & Dependencies**: Verify that all required dependencies exist in target package manifests and environment paths.
2. **Runtime Context & Platform Invariants**: Confirm target platform constraints (Node.js, Browser, Mobile OS, Edge runtime) before applying APIs.
3. **Execution Guardrails**: Identify potential side-effects, state mutations, and unhandled asynchronous exceptions.
4. **Validation & Type Contracts**: Validate input data schemas and strict type constraints across all module interfaces.
5. **Observability & Proof of Execution**: Ensure execution produces tangible verification signals (terminal output, tests, metrics).
## Activation Boundaries
- **Activate when:** Use when designing, implementing, auditing, and hardening csharp developer server logic, APIs, background jobs, and error boundaries.
- **DO NOT activate when:** The task falls outside the `csharp-developer` domain or is managed by a different dedicated specialist agent.
## π Multi-Pass Execution Protocol
| Pass | Phase | Core Action | Adaptive Depth |
|:---|:---|:---|:---|
| **Pass 1** | **Understand** | Deconstruct the user's explicit objective, implicit requirements, and platform constraints. | Fast / Standard / Deep |
| **Pass 2** | **Plan** | Decompose task into smallest logical steps; map dependencies, affected files, and tool calls. | Standard / Deep |
| **Pass 3** | **Execute** | Implement solution with production-grade craft, zero placeholders, and strict typing. | All Modes |
| **Pass 4** | **Verify** | Run linters, unit tests, or compiler checks to validate structural correctness. | All Modes |
| **Pass 5** | **Attack & Falsify** | Perform adversarial search for edge-case failures, counterexamples, race conditions, and traps. | Standard / Deep |
| **Pass 6** | **Harden** | Eliminate discovered friction, optimize performance, and harden error boundaries. | Standard / Deep |
| **Pass 7** | **Quality Gate** | Enforce Verification-Before-Completion (VBC) with concrete terminal proof before finalizing. | All Modes |
---
## π οΈ Technical Architecture & Reference Recipes
---
## Modern C# Language Features
### Records & Primary Constructors
```csharp
// Records β immutable data types with value equality
public record UserDto(string Name, string Email, string Role = "user");
// With custom validation
public record CreateUserRequest(string Name, string Email)
{
public string Name { get; init; } = !string.IsNullOrWhiteSpace(Name)
? Name.Trim()
: throw new ArgumentException("Name is required", nameof(Name));
}
// Primary constructors (C# 12+) β classes too
public class UserService(IUserRepository repo, ILogger<UserService> logger)
{
public async Task<User?> GetUserAsync(int id, CancellationToken ct = default)
{
logger.LogInformation("Fetching user {UserId}", id);
return await repo.GetByIdAsync(id, ct);
}
}
// β HALLUCINATION TRAP: Primary constructor parameters are NOT fields
// They're captured by closure β don't use them where a field is needed
// For mutable backing, assign to a private field explicitly
```
### Pattern Matching (C# 12+)
```csharp
// Switch expressions with pattern matching
public static string ClassifyTemperature(double temp) => temp switch
{
< 0 => "Freezing",
>= 0 and < 15 => "Cold",
>= 15 and < 25 => "Comfortable",
>= 25 and < 35 => "Warm",
>= 35 => "Hot",
};
// Property patterns
public static decimal CalculateDiscount(Order order) => order switch
{
{ Total: > 1000, Customer.IsPremium: true } => 0.20m,
{ Total: > 500 } => 0.10m,
{ Customer.IsPremium: true } => 0.05m,
_ => 0m,
};
// List patterns (C# 11+)
public static string DescribeArray(int[] arr) => arr switch
{
[] => "Empty",
[var single] => $"Single: {single}",
[var first, .., var last] => $"First: {first}, Last: {last}",
};
```
### Collection Expressions & Ranges
```csharp
// Collection expressions (C# 12+)
List<int> numbers = [1, 2, 3, 4, 5];
int[] array = [10, 20, 30];
Span<byte> bytes = [0xFF, 0x00, 0xAB];
// Spread operator
int[] combined = [..numbers, ..array, 99];
// Ranges and indices
var last = array[^1]; // last element
var slice = array[1..^1]; // skip first and last
var firstThree = array[..3]; // first 3 elements
```
### Nullable Reference Types
```csharp
// Enable globally in .csproj
// <Nullable>enable</Nullable>
public class UserService
{
// Non-nullable β compiler enforces this is never null
public string GetDisplayName(User user)
{
return user.DisplayName ?? user.Email; // DisplayName might be null
}
// Nullable return β caller MUST handle null
public async Task<User?> FindUserAsync(string email, CancellationToken ct)
{
return await _db.Users.FirstOrDefaultAsync(u => u.Email == email, ct);
}
// β HALLUCINATION TRAP: Never use the null-forgiving operator (!) to suppress warnings
// β var user = await FindUserAsync(email, ct)!; β hides nulls, crashes at runtime
// β
var user = await FindUserAsync(email, ct) ?? throw new NotFoundException("User");
}
```
---
## ASP.NET Core Minimal APIs
### Route Structure
```csharp
var builder = WebApplication.CreateBuilder(args);
// Services
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
// Route groups
var api = app.MapGroup("/api").RequireAuthorization();
var users = api.MapGroup("/users").WithTags("Users");
users.MapGet("/", GetUsersAsync);
users.MapGet("/{id:int}", GetUserByIdAsync);
users.MapPost("/", CreateUserAsync).AllowAnonymous();
users.MapPut("/{id:int}", UpdateUserAsync);
users.MapDelete("/{id:int}", DeleteUserAsync);
app.Run();
```
### Handler Methods
```csharp
static async Task<Results<Ok<UserDto>, NotFound>> GetUserByIdAsync(
int id,
IUserService userService,
CancellationToken ct)
{
var user = await userService.GetByIdAsync(id, ct);
return user is not null
? TypedResults.Ok(user.ToDto())
: TypedResults.NotFound();
}
static async Task<Results<Created<UserDto>, ValidationProblem>> CreateUserAsync(
CreateUserRequest request,
IUserService userService,
IValidator<CreateUserRequest> validator,
CancellationToken ct)
{
var validation = await validator.ValidateAsync(request, ct);
if (!validation.IsValid)
return TypedResults.ValidationProblem(validation.ToDictionary());
var user = await userService.CreateAsync(request, ct);
return TypedResults.Created($"/api/users/{user.Id}", user.ToDto());
}
// β HALLUCINATION TRAP: Always accept CancellationToken in async handlers
// ASP.NET Core provides it automatically via DI
// Without it, requests can't be cancelled on client disconnect
```
### Endpoint Filters (Middleware for Endpoints)
```csharp
// Validation filter
public class ValidationFilter<T> : IEndpointFilter where T : class
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext ctx,
EndpointFilterDelegate next)
{
var validator = ctx.HttpContext.RequestServices.GetService<IValidator<T>>();
var argument = ctx.Arguments.OfType<T>().FirstOrDefault();
if (validator is not null && argument is not null)
{
var result = await validator.ValidateAsync(argument);
if (!result.IsValid)
return TypedResults.ValidationProblem(result.ToDictionary());
}
return await next(ctx);
}
}
// Usage:
users.MapPost("/", CreateUserAsync)
.AddEndpointFilter<ValidationFilter<CreateUserRequest>>();
```
---
## Entity Framework Core
### DbContext & Configuration
```csharp
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<User> Users => Set<User>();
public DbSet<Post> Posts => Set<Post>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
// Auto-set timestamps
public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
{
foreach (var entry in ChangeTracker.Entries<BaseEntity>())
{
if (entry.State == EntityState.Added)
entry.Entity.CreatedAt = DateTime.UtcNow;
if (entry.State is EntityState.Added or EntityState.Modified)
entry.Entity.UpdatedAt = DateTime.UtcNow;
}
return await base.SaveChangesAsync(ct);
}
}
// Entity configuration (separate file per entity)
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasIndex(u => u.Email).IsUnique();
builder.Property(u => u.Name).HasMaxLength(100).IsRequired();
builder.Property(u => u.Email).HasMaxLength(255).IsRequired();
builder.HasMany(u => u.Posts).WithOne(p => p.Author).HasForeignKey(p => p.AuthorId);
}
}
```
### Query Patterns
```csharp
// β
Efficient queries β project to DTOs at the database level
public async Task<List<UserDto>> GetActiveUsersAsync(CancellationToken ct)
{
return await _db.Users
.AsNoTracking() // read-only β no change tracking overhead
.Where(u => u.IsActive)
.OrderByDescending(u => u.CreatedAt)
.Select(u => new UserDto(u.Name, u.Email, u.Role)) // projects SQL SELECT
.ToListAsync(ct);
}
// β HALLUCINATION TRAP: Loading entities then mapping is N+1 and memory waste
// β var users = await _db.Users.ToListAsync(ct); β loads ALL columns, ALL rows
// return users.Select(u => new UserDto(u.Name, u.Email)); β maps in memory
// β
Use .Select() BEFORE .ToListAsync() to project at DB level
// Pagination
public async Task<PagedResult<UserDto>> GetUsersPagedAsync(int page, int pageSize, CancellationToken ct)
{
var query = _db.Users.AsNoTracking().Where(u => u.IsActive);
var totalCount = await query.CountAsync(ct);
var items = await query
.OrderBy(u => u.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(u => new UserDto(u.Name, u.Email, u.Role))
.ToListAsync(ct);
return new PagedResult<UserDto>(items, totalCount, page, pageSize);
}
// Compiled queries (for hot paths)
private static readonly Func<AppDbContext, string, CancellationToken, Task<User?>> _getUserByEmail =
EF.CompileAsyncQuery((AppDbContext db, string email, CancellationToken ct) =>
db.Users.FirstOrDefault(u => u.Email == email));
```
---
## Async Patterns
```csharp
// β
Correct async patterns
public async Task<Result<User>> ProcessUserAsync(int id, CancellationToken ct)
{
// Parallel async operations
var (user, permissions) = await (
_userRepo.GetByIdAsync(id, ct),
_permissionService.GetPermissionsAsync(id, ct)
);
// Async streams (IAsyncEnumerable)
await foreach (var notification in GetNotificationsAsync(id, ct))
{
await SendNotificationAsync(notification, ct);
}
return Result.Ok(user);
}
// Channels (producer-consumer)
var channel = Channel.CreateBounded<WorkItem>(100);
// Producer
async Task ProduceAsync(ChannelWriter<WorkItem> writer, CancellationToken ct)
{
await foreach (var item in GetWorkItemsAsync(ct))
{
await writer.WriteAsync(item, ct);
}
writer.Complete();
}
// Consumer
async Task ConsumeAsync(ChannelReader<WorkItem> reader, CancellationToken ct)
{
await foreach (var item in reader.ReadAllAsync(ct))
{
await ProcessAsync(item, ct);
}
}
// β HALLUCINATION TRAP: Never use .Result or .Wait() on async methods
// β var user = GetUserAsync(id).Result; β deadlock risk
// β GetUserAsync(id).Wait(); β deadlock risk
// β
var user = await GetUserAsync(id, ct);
```
---
## Performance Patterns
```csharp
// Span<T> β zero-allocation slicing
public static ReadOnlySpan<char> ExtractDomain(ReadOnlySpan<char> email)
{
var atIndex = email.IndexOf('@');
return atIndex >= 0 ? email[(atIndex + 1)..] : ReadOnlySpan<char>.Empty;
}
// ArrayPool β rent instead of allocate
public static void ProcessLargeData()
{
var buffer = ArrayPool<byte>.Shared.Rent(8192);
try
{
// Use buffer...
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
// Frozen collections (immutable, optimized lookup)
FrozenDictionary<string, int> lookup = new Dictionary<string, int>
{
["admin"] = 1,
["user"] = 2,
["moderator"] = 3,
}.ToFrozenDictionary();
```
---
## Testing with xUnit
```csharp
public class UserServiceTests
{
private readonly Mock<IUserRepository> _repoMock = new();
private readonly Mock<ILogger<UserService>> _loggerMock = new();
private readonly UserService _sut;
public UserServiceTests()
{
_sut = new UserService(_repoMock.Object, _loggerMock.Object);
}
[Fact]
public async Task GetUserAsync_ReturnsUser_WhenFound()
{
// Arrange
var expected = new User { Id = 1, Name = "Alice", Email = "alice@test.com" };
_repoMock.Setup(r => r.GetByIdAsync(1, It.IsAny<CancellationToken>()))
.ReturnsAsync(expected);
// Act
var result = await _sut.GetUserAsync(1);
// Assert
Assert.NotNull(result);
Assert.Equal("Alice", result.Name);
}
[Fact]
public async Task GetUserAsync_ReturnsNull_WhenNotFound()
{
_repoMock.Setup(r => r.GetByIdAsync(999, It.IsAny<CancellationToken>()))
.ReturnsAsync((User?)null);
var result = await _sut.GetUserAsync(999);
Assert.Null(result);
}
[Theory]
[InlineData("", "required")]
[InlineData("ab", "too short")]
public async Task CreateUser_Fails_WithInvalidName(string name, string expectedError)
{
var request = new CreateUserRequest(name, "test@test.com");
var ex = await Assert.ThrowsAsync<ValidationException>(
() => _sut.CreateAsync(request));
Assert.Contains(expectedError, ex.Message, StringComparison.OrdinalIgnoreCase);
}
}
// Integration test with WebApplicationFactory
public class UsersApiTests(WebApplicationFactory<Program> factory)
: IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client = factory.CreateClient();
[Fact]
public async Task GetUsers_Returns200()
{
var response = await _client.GetAsync("/api/users");
response.EnsureSuccessStatusCode();
var users = await response.Content.ReadFromJsonAsync<List<UserDto>>();
Assert.NotNull(users);
}
}
```
## π¨ Edge-Case & Failure Mode Matrix
| Scenario | Risk | Production Mitigation |
|:---|:---|:---|
| **Empty or Null Inputs** | Unhandled exception or unexpected rendering collapse | Enforce fallback guards, optional chaining, and explicit empty state handlers |
| **Network Timeout / Latency** | Hanging operations or duplicate side-effects | Implement bounded abort controllers, exponential backoff, and idempotency keys |
| **Concurrency / Race Conditions** | Stale state overwrite or inconsistent data mutations | Use atomic transactions, mutex locking, or cancel-on-resubmit controls |
| **Invalid Schema / Malformed Payload** | Downstream runtime errors or security injection | Validate boundary payloads with Zod/Pydantic schemas prior to execution |
| **Resource / Memory Saturation** | OOM errors, frame drops, or memory leaks | Clean up listeners, cancel active timers, and enforce pagination/virtualization |
## π€ LLM-Specific Traps Table
| Anti-Pattern | What AI Commonly Does Wrong | What Is Actually Correct |
|:---|:---|:---|
| **Unchecked Payload Cast** | Casting request bodies to TypeScript types without runtime schema validation | Parse request payloads through Zod/Pydantic schemas before business logic |
| **Silent Error Swallowing** | Catching errors with empty catch blocks or logging without rethrowing | Propagate structured errors with status codes and contextual stack traces |
| **Unparameterized Query** | Concatenating user inputs into SQL/Prisma query strings | Always use parameterized bindings or type-safe ORM query builders |
## ποΈ Tribunal Verification & Guardrails
**Active Reviewers:** `logic-reviewer` Β· `security-auditor` Β· `api-architect` Β· `resilience-reviewer`
**Slash Command:** `/review` or `/tribunal-full`
### π¬ Evidence Standard (Tri-State Verification)
Every finding, audit statement, or completion claim must classify its factual certainty:
- **`[OBSERVED]`**: Directly confirmed in the codebase or verified via executed terminal command.
- **`[INFERRED]`**: Logically deduced from code patterns, architectural data flow, or schema relations.
- **`[UNVERIFIED]`**: Speculative hypothesis or runtime possibility requiring active testing or measurement.
### β
Pre-Flight Self-Audit Checklist
```
β
Are all inputs and boundary payloads validated against schemas (Zod/Pydantic)?
β
Are SQL and database queries parameterized with zero string concatenation?
β
Are error boundaries and timeout/retry policies explicitly declared?
β
Are authentication and object-level authorization (IDOR/BOLA) checked before business logic?
β
Did I verify that imported dependencies exist in package manifests?
```
### π Verification-Before-Completion (VBC) Protocol
**CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
- β **Forbidden:** Declaring a task complete because the output "looks correct."
- β
**Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing test suites, compiler success, or equivalent operational proof) that your output works as intended.
Attribution
Comments
Loading commentsβ¦