claude-code-arcane 1.1.1 → 1.3.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 (61) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -0
  3. package/agents/engineering/dotnet-engineer.md +78 -0
  4. package/dist/cli.js +284 -72
  5. package/docs/RELEASE-SETUP.md +99 -0
  6. package/docs/SKILLS-CATALOG.md +26 -3
  7. package/docs/presentations/arcane-overview-12.pptx +0 -0
  8. package/docs/presentations/arcane-overview.pptx +0 -0
  9. package/docs/presentations/build_arcane_deck.py +310 -0
  10. package/docs/presentations/build_arcane_deck_12.py +399 -0
  11. package/package.json +1 -1
  12. package/profiles/backend-dotnet.yaml +54 -0
  13. package/profiles/job-hunt.yaml +35 -0
  14. package/profiles/unity-design.yaml +1 -0
  15. package/profiles/unity-dev.yaml +1 -0
  16. package/rules/dotnet-code.md +64 -0
  17. package/skills/cold-outreach/SKILL.md +65 -0
  18. package/skills/cold-outreach/references/recruiter-playbook.md +65 -0
  19. package/skills/cover-letter/SKILL.md +66 -0
  20. package/skills/cv-ats-export/SKILL.md +64 -0
  21. package/skills/cv-ats-export/scripts/cv_export.py +306 -0
  22. package/skills/cv-tailor/SKILL.md +70 -0
  23. package/skills/cv-tailor/references/ats-keywords.md +46 -0
  24. package/skills/dotnet-architecture/SKILL.md +66 -0
  25. package/skills/dotnet-architecture/references/anti-patterns.md +12 -0
  26. package/skills/dotnet-architecture/references/checklist.md +19 -0
  27. package/skills/dotnet-architecture/references/patterns.md +118 -0
  28. package/skills/dotnet-architecture/references/project-structure.md +78 -0
  29. package/skills/dotnet-best-practices/SKILL.md +76 -0
  30. package/skills/dotnet-best-practices/references/api-design.md +75 -0
  31. package/skills/dotnet-best-practices/references/architecture.md +62 -0
  32. package/skills/dotnet-best-practices/references/async.md +62 -0
  33. package/skills/dotnet-best-practices/references/database.md +69 -0
  34. package/skills/dotnet-best-practices/references/dependency-injection.md +73 -0
  35. package/skills/dotnet-best-practices/references/devops.md +76 -0
  36. package/skills/dotnet-best-practices/references/error-handling.md +72 -0
  37. package/skills/dotnet-best-practices/references/performance.md +63 -0
  38. package/skills/dotnet-best-practices/references/security.md +73 -0
  39. package/skills/dotnet-best-practices/references/testing.md +76 -0
  40. package/skills/dotnet-scaffold/SKILL.md +99 -0
  41. package/skills/install-mcp/SKILL.md +107 -0
  42. package/skills/install-mcp/references/manual-setup.md +92 -0
  43. package/skills/interview-prep/SKILL.md +69 -0
  44. package/skills/interview-prep/references/star-framework.md +42 -0
  45. package/skills/job-hunt/SKILL.md +92 -0
  46. package/skills/job-hunt/references/templates/Aplicacion.md +48 -0
  47. package/skills/job-hunt/references/templates/CV Custom.md +53 -0
  48. package/skills/job-hunt/references/templates/Contacto.md +30 -0
  49. package/skills/job-hunt/references/templates/Dashboard.md +45 -0
  50. package/skills/job-hunt/references/templates/Empresa.md +36 -0
  51. package/skills/job-hunt/references/templates/Entrevista.md +44 -0
  52. package/skills/job-hunt/references/templates/Perfil.md +38 -0
  53. package/skills/job-search/SKILL.md +83 -0
  54. package/skills/job-search/references/scoring-rubric.md +43 -0
  55. package/skills/linkedin-optimize/SKILL.md +79 -0
  56. package/skills/master-profile/SKILL.md +69 -0
  57. package/skills/network-map/SKILL.md +61 -0
  58. package/skills/network-map/scripts/network_map.py +109 -0
  59. package/skills/personal-brand/SKILL.md +54 -0
  60. package/skills/personal-brand/references/post-pillars.md +66 -0
  61. package/skills/portfolio-site/SKILL.md +59 -0
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: dotnet-architecture
3
+ description: "Arquitectura de backends .NET: Vertical Slice Architecture (features) y Clean Architecture (capas), con guía de cuándo usar cada una, estructura, patrones (MediatR/Result/outbox) y anti-patterns. Usar al diseñar o revisar la estructura de un proyecto ASP.NET Core."
4
+ category: "backend"
5
+ argument-hint: "[vertical-slice|clean|when-to-use]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
8
+ ---
9
+ # dotnet-architecture — VSA & Clean Architecture para .NET
10
+
11
+ Dos enfoques production-ready para estructurar un backend ASP.NET Core. No son excluyentes: muchos proyectos
12
+ usan Clean Architecture en el borde y slices verticales adentro. Esta skill ayuda a **elegir** y a aplicar
13
+ cada uno correctamente.
14
+
15
+ ## MANDATORY WORKFLOW
16
+
17
+ **No imponer arquitectura por moda. Elegir según complejidad del dominio y vida del proyecto.**
18
+
19
+ ### Step 0: Elegir enfoque (`when-to-use`)
20
+
21
+ | Señal | Recomendación |
22
+ |-------|---------------|
23
+ | CRUD, prototipos, APIs chicas, dominio fino, equipo chico | **Vertical Slice** (o single project) |
24
+ | Dominio complejo, reglas de negocio ricas, long-lived, varios equipos | **Clean Architecture** |
25
+ | Necesitás aislar el dominio de frameworks/DB para test y reemplazo | **Clean Architecture** |
26
+ | Querés velocidad y bajo overhead de indirección | **Vertical Slice** |
27
+
28
+ > Evitá Clean Architecture para CRUD simple: los proyectos extra y la indirección cuestan más de lo que aportan
29
+ > cuando la lógica de negocio es delgada. Empezá con Vertical Slice y migrá si el dominio crece.
30
+
31
+ ### Step 1: Aplicar la estructura
32
+
33
+ - **Vertical Slice:** un folder por feature/use-case; cada slice tiene su request, handler, validator y endpoint juntos. Ver `references/project-structure.md`.
34
+ - **Clean Architecture:** proyectos `Domain → Application → Infrastructure → Api`; las dependencias apuntan **hacia adentro**, nunca al revés. Ver `references/project-structure.md`.
35
+
36
+ ### Step 2: Patrones idiomáticos
37
+
38
+ Leer `references/patterns.md`: MediatR/handlers por use-case, `IApplicationDbContext` (exponer EF Core sin repos
39
+ genéricos), pipeline behaviors (validación/logging/transacción), Result pattern en vez de excepciones para flujo,
40
+ y outbox pattern para consistencia entre DB y eventos.
41
+
42
+ ### Step 3: Revisar contra anti-patterns + checklist
43
+
44
+ - `references/anti-patterns.md` — repos genéricos sobre EF Core, fat controllers, dependencias que apuntan hacia afuera, lógica de dominio en Infrastructure.
45
+ - `references/checklist.md` — verificación de cierre antes de dar la estructura por buena.
46
+
47
+ Antes de aplicar una reestructuración significativa, confirmar el approach con el usuario (Question → Decision → Approval). Si todo el checklist pasa → arquitectura **COMPLIANT**.
48
+
49
+ ## Resumen de enfoques
50
+
51
+ | Aspecto | Vertical Slice | Clean Architecture |
52
+ |---------|----------------|--------------------|
53
+ | Eje de organización | Feature / use-case | Capa técnica |
54
+ | Proyectos | 1 (+ tests) | 4 (Domain/App/Infra/Api) |
55
+ | Acoplamiento entre features | Bajo (slices independientes) | Mediado por Application |
56
+ | Overhead inicial | Bajo | Alto |
57
+ | Mejor para | CRUD, features, velocidad | Dominios complejos, long-lived |
58
+ | Riesgo | Duplicación entre slices | Sobre-ingeniería |
59
+
60
+ ## Próximos pasos
61
+
62
+ Definida la arquitectura → `/dotnet-scaffold` para generar el proyecto, y `/dotnet-best-practices` para revisar el código contra las 40 reglas.
63
+
64
+ ---
65
+
66
+ _Inspirado en [ardalis/CleanArchitecture](https://github.com/ardalis/CleanArchitecture), [nadirbad/VerticalSliceArchitecture](https://github.com/nadirbad/VerticalSliceArchitecture) y la guía de Vertical Slice de [Milan Jovanović](https://www.milanjovanovic.tech/blog/vertical-slice-architecture-dotnet). Adaptado al formato Arcane._
@@ -0,0 +1,12 @@
1
+ # Anti-Patterns
2
+
3
+ - **`IRepository<T>` / `IUnitOfWork` genérico sobre EF Core** — el `DbContext` ya es repositorio (`DbSet<>`) y Unit of Work (`SaveChangesAsync`); envolverlo agrega indirección sin valor y te tapa LINQ/Include/proyecciones.
4
+ - **Fat controllers / endpoints con lógica de negocio** — el endpoint solo traduce HTTP ↔ request y mapea el resultado; las reglas van en el handler/dominio, no en el controlador.
5
+ - **Dependencias apuntando hacia afuera** — `Domain` referenciando `Infrastructure` o EF Core rompe la regla de dependencias: el dominio deja de ser testeable y reemplazable.
6
+ - **Application anémica que solo reenvía a repos** — si el handler no hace más que `return repo.Get(id)`, la capa no aporta nada; movés lógica de negocio real al dominio/handler o eliminás la indirección.
7
+ - **Filtrar entidades EF como respuestas de API** — exponer entidades de persistencia acopla el contrato HTTP al schema de la DB y arrastra lazy-loading/ciclos; devolvé DTOs en el borde.
8
+ - **Forzar Clean Architecture en un CRUD simple** — cuatro proyectos y pipeline behaviors para listar y guardar registros es sobre-ingeniería; empezá con Vertical Slice o single project.
9
+ - **Service locator estático / `BuildServiceProvider()` en `Program.cs`** — construir el provider a mano crea grafos duplicados y dependencias ocultas; registrá todo en el contenedor y dejá que resuelva por constructor.
10
+ - **Un `Services/` y `Controllers/` gigantes** — organizar por tipo técnico dispersa cada feature en cinco carpetas; organizá por feature/slice para que un caso de uso viva junto.
11
+
12
+ _Ref: https://www.milanjovanovic.tech/blog/vertical-slice-architecture-dotnet · https://github.com/ardalis/CleanArchitecture · https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/_
@@ -0,0 +1,19 @@
1
+ # Checklist
2
+
3
+ Verificá antes de dar la estructura por buena.
4
+
5
+ - [ ] Las dependencias apuntan hacia adentro: `Api → Infrastructure → Application → Domain`
6
+ - [ ] `Domain` no tiene `<ProjectReference>` ni paquetes de framework (sin EF, sin ASP.NET)
7
+ - [ ] No hay `IRepository<T>` / `IUnitOfWork` genérico envolviendo EF Core
8
+ - [ ] EF Core se expone vía `IApplicationDbContext` en Application, implementado en Infrastructure
9
+ - [ ] En Vertical Slice, cada slice es autocontenido (request + handler + validator + endpoint)
10
+ - [ ] Los slices no dependen entre sí (acoplamiento solo vía `Common`/`Infrastructure`)
11
+ - [ ] DTOs en el borde: ninguna entidad EF se devuelve como respuesta de API
12
+ - [ ] Cada handler tiene una sola responsabilidad (un caso de uso por archivo)
13
+ - [ ] `Program.cs` es solo composition root (DI + pipeline + map endpoints), sin lógica de negocio
14
+ - [ ] La validación corre en un pipeline behavior, no repetida en cada endpoint
15
+ - [ ] Fallos esperados se modelan con `Result<T>`/`ErrorOr`, no con excepciones de flujo
16
+ - [ ] Las migraciones EF Core están versionadas en git
17
+ - [ ] El enfoque elegido (VSA vs Clean) corresponde a la complejidad real del dominio
18
+
19
+ _Ref: https://github.com/ardalis/CleanArchitecture · https://github.com/nadirbad/VerticalSliceArchitecture · https://www.milanjovanovic.tech/blog/clean-architecture-folder-structure_
@@ -0,0 +1,118 @@
1
+ # Patrones Idiomáticos (.NET 10 / ASP.NET Core)
2
+
3
+ ## Handler por caso de uso
4
+
5
+ Un request (record) + un handler. Con MediatR o con un sender hand-rolled mínimo.
6
+
7
+ ```csharp
8
+ public sealed record CreateOrder(Guid CustomerId, decimal Amount) : IRequest<Result<Guid>>;
9
+
10
+ internal sealed class CreateOrderHandler(IApplicationDbContext db)
11
+ : IRequestHandler<CreateOrder, Result<Guid>>
12
+ {
13
+ public async Task<Result<Guid>> Handle(CreateOrder cmd, CancellationToken ct)
14
+ {
15
+ var order = Order.Create(cmd.CustomerId, cmd.Amount);
16
+ db.Orders.Add(order);
17
+ await db.SaveChangesAsync(ct);
18
+ return order.Id;
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## IApplicationDbContext (EF Core detrás de una interfaz, sin repo genérico)
24
+
25
+ Exponé el `DbContext` a través de una interfaz en `Application`; implementala en
26
+ `Infrastructure`. El `DbContext` ya es Unit of Work + repositorio: no lo envuelvas.
27
+
28
+ ```csharp
29
+ // Application/Common/IApplicationDbContext.cs
30
+ public interface IApplicationDbContext
31
+ {
32
+ DbSet<Order> Orders { get; }
33
+ DbSet<Customer> Customers { get; }
34
+ Task<int> SaveChangesAsync(CancellationToken ct = default);
35
+ }
36
+
37
+ // Infrastructure/Persistence/AppDbContext.cs
38
+ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
39
+ : DbContext(options), IApplicationDbContext
40
+ {
41
+ public DbSet<Order> Orders => Set<Order>();
42
+ public DbSet<Customer> Customers => Set<Customer>();
43
+ }
44
+ ```
45
+
46
+ ## Pipeline behaviors (validación, logging, transacción)
47
+
48
+ Cross-cutting concerns alrededor de cada handler, sin ensuciar la lógica.
49
+
50
+ ```csharp
51
+ public sealed class ValidationBehavior<TReq, TRes>(IEnumerable<IValidator<TReq>> validators)
52
+ : IPipelineBehavior<TReq, TRes> where TReq : notnull
53
+ {
54
+ public async Task<TRes> Handle(TReq req, RequestHandlerDelegate<TRes> next, CancellationToken ct)
55
+ {
56
+ foreach (var v in validators)
57
+ {
58
+ var result = await v.ValidateAsync(req, ct);
59
+ if (!result.IsValid) throw new ValidationException(result.Errors);
60
+ }
61
+ return await next();
62
+ }
63
+ }
64
+ ```
65
+
66
+ Un `TransactionBehavior` similar abre `BeginTransactionAsync` antes de `next()` y
67
+ hace commit/rollback alrededor: el UnitOfWork vive acá, no en repos a mano.
68
+
69
+ ## Result pattern para fallos esperados
70
+
71
+ No tires excepciones para flujo de negocio esperado (not found, validación, conflicto).
72
+ Devolvé `Result<T>` (o `ErrorOr<T>`) y mapealo a HTTP en el endpoint.
73
+
74
+ ```csharp
75
+ public readonly record struct Error(string Code, string Message);
76
+
77
+ public async Task<Result<OrderDto>> Handle(GetOrder q, CancellationToken ct)
78
+ {
79
+ var order = await db.Orders.FindAsync([q.Id], ct);
80
+ return order is null
81
+ ? new Error("Order.NotFound", "Order no existe")
82
+ : order.ToDto();
83
+ }
84
+ ```
85
+
86
+ ## Outbox pattern (consistencia DB + eventos)
87
+
88
+ Para publicar eventos de forma confiable: persistí el evento en una tabla `Outbox`
89
+ **dentro de la misma transacción** que el cambio de datos. Un background worker
90
+ (`BackgroundService`) lee la tabla y publica al broker; así nunca queda el estado
91
+ guardado sin el evento ni el evento sin el estado.
92
+
93
+ ## TypedResults en minimal API endpoints
94
+
95
+ El endpoint es delgado: traduce HTTP ↔ request y mapea el `Result` a status code.
96
+
97
+ ```csharp
98
+ public static class OrderEndpoints
99
+ {
100
+ public static void Map(this IEndpointRouteBuilder app)
101
+ {
102
+ var group = app.MapGroup("/orders").WithTags("Orders");
103
+
104
+ group.MapPost("/", async (CreateOrder cmd, ISender sender, CancellationToken ct) =>
105
+ {
106
+ var result = await sender.Send(cmd, ct);
107
+ return result.IsError
108
+ ? TypedResults.Problem(result.FirstError.Message)
109
+ : TypedResults.Created($"/orders/{result.Value}", result.Value);
110
+ });
111
+ }
112
+ }
113
+ ```
114
+
115
+ `TypedResults` da el tipo de respuesta exacto (útil para OpenAPI y tests) en vez del
116
+ `Results` no tipado.
117
+
118
+ _Ref: https://www.milanjovanovic.tech/blog/internal-vs-public-apis-in-clean-architecture · https://github.com/ardalis/CleanArchitecture · https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/responses_
@@ -0,0 +1,78 @@
1
+ # Estructura de Directorios
2
+
3
+ Dos formas válidas de estructurar un backend .NET 10 / ASP.NET Core. Elegí según
4
+ complejidad del dominio (ver `when-to-use` en la SKILL).
5
+
6
+ ## Vertical Slice Architecture (un proyecto)
7
+
8
+ Cada feature es un folder autocontenido: request, handler, validator y endpoint
9
+ viven juntos. Tocás un caso de uso → tocás un solo archivo/carpeta.
10
+
11
+ ```
12
+ src/
13
+ Features/
14
+ Orders/
15
+ CreateOrder.cs ← record Command + Handler + Validator juntos
16
+ GetOrders.cs ← record Query + Handler (paginación/proyección)
17
+ OrderEndpoints.cs ← MapGroup("/orders") + TypedResults
18
+ Customers/
19
+ RegisterCustomer.cs
20
+ CustomerEndpoints.cs
21
+ Common/
22
+ Behaviors/ ← ValidationBehavior, LoggingBehavior, TxBehavior
23
+ Result.cs ← Result<T> / Error compartido
24
+ Infrastructure/
25
+ AppDbContext.cs ← EF Core DbContext (DbSet<> por entidad)
26
+ Migrations/ ← migraciones versionadas en git
27
+ Program.cs ← composition root: DI, pipeline, MapEndpoints
28
+ appsettings.json
29
+ ```
30
+
31
+ - Cada slice depende de `Common` e `Infrastructure`, no de otros slices.
32
+ - Un slice nuevo = un archivo nuevo; borrarlo no rompe a los demás (bajo acoplamiento).
33
+ - El `DbContext` es compartido; cada handler usa solo los `DbSet<>` que necesita.
34
+
35
+ ## Clean Architecture (cuatro proyectos)
36
+
37
+ Separación por capa técnica. Las dependencias apuntan **hacia adentro**: el dominio
38
+ no conoce a nadie; la infraestructura y la API dependen de las capas internas.
39
+
40
+ ```
41
+ src/
42
+ MyApp.Domain/ ← Entidades, value objects, domain events, reglas
43
+ Entities/Order.cs ← cero deps externas (ni EF, ni ASP.NET)
44
+ ValueObjects/Money.cs
45
+ Common/Entity.cs
46
+ MyApp.Application/ ← Casos de uso (handlers), DTOs, interfaces
47
+ Orders/CreateOrder.cs
48
+ Common/IApplicationDbContext.cs ← expone DbSet<> + SaveChangesAsync
49
+ Common/Behaviors/
50
+ MyApp.Infrastructure/ ← EF Core, Identity, servicios externos
51
+ Persistence/AppDbContext.cs ← : DbContext, IApplicationDbContext
52
+ Persistence/Migrations/
53
+ Services/EmailSender.cs
54
+ DependencyInjection.cs
55
+ MyApp.Api/ ← Endpoints, Program.cs, wiring de DI
56
+ Endpoints/OrderEndpoints.cs
57
+ Program.cs
58
+ ```
59
+
60
+ ### Dirección de las referencias (`.csproj`)
61
+
62
+ ```
63
+ MyApp.Api → referencia → MyApp.Infrastructure, MyApp.Application
64
+ MyApp.Infrastructure → referencia → MyApp.Application
65
+ MyApp.Application → referencia → MyApp.Domain
66
+ MyApp.Domain → referencia → (NADA)
67
+ ```
68
+
69
+ - `Domain.csproj` no tiene ningún `<ProjectReference>` ni paquete de framework.
70
+ - `Application` define `IApplicationDbContext`; `Infrastructure` lo implementa.
71
+ Así la lógica de negocio depende de una abstracción, no de EF Core directamente.
72
+ - La inversión de dependencias se logra en `Api/Program.cs`, que es el único lugar
73
+ que conoce a las cuatro capas y arma el grafo de DI.
74
+
75
+ > Regla de oro: si una flecha de dependencia apunta hacia afuera del dominio, la
76
+ > arquitectura está rota. El dominio es el centro estable.
77
+
78
+ _Ref: https://github.com/ardalis/CleanArchitecture · https://github.com/nadirbad/VerticalSliceArchitecture · https://www.milanjovanovic.tech/blog/vertical-slice-architecture-dotnet_
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: dotnet-best-practices
3
+ description: "ASP.NET Core / C# production best practices: 40 reglas en 10 categorías (arquitectura, DI, errores, seguridad, performance, async, EF Core, testing, API, devops) priorizadas por impacto. Usar al escribir/revisar código .NET backend."
4
+ category: "backend"
5
+ argument-hint: "[architecture|di|errors|security|performance|async|database|testing|api|all]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
8
+ ---
9
+ # dotnet-best-practices — Guía priorizada por impacto
10
+
11
+ 40 reglas para aplicaciones ASP.NET Core (.NET 10) production-ready, organizadas en 10 categorías y priorizadas de **CRITICAL** a **LOW-MEDIUM**. Cada regla del catálogo (en `references/`) trae impacto, ejemplo incorrecto, ejemplo correcto y link a docs.
12
+
13
+ ## MANDATORY WORKFLOW
14
+
15
+ **No aplicar las 40 reglas a ciegas. Diagnosticar primero, priorizar por impacto.**
16
+
17
+ ### Step 0: Determinar scope
18
+
19
+ 1. **¿Código nuevo o auditoría de existente?** Si es auditoría, leer primero el feature/proyecto objetivo.
20
+ 2. **¿Qué categoría aplica?** Mapear el área del problema (ver tabla) o usar `all` para review completa.
21
+ 3. **¿Hay síntomas concretos?** (deadlocks, N+1, 500 inconsistentes, leaks) → ir directo a la categoría relevante.
22
+
23
+ ### Step 1: Priorizar por impacto
24
+
25
+ Aplicar en este orden — no bajar de nivel hasta resolver el anterior:
26
+
27
+ | Prioridad | Categoría | Impacto | Reference |
28
+ |-----------|-----------|---------|-----------|
29
+ | 1 | Architecture | CRITICAL | `references/architecture.md` |
30
+ | 2 | Dependency Injection | CRITICAL | `references/dependency-injection.md` |
31
+ | 3 | Async & Concurrency | HIGH | `references/async.md` |
32
+ | 4 | Error Handling | HIGH | `references/error-handling.md` |
33
+ | 5 | Security | HIGH | `references/security.md` |
34
+ | 6 | Performance | HIGH | `references/performance.md` |
35
+ | 7 | Database & EF Core | MEDIUM-HIGH | `references/database.md` |
36
+ | 8 | Testing | MEDIUM-HIGH | `references/testing.md` |
37
+ | 9 | API Design | MEDIUM | `references/api-design.md` |
38
+ | 10 | DevOps & Deployment | LOW-MEDIUM | `references/devops.md` |
39
+
40
+ ### Step 2: Aplicar + verificar
41
+
42
+ Para cada regla relevante: leer el ejemplo correcto en el reference, aplicarlo, y verificar contra el checklist de cierre.
43
+
44
+ ### Step 3: Checklist de cierre
45
+
46
+ - [ ] `<Nullable>enable</Nullable>` + warnings as errors; cero `#nullable disable`
47
+ - [ ] Async end-to-end con `CancellationToken`; cero `.Result`/`.Wait()`/`.GetAwaiter().GetResult()`
48
+ - [ ] DI por constructor con scope correcto; sin `BuildServiceProvider()` manual ni service locator
49
+ - [ ] Errores vía `ProblemDetails` (RFC 7807); sin stack traces expuestos
50
+ - [ ] Sin N+1 en EF Core (`Include`/projection); `AsNoTracking()` en lecturas
51
+ - [ ] Migraciones versionadas; nunca `EnsureCreated()` en prod
52
+ - [ ] AuthZ por policies declarativas; secrets en secret manager (no en `appsettings.json`)
53
+ - [ ] Structured logging (Serilog JSON) + health checks
54
+ - [ ] Tests con xUnit; integración contra Postgres real (Testcontainers), no mocks de DbContext
55
+
56
+ Si todo el checklist pasa → código **COMPLIANT**. Antes de escribir cambios significativos, confirmar el approach con el usuario (Question → Decision → Approval).
57
+
58
+ ## Tabla de mapeo síntoma → categoría
59
+
60
+ | Síntoma | Categoría |
61
+ |---------|-----------|
62
+ | Deadlocks / thread pool starvation / app cuelga | Async, Performance |
63
+ | Lentitud en endpoints, muchas queries | Performance, Database |
64
+ | `InvalidOperationException` de DI / scopes / captured dependency | DI, Architecture |
65
+ | Errores 500 inconsistentes / leaks de stack traces | Error Handling |
66
+ | Endpoints expuestos / brute force / data sensible en logs | Security |
67
+ | Tests frágiles o que pegan a servicios/DB reales sin control | Testing |
68
+ | Respuestas inconsistentes / over-fetching de entidades | API Design |
69
+
70
+ ## Próximos pasos
71
+
72
+ Para estructurar el proyecto → `/dotnet-architecture`. Para iniciar un backend nuevo con estos defaults → `/dotnet-scaffold`.
73
+
74
+ ---
75
+
76
+ _Adaptado de [github/awesome-copilot](https://github.com/github/awesome-copilot) (dotnet-best-practices), [dotnet/skills](https://github.com/dotnet/skills) y [Aaronontheweb/dotnet-skills](https://github.com/Aaronontheweb/dotnet-skills). Reorganizado al formato skill+references de Arcane._
@@ -0,0 +1,75 @@
1
+ # 9. API Design (MEDIUM)
2
+
3
+ ## 9.1 DTOs, Never Expose EF Entities — MEDIUM
4
+
5
+ Serializar entidades de EF filtra columnas internas, arrastra relaciones y acopla el contrato HTTP al schema de la base. Usar DTOs/records dedicados.
6
+
7
+ **Incorrecto:**
8
+ ```csharp
9
+ app.MapGet("/users/{id}", async (int id, AppDbContext db) => await db.Users.FindAsync(id)); // expone la entidad
10
+ ```
11
+ **Correcto:**
12
+ ```csharp
13
+ public record UserDto(int Id, string Email);
14
+ app.MapGet("/users/{id}", async (int id, AppDbContext db) =>
15
+ await db.Users.Where(u => u.Id == id).Select(u => new UserDto(u.Id, u.Email)).FirstOrDefaultAsync());
16
+ ```
17
+
18
+ ## 9.2 API Versioning — MEDIUM
19
+
20
+ Versionar la API permite evolucionar el contrato sin romper clientes existentes. `Asp.Versioning` lo integra con minimal APIs y OpenAPI.
21
+
22
+ **Correcto:**
23
+ ```csharp
24
+ builder.Services.AddApiVersioning(o => o.DefaultApiVersion = new ApiVersion(1, 0));
25
+ var v1 = app.NewVersionedApi().MapGroup("/api/v{version:apiVersion}").HasApiVersion(1.0);
26
+ v1.MapGet("/orders", GetOrders);
27
+ ```
28
+
29
+ ## 9.3 Consistent Errors with ProblemDetails — MEDIUM
30
+
31
+ `ProblemDetails` (RFC 9457) da un formato de error estándar y machine-readable en toda la API.
32
+
33
+ **Correcto:**
34
+ ```csharp
35
+ builder.Services.AddProblemDetails();
36
+ app.UseExceptionHandler();
37
+ // en un handler:
38
+ return TypedResults.Problem(title: "Order not found", statusCode: StatusCodes.Status404NotFound);
39
+ ```
40
+
41
+ ## 9.4 Correct Status Codes via TypedResults — MEDIUM
42
+
43
+ `TypedResults` devuelve el status code correcto de forma tipada y mejora los metadatos de OpenAPI.
44
+
45
+ **Correcto:**
46
+ ```csharp
47
+ app.MapPost("/orders", async (CreateOrder cmd, IOrderService svc) =>
48
+ {
49
+ var order = await svc.CreateAsync(cmd);
50
+ return TypedResults.Created($"/orders/{order.Id}", order); // 201 + Location
51
+ });
52
+ ```
53
+
54
+ ## 9.5 Pagination & Idempotency for Writes — MEDIUM
55
+
56
+ Paginar colecciones evita respuestas ilimitadas; una Idempotency-Key hace que un POST repetido (reintento de red) no duplique el recurso.
57
+
58
+ **Correcto:**
59
+ ```csharp
60
+ app.MapGet("/orders", (int page = 1, int pageSize = 20) => /* Skip/Take con límites */ );
61
+ app.MapPost("/payments", ([FromHeader(Name = "Idempotency-Key")] string key, Payment p) =>
62
+ /* deduplicar por key antes de procesar */ );
63
+ ```
64
+
65
+ ## 9.6 Built-in OpenAPI — LOW
66
+
67
+ `Microsoft.AspNetCore.OpenApi` genera el documento OpenAPI sin dependencias externas.
68
+
69
+ **Correcto:**
70
+ ```csharp
71
+ builder.Services.AddOpenApi();
72
+ app.MapOpenApi(); // /openapi/v1.json
73
+ ```
74
+
75
+ _Ref: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/apiversioning · https://learn.microsoft.com/en-us/aspnet/core/web-api/handle-errors · https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview · https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/responses_
@@ -0,0 +1,62 @@
1
+ # 1. Architecture (CRITICAL)
2
+
3
+ ## 1.1 Organize by Feature, Not Technical Layers — CRITICAL
4
+
5
+ Carpetas por feature (vertical slice) → cohesión alta y cambios localizados; las carpetas por capa técnica dispersan un cambio en 5 sitios.
6
+
7
+ **Incorrecto** — carpetas técnicas globales:
8
+ ```csharp
9
+ // Controllers/, Services/, Repositories/, Dtos/ ... un cambio toca 4 carpetas
10
+ ```
11
+ **Correcto** — un slice por feature:
12
+ ```csharp
13
+ // Features/Orders/{ CreateOrder.cs, GetOrder.cs, OrdersEndpoints.cs, OrderDto.cs }
14
+ // Features/Users/{ RegisterUser.cs, UsersEndpoints.cs, UserDto.cs }
15
+ ```
16
+
17
+ ## 1.2 Thin Endpoints — CRITICAL
18
+
19
+ El endpoint parsea, delega y forma la respuesta. Sin lógica de negocio: así es testeable y reutilizable desde otros transportes.
20
+
21
+ **Incorrecto:**
22
+ ```csharp
23
+ app.MapPost("/orders", async (CreateOrderDto dto, AppDbContext db) =>
24
+ {
25
+ if (dto.Items.Count == 0) return Results.BadRequest(); // negocio en el endpoint
26
+ var total = dto.Items.Sum(i => i.Price * i.Qty); // negocio en el endpoint
27
+ db.Orders.Add(new Order { Total = total });
28
+ await db.SaveChangesAsync();
29
+ return Results.Ok();
30
+ });
31
+ ```
32
+ **Correcto:**
33
+ ```csharp
34
+ app.MapPost("/orders", async (CreateOrderRequest req, CreateOrderHandler handler, CancellationToken ct) =>
35
+ {
36
+ var result = await handler.HandleAsync(req, ct);
37
+ return result.IsSuccess ? Results.Created($"/orders/{result.Value.Id}", result.Value) : result.ToProblem();
38
+ });
39
+ ```
40
+
41
+ ## 1.3 Dependencies Point Inward / Slices Independent — HIGH
42
+
43
+ El dominio no conoce infraestructura (Clean Architecture): el handler depende de una abstracción `IOrderRepository`, no de `AppDbContext`. Entre slices, sin acoplamiento directo; coordinar vía eventos de dominio o un mediador.
44
+
45
+ ## 1.4 Single Responsibility per Handler — HIGH
46
+
47
+ Un handler resuelve un caso de uso. `CreateOrderHandler` no envía emails ni cobra: emite un evento y otro handler reacciona. Mejora testabilidad y limita el blast radius de cada cambio.
48
+
49
+ ## 1.5 Program.cs as Composition Root Only — MEDIUM-HIGH
50
+
51
+ `Program.cs` solo registra servicios y arma el pipeline; nada de lógica de negocio. Mover el wiring a extensiones (`builder.Services.AddOrdersFeature()`) mantiene el root legible.
52
+
53
+ ```csharp
54
+ var builder = WebApplication.CreateBuilder(args);
55
+ builder.Services.AddOrdersFeature().AddAuthFeature();
56
+ var app = builder.Build();
57
+ app.UseExceptionHandler().UseAuthorization();
58
+ app.MapOrdersEndpoints();
59
+ app.Run();
60
+ ```
61
+
62
+ _Ref: https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures · https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/min-api-filters_
@@ -0,0 +1,62 @@
1
+ # 3. Async & Concurrency (HIGH)
2
+
3
+ ## 3.1 Async All the Way — CRITICAL
4
+
5
+ `.Result`, `.Wait()` y `.GetAwaiter().GetResult()` bloquean el hilo: deadlocks y thread-pool starvation bajo carga. Propagar `async`/`await` de punta a punta.
6
+
7
+ **Incorrecto:**
8
+ ```csharp
9
+ public Order Get(int id) => repo.GetAsync(id).Result; // bloquea el thread-pool
10
+ ```
11
+ **Correcto:**
12
+ ```csharp
13
+ public async Task<Order> GetAsync(int id, CancellationToken ct) => await repo.GetAsync(id, ct);
14
+ ```
15
+
16
+ ## 3.2 Accept and Propagate CancellationToken — HIGH
17
+
18
+ El token aborta el trabajo cuando el cliente corta la conexión, liberando hilos y conexiones de DB. Pasarlo a cada llamada async, hasta EF Core y `HttpClient`.
19
+
20
+ **Correcto:**
21
+ ```csharp
22
+ app.MapGet("/orders/{id:int}", async (int id, AppDbContext db, CancellationToken ct) =>
23
+ await db.Orders.FirstOrDefaultAsync(o => o.Id == id, ct) is { } o ? Results.Ok(o) : Results.NotFound());
24
+ ```
25
+
26
+ ## 3.3 No async void — HIGH
27
+
28
+ `async void` no se puede esperar y sus excepciones tumban el proceso. Usar `async Task`. La única excepción son los event handlers de UI/eventos.
29
+
30
+ **Incorrecto:**
31
+ ```csharp
32
+ public async void Process() => await DoWorkAsync(); // excepción => crash no observable
33
+ ```
34
+ **Correcto:**
35
+ ```csharp
36
+ public async Task ProcessAsync(CancellationToken ct) => await DoWorkAsync(ct);
37
+ ```
38
+
39
+ ## 3.4 ConfigureAwait(false) in Libraries — MEDIUM-HIGH
40
+
41
+ En código de librería reutilizable, `ConfigureAwait(false)` evita volver al contexto capturado y previene deadlocks en hosts con `SynchronizationContext`. En apps ASP.NET Core (sin contexto) no hace falta.
42
+
43
+ ```csharp
44
+ var data = await httpClient.GetStringAsync(url, ct).ConfigureAwait(false);
45
+ ```
46
+
47
+ ## 3.5 Task.WhenAll for Parallel Independent Work — MEDIUM-HIGH
48
+
49
+ Operaciones independientes en paralelo en vez de secuencial. No compartir un `DbContext` entre tasks concurrentes (no es thread-safe): usar un scope/contexto por task.
50
+
51
+ ```csharp
52
+ var (user, prefs) = (await Task.WhenAll(GetUserAsync(id, ct), GetPrefsAsync(id, ct))) switch
53
+ {
54
+ var r => (r[0], r[1])
55
+ };
56
+ ```
57
+
58
+ ## 3.6 No Fire-and-Forget Without Error Handling — HIGH
59
+
60
+ Un `Task` descartado traga excepciones y puede morir al reciclar el proceso. Para trabajo en background usar un `BackgroundService` / cola, con logging del error.
61
+
62
+ _Ref: https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/async-scenarios · https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern_
@@ -0,0 +1,69 @@
1
+ # 7. Database & EF Core (MEDIUM-HIGH)
2
+
3
+ ## 7.1 Avoid N+1 with Include or Projection — HIGH
4
+
5
+ Acceder a relaciones en un loop dispara una query por fila. Usar `Include` o proyectar a un DTO con `Select` resuelve todo en una sola query.
6
+
7
+ **Incorrecto:**
8
+ ```csharp
9
+ var orders = await db.Orders.ToListAsync();
10
+ foreach (var o in orders) o.Customer = await db.Customers.FindAsync(o.CustomerId); // N+1
11
+ ```
12
+ **Correcto:**
13
+ ```csharp
14
+ var orders = await db.Orders
15
+ .Select(o => new OrderDto(o.Id, o.Customer.Name, o.Total))
16
+ .ToListAsync();
17
+ ```
18
+
19
+ ## 7.2 AsNoTracking for Read-Only Queries — HIGH
20
+
21
+ El change tracker añade overhead innecesario en lecturas. `AsNoTracking()` evita las snapshots y reduce allocations.
22
+
23
+ **Correcto:**
24
+ ```csharp
25
+ var products = await db.Products.AsNoTracking().Where(p => p.Active).ToListAsync();
26
+ ```
27
+
28
+ ## 7.3 DbContext is the Unit of Work — MEDIUM-HIGH
29
+
30
+ `DbContext` ya implementa Unit of Work + Repository. Envolverlo en un repositorio genérico oculta `Include`, proyecciones y `SaveChanges`, añadiendo abstracción sin valor.
31
+
32
+ **Incorrecto:**
33
+ ```csharp
34
+ public class GenericRepository<T> { Task<T> GetById(int id); /* envuelve DbSet sin necesidad */ }
35
+ ```
36
+ **Correcto:**
37
+ ```csharp
38
+ // Inyectar AppDbContext (o un servicio de dominio que lo use) directamente
39
+ public sealed class OrderService(AppDbContext db) { /* usa db.Orders, db.SaveChangesAsync */ }
40
+ ```
41
+
42
+ ## 7.4 Versioned Migrations, Never EnsureCreated in Prod — MEDIUM-HIGH
43
+
44
+ `EnsureCreated()` no usa migraciones y no es upgradeable: hay que migrar el schema versionado con la CLI de EF.
45
+
46
+ **Incorrecto:**
47
+ ```csharp
48
+ await db.Database.EnsureCreatedAsync(); // no migrable, pierde historial de schema
49
+ ```
50
+ **Correcto:**
51
+ ```bash
52
+ dotnet ef migrations add AddOrderIndex
53
+ dotnet ef database update # en deploy: db.Database.MigrateAsync()
54
+ ```
55
+
56
+ ## 7.5 Connection Resiliency & Split Queries — MEDIUM
57
+
58
+ Habilitar reintentos ante fallos transitorios y `AsSplitQuery()` para `Include` grandes evita la explosión cartesiana de un único JOIN.
59
+
60
+ **Correcto:**
61
+ ```csharp
62
+ builder.Services.AddDbContext<AppDbContext>(o =>
63
+ o.UseNpgsql(conn, npgsql => npgsql.EnableRetryOnFailure()));
64
+
65
+ var blogs = await db.Blogs.Include(b => b.Posts).Include(b => b.Tags)
66
+ .AsSplitQuery().ToListAsync();
67
+ ```
68
+
69
+ _Ref: https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager-loading · https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations · https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency_