Skip to main content

AI Agent Workflows

Catalog of repository-specific patterns and available agent slash commands.

Available Workflowsโ€‹

CommandPurpose
/add-entityScaffold a new domain entity end-to-end (Domain โ†’ Infra โ†’ Api โ†’ Worker)
/add-featureAdd new behavior to an existing entity
/feature-flowFull end-to-end development flow from requirement to merged code
/db-migrationCreate and apply an EF Core database migration
/run-testsRun unit tests with optional coverage report
/code-reviewPre-merge checklist for code quality
/docScaffold and sync documentation
/planDesign feature + freeze context (FSD spec)
/searchBM25 search across codebase (token saver)

Key Patterns to Knowโ€‹

Transaction Capture Patternโ€‹

// โœ… GOOD โ€” result assigned inside the lambda
PortfolioPositionResponse result = null!; // null-forgiving acceptable: assigned before read
await _unitOfWork.ExecuteTransactionAsync(async () => {
var updated = await _repo.UpdateAsync(entity);
result = MapToResponse(updated);
}, ct);
return result;

Primary Constructor Injection (C# 12)โ€‹

public class PortfolioService(IUnitOfWork unitOfWork, IStockDataService stockData)
: IPortfolioService
{
private readonly IUnitOfWork _unitOfWork = unitOfWork;
private readonly IStockDataService _stockData = stockData;

public async Task<PortfolioPositionResponse> OpenPositionAsync(
CreatePositionRequest request, string userId, CancellationToken ct)
{
var listing = await _unitOfWork.StockListings.FindBySymbolAsync(request.TickerSymbol, ct)
?? throw new InvalidOperationException($"Symbol {request.TickerSymbol} must be resolved first.");

PortfolioPositionResponse result = null!;
await _unitOfWork.ExecuteTransactionAsync(async () => {
var trade = new Trade { UserId = Guid.Parse(userId), ... };
await _unitOfWork.Trades.AddAsync(trade, ct);
result = MapToResponse(trade, listing);
}, ct);
return result;
}
}

Finnhub Null Guardโ€‹

// Always null-check before using Finnhub data
var quote = await _finnhub.GetQuoteAsync(symbol, ct);
if (quote?.CurrentPrice is null or 0)
{
_logger.LogWarning("[SyncPrices] {Symbol} returned zero price. Skipping.", symbol);
return;
}

Read-Only EF Queriesโ€‹

// All read-only queries must use AsNoTracking()
var listings = await _db.StockListings
.AsNoTracking()
.Where(s => s.IsActive)
.ToListAsync(ct);

File Placement Quick Referenceโ€‹

What you're creatingWhere it goes
New entityDomain/Entities/Postgres/
New DTODomain/DTOs/
New repository interfaceDomain/Interfaces/
New validatorDomain/Validators/
EF Core configInfrastructure/Persistence/Configurations/
Repository implInfrastructure/Persistence/Repositories/
External clientInfrastructure/External/
New controllerApi/Controllers/
New API serviceApi/Services/
New scheduled jobWorker/ScheduledJobs/
New SQS handlerWorker/IntegrationEvents/Handlers/