@polymorphism-tech/morph-spec 4.15.0 → 4.17.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 (100) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +4 -0
  3. package/bin/task-manager.js +999 -962
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/agents/ai-agents/vector-search-expert.md +247 -233
  8. package/framework/agents/backend/ms-agent-expert.md +2 -1
  9. package/framework/agents/backend/neon-expert.md +397 -0
  10. package/framework/agents/frontend/gsap-animator.md +131 -0
  11. package/framework/agents.json +1175 -1077
  12. package/framework/commands/morph-preflight.md +200 -192
  13. package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
  14. package/framework/rules/gsap-standards.md +13 -0
  15. package/framework/rules/neon-standards.md +25 -0
  16. package/framework/skills/level-0-meta/mcp-registry.json +1 -1
  17. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +472 -472
  18. package/framework/skills/level-0-meta/morph-init/SKILL.md +30 -11
  19. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +5 -5
  20. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +5 -5
  21. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +7 -7
  22. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +24 -24
  23. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +12 -12
  24. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +9 -9
  25. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
  26. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
  27. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -143
  28. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
  29. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  30. package/framework/standards/STANDARDS.json +184 -107
  31. package/framework/standards/ai-agents/production.md +1 -0
  32. package/framework/standards/ai-agents/setup.md +3 -2
  33. package/framework/standards/ai-agents/workflows.md +1 -0
  34. package/framework/standards/architecture/vertical-slice/vertical-slice.md +4 -4
  35. package/framework/standards/backend/database/postgresql/database.md +58 -78
  36. package/framework/standards/backend/dotnet/scalar-ui.md +172 -172
  37. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -274
  38. package/framework/standards/backend/integrations/asaas/asaas-api.md +1 -1
  39. package/framework/standards/backend/integrations/clerk/clerk-auth.md +23 -21
  40. package/framework/standards/frontend/design-system/animations.md +9 -0
  41. package/framework/standards/frontend/gsap/gsap-core.md +463 -0
  42. package/framework/standards/frontend/gsap/gsap-plugins.md +294 -0
  43. package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +318 -0
  44. package/framework/standards/frontend/nextjs/motion-patterns.md +1 -0
  45. package/framework/standards/frontend/nextjs/nextjs-patterns.md +47 -37
  46. package/framework/standards/infrastructure/docker/easypanel-deploy.md +4 -8
  47. package/framework/standards/infrastructure/neon/neon-pgrag.md +239 -0
  48. package/framework/standards/infrastructure/neon/neon-pgvector.md +236 -0
  49. package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +310 -0
  50. package/framework/standards/infrastructure/neon/neon-setup.md +186 -0
  51. package/framework/standards/integration/mcp/mcp-tools.md +32 -34
  52. package/framework/templates/README.md +18 -18
  53. package/framework/templates/REGISTRY.json +49 -96
  54. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  55. package/framework/templates/code/dotnet/backend/repository.cs +148 -148
  56. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  57. package/framework/templates/code/dotnet/database/vector-entity.cs +93 -0
  58. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +1 -1
  59. package/framework/templates/code/sql/{supabase-migration.sql → postgresql-migration.sql} +8 -17
  60. package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +10 -13
  61. package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +8 -8
  62. package/framework/templates/code/typescript/contracts.ts +167 -168
  63. package/framework/templates/context/CONTEXT.md +3 -3
  64. package/framework/templates/docs/proposal.md +6 -6
  65. package/framework/templates/docs/ui-components.md +1 -1
  66. package/framework/templates/feature/decisions.md +121 -121
  67. package/framework/templates/feature/recap.md +145 -145
  68. package/framework/templates/feature/tasks.md +199 -199
  69. package/framework/templates/frontend/nextjs/gsap-scroll-section.tsx.hbs +44 -0
  70. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -67
  71. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  72. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  73. package/framework/templates/infrastructure/docker/easypanel.template.json +46 -54
  74. package/framework/templates/infrastructure/github/README.md +56 -56
  75. package/framework/templates/integrations/semantic-kernel-neon.cs +125 -0
  76. package/framework/workflows/README.md +1 -1
  77. package/framework/workflows/docs/full-morph.md +69 -69
  78. package/package.json +1 -1
  79. package/src/commands/agents/dispatch-agents.js +19 -15
  80. package/src/commands/project/setup-infra-cmd.js +1 -1
  81. package/src/commands/state/advance-phase.js +5 -0
  82. package/src/commands/tasks/task.js +3 -0
  83. package/src/core/state/state-manager.js +45 -1
  84. package/src/core/templates/template-registry.js +1 -66
  85. package/src/lib/detectors/index.js +2 -2
  86. package/src/lib/generators/architecture-scanner.js +640 -640
  87. package/src/lib/installers/mcp-installer.js +1 -1
  88. package/src/lib/tasks/task-classifier.js +15 -1
  89. package/src/lib/tasks/test-runner.js +5 -1
  90. package/src/lib/validators/content/content-validator.js +9 -10
  91. package/src/lib/validators/validation-runner.js +7 -5
  92. package/src/scripts/setup-infra.js +60 -30
  93. package/framework/agents/backend/supabase-expert.md +0 -256
  94. package/framework/rules/supabase-standards.md +0 -29
  95. package/framework/standards/infrastructure/supabase/mcp-setup.md +0 -142
  96. package/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
  97. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +0 -206
  98. package/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
  99. package/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
  100. package/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
