@eltonssouza/development-utility-kit 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.claude/agents/README.md +24 -0
  2. package/.claude/agents/analyst.md +198 -0
  3. package/.claude/agents/backend-developer.md +126 -0
  4. package/.claude/agents/brain-keeper.md +229 -0
  5. package/.claude/agents/code-reviewer.md +181 -0
  6. package/.claude/agents/database-engineer.md +94 -0
  7. package/.claude/agents/devops-engineer.md +141 -0
  8. package/.claude/agents/frontend-developer.md +97 -0
  9. package/.claude/agents/gate-keeper.md +118 -0
  10. package/.claude/agents/migrator.md +291 -0
  11. package/.claude/agents/mobile-developer.md +80 -0
  12. package/.claude/agents/n8n-specialist.md +94 -0
  13. package/.claude/agents/product-owner.md +115 -0
  14. package/.claude/agents/qa-engineer.md +232 -0
  15. package/.claude/agents/release-engineer.md +204 -0
  16. package/.claude/agents/scaffold.md +87 -0
  17. package/.claude/agents/security-engineer.md +199 -0
  18. package/.claude/agents/sprint-runner.md +46 -0
  19. package/.claude/agents/stack-resolver.md +104 -0
  20. package/.claude/agents/tech-lead.md +182 -0
  21. package/.claude/agents/update-template.md +54 -0
  22. package/.claude/agents/ux-designer.md +118 -0
  23. package/.claude/hooks/flow-guard.js +261 -0
  24. package/.claude/hooks/flow-state.js +197 -0
  25. package/.claude/local/CLAUDE.md +71 -0
  26. package/.claude/settings.json +55 -0
  27. package/.claude/skills/README.md +331 -0
  28. package/.claude/skills/active-project/SKILL.md +131 -0
  29. package/.claude/skills/api-integration-test/SKILL.md +84 -0
  30. package/.claude/skills/auto-test-guard/SKILL.md +239 -0
  31. package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
  32. package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
  33. package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
  34. package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
  35. package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
  36. package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
  37. package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
  38. package/.claude/skills/brain-keeper/SKILL.md +62 -0
  39. package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
  40. package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
  41. package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
  42. package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
  43. package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
  44. package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
  45. package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
  46. package/.claude/skills/brain-keeper/templates/README.md +51 -0
  47. package/.claude/skills/brain-keeper/templates/adr.md +40 -0
  48. package/.claude/skills/brain-keeper/templates/bug.md +35 -0
  49. package/.claude/skills/brain-keeper/templates/daily.md +38 -0
  50. package/.claude/skills/brain-keeper/templates/feature.md +62 -0
  51. package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
  52. package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
  53. package/.claude/skills/caveman/SKILL.md +189 -0
  54. package/.claude/skills/create-stack-pack/SKILL.md +281 -0
  55. package/.claude/skills/grill-me/SKILL.md +80 -0
  56. package/.claude/skills/pair-debug/SKILL.md +288 -0
  57. package/.claude/skills/prd-ready-check/SKILL.md +86 -0
  58. package/.claude/skills/project-manager/SKILL.md +334 -0
  59. package/.claude/skills/quality-standards/SKILL.md +203 -0
  60. package/.claude/skills/quick-feature/SKILL.md +266 -0
  61. package/.claude/skills/run-sprint/SKILL.md +41 -0
  62. package/.claude/skills/scaffold/SKILL.md +60 -0
  63. package/.claude/skills/stack-discovery/SKILL.md +161 -0
  64. package/.claude/skills/test-coverage-auditor/SKILL.md +87 -0
  65. package/.claude/skills/to-issues/SKILL.md +163 -0
  66. package/.claude/skills/to-prd/SKILL.md +130 -0
  67. package/.claude/skills/update-template/SKILL.md +256 -0
  68. package/.claude/stacks/CODEOWNERS +30 -0
  69. package/.claude/stacks/README.md +97 -0
  70. package/.claude/stacks/_template.md +116 -0
  71. package/.claude/stacks/dotnet/aspire-9.md +528 -0
  72. package/.claude/stacks/go/gin-1.10.md +570 -0
  73. package/.claude/stacks/java/spring-boot-3.md +376 -0
  74. package/.claude/stacks/java/spring-boot-4.md +438 -0
  75. package/.claude/stacks/node/express-5.md +538 -0
  76. package/.claude/stacks/python/django-5.md +483 -0
  77. package/.claude/stacks/python/fastapi-0.115.md +522 -0
  78. package/.claude/stacks/typescript/angular-18.md +420 -0
  79. package/.claude/stacks/typescript/angular-19.md +397 -0
  80. package/.claude/stacks/typescript/angular-21.md +494 -0
  81. package/CLAUDE.md +472 -0
  82. package/README.md +412 -0
  83. package/bin/cli.js +848 -0
  84. package/bin/lib/adr.js +146 -0
  85. package/bin/lib/backup.js +62 -0
  86. package/bin/lib/detect-stack.js +476 -0
  87. package/bin/lib/doctor.js +527 -0
  88. package/bin/lib/help.js +328 -0
  89. package/bin/lib/identity.js +108 -0
  90. package/bin/lib/lint-allowlist.json +15 -0
  91. package/bin/lib/lint.js +798 -0
  92. package/bin/lib/local-dir.js +68 -0
  93. package/bin/lib/manifest.js +236 -0
  94. package/bin/lib/sync-all.js +394 -0
  95. package/bin/lib/version-check.js +398 -0
  96. package/dashboard/db.js +321 -0
  97. package/dashboard/package.json +22 -0
  98. package/dashboard/public/app.js +853 -0
  99. package/dashboard/public/content/docs/agents-reference.en.md +911 -0
  100. package/dashboard/public/content/docs/architecture-overview.en.md +252 -0
  101. package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
  102. package/dashboard/public/content/docs/cli-reference.en.md +538 -0
  103. package/dashboard/public/content/docs/git-flow.en.md +525 -0
  104. package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
  105. package/dashboard/public/content/docs/hooks-reference.en.md +404 -0
  106. package/dashboard/public/content/docs/pipeline.en.md +414 -0
  107. package/dashboard/public/content/docs/plugins.en.md +289 -0
  108. package/dashboard/public/content/docs/quality-gate.en.md +315 -0
  109. package/dashboard/public/content/docs/skills-reference.en.md +484 -0
  110. package/dashboard/public/content/docs/stack-rules.en.md +362 -0
  111. package/dashboard/public/content/docs/troubleshooting.en.md +565 -0
  112. package/dashboard/public/content/manifest.json +114 -0
  113. package/dashboard/public/content/manual/backend.en.md +1053 -0
  114. package/dashboard/public/content/manual/existing-project.en.md +848 -0
  115. package/dashboard/public/content/manual/frontend.en.md +1008 -0
  116. package/dashboard/public/content/manual/fullstack.en.md +1459 -0
  117. package/dashboard/public/content/manual/mobile.en.md +837 -0
  118. package/dashboard/public/content/manual/quickstart.en.md +169 -0
  119. package/dashboard/public/index.html +217 -0
  120. package/dashboard/public/style.css +857 -0
  121. package/dashboard/public/vendor/marked.min.js +69 -0
  122. package/dashboard/rtk.js +143 -0
  123. package/dashboard/server-app.js +421 -0
  124. package/dashboard/server.js +104 -0
  125. package/dashboard/test/sprint1.test.js +406 -0
  126. package/dashboard/test/sprint2.test.js +571 -0
  127. package/dashboard/test/sprint3.test.js +560 -0
  128. package/package.json +33 -0
  129. package/scripts/hooks/subagent-telemetry.sh +14 -0
  130. package/scripts/hooks/telemetry-writer.js +250 -0
  131. package/scripts/latest-versions.json +56 -0
@@ -0,0 +1,116 @@
1
+ ---
2
+ stack: <lang>/<framework> # e.g. java/spring-boot-3, python/django, go/gin
3
+ versions_covered: "<range>" # e.g. "3.0.x — 3.4.x", "5.0.x — 5.1.x"
4
+ last_validated: <YYYY-MM-DD> # date of last validation in real project
5
+ validated_against: "<description>" # e.g. "projeto-x v1.2 (Java 21 + SB 3.2.5)"
6
+ status: active # active | deprecated | archived
7
+ pack_owner: "@<github-handle>" # e.g. "@elton"
8
+ security_review: <YYYY-MM-DD> # date of last security-focused review
9
+ next_review_due: <YYYY-MM-DD> # security_review + 12 months
10
+ ---
11
+
12
+ # <Stack name + version>
13
+
14
+ <Intro 1-3 lines: what this pack covers, when to use it vs alternative pack/version.>
15
+
16
+ ## 1. When to use this pack
17
+
18
+ - Project declares `Primary stack: <X>` in `## Project Identity`.
19
+ - Build manifest (`pom.xml` / `package.json` / `pyproject.toml` / `go.mod` / etc.) declares versions within `versions_covered`.
20
+ - (Optional) Greenfield/legacy guidance — when to prefer next-major pack instead.
21
+
22
+ ## 2. Stack baseline (what this pack assumes)
23
+
24
+ | Component | Version range | Notes |
25
+ |---|---|---|
26
+ | <language> | <range> | <notes — LTS, EOL dates> |
27
+ | <framework> | <range> | <key feature for this major> |
28
+ | <build tool> | <range> | <Maven Wrapper, npm, etc.> |
29
+ | <test framework> | <range> | <what to avoid — e.g. never H2 as Postgres> |
30
+ | <observability> | <stack> | <Micrometer, OpenTelemetry, etc.> |
31
+
32
+ ## 3. Project structure
33
+
34
+ ```
35
+ <tree of canonical folder layout for this stack>
36
+ ```
37
+
38
+ ## 4. Code patterns
39
+
40
+ ### <Pattern name 1 — e.g. DTOs>
41
+ <Real compilable snippet showing the canonical pattern + 1-line rule.>
42
+
43
+ ### <Pattern name 2 — e.g. Error handling>
44
+ <Snippet + rule.>
45
+
46
+ ### <Pattern name 3, 4, 5...>
47
+
48
+ ## 5. Testing
49
+
50
+ ### Unit
51
+ <Snippet + framework versions + rules.>
52
+
53
+ ### Integration
54
+ <Snippet — REAL test pattern, not pseudocode.>
55
+
56
+ ### <Other test types if relevant — slice, contract, mutation, e2e>
57
+
58
+ ## 6. Build & run commands
59
+
60
+ ```bash
61
+ <exact commands a dev would run — build, test, run, lint, coverage>
62
+ ```
63
+
64
+ ## 7. Security (per ADR-007 + ADR-027 — MANDATORY section)
65
+
66
+ ### 7.1 Authentication & Authorization
67
+ <Stack-specific auth: framework class names, hashing, MFA hooks>
68
+
69
+ ### 7.2 CORS
70
+ <Snippet — never `*` in prod>
71
+
72
+ ### 7.3 Validation & input sanitization
73
+ <SQL injection, XSS, path traversal — stack-specific mitigation>
74
+
75
+ ### 7.4 Secrets management
76
+ <Env vars, vault, never in committed files>
77
+
78
+ ### 7.5 Rate limiting
79
+ <Library + config snippet for public endpoints>
80
+
81
+ ### 7.6 OWASP Top 10 mapping
82
+
83
+ | OWASP | Mitigation in this stack |
84
+ |---|---|
85
+ | A01 Broken Access Control | <stack-specific> |
86
+ | A02 Cryptographic Failures | <stack-specific> |
87
+ | A03 Injection | <stack-specific> |
88
+ | A04 Insecure Design | <stack-specific> |
89
+ | A05 Security Misconfiguration | <stack-specific> |
90
+ | A06 Vulnerable Components | <CVE scanner + Renovate/Dependabot> |
91
+ | A07 Auth Failures | <rate limit + account lock + MFA> |
92
+ | A08 Data Integrity | <signature verification, supply chain, SBOM> |
93
+ | A09 Logging Failures | <structured logs + correlation ID + no PII> |
94
+ | A10 SSRF | <allowlist for outbound, validate URLs> |
95
+
96
+ ### 7.7 LGPD / GDPR / compliance specifics (if applicable)
97
+ <PII tagging, soft-delete, data subject access, encryption at rest>
98
+
99
+ ## 8. Anti-patterns (block in code-review)
100
+
101
+ | ❌ Bad | ✅ Good | Why |
102
+ |---|---|---|
103
+ | <bad pattern> | <good replacement> | <reason> |
104
+ | ... | ... | ... |
105
+
106
+ ## 9. Migration hints — <this version> → <next major>
107
+
108
+ <Short list of breaking changes when migrating. Points to `migrator` agent.>
109
+
110
+ ## 10. References
111
+
112
+ - ADR-007 (Senior+ gate thresholds)
113
+ - ADR-026 (Generic agents + packs architecture)
114
+ - ADR-027 (Pack governance)
115
+ - <Official framework migration guide URLs>
116
+ - <OWASP / compliance references>
@@ -0,0 +1,528 @@
1
+ ---
2
+ stack: dotnet/aspire-9
3
+ versions_covered: "9.0.x — 9.2.x"
4
+ last_validated: 2026-05-28
5
+ validated_against: "reference pack — .NET 9 + Aspire 9.0 + EF Core 9 + xUnit 2.9"
6
+ status: active
7
+ pack_owner: "@elton"
8
+ security_review: 2026-05-28
9
+ next_review_due: 2027-05-28
10
+ ---
11
+
12
+ # .NET 9 + Aspire 9.x
13
+
14
+ Canonical knowledge pack for .NET cloud-native projects using **.NET Aspire 9.x** as the orchestration framework on top of .NET 9 + ASP.NET Core 9 + EF Core 9. Aspire is Microsoft's opinionated stack for distributed apps: it ships AppHost orchestration, service discovery, telemetry defaults, resilience defaults, and Dev Dashboard out of the box. For traditional ASP.NET Core projects without orchestration needs, this pack still applies — Aspire is additive.
15
+
16
+ ## 1. When to use this pack
17
+
18
+ - Project declares `Primary stack: .NET 9 + Aspire 9.x + ASP.NET Core` in `## Project Identity`.
19
+ - Solution contains a `<Solution>.AppHost` project referencing `Aspire.Hosting` and at least one ASP.NET Core service project.
20
+ - Project follows the Aspire convention of `*.AppHost` (orchestration) + `*.ServiceDefaults` (shared telemetry/resilience) + one or more service projects.
21
+ - Greenfield .NET on cloud-native infra (Kubernetes, Azure Container Apps, AWS ECS) — Aspire's manifest export makes deployment cleaner.
22
+ - For Windows desktop apps, MAUI, or library-only solutions: Aspire is not applicable. This pack covers cloud-native services only.
23
+
24
+ ## 2. Stack baseline (what this pack assumes)
25
+
26
+ | Component | Version range | Notes |
27
+ |---|---|---|
28
+ | .NET SDK | 9.0.x (min) / 10.0 preview (latest) | Native AOT supported for many service patterns; minimal APIs maduro |
29
+ | Aspire workload | 9.0.x — 9.2.x | `dotnet workload install aspire`; AppHost + ServiceDefaults convention |
30
+ | ASP.NET Core | 9.0.x | Minimal APIs are the default; `OpenAPI.NET` package replaces Swashbuckle for OpenAPI 3.1 |
31
+ | EF Core | 9.0.x | Bulk insert/update first-class; `IExecutionStrategy` mandatory for retry-aware transactions |
32
+ | Database providers | Npgsql 9.x (Postgres) / Microsoft.EntityFrameworkCore.SqlServer 9.x | NEVER InMemory provider for tests if prod is real DB |
33
+ | Validation | FluentValidation 11.10+ (integrated via `SharpGrip.FluentValidation.AutoValidation`) OR DataAnnotations | FluentValidation preferred for complex rules |
34
+ | Build | `dotnet build` + `dotnet publish` (Native AOT optional) | SLN format ok; new `slnx` (preview) acceptable |
35
+ | Tests | xUnit 2.9+ + FluentAssertions 6.x + Moq 4.20 or NSubstitute 5.x | Testcontainers .NET 4.0+ for integration |
36
+ | Mutation | Stryker.NET 4.4+ | Target ≥70% on `Domain` + `Application` |
37
+ | Coverage | `coverlet.collector` + `dotnet test --collect:"XPlat Code Coverage"` | Target ≥85% lines |
38
+ | Static analysis | Roslyn analyzers (`Microsoft.CodeAnalysis.NetAnalyzers`, `StyleCop`, `SonarAnalyzer.CSharp`) | TreatWarningsAsErrors = true in CI |
39
+ | Security scan | `dotnet list package --vulnerable --include-transitive` + `dotnet-security-scan` | 0 CVE with CVSS ≥7.0 |
40
+ | Observability | Aspire ServiceDefaults pre-wires OpenTelemetry SDK + Health Checks | W3C Trace Context; auto-instruments HTTP, EF Core, http.Client |
41
+ | Service discovery | Aspire built-in (DNS-based in dev, K8s in prod) | No Consul/Eureka needed |
42
+ | Background jobs | `Hangfire` 1.8+ OR `Quartz.NET` 3.x | Or Azure Functions / AWS Lambda for FaaS pattern |
43
+ | Resilience | Polly 8.x (integrated via `Microsoft.Extensions.Http.Resilience`) | Standard pipelines: retry + circuit breaker + timeout |
44
+
45
+ ## 3. Project structure (DDD / Clean Architecture)
46
+
47
+ ```
48
+ solution/
49
+ ├── solution.sln
50
+ ├── Directory.Build.props
51
+ ├── src/
52
+ │ ├── Solution.AppHost/ # Aspire orchestrator
53
+ │ │ ├── Program.cs # builder.AddProject<...>()
54
+ │ │ └── Solution.AppHost.csproj
55
+ │ ├── Solution.ServiceDefaults/ # shared OTel/HealthCheck/Resilience
56
+ │ │ ├── Extensions.cs # AddServiceDefaults()
57
+ │ │ └── Solution.ServiceDefaults.csproj
58
+ │ ├── Solution.Domain/ # pure C#; no EF, no ASP.NET
59
+ │ │ ├── Products/
60
+ │ │ │ ├── Product.cs # entity / value object
61
+ │ │ │ ├── IProductRepository.cs # interface
62
+ │ │ │ └── ProductService.cs
63
+ │ │ └── Solution.Domain.csproj
64
+ │ ├── Solution.Application/ # use cases (1 class each)
65
+ │ │ ├── Products/
66
+ │ │ │ ├── CreateProductHandler.cs
67
+ │ │ │ ├── ListProductsHandler.cs
68
+ │ │ │ └── Dto.cs
69
+ │ │ └── Solution.Application.csproj
70
+ │ ├── Solution.Infrastructure/ # EF Core, HttpClient, AWS SDK
71
+ │ │ ├── Persistence/
72
+ │ │ │ ├── AppDbContext.cs
73
+ │ │ │ ├── Migrations/
74
+ │ │ │ └── ProductRepository.cs
75
+ │ │ └── Solution.Infrastructure.csproj
76
+ │ └── Solution.Api/ # minimal APIs / controllers
77
+ │ ├── Endpoints/
78
+ │ │ ├── Products.cs
79
+ │ │ └── ErrorEndpoints.cs # RFC 9457 ProblemDetails
80
+ │ ├── Program.cs # bootstrap
81
+ │ └── Solution.Api.csproj
82
+ └── tests/
83
+ ├── Solution.Domain.Tests/
84
+ ├── Solution.Application.Tests/
85
+ └── Solution.Api.IntegrationTests/
86
+ ```
87
+
88
+ **Rule**: `Domain` project references nothing from EF Core or ASP.NET. `Application` references only `Domain`. `Infrastructure` references EF Core. `Api` references `Application` + `Infrastructure` (for DI registration). Project references enforced by `Directory.Build.props` — compile fails on violation.
89
+
90
+ ## 4. Code patterns
91
+
92
+ ### Domain entity (no EF Core, no ASP.NET)
93
+
94
+ ```csharp
95
+ // src/Solution.Domain/Products/Product.cs
96
+ namespace Solution.Domain.Products;
97
+
98
+ public sealed class Product
99
+ {
100
+ public Guid Id { get; }
101
+ public string Name { get; private set; }
102
+ public decimal Price { get; private set; }
103
+ public int Stock { get; private set; }
104
+ public DateTimeOffset CreatedAt { get; }
105
+
106
+ private Product(Guid id, string name, decimal price, int stock, DateTimeOffset createdAt)
107
+ => (Id, Name, Price, Stock, CreatedAt) = (id, name, price, stock, createdAt);
108
+
109
+ public static Product Create(string name, decimal price, int stock)
110
+ {
111
+ if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("name required");
112
+ if (price <= 0) throw new ArgumentException("price must be positive");
113
+ if (stock < 0) throw new ArgumentException("stock cannot be negative");
114
+ return new Product(Guid.NewGuid(), name, price, stock, DateTimeOffset.UtcNow);
115
+ }
116
+
117
+ public void Reserve(int qty)
118
+ {
119
+ if (qty <= 0) throw new InvalidOperationException("qty must be positive");
120
+ if (qty > Stock) throw new InvalidOperationException("insufficient stock");
121
+ Stock -= qty;
122
+ }
123
+ }
124
+ ```
125
+
126
+ **Rule**: `decimal` for money (NOT `double` / `float`). Entity constructor private; static `Create` factory enforces invariants. Business rules as methods on the entity.
127
+
128
+ ### Repository port + EF Core adapter
129
+
130
+ ```csharp
131
+ // src/Solution.Domain/Products/IProductRepository.cs
132
+ namespace Solution.Domain.Products;
133
+
134
+ public interface IProductRepository
135
+ {
136
+ Task<Product?> GetAsync(Guid id, CancellationToken ct);
137
+ Task SaveAsync(Product product, CancellationToken ct);
138
+ }
139
+ ```
140
+
141
+ ```csharp
142
+ // src/Solution.Infrastructure/Persistence/AppDbContext.cs
143
+ using Microsoft.EntityFrameworkCore;
144
+ using Solution.Domain.Products;
145
+
146
+ namespace Solution.Infrastructure.Persistence;
147
+
148
+ public sealed class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
149
+ {
150
+ public DbSet<Product> Products => Set<Product>();
151
+
152
+ protected override void OnModelCreating(ModelBuilder mb)
153
+ {
154
+ var product = mb.Entity<Product>();
155
+ product.ToTable("products");
156
+ product.HasKey(p => p.Id);
157
+ product.Property(p => p.Name).HasMaxLength(120).IsRequired();
158
+ product.Property(p => p.Price).HasPrecision(12, 2);
159
+ product.Property(p => p.CreatedAt);
160
+ product.HasIndex(p => p.Name);
161
+ }
162
+ }
163
+ ```
164
+
165
+ ```csharp
166
+ // src/Solution.Infrastructure/Persistence/ProductRepository.cs
167
+ using Microsoft.EntityFrameworkCore;
168
+ using Solution.Domain.Products;
169
+
170
+ namespace Solution.Infrastructure.Persistence;
171
+
172
+ public sealed class ProductRepository(AppDbContext db) : IProductRepository
173
+ {
174
+ public Task<Product?> GetAsync(Guid id, CancellationToken ct)
175
+ => db.Products.AsNoTracking().FirstOrDefaultAsync(p => p.Id == id, ct);
176
+
177
+ public async Task SaveAsync(Product product, CancellationToken ct)
178
+ {
179
+ if (await db.Products.AnyAsync(p => p.Id == product.Id, ct))
180
+ db.Products.Update(product);
181
+ else
182
+ db.Products.Add(product);
183
+ await db.SaveChangesAsync(ct);
184
+ }
185
+ }
186
+ ```
187
+
188
+ **Rule**: `AsNoTracking()` for read-only queries (perf). `SaveChangesAsync` always with CancellationToken. UUID primary key.
189
+
190
+ ### Minimal API endpoint (ASP.NET Core 9 canonical)
191
+
192
+ ```csharp
193
+ // src/Solution.Api/Endpoints/Products.cs
194
+ using Solution.Application.Products;
195
+
196
+ namespace Solution.Api.Endpoints;
197
+
198
+ public static class ProductEndpoints
199
+ {
200
+ public record CreateProductRequest(string Name, decimal Price, int Stock);
201
+ public record ProductResponse(Guid Id, string Name, decimal Price, int Stock, DateTimeOffset CreatedAt);
202
+
203
+ public static void MapProductEndpoints(this IEndpointRouteBuilder app)
204
+ {
205
+ var group = app.MapGroup("/api/v1/products").WithTags("Products");
206
+
207
+ group.MapPost("/", async (
208
+ CreateProductRequest req,
209
+ CreateProductHandler handler,
210
+ CancellationToken ct) =>
211
+ {
212
+ var product = await handler.HandleAsync(req.Name, req.Price, req.Stock, ct);
213
+ return Results.Created(
214
+ $"/api/v1/products/{product.Id}",
215
+ new ProductResponse(product.Id, product.Name, product.Price, product.Stock, product.CreatedAt));
216
+ })
217
+ .WithName("CreateProduct")
218
+ .Produces<ProductResponse>(StatusCodes.Status201Created)
219
+ .ProducesValidationProblem(StatusCodes.Status422UnprocessableEntity);
220
+ }
221
+ }
222
+ ```
223
+
224
+ ```csharp
225
+ // src/Solution.Api/Program.cs
226
+ using Solution.Infrastructure.Persistence;
227
+ using Solution.Application.Products;
228
+ using Microsoft.EntityFrameworkCore;
229
+
230
+ var builder = WebApplication.CreateBuilder(args);
231
+
232
+ // Aspire ServiceDefaults: telemetry, health checks, service discovery, resilience
233
+ builder.AddServiceDefaults();
234
+
235
+ // EF Core + connection string via Aspire reference
236
+ builder.AddNpgsqlDbContext<AppDbContext>("postgresdb");
237
+
238
+ builder.Services.AddScoped<IProductRepository, ProductRepository>();
239
+ builder.Services.AddScoped<CreateProductHandler>();
240
+
241
+ builder.Services.AddProblemDetails();
242
+ builder.Services.AddOpenApi();
243
+
244
+ var app = builder.Build();
245
+
246
+ app.MapDefaultEndpoints(); // /health, /alive, /metrics from ServiceDefaults
247
+ app.UseExceptionHandler(); // converts unhandled to RFC 9457
248
+ app.MapOpenApi();
249
+ app.MapProductEndpoints();
250
+
251
+ app.Run();
252
+ ```
253
+
254
+ **Rule**: minimal APIs are the default (no `Controllers/` folder unless team prefers MVC). `Results.Created/...` returns typed `IResult`. `Produces<>` for OpenAPI. ServiceDefaults pre-wires observability.
255
+
256
+ ### Aspire AppHost orchestration
257
+
258
+ ```csharp
259
+ // src/Solution.AppHost/Program.cs
260
+ var builder = DistributedApplication.CreateBuilder(args);
261
+
262
+ var postgres = builder.AddPostgres("postgres")
263
+ .WithDataVolume()
264
+ .AddDatabase("postgresdb");
265
+
266
+ var api = builder.AddProject<Projects.Solution_Api>("api")
267
+ .WithReference(postgres)
268
+ .WaitFor(postgres);
269
+
270
+ builder.Build().Run();
271
+ ```
272
+
273
+ **Rule**: AppHost is the single source of truth for service topology in dev. `WithReference()` injects connection strings via env vars. `WaitFor()` ensures startup order.
274
+
275
+ ## 5. Testing
276
+
277
+ ### Unit (xUnit + FluentAssertions, no EF / no ASP.NET)
278
+
279
+ ```csharp
280
+ // tests/Solution.Domain.Tests/Products/ProductTests.cs
281
+ using FluentAssertions;
282
+ using Solution.Domain.Products;
283
+
284
+ public class ProductTests
285
+ {
286
+ [Fact]
287
+ public void Reserve_DecreasesStock()
288
+ {
289
+ var p = Product.Create("widget", 9.90m, 10);
290
+ p.Reserve(3);
291
+ p.Stock.Should().Be(7);
292
+ }
293
+
294
+ [Fact]
295
+ public void Reserve_ThrowsOnZero()
296
+ {
297
+ var p = Product.Create("widget", 9.90m, 10);
298
+ var act = () => p.Reserve(0);
299
+ act.Should().Throw<InvalidOperationException>();
300
+ }
301
+ }
302
+ ```
303
+
304
+ ### Integration (WebApplicationFactory + Testcontainers Postgres)
305
+
306
+ ```csharp
307
+ // tests/Solution.Api.IntegrationTests/ProductEndpointsTests.cs
308
+ using FluentAssertions;
309
+ using Microsoft.AspNetCore.Mvc.Testing;
310
+ using Testcontainers.PostgreSql;
311
+
312
+ public class ProductEndpointsTests : IAsyncLifetime
313
+ {
314
+ private readonly PostgreSqlContainer _pg = new PostgreSqlBuilder()
315
+ .WithImage("postgres:16-alpine")
316
+ .Build();
317
+
318
+ private WebApplicationFactory<Program>? _factory;
319
+
320
+ public async Task InitializeAsync()
321
+ {
322
+ await _pg.StartAsync();
323
+ Environment.SetEnvironmentVariable("ConnectionStrings__postgresdb", _pg.GetConnectionString());
324
+ _factory = new WebApplicationFactory<Program>();
325
+ }
326
+
327
+ public async Task DisposeAsync()
328
+ {
329
+ await _pg.DisposeAsync();
330
+ _factory?.Dispose();
331
+ }
332
+
333
+ [Fact]
334
+ public async Task Post_CreatesProduct()
335
+ {
336
+ var client = _factory!.CreateClient();
337
+ var resp = await client.PostAsJsonAsync("/api/v1/products", new {
338
+ Name = "widget", Price = 9.90m, Stock = 100,
339
+ });
340
+ resp.StatusCode.Should().Be(HttpStatusCode.Created);
341
+ }
342
+ }
343
+ ```
344
+
345
+ **Rule**: NEVER `UseInMemoryDatabase()` if prod is Postgres. Testcontainers .NET 4.0+ is the standard.
346
+
347
+ ### Mutation (Stryker.NET)
348
+
349
+ ```bash
350
+ dotnet stryker --project Solution.Domain --threshold-high 80 --threshold-low 70 --threshold-break 60
351
+ # Target: mutation score >= 70% on Domain + Application
352
+ ```
353
+
354
+ ## 6. Build & run commands
355
+
356
+ ```bash
357
+ # Setup (install workload once per machine)
358
+ dotnet workload install aspire
359
+
360
+ # Restore
361
+ dotnet restore
362
+
363
+ # Build (with warnings as errors)
364
+ dotnet build -p:TreatWarningsAsErrors=true
365
+
366
+ # Run via Aspire AppHost (boots all services + Dev Dashboard)
367
+ dotnet run --project src/Solution.AppHost
368
+
369
+ # Run a single service directly (skips Aspire orchestration)
370
+ dotnet run --project src/Solution.Api
371
+
372
+ # Migrations (EF Core)
373
+ dotnet ef migrations add InitialCreate --project src/Solution.Infrastructure --startup-project src/Solution.Api
374
+ dotnet ef database update --project src/Solution.Infrastructure --startup-project src/Solution.Api
375
+
376
+ # Tests
377
+ dotnet test # all
378
+ dotnet test --collect:"XPlat Code Coverage" # with coverage
379
+ dotnet test --filter "Category!=Integration" # fast loop
380
+
381
+ # Mutation
382
+ dotnet stryker
383
+
384
+ # Lint (Roslyn analyzers built in; surface via build)
385
+ dotnet format --verify-no-changes # CI gate
386
+
387
+ # Security scan
388
+ dotnet list package --vulnerable --include-transitive
389
+ dotnet list package --outdated
390
+
391
+ # Native AOT publish (when applicable)
392
+ dotnet publish src/Solution.Api -c Release -r linux-x64 -p:PublishAot=true
393
+
394
+ # Aspire manifest export (for K8s/Azure deployment)
395
+ dotnet run --project src/Solution.AppHost --publisher manifest --output-path ./aspire-manifest.json
396
+ ```
397
+
398
+ ## 7. Security (per ADR-007 + ADR-027 — MANDATORY section)
399
+
400
+ ### 7.1 Authentication & Authorization
401
+
402
+ - **JWT Bearer**: `Microsoft.AspNetCore.Authentication.JwtBearer`. Configure validation parameters (issuer, audience, signing key, lifetime). RS256 preferred.
403
+ - **Identity**: `Microsoft.AspNetCore.Identity` for user management; ASP.NET Core 9 ships `MapIdentityApi<T>()` for minimal-API friendly endpoints.
404
+ - **OAuth2 / OIDC**: `Microsoft.AspNetCore.Authentication.OpenIdConnect` for Auth0/Entra/Keycloak.
405
+ - **Password hashing**: ASP.NET Core Identity uses PBKDF2 by default. For new projects consider switching to Argon2 via `BCrypt.Net-Next` or `Konscious.Security.Cryptography.Argon2`.
406
+ - **Authorization**: `[Authorize(Policy = "...")]` attributes; minimal APIs use `.RequireAuthorization("policy")`. Object-level checks inside the handler.
407
+
408
+ ### 7.2 CORS
409
+
410
+ ```csharp
411
+ builder.Services.AddCors(opt => opt.AddPolicy("Default", p =>
412
+ p.WithOrigins("https://app.example.com") // NEVER WithAnyOrigin() in prod
413
+ .AllowCredentials()
414
+ .WithMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
415
+ .WithHeaders("Authorization", "Content-Type")));
416
+
417
+ app.UseCors("Default");
418
+ ```
419
+
420
+ ### 7.3 Validation & input sanitization
421
+
422
+ - **SQL injection**: EF Core parameterizes by default. NEVER `FromSqlRaw($"... {input}")` — use `FromSqlInterpolated($"... {input}")` (parameterizes) OR `FromSqlRaw` with parameters separately.
423
+ - **DataAnnotations or FluentValidation**: required on every request DTO. `[Required]`, `[MaxLength]`, `[Range]`, etc.
424
+ - **Path traversal**: `Path.GetFullPath` + `StartsWith` check against an allowlisted base directory.
425
+ - **Antiforgery**: `app.UseAntiforgery()` if you serve form-based POSTs; not needed for stateless JWT APIs.
426
+
427
+ ### 7.4 Secrets management
428
+
429
+ - **dev**: `dotnet user-secrets` (per-user, not in source).
430
+ - **prod**: Azure Key Vault / AWS Secrets Manager / HashiCorp Vault via `Microsoft.Extensions.Configuration.<Provider>`.
431
+ - **Aspire**: AppHost wires connection strings via `WithReference()` — secrets surface via configuration, not env vars in code.
432
+ - Never `appsettings.json` with real secrets. `appsettings.json` committed; `appsettings.Production.json` typically NOT committed.
433
+
434
+ ### 7.5 Rate limiting
435
+
436
+ ASP.NET Core 9 has built-in rate limiting:
437
+
438
+ ```csharp
439
+ builder.Services.AddRateLimiter(opt =>
440
+ {
441
+ opt.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(ctx =>
442
+ RateLimitPartition.GetFixedWindowLimiter(
443
+ partitionKey: ctx.Connection.RemoteIpAddress?.ToString() ?? "anon",
444
+ factory: _ => new FixedWindowRateLimiterOptions {
445
+ PermitLimit = 100, Window = TimeSpan.FromMinutes(1) }));
446
+
447
+ opt.AddPolicy("auth", ctx => RateLimitPartition.GetFixedWindowLimiter(
448
+ ctx.Connection.RemoteIpAddress?.ToString() ?? "anon",
449
+ _ => new FixedWindowRateLimiterOptions {
450
+ PermitLimit = 5, Window = TimeSpan.FromMinutes(1) }));
451
+ });
452
+
453
+ app.UseRateLimiter();
454
+ // Per-endpoint: .RequireRateLimiting("auth")
455
+ ```
456
+
457
+ ### 7.6 OWASP Top 10 mapping
458
+
459
+ | OWASP | Mitigation in .NET 9 + ASP.NET Core |
460
+ |---|---|
461
+ | A01 Broken Access Control | `[Authorize]` + policy-based auth; object-level check in handler; default-deny via `[FallbackAuth]` |
462
+ | A02 Cryptographic Failures | PBKDF2 (Identity default) or Argon2 password hashing; TLS at reverse proxy; HSTS via `app.UseHsts()`; JWT RS256 with key rotation |
463
+ | A03 Injection | EF Core parameterization; `FromSqlInterpolated` (safe) vs `FromSqlRaw` (review); DataAnnotations/FluentValidation on every DTO |
464
+ | A04 Insecure Design | DDD layering enforced (`Domain` no EF/ASP.NET); use case handler = single async method; ADRs document deviations |
465
+ | A05 Security Misconfiguration | `app.UseHsts()`; `Server` header removed; CORS explicit allowlist; `ASPNETCORE_ENVIRONMENT=Production` |
466
+ | A06 Vulnerable Components | `dotnet list package --vulnerable` in CI; Dependabot for `.csproj` bumps; `Directory.Packages.props` for central version mgmt |
467
+ | A07 Auth Failures | Built-in rate limiter on auth endpoints; account lockout via Identity options; MFA via `Microsoft.AspNetCore.Identity.Extensions` |
468
+ | A08 Data Integrity | `nuget.config` with verified package sources; SBOM via `dotnet-sbom-tool`; signed assemblies for internal libs |
469
+ | A09 Logging Failures | Serilog or built-in `Microsoft.Extensions.Logging` with JSON formatter; correlation ID via `AddOpenTelemetry()`; **NEVER** log request body raw (PII) |
470
+ | A10 SSRF | Centralized `IHttpClientFactory` with named clients + delegating handler that allowlists outbound hosts; reject private CIDRs by default |
471
+
472
+ ### 7.7 LGPD / GDPR / compliance specifics
473
+
474
+ - **PII tagging**: attribute `[PersonalData]` (from ASP.NET Identity) on entity properties; custom Roslyn analyzer flags missing tags on string properties named Email/Document/CPF.
475
+ - **Soft delete**: `IsDeleted` + `DeletedAt` columns; global query filter in `OnModelCreating` excludes deleted rows. NEVER hard delete user-owned data.
476
+ - **Data subject access (Art 15)**: `GET /api/v1/me/export` returns user data as ZIP (use ASP.NET Identity's built-in personal data export).
477
+ - **Erasure (Art 17)**: `DELETE /api/v1/me` redacts PII fields while preserving FKs.
478
+ - **Encryption at rest**: SQL Server TDE / PostgreSQL TDE (cloud-managed) OR column-level via `Microsoft.AspNetCore.DataProtection` for sensitive fields.
479
+
480
+ ## 8. Anti-patterns (block in code-review)
481
+
482
+ | ❌ Bad | ✅ Good | Why |
483
+ |---|---|---|
484
+ | `double` or `float` for money | `decimal` | IEEE 754 precision loss is real |
485
+ | `DbContext` injected as singleton | `AddDbContext<>` (scoped — default) | `DbContext` is not thread-safe |
486
+ | `.Result` or `.Wait()` on Tasks | `await` everywhere | Deadlocks under ASP.NET sync context; lost exceptions |
487
+ | `FromSqlRaw($"... {input}")` (interpolated string) | `FromSqlInterpolated($"... {input}")` | The Raw + interpolated combo is SQL injection; Interpolated parameterizes |
488
+ | Public setters on domain entity properties | Private setters + business methods | Encapsulation breaks otherwise |
489
+ | `UseInMemoryDatabase()` for integration tests | Testcontainers Postgres / SQL Server | InMemory has DIFFERENT semantics (no FK constraints, etc.) |
490
+ | `Controller` everywhere when minimal APIs fit | Minimal APIs for simple CRUD; controllers only when MVC features needed | Less ceremony, less code |
491
+ | `Task<ActionResult<T>>` from minimal API | `Results.Ok(...)` / `Results.Created(...)` | Minimal APIs use `IResult`, not `ActionResult` |
492
+ | Ignoring `CancellationToken` in async signatures | Accept + pass `CancellationToken` everywhere | Caller cancellation must propagate to DB / HTTP |
493
+ | `JsonSerializer.Serialize(...)` without options | Configure `JsonOptions` once (camelCase, ignore nulls, etc.) | Inconsistent serialization across endpoints |
494
+ | Catching `Exception` in handler | Let global exception handler do it; catch specific domain exceptions | Hides bugs, breaks 500 telemetry |
495
+ | `appsettings.json` with secrets | User secrets in dev, Key Vault in prod | Secrets in repo = secrets in git history |
496
+ | Native AOT for solutions using reflection-heavy libs | Trim warnings audited; switch lib OR keep JIT | Runtime crash discovered late |
497
+
498
+ ## 9. Migration hints — .NET 8 / Aspire preview → 9
499
+
500
+ Breaking changes worth flagging when `migrator` agent runs .NET 8 → 9 + Aspire 1.x → 9:
501
+
502
+ - **.NET 9 SDK required**; downgrading targetFramework to net8.0 still works but new APIs unavailable.
503
+ - **Aspire workload re-install**: `dotnet workload update` after .NET 9 SDK install.
504
+ - **`Aspire.Hosting.AppHost` package renamed/reorganized** — review AppHost `using` directives.
505
+ - **OpenAPI**: ASP.NET 9 ships `Microsoft.AspNetCore.OpenApi` natively. Remove Swashbuckle if you only used basic Swagger UI; keep Swashbuckle if you used its extensibility heavily.
506
+ - **EF Core 9 bulk operations**: `ExecuteUpdate` / `ExecuteDelete` work with complex predicates now. Audit places where you wrote manual batching loops.
507
+ - **`IExceptionHandler`** interface (NET 8+) is the canonical exception hook — replaces `UseExceptionHandler(builder => ...)` patterns from earlier.
508
+ - **Rate limiting** moved out of preview (built-in since .NET 7); remove `Microsoft.AspNetCore.RateLimiting` package if you added it manually.
509
+ - **Native AOT** broader compat: many libs that didn't AOT in .NET 8 now do. Retry compilation if you backed off before.
510
+ - **`PublishAot`** sets multiple sub-properties; review project file for redundant manual settings.
511
+
512
+ Hand off to `migrator` with: current .NET version, Aspire version, list of AppHost dependencies, list of services using deprecated APIs.
513
+
514
+ ## 10. References
515
+
516
+ - [.NET Aspire documentation](https://learn.microsoft.com/en-us/dotnet/aspire/)
517
+ - [ASP.NET Core 9 docs](https://learn.microsoft.com/en-us/aspnet/core/)
518
+ - [EF Core 9 docs](https://learn.microsoft.com/en-us/ef/core/)
519
+ - [Minimal APIs guide](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis)
520
+ - [Polly resilience library](https://www.thepollyproject.org/)
521
+ - [Testcontainers .NET](https://dotnet.testcontainers.org/)
522
+ - [Stryker.NET mutation testing](https://stryker-mutator.io/docs/stryker-net/introduction/)
523
+ - [OWASP .NET Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html)
524
+ - [ASP.NET Core security overview](https://learn.microsoft.com/en-us/aspnet/core/security/)
525
+ - ADR-007 (Senior+ gate thresholds — coverage ≥85%, mutation ≥70%)
526
+ - ADR-026 (Generic agents + stack packs architecture)
527
+ - ADR-027 (Pack governance — frontmatter + security mandatory + CODEOWNERS + annual review)
528
+ - ADR-029 (Canonical pack format — this document follows it)