@@ -1,274 +1,274 @@
1
- # VSA Handler Patterns — Complete Feature Slice Guide
2
-
3
- > **Scope:** dotnet-vsa
4
- > **Layer:** 0 (always load)
5
- > **Keywords:** vsa, handler, feature-slice, vertical-slice, minimal-api, endpoint
6
- > **Load When:** dotnet VSA projects
7
-
8
- ## Overview
9
-
10
- Each feature in VSA is a self-contained slice with 5 files in a feature folder. Handlers implement `IHandler<TRequest, TResponse>` (not MediatR). Dependencies use primary constructors. The data path is `Handler -> Repository -> UnitOfWork` with no service layer in between.
11
-
12
- ---
13
-
14
- ## IHandler\<TRequest, TResponse\>
15
-
16
- A single-method interface — intentionally minimal:
17
-
18
- ```csharp
19
- public interface IHandler<in TRequest, TResponse>
20
- {
21
- Task<TResponse> HandleAsync(TRequest command, CancellationToken cancellationToken);
22
- }
23
- ```
24
-
25
- **Why not MediatR:** No `IRequest`/`INotification` marker interfaces, no `Send()` indirection, no service locator. Handlers are resolved directly from DI by their interface type. Decorators (validation, logging) are applied via Scrutor, not MediatR pipeline behaviors.
26
-
27
- ---
28
-
29
- ## Feature Folder Structure (5 Files Per Slice)
30
-
31
- ```
32
- Features/
33
- BookFeature/
34
- CreateBook/
35
- CreateBookRequest.cs (sealed record — can be inline in handler)
36
- CreateBookResponse.cs (sealed record — can be inline in handler)
37
- CreateBookHandler.cs (business logic)
38
- CreateBookValidator.cs (FluentValidation)
39
- CreateBookEndpoint.cs (minimal API mapping)
40
- GetBookById/
41
- ...
42
- UpdateBook/
43
- ...
44
- DeleteBook/
45
- ...
46
- BookErrors.cs (shared error factory for the feature)
47
- ```
48
-
49
- Request/Response DTOs can be defined inline in the handler file for simple slices:
50
-
51
- ```csharp
52
- namespace ProjectName.Features.BookFeature.CreateBook;
53
-
54
- // Sealed records for DTOs — immutable, structural equality
55
- public sealed record CreateBookRequest(
56
- string Title, string Author, string ISBN, decimal Price, int PublishedYear);
57
-
58
- public sealed record CreateBookResponse(
59
- Guid Id, string Title, string Author, string ISBN, decimal Price, int PublishedYear);
60
- ```
61
-
62
- ---
63
-
64
- ## Complete Handler Example
65
-
66
- ```csharp
67
- namespace ProjectName.Features.BookFeature.CreateBook;
68
-
69
- using ProjectName.Abstractions;
70
- using ProjectName.Entities;
71
- using ProjectName.Repository;
72
-
73
- public sealed record CreateBookRequest(
74
- string Title, string Author, string ISBN, decimal Price, int PublishedYear);
75
-
76
- public sealed record CreateBookResponse(
77
- Guid Id, string Title, string Author, string ISBN, decimal Price, int PublishedYear);
78
-
79
- // Primary constructor for DI — no field declarations needed
80
- public sealed class CreateBookHandler(
81
- IRepository<Book> bookRepo,
82
- IUnitOfWork unitOfWork) : IHandler<CreateBookRequest, Result<CreateBookResponse>>
83
- {
84
- public async Task<Result<CreateBookResponse>> HandleAsync(
85
- CreateBookRequest command, CancellationToken cancellationToken)
86
- {
87
- var book = new Book
88
- {
89
- Id = Guid.CreateVersion7(), // .NET 9+ — time-sortable UUID
90
- Title = command.Title,
91
- Author = command.Author,
92
- ISBN = command.ISBN,
93
- Price = command.Price,
94
- PublishedYear = command.PublishedYear
95
- };
96
-
97
- await bookRepo.AddAsync(book, cancellationToken);
98
- await unitOfWork.CommitAsync(cancellationToken);
99
-
100
- return Result.Success(new CreateBookResponse(
101
- book.Id, book.Title, book.Author, book.ISBN, book.Price, book.PublishedYear));
102
- }
103
- }
104
- ```
105
-
106
- ### Key Patterns
107
-
108
- - **`sealed class`** — handlers are never inherited
109
- - **`sealed record`** — DTOs are immutable value types
110
- - **Primary constructors** — `(IRepository<Book> bookRepo, IUnitOfWork unitOfWork)` replaces field + constructor boilerplate
111
- - **`Guid.CreateVersion7()`** — time-sortable, index-friendly UUID (not `Guid.NewGuid()`)
112
- - **`CancellationToken`** — passed through every async call
113
- - **`Result<T>` return** — never throw for expected failures
114
-
115
- ---
116
-
117
- ## Complete Endpoint Example
118
-
119
- ```csharp
120
- namespace ProjectName.Features.BookFeature.CreateBook;
121
-
122
- using ProjectName.Abstractions;
123
- using ProjectName.Constants;
124
- using ProjectName.Extensions;
125
-
126
- internal sealed class CreateBookEndpoint : IApiEndpoint
127
- {
128
- public void MapEndpoint(WebApplication app)
129
- {
130
- app.MapPost("books", async (
131
- IHandler<CreateBookRequest, Result<CreateBookResponse>> handler,
132
- CreateBookRequest command,
133
- CancellationToken cancellationToken) =>
134
- {
135
- var result = await handler.HandleAsync(command, cancellationToken);
136
- return result.Match(
137
- onSuccess: () => Results.Ok(result.Value),
138
- onFailure: error => Results.BadRequest(error));
139
- })
140
- .WithTags(ApiTags.Books)
141
- .Produces<CreateBookResponse>(StatusCodes.Status200OK)
142
- .Produces(StatusCodes.Status400BadRequest);
143
- }
144
- }
145
- ```
146
-
147
- ### Endpoint Conventions
148
-
149
- - **`internal sealed`** — endpoints are not public API
150
- - **`IApiEndpoint`** — single interface for auto-discovery
151
- - **Handler injected via DI** — resolved as `IHandler<TReq, TRes>` parameter
152
- - **`Match()` for responses** — never access `.Value` directly
153
- - **`.WithTags()`** — always tag for Scalar API docs
154
- - **`.Produces<T>()`** — always document response types
155
-
156
- ---
157
-
158
- ## IApiEndpoint Interface
159
-
160
- ```csharp
161
- public interface IApiEndpoint
162
- {
163
- void MapEndpoint(WebApplication app);
164
- }
165
- ```
166
-
167
- All endpoint classes implementing this interface are auto-discovered and mapped via `MapApiEndpoints()` in Program.cs.
168
-
169
- ---
170
-
171
- ## Repository + UnitOfWork (No Service Layer)
172
-
173
- Handlers call repositories directly. There is no service/manager layer:
174
-
175
- ```csharp
176
- public interface IRepository<T> where T : class
177
- {
178
- Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
179
- Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
180
- Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
181
- Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
182
- Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
183
- Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
184
- void RemoveRange(IEnumerable<T> entities);
185
- }
186
-
187
- public interface IUnitOfWork
188
- {
189
- Task<int> CommitAsync(CancellationToken cancellationToken);
190
- Task RollbackAsync();
191
- }
192
- ```
193
-
194
- **Pattern:** `Repository.AddAsync()` -> `UnitOfWork.CommitAsync()` — always commit explicitly.
195
-
196
- ---
197
-
198
- ## Feature Error Factory
199
-
200
- Each feature defines its own error constants:
201
-
202
- ```csharp
203
- // Features/BookFeature/BookErrors.cs
204
- public static class BookErrors
205
- {
206
- public static Error NotFound(Guid bookId) =>
207
- Error.NotFound("Book.NotFound", $"Book with ID '{bookId}' was not found.");
208
-
209
- public static Error DuplicateIsbn(string isbn) =>
210
- Error.Conflict("Book.DuplicateIsbn", $"A book with ISBN '{isbn}' already exists.");
211
-
212
- public static readonly Error InvalidPrice =
213
- Error.Failure("Book.InvalidPrice", "Book price must be greater than zero.");
214
- }
215
- ```
216
-
217
- Usage in handler:
218
-
219
- ```csharp
220
- var existing = await bookRepo.GetByIdAsync(command.BookId, cancellationToken);
221
- if (existing is null)
222
- return BookErrors.NotFound(command.BookId); // implicit conversion to Result<T>
223
- ```
224
-
225
- ---
226
-
227
- ## Program.cs DI Registration Order
228
-
229
- ```csharp
230
- // 1. OpenAPI
231
- builder.Services.AddOpenApi();
232
-
233
- // 2. Infrastructure (Repository, UnitOfWork, DbContext)
234
- builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
235
- builder.Services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));
236
- builder.Services.AddSQLDatabaseConfiguration(builder.Configuration);
237
-
238
- // 3. Endpoint auto-discovery
239
- builder.Services.RegisterApiEndpointsFromAssembly(Assembly.GetExecutingAssembly());
240
-
241
- // 4. Health checks
242
- builder.Services.AddHealthChecksConfiguration();
243
-
244
- // 5. Validators (MUST be before handlers)
245
- builder.Services.AddValidatorsFromAssembly(typeof(CreateBookValidator).Assembly);
246
-
247
- // 6. Handlers + Decorators (Scrutor auto-discovery)
248
- builder.Services.AddHandlersFromAssembly(typeof(Program).Assembly);
249
-
250
- // 7. Exception handling
251
- builder.Services.AddExceptionHandler<CustomExceptionHandler>()
252
- .AddProblemDetails();
253
- ```
254
-
255
- ---
256
-
257
- ## Anti-Patterns
258
-
259
- | Do NOT | Do Instead |
260
- |--------|------------|
261
- | Use MediatR `IRequest`/`Send()` | Use `IHandler<TReq, TRes>` resolved directly from DI |
262
- | Create a service/manager layer | Handler calls Repository + UnitOfWork directly |
263
- | Use `Guid.NewGuid()` | Use `Guid.CreateVersion7()` for time-sortable IDs |
264
- | Make handler classes non-sealed | Always `sealed class` |
265
- | Use mutable DTOs (class with setters) | Always `sealed record` with positional parameters |
266
- | Omit `CancellationToken` from async methods | Pass `CancellationToken` through every async call |
267
- | Put business logic in endpoints | Endpoints only map HTTP and call `Match()` |
268
- | Share handlers across features | Each feature has its own handler, even if similar |
269
- | Use constructor + field for DI | Use primary constructors: `class Handler(IDep dep)` |
270
- | Group by technical concern (Controllers/, Services/) | Group by feature: `Features/BookFeature/CreateBook/` |
271
-
272
- ---
273
-
274
- *MORPH-SPEC by Polymorphism Tech*
1
+ # VSA Handler Patterns — Complete Feature Slice Guide
2
+
3
+ > **Scope:** dotnet-vsa
4
+ > **Layer:** 0 (always load)
5
+ > **Keywords:** vsa, handler, feature-slice, vertical-slice, minimal-api, endpoint
6
+ > **Load When:** dotnet VSA projects
7
+
8
+ ## Overview
9
+
10
+ Each feature in VSA is a self-contained slice with 5 files in a feature folder. Handlers implement `IHandler<TRequest, TResponse>` (not MediatR). Dependencies use primary constructors. The data path is `Handler -> Repository -> UnitOfWork` with no service layer in between.
11
+
12
+ ---
13
+
14
+ ## IHandler\<TRequest, TResponse\>
15
+
16
+ A single-method interface — intentionally minimal:
17
+
18
+ ```csharp
19
+ public interface IHandler<in TRequest, TResponse>
20
+ {
21
+ Task<TResponse> HandleAsync(TRequest command, CancellationToken cancellationToken);
22
+ }
23
+ ```
24
+
25
+ **Why not MediatR:** No `IRequest`/`INotification` marker interfaces, no `Send()` indirection, no service locator. Handlers are resolved directly from DI by their interface type. Decorators (validation, logging) are applied via Scrutor, not MediatR pipeline behaviors.
26
+
27
+ ---
28
+
29
+ ## Feature Folder Structure (5 Files Per Slice)
30
+
31
+ ```
32
+ Features/
33
+ BookFeature/
34
+ CreateBook/
35
+ CreateBookRequest.cs (sealed record — can be inline in handler)
36
+ CreateBookResponse.cs (sealed record — can be inline in handler)
37
+ CreateBookHandler.cs (business logic)
38
+ CreateBookValidator.cs (FluentValidation)
39
+ CreateBookEndpoint.cs (minimal API mapping)
40
+ GetBookById/
41
+ ...
42
+ UpdateBook/
43
+ ...
44
+ DeleteBook/
45
+ ...
46
+ BookErrors.cs (shared error factory for the feature)
47
+ ```
48
+
49
+ Request/Response DTOs can be defined inline in the handler file for simple slices:
50
+
51
+ ```csharp
52
+ namespace ProjectName.Features.BookFeature.CreateBook;
53
+
54
+ // Sealed records for DTOs — immutable, structural equality
55
+ public sealed record CreateBookRequest(
56
+ string Title, string Author, string ISBN, decimal Price, int PublishedYear);
57
+
58
+ public sealed record CreateBookResponse(
59
+ Guid Id, string Title, string Author, string ISBN, decimal Price, int PublishedYear);
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Complete Handler Example
65
+
66
+ ```csharp
67
+ namespace ProjectName.Features.BookFeature.CreateBook;
68
+
69
+ using ProjectName.Abstractions;
70
+ using ProjectName.Entities;
71
+ using ProjectName.Repository;
72
+
73
+ public sealed record CreateBookRequest(
74
+ string Title, string Author, string ISBN, decimal Price, int PublishedYear);
75
+
76
+ public sealed record CreateBookResponse(
77
+ Guid Id, string Title, string Author, string ISBN, decimal Price, int PublishedYear);
78
+
79
+ // Primary constructor for DI — no field declarations needed
80
+ public sealed class CreateBookHandler(
81
+ IRepository<Book> bookRepo,
82
+ IUnitOfWork unitOfWork) : IHandler<CreateBookRequest, Result<CreateBookResponse>>
83
+ {
84
+ public async Task<Result<CreateBookResponse>> HandleAsync(
85
+ CreateBookRequest command, CancellationToken cancellationToken)
86
+ {
87
+ var book = new Book
88
+ {
89
+ Id = Guid.CreateVersion7(), // .NET 9+ — time-sortable UUID
90
+ Title = command.Title,
91
+ Author = command.Author,
92
+ ISBN = command.ISBN,
93
+ Price = command.Price,
94
+ PublishedYear = command.PublishedYear
95
+ };
96
+
97
+ await bookRepo.AddAsync(book, cancellationToken);
98
+ await unitOfWork.CommitAsync(cancellationToken);
99
+
100
+ return Result.Success(new CreateBookResponse(
101
+ book.Id, book.Title, book.Author, book.ISBN, book.Price, book.PublishedYear));
102
+ }
103
+ }
104
+ ```
105
+
106
+ ### Key Patterns
107
+
108
+ - **`sealed class`** — handlers are never inherited
109
+ - **`sealed record`** — DTOs are immutable value types
110
+ - **Primary constructors** — `(IRepository<Book> bookRepo, IUnitOfWork unitOfWork)` replaces field + constructor boilerplate
111
+ - **`Guid.CreateVersion7()`** — time-sortable, index-friendly UUID (not `Guid.NewGuid()`)
112
+ - **`CancellationToken`** — passed through every async call
113
+ - **`Result<T>` return** — never throw for expected failures
114
+
115
+ ---
116
+
117
+ ## Complete Endpoint Example
118
+
119
+ ```csharp
120
+ namespace ProjectName.Features.BookFeature.CreateBook;
121
+
122
+ using ProjectName.Abstractions;
123
+ using ProjectName.Constants;
124
+ using ProjectName.Extensions;
125
+
126
+ internal sealed class CreateBookEndpoint : IApiEndpoint
127
+ {
128
+ public void MapEndpoint(IEndpointRouteBuilder app)
129
+ {
130
+ app.MapPost("books", async (
131
+ IHandler<CreateBookRequest, Result<CreateBookResponse>> handler,
132
+ CreateBookRequest command,
133
+ CancellationToken cancellationToken) =>
134
+ {
135
+ var result = await handler.HandleAsync(command, cancellationToken);
136
+ return result.Match(
137
+ onSuccess: () => Results.Ok(result.Value),
138
+ onFailure: error => Results.BadRequest(error));
139
+ })
140
+ .WithTags(ApiTags.Books)
141
+ .Produces<CreateBookResponse>(StatusCodes.Status200OK)
142
+ .Produces(StatusCodes.Status400BadRequest);
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### Endpoint Conventions
148
+
149
+ - **`internal sealed`** — endpoints are not public API
150
+ - **`IApiEndpoint`** — single interface for auto-discovery
151
+ - **Handler injected via DI** — resolved as `IHandler<TReq, TRes>` parameter
152
+ - **`Match()` for responses** — never access `.Value` directly
153
+ - **`.WithTags()`** — always tag for Scalar API docs
154
+ - **`.Produces<T>()`** — always document response types
155
+
156
+ ---
157
+
158
+ ## IApiEndpoint Interface
159
+
160
+ ```csharp
161
+ public interface IApiEndpoint
162
+ {
163
+ void MapEndpoint(IEndpointRouteBuilder app);
164
+ }
165
+ ```
166
+
167
+ All endpoint classes implementing this interface are auto-discovered and mapped via `MapApiEndpoints()` in Program.cs.
168
+
169
+ ---
170
+
171
+ ## Repository + UnitOfWork (No Service Layer)
172
+
173
+ Handlers call repositories directly. There is no service/manager layer:
174
+
175
+ ```csharp
176
+ public interface IRepository<T> where T : class
177
+ {
178
+ Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
179
+ Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
180
+ Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
181
+ Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
182
+ Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
183
+ Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
184
+ void RemoveRange(IEnumerable<T> entities);
185
+ }
186
+
187
+ public interface IUnitOfWork
188
+ {
189
+ Task<int> CommitAsync(CancellationToken cancellationToken);
190
+ Task RollbackAsync(CancellationToken cancellationToken = default);
191
+ }
192
+ ```
193
+
194
+ **Pattern:** `Repository.AddAsync()` -> `UnitOfWork.CommitAsync()` — always commit explicitly.
195
+
196
+ ---
197
+
198
+ ## Feature Error Factory
199
+
200
+ Each feature defines its own error constants:
201
+
202
+ ```csharp
203
+ // Features/BookFeature/BookErrors.cs
204
+ public static class BookErrors
205
+ {
206
+ public static Error NotFound(Guid bookId) =>
207
+ Error.NotFound("Book.NotFound", $"Book with ID '{bookId}' was not found.");
208
+
209
+ public static Error DuplicateIsbn(string isbn) =>
210
+ Error.Conflict("Book.DuplicateIsbn", $"A book with ISBN '{isbn}' already exists.");
211
+
212
+ public static readonly Error InvalidPrice =
213
+ Error.Failure("Book.InvalidPrice", "Book price must be greater than zero.");
214
+ }
215
+ ```
216
+
217
+ Usage in handler:
218
+
219
+ ```csharp
220
+ var existing = await bookRepo.GetByIdAsync(command.BookId, cancellationToken);
221
+ if (existing is null)
222
+ return BookErrors.NotFound(command.BookId); // implicit conversion to Result<T>
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Program.cs DI Registration Order
228
+
229
+ ```csharp
230
+ // 1. OpenAPI
231
+ builder.Services.AddOpenApi();
232
+
233
+ // 2. Infrastructure (Repository, UnitOfWork, DbContext)
234
+ builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
235
+ builder.Services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));
236
+ builder.Services.AddSQLDatabaseConfiguration(builder.Configuration);
237
+
238
+ // 3. Endpoint auto-discovery
239
+ builder.Services.RegisterApiEndpointsFromAssembly(Assembly.GetExecutingAssembly());
240
+
241
+ // 4. Health checks
242
+ builder.Services.AddHealthChecksConfiguration();
243
+
244
+ // 5. Validators (MUST be before handlers)
245
+ builder.Services.AddValidatorsFromAssembly(typeof(CreateBookValidator).Assembly);
246
+
247
+ // 6. Handlers + Decorators (Scrutor auto-discovery)
248
+ builder.Services.AddHandlersFromAssembly(typeof(Program).Assembly);
249
+
250
+ // 7. Exception handling
251
+ builder.Services.AddExceptionHandler<CustomExceptionHandler>()
252
+ .AddProblemDetails();
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Anti-Patterns
258
+
259
+ | Do NOT | Do Instead |
260
+ |--------|------------|
261
+ | Use MediatR `IRequest`/`Send()` | Use `IHandler<TReq, TRes>` resolved directly from DI |
262
+ | Create a service/manager layer | Handler calls Repository + UnitOfWork directly |
263
+ | Use `Guid.NewGuid()` | Use `Guid.CreateVersion7()` for time-sortable IDs |
264
+ | Make handler classes non-sealed | Always `sealed class` |
265
+ | Use mutable DTOs (class with setters) | Always `sealed record` with positional parameters |
266
+ | Omit `CancellationToken` from async methods | Pass `CancellationToken` through every async call |
267
+ | Put business logic in endpoints | Endpoints only map HTTP and call `Match()` |
268
+ | Share handlers across features | Each feature has its own handler, even if similar |
269
+ | Use constructor + field for DI | Use primary constructors: `class Handler(IDep dep)` |
270
+ | Group by technical concern (Controllers/, Services/) | Group by feature: `Features/BookFeature/CreateBook/` |
271
+
272
+ ---
273
+
274
+ *MORPH-SPEC by Polymorphism Tech*
@@ -122,7 +122,7 @@ export async function POST(req: Request) {
122
122
 
123
123
  ---
124
124
 
125
- ## Database Schema (Supabase)
125
+ ## Database Schema (PostgreSQL / Neon)
126
126
 
127
127
  ```sql
128
128
  -- Store payment records
@@ -221,27 +221,29 @@ export async function POST(req: Request) {
221
221
 
222
222
  ---
223
223
 
224
- ## Supabase Integration
225
-
226
- ### Sync Users to Supabase
227
-
228
- ```typescript
229
- // lib/clerk/sync-user.ts
230
- import { createClient } from '@supabase/supabase-js';
231
-
232
- async function handleUserCreated(userData: any) {
233
- const supabase = createClient(
234
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
235
- process.env.SUPABASE_SERVICE_ROLE_KEY!
236
- );
237
-
238
- await supabase.from('profiles').insert({
239
- clerk_user_id: userData.id,
240
- email: userData.email_addresses[0].email_address,
241
- first_name: userData.first_name,
242
- last_name: userData.last_name,
243
- avatar_url: userData.image_url
244
- });
224
+ ## Neon Integration
225
+
226
+ ### Sync Users to Neon PostgreSQL (via EF Core / Npgsql)
227
+
228
+ ```csharp
229
+ // Services/ClerkUserSyncService.cs
230
+ public sealed class ClerkUserSyncService(IDbContextFactory<AppDbContext> dbFactory)
231
+ {
232
+ public async Task HandleUserCreatedAsync(ClerkUserData userData, CancellationToken ct)
233
+ {
234
+ await using var db = await dbFactory.CreateDbContextAsync(ct);
235
+
236
+ db.Profiles.Add(new Profile
237
+ {
238
+ ClerkUserId = userData.Id,
239
+ Email = userData.EmailAddresses[0].EmailAddress,
240
+ FirstName = userData.FirstName,
241
+ LastName = userData.LastName,
242
+ AvatarUrl = userData.ImageUrl
243
+ });
244
+
245
+ await db.SaveChangesAsync(ct);
246
+ }
245
247
  }
246
248
  ```
247
249
 
@@ -146,6 +146,15 @@ else { <h2>@_title</h2> }
146
146
  - [ ] `@media (prefers-reduced-motion)` implemented
147
147
  - [ ] Shimmer for loading states
148
148
 
149
+ ## JavaScript Animation Libraries
150
+
151
+ For animations beyond CSS capabilities:
152
+
153
+ | Need | Library | Standard |
154
+ |------|---------|----------|
155
+ | Simple UI transitions (enter/exit, layout, gestures) | Framer Motion | `frontend/nextjs/motion-patterns.md` |
156
+ | Complex animations (ScrollTrigger, SVG morph, timelines) | GSAP | `frontend/gsap/gsap-core.md` |
157
+
149
158
  ---
150
159
 
151
160
  *MORPH-SPEC by Polymorphism Tech*